mirror of
https://github.com/libvirt/libvirt.git
synced 2025-02-25 18:55:26 -06:00
esx: Make storage pool lookup by name and UUID more robust
Don't rely on summary.url anymore, because its value is different between an esx:// and vpx:// connection. Use host.mountInfo.path instead. Don't fallback to lookup by UUID (actually lookup by absolute path) in esxVI_LookupDatastoreByName when lookup by name fails. Add a seperate function for this: esxVI_LookupDatastoreByAbsolutePath
This commit is contained in:
parent
e4938ce2f1
commit
5254546bba
@ -2193,8 +2193,8 @@ esxDomainDumpXML(virDomainPtr domain, int flags)
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (esxUtil_ParseDatastoreRelatedPath(vmPathName, &datastoreName,
|
if (esxUtil_ParseDatastorePath(vmPathName, &datastoreName, &directoryName,
|
||||||
&directoryName, &fileName) < 0) {
|
&fileName) < 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2572,8 +2572,8 @@ esxDomainDefineXML(virConnectPtr conn, const char *xml ATTRIBUTE_UNUSED)
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (esxUtil_ParseDatastoreRelatedPath(disk->src, &datastoreName,
|
if (esxUtil_ParseDatastorePath(disk->src, &datastoreName, &directoryName,
|
||||||
&directoryName, &fileName) < 0) {
|
&fileName) < 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,61 +196,64 @@ esxStoragePoolLookupByName(virConnectPtr conn, const char *name)
|
|||||||
esxPrivate *priv = conn->storagePrivateData;
|
esxPrivate *priv = conn->storagePrivateData;
|
||||||
esxVI_String *propertyNameList = NULL;
|
esxVI_String *propertyNameList = NULL;
|
||||||
esxVI_ObjectContent *datastore = NULL;
|
esxVI_ObjectContent *datastore = NULL;
|
||||||
esxVI_Boolean accessible = esxVI_Boolean_Undefined;
|
esxVI_DynamicProperty *dynamicProperty = NULL;
|
||||||
char *summaryUrl = NULL;
|
esxVI_DatastoreHostMount *datastoreHostMountList = NULL;
|
||||||
|
esxVI_DatastoreHostMount *datastoreHostMount = NULL;
|
||||||
char *suffix = NULL;
|
char *suffix = NULL;
|
||||||
int suffixLength;
|
int suffixLength;
|
||||||
char uuid_string[VIR_UUID_STRING_BUFLEN] = "00000000-00000000-0000-000000000000";
|
char uuid_string[VIR_UUID_STRING_BUFLEN] = "00000000-00000000-0000-000000000000";
|
||||||
unsigned char uuid[VIR_UUID_BUFLEN];
|
unsigned char uuid[VIR_UUID_BUFLEN];
|
||||||
char *realName = NULL;
|
|
||||||
virStoragePoolPtr pool = NULL;
|
virStoragePoolPtr pool = NULL;
|
||||||
|
|
||||||
if (esxVI_EnsureSession(priv->primary) < 0) {
|
if (esxVI_EnsureSession(priv->primary) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (esxVI_String_AppendValueListToList(&propertyNameList,
|
if (esxVI_String_AppendValueToList(&propertyNameList, "host") < 0 ||
|
||||||
"summary.accessible\0"
|
|
||||||
"summary.name\0"
|
|
||||||
"summary.url\0") < 0 ||
|
|
||||||
esxVI_LookupDatastoreByName(priv->primary, name,
|
esxVI_LookupDatastoreByName(priv->primary, name,
|
||||||
propertyNameList, &datastore,
|
propertyNameList, &datastore,
|
||||||
esxVI_Occurrence_RequiredItem) < 0 ||
|
esxVI_Occurrence_RequiredItem) < 0) {
|
||||||
esxVI_GetBoolean(datastore, "summary.accessible",
|
|
||||||
&accessible, esxVI_Occurrence_RequiredItem) < 0) {
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Datastores don't have a UUID. We can use the 'summary.url' property as
|
* Datastores don't have a UUID. We can use the 'host.mountInfo.path'
|
||||||
* source for a "UUID" on ESX, because the property value has this format:
|
* property as source for a "UUID" on ESX, because the property value has
|
||||||
|
* this format:
|
||||||
*
|
*
|
||||||
* summary.url = /vmfs/volumes/4b0beca7-7fd401f3-1d7f-000ae484a6a3
|
* host.mountInfo.path = /vmfs/volumes/4b0beca7-7fd401f3-1d7f-000ae484a6a3
|
||||||
* summary.url = /vmfs/volumes/b24b7a78-9d82b4f5 (short format)
|
* host.mountInfo.path = /vmfs/volumes/b24b7a78-9d82b4f5 (short format)
|
||||||
*
|
*
|
||||||
* The 'summary.url' property comes in two forms, with a complete "UUID"
|
* The 'host.mountInfo.path' property comes in two forms, with a complete
|
||||||
* and a short "UUID".
|
* "UUID" and a short "UUID".
|
||||||
*
|
*
|
||||||
* But this trailing "UUID" is not guaranteed to be there. On the other
|
* But this trailing "UUID" is not guaranteed to be there. On the other
|
||||||
* hand we already rely on another implementation detail of the ESX server:
|
* hand we already rely on another implementation detail of the ESX server:
|
||||||
* The object name of virtual machine contains an integer, we use that as
|
* The object name of virtual machine contains an integer, we use that as
|
||||||
* domain ID.
|
* domain ID.
|
||||||
*
|
|
||||||
* The 'summary.url' property of an inaccessible datastore is invalid.
|
|
||||||
*/
|
*/
|
||||||
/* FIXME: Need to handle this for a vpx:// connection */
|
for (dynamicProperty = datastore->propSet; dynamicProperty != NULL;
|
||||||
if (accessible == esxVI_Boolean_True && priv->host != NULL &&
|
dynamicProperty = dynamicProperty->_next) {
|
||||||
priv->host->productVersion & esxVI_ProductVersion_ESX) {
|
if (STREQ(dynamicProperty->name, "host")) {
|
||||||
if (esxVI_GetStringValue(datastore, "summary.url", &summaryUrl,
|
if (esxVI_DatastoreHostMount_CastListFromAnyType
|
||||||
esxVI_Occurrence_RequiredItem) < 0) {
|
(dynamicProperty->val, &datastoreHostMountList) < 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (datastoreHostMount = datastoreHostMountList; datastoreHostMount != NULL;
|
||||||
|
datastoreHostMount = datastoreHostMount->_next) {
|
||||||
|
if (STRNEQ(priv->primary->hostSystem->_reference->value,
|
||||||
|
datastoreHostMount->key->value)) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((suffix = STRSKIP(summaryUrl, "/vmfs/volumes/")) == NULL) {
|
if ((suffix = STRSKIP(datastoreHostMount->mountInfo->path,
|
||||||
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
|
"/vmfs/volumes/")) == NULL) {
|
||||||
_("Datastore URL '%s' has unexpected prefix, "
|
break;
|
||||||
"expecting '/vmfs/volumes/' prefix"), summaryUrl);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suffixLength = strlen(suffix);
|
suffixLength = strlen(suffix);
|
||||||
@ -266,8 +269,8 @@ esxStoragePoolLookupByName(virConnectPtr conn, const char *name)
|
|||||||
*/
|
*/
|
||||||
memcpy(uuid_string, suffix, suffixLength);
|
memcpy(uuid_string, suffix, suffixLength);
|
||||||
} else {
|
} else {
|
||||||
VIR_WARN("Datastore URL suffix '%s' has unexpected format, "
|
VIR_WARN("Datastore host mount path suffix '%s' has unexpected "
|
||||||
"cannot deduce a UUID from it", suffix);
|
"format, cannot deduce a UUID from it", suffix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,16 +281,12 @@ esxStoragePoolLookupByName(virConnectPtr conn, const char *name)
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (esxVI_GetStringValue(datastore, "summary.name", &realName,
|
pool = virGetStoragePool(conn, name, uuid);
|
||||||
esxVI_Occurrence_RequiredItem) < 0) {
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
pool = virGetStoragePool(conn, realName, uuid);
|
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
esxVI_String_Free(&propertyNameList);
|
esxVI_String_Free(&propertyNameList);
|
||||||
esxVI_ObjectContent_Free(&datastore);
|
esxVI_ObjectContent_Free(&datastore);
|
||||||
|
esxVI_DatastoreHostMount_Free(&datastoreHostMountList);
|
||||||
|
|
||||||
return pool;
|
return pool;
|
||||||
}
|
}
|
||||||
@ -301,6 +300,7 @@ esxStoragePoolLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
|
|||||||
esxVI_String *propertyNameList = NULL;
|
esxVI_String *propertyNameList = NULL;
|
||||||
esxVI_ObjectContent *datastore = NULL;
|
esxVI_ObjectContent *datastore = NULL;
|
||||||
char uuid_string[VIR_UUID_STRING_BUFLEN] = "";
|
char uuid_string[VIR_UUID_STRING_BUFLEN] = "";
|
||||||
|
char *absolutePath = NULL;
|
||||||
char *name = NULL;
|
char *name = NULL;
|
||||||
virStoragePoolPtr pool = NULL;
|
virStoragePoolPtr pool = NULL;
|
||||||
|
|
||||||
@ -309,7 +309,7 @@ esxStoragePoolLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert from UUID to datastore URL form by stripping the second '-':
|
* Convert UUID to 'host.mountInfo.path' form by stripping the second '-':
|
||||||
*
|
*
|
||||||
* <---- 14 ----><-------- 22 --------> <---- 13 ---><-------- 22 -------->
|
* <---- 14 ----><-------- 22 --------> <---- 13 ---><-------- 22 -------->
|
||||||
* 4b0beca7-7fd4-01f3-1d7f-000ae484a6a3 -> 4b0beca7-7fd401f3-1d7f-000ae484a6a3
|
* 4b0beca7-7fd4-01f3-1d7f-000ae484a6a3 -> 4b0beca7-7fd401f3-1d7f-000ae484a6a3
|
||||||
@ -317,14 +317,15 @@ esxStoragePoolLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
|
|||||||
virUUIDFormat(uuid, uuid_string);
|
virUUIDFormat(uuid, uuid_string);
|
||||||
memmove(uuid_string + 13, uuid_string + 14, 22 + 1);
|
memmove(uuid_string + 13, uuid_string + 14, 22 + 1);
|
||||||
|
|
||||||
/*
|
if (virAsprintf(&absolutePath, "/vmfs/volumes/%s", uuid_string) < 0) {
|
||||||
* Use esxVI_LookupDatastoreByName because it also does try to match "UUID"
|
virReportOOMError();
|
||||||
* part of the 'summary.url' property if there is no name match.
|
goto cleanup;
|
||||||
*/
|
}
|
||||||
|
|
||||||
if (esxVI_String_AppendValueToList(&propertyNameList, "summary.name") < 0 ||
|
if (esxVI_String_AppendValueToList(&propertyNameList, "summary.name") < 0 ||
|
||||||
esxVI_LookupDatastoreByName(priv->primary, uuid_string,
|
esxVI_LookupDatastoreByAbsolutePath(priv->primary, absolutePath,
|
||||||
propertyNameList, &datastore,
|
propertyNameList, &datastore,
|
||||||
esxVI_Occurrence_OptionalItem) < 0) {
|
esxVI_Occurrence_OptionalItem) < 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,9 +339,16 @@ esxStoragePoolLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
|
|||||||
if (datastore == NULL && STREQ(uuid_string + 17, "-0000-000000000000")) {
|
if (datastore == NULL && STREQ(uuid_string + 17, "-0000-000000000000")) {
|
||||||
uuid_string[17] = '\0';
|
uuid_string[17] = '\0';
|
||||||
|
|
||||||
if (esxVI_LookupDatastoreByName(priv->primary, uuid_string,
|
VIR_FREE(absolutePath);
|
||||||
propertyNameList, &datastore,
|
|
||||||
esxVI_Occurrence_RequiredItem) < 0) {
|
if (virAsprintf(&absolutePath, "/vmfs/volumes/%s", uuid_string) < 0) {
|
||||||
|
virReportOOMError();
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esxVI_LookupDatastoreByAbsolutePath(priv->primary, absolutePath,
|
||||||
|
propertyNameList, &datastore,
|
||||||
|
esxVI_Occurrence_RequiredItem) < 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -348,7 +356,7 @@ esxStoragePoolLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
|
|||||||
if (datastore == NULL) {
|
if (datastore == NULL) {
|
||||||
virUUIDFormat(uuid, uuid_string);
|
virUUIDFormat(uuid, uuid_string);
|
||||||
|
|
||||||
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
|
ESX_VI_ERROR(VIR_ERR_NO_STORAGE_POOL,
|
||||||
_("Could not find datastore with UUID '%s'"),
|
_("Could not find datastore with UUID '%s'"),
|
||||||
uuid_string);
|
uuid_string);
|
||||||
|
|
||||||
@ -363,6 +371,7 @@ esxStoragePoolLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
|
|||||||
pool = virGetStoragePool(conn, name, uuid);
|
pool = virGetStoragePool(conn, name, uuid);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
VIR_FREE(absolutePath);
|
||||||
esxVI_String_Free(&propertyNameList);
|
esxVI_String_Free(&propertyNameList);
|
||||||
esxVI_ObjectContent_Free(&datastore);
|
esxVI_ObjectContent_Free(&datastore);
|
||||||
|
|
||||||
|
@ -274,12 +274,11 @@ esxUtil_ParseVirtualMachineIDString(const char *id_string, int *id)
|
|||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
esxUtil_ParseDatastoreRelatedPath(const char *datastoreRelatedPath,
|
esxUtil_ParseDatastorePath(const char *datastorePath, char **datastoreName,
|
||||||
char **datastoreName,
|
char **directoryName, char **fileName)
|
||||||
char **directoryName, char **fileName)
|
|
||||||
{
|
{
|
||||||
int result = -1;
|
int result = -1;
|
||||||
char *copyOfDatastoreRelatedPath = NULL;
|
char *copyOfDatastorePath = NULL;
|
||||||
char *tmp = NULL;
|
char *tmp = NULL;
|
||||||
char *saveptr = NULL;
|
char *saveptr = NULL;
|
||||||
char *preliminaryDatastoreName = NULL;
|
char *preliminaryDatastoreName = NULL;
|
||||||
@ -293,18 +292,17 @@ esxUtil_ParseDatastoreRelatedPath(const char *datastoreRelatedPath,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (esxVI_String_DeepCopyValue(©OfDatastoreRelatedPath,
|
if (esxVI_String_DeepCopyValue(©OfDatastorePath, datastorePath) < 0) {
|
||||||
datastoreRelatedPath) < 0) {
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Expected format: '[<datastore>] <path>' */
|
/* Expected format: '[<datastore>] <path>' */
|
||||||
if ((tmp = STRSKIP(copyOfDatastoreRelatedPath, "[")) == NULL ||
|
if ((tmp = STRSKIP(copyOfDatastorePath, "[")) == NULL ||
|
||||||
(preliminaryDatastoreName = strtok_r(tmp, "]", &saveptr)) == NULL ||
|
(preliminaryDatastoreName = strtok_r(tmp, "]", &saveptr)) == NULL ||
|
||||||
(directoryAndFileName = strtok_r(NULL, "", &saveptr)) == NULL) {
|
(directoryAndFileName = strtok_r(NULL, "", &saveptr)) == NULL) {
|
||||||
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
||||||
_("Datastore related path '%s' doesn't have expected format "
|
_("Datastore path '%s' doesn't have expected format "
|
||||||
"'[<datastore>] <path>'"), datastoreRelatedPath);
|
"'[<datastore>] <path>'"), datastorePath);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,8 +321,8 @@ esxUtil_ParseDatastoreRelatedPath(const char *datastoreRelatedPath,
|
|||||||
|
|
||||||
if (*separator == '\0') {
|
if (*separator == '\0') {
|
||||||
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
|
||||||
_("Datastore related path '%s' doesn't reference a file"),
|
_("Datastore path '%s' doesn't reference a file"),
|
||||||
datastoreRelatedPath);
|
datastorePath);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,7 +346,7 @@ esxUtil_ParseDatastoreRelatedPath(const char *datastoreRelatedPath,
|
|||||||
VIR_FREE(*fileName);
|
VIR_FREE(*fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
VIR_FREE(copyOfDatastoreRelatedPath);
|
VIR_FREE(copyOfDatastorePath);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -51,9 +51,8 @@ void esxUtil_FreeParsedUri(esxUtil_ParsedUri **parsedUri);
|
|||||||
|
|
||||||
int esxUtil_ParseVirtualMachineIDString(const char *id_string, int *id);
|
int esxUtil_ParseVirtualMachineIDString(const char *id_string, int *id);
|
||||||
|
|
||||||
int esxUtil_ParseDatastoreRelatedPath(const char *datastoreRelatedPath,
|
int esxUtil_ParseDatastorePath(const char *datastorePath, char **datastoreName,
|
||||||
char **datastoreName,
|
char **directoryName, char **fileName);
|
||||||
char **directoryName, char **fileName);
|
|
||||||
|
|
||||||
int esxUtil_ResolveHostname(const char *hostname,
|
int esxUtil_ResolveHostname(const char *hostname,
|
||||||
char *ipAddress, size_t ipAddress_length);
|
char *ipAddress, size_t ipAddress_length);
|
||||||
|
203
src/esx/esx_vi.c
203
src/esx/esx_vi.c
@ -2422,10 +2422,7 @@ esxVI_LookupDatastoreByName(esxVI_Context *ctx, const char *name,
|
|||||||
esxVI_String *completePropertyNameList = NULL;
|
esxVI_String *completePropertyNameList = NULL;
|
||||||
esxVI_ObjectContent *datastoreList = NULL;
|
esxVI_ObjectContent *datastoreList = NULL;
|
||||||
esxVI_ObjectContent *candidate = NULL;
|
esxVI_ObjectContent *candidate = NULL;
|
||||||
esxVI_DynamicProperty *dynamicProperty = NULL;
|
char *name_candidate;
|
||||||
esxVI_Boolean accessible = esxVI_Boolean_Undefined;
|
|
||||||
size_t offset = 14; /* = strlen("/vmfs/volumes/") */
|
|
||||||
int numInaccessibleDatastores = 0;
|
|
||||||
|
|
||||||
if (datastore == NULL || *datastore != NULL) {
|
if (datastore == NULL || *datastore != NULL) {
|
||||||
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
|
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
|
||||||
@ -2435,118 +2432,36 @@ esxVI_LookupDatastoreByName(esxVI_Context *ctx, const char *name,
|
|||||||
/* Get all datastores */
|
/* Get all datastores */
|
||||||
if (esxVI_String_DeepCopyList(&completePropertyNameList,
|
if (esxVI_String_DeepCopyList(&completePropertyNameList,
|
||||||
propertyNameList) < 0 ||
|
propertyNameList) < 0 ||
|
||||||
esxVI_String_AppendValueListToList(&completePropertyNameList,
|
esxVI_String_AppendValueToList(&completePropertyNameList,
|
||||||
"summary.accessible\0"
|
"summary.name") < 0 ||
|
||||||
"summary.name\0"
|
|
||||||
"summary.url\0") < 0 ||
|
|
||||||
esxVI_LookupDatastoreList(ctx, completePropertyNameList,
|
esxVI_LookupDatastoreList(ctx, completePropertyNameList,
|
||||||
&datastoreList) < 0) {
|
&datastoreList) < 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (datastoreList == NULL) {
|
|
||||||
if (occurrence == esxVI_Occurrence_OptionalItem) {
|
|
||||||
goto success;
|
|
||||||
} else {
|
|
||||||
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
||||||
_("No datastores available"));
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Search for a matching datastore */
|
/* Search for a matching datastore */
|
||||||
for (candidate = datastoreList; candidate != NULL;
|
for (candidate = datastoreList; candidate != NULL;
|
||||||
candidate = candidate->_next) {
|
candidate = candidate->_next) {
|
||||||
accessible = esxVI_Boolean_Undefined;
|
name_candidate = NULL;
|
||||||
|
|
||||||
for (dynamicProperty = candidate->propSet; dynamicProperty != NULL;
|
if (esxVI_GetStringValue(candidate, "summary.name", &name_candidate,
|
||||||
dynamicProperty = dynamicProperty->_next) {
|
esxVI_Occurrence_RequiredItem) < 0) {
|
||||||
if (STREQ(dynamicProperty->name, "summary.accessible")) {
|
|
||||||
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
||||||
esxVI_Type_Boolean) < 0) {
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
accessible = dynamicProperty->val->boolean;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accessible == esxVI_Boolean_Undefined) {
|
|
||||||
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
||||||
_("Got incomplete response while querying for the "
|
|
||||||
"datastore 'summary.accessible' property"));
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accessible == esxVI_Boolean_False) {
|
if (STREQ(name_candidate, name)) {
|
||||||
++numInaccessibleDatastores;
|
if (esxVI_ObjectContent_DeepCopy(datastore, candidate) < 0) {
|
||||||
}
|
goto cleanup;
|
||||||
|
|
||||||
for (dynamicProperty = candidate->propSet; dynamicProperty != NULL;
|
|
||||||
dynamicProperty = dynamicProperty->_next) {
|
|
||||||
if (STREQ(dynamicProperty->name, "summary.name")) {
|
|
||||||
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
||||||
esxVI_Type_String) < 0) {
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (STREQ(dynamicProperty->val->string, name)) {
|
|
||||||
if (esxVI_ObjectContent_DeepCopy(datastore,
|
|
||||||
candidate) < 0) {
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Found datastore with matching name */
|
|
||||||
goto success;
|
|
||||||
}
|
|
||||||
} else if (STREQ(dynamicProperty->name, "summary.url") &&
|
|
||||||
ctx->productVersion & esxVI_ProductVersion_ESX) {
|
|
||||||
if (accessible == esxVI_Boolean_False) {
|
|
||||||
/*
|
|
||||||
* The 'summary.url' property of an inaccessible datastore
|
|
||||||
* is invalid and cannot be used to identify the datastore.
|
|
||||||
*/
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
||||||
esxVI_Type_String) < 0) {
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! STRPREFIX(dynamicProperty->val->string,
|
|
||||||
"/vmfs/volumes/")) {
|
|
||||||
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
||||||
_("Datastore URL '%s' has unexpected prefix, "
|
|
||||||
"expecting '/vmfs/volumes/' prefix"),
|
|
||||||
dynamicProperty->val->string);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (STREQ(dynamicProperty->val->string + offset, name)) {
|
|
||||||
if (esxVI_ObjectContent_DeepCopy(datastore,
|
|
||||||
candidate) < 0) {
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Found datastore with matching URL suffix */
|
|
||||||
goto success;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Found datastore with matching name
|
||||||
|
goto success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (occurrence != esxVI_Occurrence_OptionalItem) {
|
if (*datastore == NULL && occurrence != esxVI_Occurrence_OptionalItem) {
|
||||||
if (numInaccessibleDatastores > 0) {
|
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
|
||||||
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
|
_("Could not find datastore with name '%s'"), name);
|
||||||
_("Could not find datastore '%s', maybe it's "
|
|
||||||
"inaccessible"), name);
|
|
||||||
} else {
|
|
||||||
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
||||||
_("Could not find datastore '%s'"), name);
|
|
||||||
}
|
|
||||||
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2561,6 +2476,94 @@ esxVI_LookupDatastoreByName(esxVI_Context *ctx, const char *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
esxVI_LookupDatastoreByAbsolutePath(esxVI_Context *ctx,
|
||||||
|
const char *absolutePath,
|
||||||
|
esxVI_String *propertyNameList,
|
||||||
|
esxVI_ObjectContent **datastore,
|
||||||
|
esxVI_Occurrence occurrence)
|
||||||
|
{
|
||||||
|
int result = -1;
|
||||||
|
esxVI_String *completePropertyNameList = NULL;
|
||||||
|
esxVI_ObjectContent *datastoreList = NULL;
|
||||||
|
esxVI_ObjectContent *candidate = NULL;
|
||||||
|
esxVI_DynamicProperty *dynamicProperty = NULL;
|
||||||
|
esxVI_DatastoreHostMount *datastoreHostMountList = NULL;
|
||||||
|
esxVI_DatastoreHostMount *datastoreHostMount = NULL;
|
||||||
|
|
||||||
|
if (datastore == NULL || *datastore != NULL) {
|
||||||
|
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get all datastores */
|
||||||
|
if (esxVI_String_DeepCopyList(&completePropertyNameList,
|
||||||
|
propertyNameList) < 0 ||
|
||||||
|
esxVI_String_AppendValueToList(&completePropertyNameList, "host") < 0 ||
|
||||||
|
esxVI_LookupDatastoreList(ctx, completePropertyNameList,
|
||||||
|
&datastoreList) < 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Search for a matching datastore */
|
||||||
|
for (candidate = datastoreList; candidate != NULL;
|
||||||
|
candidate = candidate->_next) {
|
||||||
|
esxVI_DatastoreHostMount_Free(&datastoreHostMountList);
|
||||||
|
|
||||||
|
for (dynamicProperty = candidate->propSet; dynamicProperty != NULL;
|
||||||
|
dynamicProperty = dynamicProperty->_next) {
|
||||||
|
if (STREQ(dynamicProperty->name, "host")) {
|
||||||
|
if (esxVI_DatastoreHostMount_CastListFromAnyType
|
||||||
|
(dynamicProperty->val, &datastoreHostMountList) < 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (datastoreHostMountList == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (datastoreHostMount = datastoreHostMountList;
|
||||||
|
datastoreHostMount != NULL;
|
||||||
|
datastoreHostMount = datastoreHostMount->_next) {
|
||||||
|
if (STRNEQ(ctx->hostSystem->_reference->value,
|
||||||
|
datastoreHostMount->key->value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (STRPREFIX(absolutePath, datastoreHostMount->mountInfo->path)) {
|
||||||
|
if (esxVI_ObjectContent_DeepCopy(datastore, candidate) < 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Found datastore with matching mount path */
|
||||||
|
goto success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*datastore == NULL && occurrence != esxVI_Occurrence_OptionalItem) {
|
||||||
|
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("Could not find datastore containing absolute path '%s'"),
|
||||||
|
absolutePath);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
success:
|
||||||
|
result = 0;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
esxVI_String_Free(&completePropertyNameList);
|
||||||
|
esxVI_ObjectContent_Free(&datastoreList);
|
||||||
|
esxVI_DatastoreHostMount_Free(&datastoreHostMountList);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
esxVI_LookupTaskInfoByTask(esxVI_Context *ctx,
|
esxVI_LookupTaskInfoByTask(esxVI_Context *ctx,
|
||||||
|
@ -364,6 +364,12 @@ int esxVI_LookupDatastoreByName(esxVI_Context *ctx, const char *name,
|
|||||||
esxVI_ObjectContent **datastore,
|
esxVI_ObjectContent **datastore,
|
||||||
esxVI_Occurrence occurrence);
|
esxVI_Occurrence occurrence);
|
||||||
|
|
||||||
|
int esxVI_LookupDatastoreByAbsolutePath(esxVI_Context *ctx,
|
||||||
|
const char *absolutePath,
|
||||||
|
esxVI_String *propertyNameList,
|
||||||
|
esxVI_ObjectContent **datastore,
|
||||||
|
esxVI_Occurrence occurrence);
|
||||||
|
|
||||||
int esxVI_LookupTaskInfoByTask(esxVI_Context *ctx,
|
int esxVI_LookupTaskInfoByTask(esxVI_Context *ctx,
|
||||||
esxVI_ManagedObjectReference *task,
|
esxVI_ManagedObjectReference *task,
|
||||||
esxVI_TaskInfo **taskInfo);
|
esxVI_TaskInfo **taskInfo);
|
||||||
|
@ -146,6 +146,12 @@ object ChoiceOption extends OptionType
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
object DatastoreHostMount
|
||||||
|
ManagedObjectReference key r
|
||||||
|
HostMountInfo mountInfo r
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
object DatastoreInfo
|
object DatastoreInfo
|
||||||
String name r
|
String name r
|
||||||
String url r
|
String url r
|
||||||
@ -251,6 +257,13 @@ object HostFileSystemVolume
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
object HostMountInfo
|
||||||
|
String path o
|
||||||
|
String accessMode r
|
||||||
|
Boolean accessible o
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
object HostNasVolume extends HostFileSystemVolume
|
object HostNasVolume extends HostFileSystemVolume
|
||||||
String remoteHost r
|
String remoteHost r
|
||||||
String remotePath r
|
String remotePath r
|
||||||
|
@ -1127,7 +1127,8 @@ additional_enum_features = { "ManagedEntityStatus" : Enum.FEATURE__ANY_TYPE
|
|||||||
"VirtualMachinePowerState" : Enum.FEATURE__ANY_TYPE }
|
"VirtualMachinePowerState" : Enum.FEATURE__ANY_TYPE }
|
||||||
|
|
||||||
|
|
||||||
additional_object_features = { "DatastoreInfo" : Object.FEATURE__ANY_TYPE | Object.FEATURE__DYNAMIC_CAST,
|
additional_object_features = { "DatastoreHostMount" : Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE,
|
||||||
|
"DatastoreInfo" : Object.FEATURE__ANY_TYPE | Object.FEATURE__DYNAMIC_CAST,
|
||||||
"Event" : Object.FEATURE__LIST,
|
"Event" : Object.FEATURE__LIST,
|
||||||
"FileInfo" : Object.FEATURE__DYNAMIC_CAST,
|
"FileInfo" : Object.FEATURE__DYNAMIC_CAST,
|
||||||
"FileQuery" : Object.FEATURE__DYNAMIC_CAST,
|
"FileQuery" : Object.FEATURE__DYNAMIC_CAST,
|
||||||
|
@ -749,8 +749,8 @@ esxVMX_AutodetectSCSIControllerModel(esxVI_Context *ctx,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (esxUtil_ParseDatastoreRelatedPath(def->src, &datastoreName,
|
if (esxUtil_ParseDatastorePath(def->src, &datastoreName, &directoryName,
|
||||||
&directoryName, &fileName) < 0) {
|
&fileName) < 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -986,19 +986,19 @@ esxVMX_GatherSCSIControllers(esxVI_Context *ctx, virDomainDefPtr def,
|
|||||||
|
|
||||||
|
|
||||||
char *
|
char *
|
||||||
esxVMX_AbsolutePathToDatastoreRelatedPath(esxVI_Context *ctx,
|
esxVMX_AbsolutePathToDatastorePath(esxVI_Context *ctx, const char *absolutePath)
|
||||||
const char *absolutePath)
|
|
||||||
{
|
{
|
||||||
bool success = false;
|
bool success = false;
|
||||||
char *copyOfAbsolutePath = NULL;
|
char *copyOfAbsolutePath = NULL;
|
||||||
char *tmp = NULL;
|
char *tmp = NULL;
|
||||||
char *saveptr = NULL;
|
char *saveptr = NULL;
|
||||||
char *datastoreRelatedPath = NULL;
|
esxVI_String *propertyNameList = NULL;
|
||||||
|
esxVI_ObjectContent *datastore = NULL;
|
||||||
|
|
||||||
|
char *datastorePath = NULL;
|
||||||
char *preliminaryDatastoreName = NULL;
|
char *preliminaryDatastoreName = NULL;
|
||||||
char *directoryAndFileName = NULL;
|
char *directoryAndFileName = NULL;
|
||||||
esxVI_DynamicProperty *dynamicProperty = NULL;
|
char *datastoreName = NULL;
|
||||||
esxVI_ObjectContent *datastore = NULL;
|
|
||||||
const char *datastoreName = NULL;
|
|
||||||
|
|
||||||
if (esxVI_String_DeepCopyValue(©OfAbsolutePath, absolutePath) < 0) {
|
if (esxVI_String_DeepCopyValue(©OfAbsolutePath, absolutePath) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1015,30 +1015,26 @@ esxVMX_AbsolutePathToDatastoreRelatedPath(esxVI_Context *ctx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ctx != NULL) {
|
if (ctx != NULL) {
|
||||||
if (esxVI_LookupDatastoreByName(ctx, preliminaryDatastoreName,
|
if (esxVI_String_AppendValueToList(&propertyNameList,
|
||||||
NULL, &datastore,
|
"summary.name") < 0 ||
|
||||||
esxVI_Occurrence_OptionalItem) < 0) {
|
esxVI_LookupDatastoreByAbsolutePath(ctx, absolutePath,
|
||||||
|
propertyNameList, &datastore,
|
||||||
|
esxVI_Occurrence_OptionalItem) < 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (datastore != NULL) {
|
if (datastore == NULL) {
|
||||||
for (dynamicProperty = datastore->propSet; dynamicProperty != NULL;
|
if (esxVI_LookupDatastoreByName(ctx, preliminaryDatastoreName,
|
||||||
dynamicProperty = dynamicProperty->_next) {
|
propertyNameList, &datastore,
|
||||||
if (STREQ(dynamicProperty->name, "summary.accessible")) {
|
esxVI_Occurrence_OptionalItem) < 0) {
|
||||||
/* Ignore it */
|
goto cleanup;
|
||||||
} else if (STREQ(dynamicProperty->name, "summary.name")) {
|
}
|
||||||
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
}
|
||||||
esxVI_Type_String) < 0) {
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
datastoreName = dynamicProperty->val->string;
|
if (datastore != NULL) {
|
||||||
break;
|
if (esxVI_GetStringValue(datastore, "summary.name", &datastoreName,
|
||||||
} else if (STREQ(dynamicProperty->name, "summary.url")) {
|
esxVI_Occurrence_RequiredItem)) {
|
||||||
/* Ignore it */
|
goto cleanup;
|
||||||
} else {
|
|
||||||
VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1053,7 +1049,7 @@ esxVMX_AbsolutePathToDatastoreRelatedPath(esxVI_Context *ctx,
|
|||||||
datastoreName = preliminaryDatastoreName;
|
datastoreName = preliminaryDatastoreName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (virAsprintf(&datastoreRelatedPath, "[%s] %s", datastoreName,
|
if (virAsprintf(&datastorePath, "[%s] %s", datastoreName,
|
||||||
directoryAndFileName) < 0) {
|
directoryAndFileName) < 0) {
|
||||||
virReportOOMError();
|
virReportOOMError();
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@ -1065,13 +1061,14 @@ esxVMX_AbsolutePathToDatastoreRelatedPath(esxVI_Context *ctx,
|
|||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (! success) {
|
if (! success) {
|
||||||
VIR_FREE(datastoreRelatedPath);
|
VIR_FREE(datastorePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
VIR_FREE(copyOfAbsolutePath);
|
VIR_FREE(copyOfAbsolutePath);
|
||||||
|
esxVI_String_Free(&propertyNameList);
|
||||||
esxVI_ObjectContent_Free(&datastore);
|
esxVI_ObjectContent_Free(&datastore);
|
||||||
|
|
||||||
return datastoreRelatedPath;
|
return datastorePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1088,7 +1085,7 @@ esxVMX_ParseFileName(esxVI_Context *ctx, const char *fileName,
|
|||||||
|
|
||||||
if (STRPREFIX(fileName, "/vmfs/volumes/")) {
|
if (STRPREFIX(fileName, "/vmfs/volumes/")) {
|
||||||
/* Found absolute path referencing a file inside a datastore */
|
/* Found absolute path referencing a file inside a datastore */
|
||||||
return esxVMX_AbsolutePathToDatastoreRelatedPath(ctx, fileName);
|
return esxVMX_AbsolutePathToDatastorePath(ctx, fileName);
|
||||||
} else if (STRPREFIX(fileName, "/")) {
|
} else if (STRPREFIX(fileName, "/")) {
|
||||||
/* Found absolute path referencing a file outside a datastore */
|
/* Found absolute path referencing a file outside a datastore */
|
||||||
src = strdup(fileName);
|
src = strdup(fileName);
|
||||||
@ -2625,8 +2622,8 @@ esxVMX_FormatFileName(esxVI_Context *ctx ATTRIBUTE_UNUSED, const char *src)
|
|||||||
|
|
||||||
if (STRPREFIX(src, "[")) {
|
if (STRPREFIX(src, "[")) {
|
||||||
/* Found potential datastore related path */
|
/* Found potential datastore related path */
|
||||||
if (esxUtil_ParseDatastoreRelatedPath(src, &datastoreName,
|
if (esxUtil_ParseDatastorePath(src, &datastoreName, &directoryName,
|
||||||
&directoryName, &fileName) < 0) {
|
&fileName) < 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,8 +56,7 @@ esxVMX_GatherSCSIControllers(esxVI_Context *ctx, virDomainDefPtr def,
|
|||||||
int virtualDev[4], bool present[4]);
|
int virtualDev[4], bool present[4]);
|
||||||
|
|
||||||
char *
|
char *
|
||||||
esxVMX_AbsolutePathToDatastoreRelatedPath(esxVI_Context *ctx,
|
esxVMX_AbsolutePathToDatastorePath(esxVI_Context *ctx, const char *absolutePath);
|
||||||
const char *absolutePath);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ testDiskNameToIndex(const void *data ATTRIBUTE_UNUSED)
|
|||||||
|
|
||||||
|
|
||||||
struct testPath {
|
struct testPath {
|
||||||
const char *datastoreRelatedPath;
|
const char *datastorePath;
|
||||||
int result;
|
int result;
|
||||||
const char *datastoreName;
|
const char *datastoreName;
|
||||||
const char *directoryName;
|
const char *directoryName;
|
||||||
@ -111,7 +111,7 @@ static struct testPath paths[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
testParseDatastoreRelatedPath(const void *data ATTRIBUTE_UNUSED)
|
testParseDatastorePath(const void *data ATTRIBUTE_UNUSED)
|
||||||
{
|
{
|
||||||
int i, result = 0;
|
int i, result = 0;
|
||||||
char *datastoreName = NULL;
|
char *datastoreName = NULL;
|
||||||
@ -123,9 +123,9 @@ testParseDatastoreRelatedPath(const void *data ATTRIBUTE_UNUSED)
|
|||||||
VIR_FREE(directoryName);
|
VIR_FREE(directoryName);
|
||||||
VIR_FREE(fileName);
|
VIR_FREE(fileName);
|
||||||
|
|
||||||
if (esxUtil_ParseDatastoreRelatedPath(paths[i].datastoreRelatedPath,
|
if (esxUtil_ParseDatastorePath(paths[i].datastorePath,
|
||||||
&datastoreName, &directoryName,
|
&datastoreName, &directoryName,
|
||||||
&fileName) != paths[i].result) {
|
&fileName) != paths[i].result) {
|
||||||
goto failure;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,7 +242,7 @@ mymain(int argc, char **argv)
|
|||||||
|
|
||||||
virSetErrorFunc(NULL, testQuietError);
|
virSetErrorFunc(NULL, testQuietError);
|
||||||
|
|
||||||
# define DO_TEST(_name) \
|
# define DO_TEST(_name) \
|
||||||
do { \
|
do { \
|
||||||
if (virtTestRun("VMware "#_name, 1, test##_name, \
|
if (virtTestRun("VMware "#_name, 1, test##_name, \
|
||||||
NULL) < 0) { \
|
NULL) < 0) { \
|
||||||
@ -252,7 +252,7 @@ mymain(int argc, char **argv)
|
|||||||
|
|
||||||
DO_TEST(IndexToDiskName);
|
DO_TEST(IndexToDiskName);
|
||||||
DO_TEST(DiskNameToIndex);
|
DO_TEST(DiskNameToIndex);
|
||||||
DO_TEST(ParseDatastoreRelatedPath);
|
DO_TEST(ParseDatastorePath);
|
||||||
DO_TEST(ConvertDateTimeToCalendarTime);
|
DO_TEST(ConvertDateTimeToCalendarTime);
|
||||||
|
|
||||||
return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
|
Loading…
Reference in New Issue
Block a user