/* * Copyright (c) 2016 Cloudbase Solutions Srl * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "wmi.h" #include #include #include #include "openvswitch/vlog.h" #include "util.h" VLOG_DEFINE_THIS_MODULE(wmi); /* WMI Job values. */ enum job_status { job_starting = 3, job_running = 4, job_completed = 7, job_wait = 4096 }; static char * sanitize_port_name(char *name) { char *p1, *p2; p1 = p2 = name; while (*p1) { if ((*p1) == '\'' || (*p1) == '\"') { p1++; } else { *p2 = *p1; p2++; p1++; } } *p2 = '\0'; return name; } /* This function will output the appropriate message for a given HRESULT.*/ static void get_hres_error(HRESULT hres) { char *error_msg = NULL; if (FACILITY_WINDOWS == HRESULT_FACILITY(hres)) { hres = HRESULT_CODE(hres); } VLOG_ERR("%s", ovs_format_message(hres)); } static boolean check_return_value(HRESULT hres) { if (FAILED(hres)) { get_hres_error(hres); return false; } return true; } static HRESULT get_variant_value(IWbemClassObject *pcls_obj, wchar_t *field_name, VARIANT *value) { HRESULT hres; VariantInit(value); hres = pcls_obj->lpVtbl->Get(pcls_obj, field_name, 0, value, 0, 0); if (FAILED(hres)) { VariantClear(value); } return hres; } /* This function retrieves the uint16_t value from a given class object with * the field name field_name. */ static HRESULT get_uint16_t_value(IWbemClassObject *pcls_obj, wchar_t *field_name, uint16_t *value) { VARIANT vt_prop; HRESULT hres = get_variant_value(pcls_obj, field_name, &vt_prop); *value = V_UI2(&vt_prop); return hres; } /* This function retrieves the unsigned int values from a given class object * with the field name field_name. */ static HRESULT get_uint_value(IWbemClassObject *pcls_obj, wchar_t *field_name, unsigned int *value) { VARIANT vt_prop; HRESULT hres = get_variant_value(pcls_obj, field_name, &vt_prop); *value = V_UI4(&vt_prop); return hres; } /* This function retrieves the unsigned short value from a given class object * with the field name field_name. */ static HRESULT get_ushort_value(IWbemClassObject *pcls_obj, wchar_t *field_name, unsigned short *value) { VARIANT vt_prop; HRESULT hres = get_variant_value(pcls_obj, field_name, &vt_prop); *value = V_UI2(&vt_prop); return hres; } /* This function retrieves the BSTR value from a given class object with * the field name field_name, to a preallocated destination dest and with the * maximum length max_dest_lgth. */ static HRESULT get_str_value(IWbemClassObject *pcls_obj, wchar_t *field_name, wchar_t *dest, int max_dest_lgth) { VARIANT vt_prop; HRESULT hres = get_variant_value(pcls_obj, field_name, &vt_prop); if (wcscpy_s(dest, max_dest_lgth, vt_prop.bstrVal)) { VariantClear(&vt_prop); VLOG_WARN("get_str_value, wcscpy_s failed :%s", ovs_strerror(errno)); return WBEM_E_FAILED; } VariantClear(&vt_prop); return S_OK; } /* This function waits for a WMI job to finish and retrieves the error code * if the job failed */ static HRESULT wait_for_job(IWbemServices *psvc, wchar_t *job_path) { IWbemClassObject *pcls_obj = NULL; HRESULT retval = 0; uint16_t job_state = 0; uint16_t error = 0; do { if(!check_return_value(psvc->lpVtbl->GetObject(psvc, job_path, 0, NULL, &pcls_obj, NULL))) { retval = WBEM_E_FAILED; break; } retval = get_uint16_t_value(pcls_obj, L"JobState", &job_state); if (FAILED(retval)) { break; } if (job_state == job_starting || job_state == job_running) { Sleep(200); } else if (job_state == job_completed) { break; } else { /* Error occurred. */ retval = get_uint16_t_value(pcls_obj, L"ErrorCode", &error); if (FAILED(retval)) { break; } VLOG_WARN("Job failed with error: %d", error); retval = WBEM_E_FAILED;; break; } if (pcls_obj != NULL) { pcls_obj->lpVtbl->Release(pcls_obj); pcls_obj = NULL; } } while(TRUE); if (pcls_obj != NULL) { pcls_obj->lpVtbl->Release(pcls_obj); pcls_obj = NULL; } return retval; } /* This function will initialize DCOM retrieving the WMI locator's ploc and * the context associated to it. */ static boolean initialize_wmi(IWbemLocator **ploc, IWbemContext **pcontext) { HRESULT hres = 0; /* Initialize COM. */ hres = CoInitialize(NULL); if (FAILED(hres)) { return false; } /* Initialize COM security. */ hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); if (FAILED(hres)) { return false; } /* Fill context. */ hres = CoCreateInstance(&CLSID_WbemContext, NULL, CLSCTX_INPROC_SERVER, &IID_IWbemContext, (void**)pcontext); if (FAILED(hres)) { return false; } fill_context(*pcontext); /* Initialize locator's (ploc) to WMI. */ hres = CoCreateInstance(&CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, &IID_IWbemLocator, (LPVOID *)ploc); if (FAILED(hres)) { return false; } return true; } /* This function connects the WMI locator's ploc to a given WMI provider * defined in server and also sets the required security levels for a local * connection to it. */ static boolean connect_set_security(IWbemLocator *ploc, IWbemContext *pcontext, wchar_t *server, IWbemServices **psvc) { HRESULT hres = 0; /* Connect to server. */ hres = ploc->lpVtbl->ConnectServer(ploc, server, NULL, NULL, 0, 0, 0, pcontext, psvc); if (FAILED(hres)) { return false; } /* Set security levels. */ hres = CoSetProxyBlanket((IUnknown *) *psvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); if (FAILED(hres)) { return false; } return true; } /* This function retrieves the first class object of a given enumeration * outputted by a query and fails if it could not retrieve the object or there * was no object to retrieve */ static boolean get_first_element(IEnumWbemClassObject *penumerate, IWbemClassObject **pcls_obj) { unsigned long retval = 0; if (penumerate == NULL) { VLOG_WARN("Enumeration Class Object is NULL. Cannot get the first" "object"); return false; } HRESULT hres = penumerate->lpVtbl->Next(penumerate, WBEM_INFINITE, 1, pcls_obj, &retval); if (!check_return_value(hres) || retval == 0) { return false; } return true; } /* This function is a wrapper that transforms a char * into a wchar_t * */ static boolean tranform_wide(char *name, wchar_t *wide_name) { unsigned long size = strlen(name) + 1; long long ret = 0; if (wide_name == NULL) { VLOG_WARN("Provided wide string is NULL"); return false; } ret = mbstowcs(wide_name, name, size); if (ret == -1) { VLOG_WARN("Invalid multibyte character is encountered"); return false; } else if (ret == size) { VLOG_WARN("Returned wide string not NULL terminated"); return false; } return true; } #define WMI_QUERY_COUNT 2048 /* This function will delete a switch internal port with a given name as input * executing "RemoveResourceSettings" as per documentation: * https://msdn.microsoft.com/en-us/library/hh850277%28v=vs.85%29.aspx * allocating the data and populating the needed fields to execute the * method */ boolean delete_wmi_port(char *name) { HRESULT hres = 0; boolean retval = true; IWbemLocator *ploc = NULL; IWbemServices *psvc = NULL; IWbemContext *pcontext = NULL; IWbemClassObject *pclass_instance = NULL; IWbemClassObject *pinput_params = NULL; IWbemClassObject *pcls_obj = NULL; IWbemClassObject *pout_params = NULL; IEnumWbemClassObject *penumerate = NULL; sanitize_port_name(name); VARIANT vt_prop; VARIANT variant_array; wchar_t *wide_name = NULL; VariantInit(&vt_prop); VariantInit(&variant_array); LONG count[1]; SAFEARRAY* psa = SafeArrayCreateVector(VT_BSTR, 0, 1); if (psa == NULL) { VLOG_WARN("Could not allocate memory for a SAFEARRAY"); retval = false; goto error; } if (!initialize_wmi(&ploc, &pcontext)) { VLOG_WARN("Could not initialize DCOM"); retval = false; goto error; } if (!connect_set_security(ploc, pcontext, L"Root\\Virtualization\\v2", &psvc)) { VLOG_WARN("Could not connect and set security for virtualization"); retval = false; goto error; } /* Get the port with the element name equal to the name input. */ wchar_t internal_port_query[WMI_QUERY_COUNT] = L"SELECT * from " L"Msvm_EthernetPortAllocationSettingData WHERE ElementName = \"" ; wide_name = malloc((strlen(name) + 1) * sizeof(wchar_t)); if (wide_name == NULL) { VLOG_WARN("Could not allocate memory for wide string"); retval = false; goto error; } if (!tranform_wide(name, wide_name)) { retval = false; goto error; } wcscat_s(internal_port_query, WMI_QUERY_COUNT, wide_name); wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\""); hres = psvc->lpVtbl->ExecQuery(psvc, L"WQL", internal_port_query, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &penumerate); if (FAILED(hres)) { retval = false; goto error; } /* Get the element path on the switch which will be deleted. */ if (!get_first_element(penumerate, &pcls_obj)) { retval = false; goto error; } penumerate->lpVtbl->Release(penumerate); penumerate = NULL; hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0); if (FAILED(hres)) { retval = false; goto error; } pcls_obj->lpVtbl->Release(pcls_obj); pcls_obj = NULL; /* Get the class object and the parameters it can have. */ hres = psvc->lpVtbl->GetObject(psvc, L"Msvm_VirtualEthernetSwitchManagementService", 0, NULL, &pcls_obj, NULL); if (FAILED(hres)) { retval = false; goto error; } hres = pcls_obj->lpVtbl->GetMethod(pcls_obj, L"RemoveResourceSettings", 0, &pinput_params, NULL); if (FAILED(hres)) { retval = false; goto error; } pcls_obj->lpVtbl->Release(pcls_obj); pcls_obj = NULL; hres = pinput_params->lpVtbl->SpawnInstance(pinput_params, 0, &pclass_instance); if (FAILED(hres)) { retval = false; goto error; } count[0] = 0; hres = SafeArrayPutElement(psa, count, vt_prop.bstrVal); if (FAILED(hres)) { retval = false; goto error; } VariantClear(&vt_prop); VariantInit(&vt_prop); variant_array.vt = VT_ARRAY | VT_BSTR; variant_array.parray = psa; hres = pclass_instance->lpVtbl->Put(pclass_instance, L"ResourceSettings", 0, &variant_array, 0); if (FAILED(hres)) { retval = false; goto error; } /* Get the object of the Msvm_VirtualEthernetSwitchManagementService which * we need to invoke the port deletion. */ hres = psvc->lpVtbl->ExecQuery(psvc, L"WQL", L"SELECT * FROM " L"Msvm_VirtualEthernetSwitchManagementService", WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &penumerate); if (FAILED(hres)) { retval = false; goto error; } if (!get_first_element(penumerate, &pcls_obj)) { retval = false; goto error; } penumerate->lpVtbl->Release(penumerate); penumerate = NULL; hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0); if (FAILED(hres)) { retval = false; goto error; } pcls_obj->lpVtbl->Release(pcls_obj); pcls_obj = NULL; /* Invoke the delete port method. */ hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal, L"RemoveResourceSettings", 0, pcontext, pclass_instance, &pout_params, NULL); if (FAILED(hres)) { retval = false; goto error; } VariantClear(&vt_prop); VariantInit(&vt_prop); hres = pout_params->lpVtbl->Get(pout_params, L"ReturnValue", 0, &vt_prop, NULL, 0); if (FAILED(hres)) { retval = false; goto error; } unsigned int retvalue = 0; hres = get_uint_value(pout_params, L"ReturnValue", &retvalue); if (FAILED(hres)) { retval = false; goto error; } if (retvalue != 0 && retvalue != job_wait) { retval = false; goto error; } if (retvalue == job_wait) { WCHAR job_path[2048]; hres = get_str_value(pout_params, L"Job", job_path, sizeof(job_path) / sizeof(WCHAR)); if (FAILED(hres)) { retval = false; goto error; } hres = wait_for_job(psvc, job_path); if (FAILED(hres)) { retval = false; } } error: VariantClear(&vt_prop); if (pcontext != NULL) { pcontext->lpVtbl->Release(pcontext); pcontext = NULL; } if (psa != NULL) { SafeArrayDestroy(psa); psa = NULL; } if (pcls_obj != NULL) { pcls_obj->lpVtbl->Release(pcls_obj); pcls_obj = NULL; } if (wide_name != NULL) { free(wide_name); wide_name = NULL; } if (!retval) { get_hres_error(hres); } if (pinput_params != NULL) { pinput_params->lpVtbl->Release(pinput_params); pinput_params = NULL; } if (pout_params != NULL) { pout_params->lpVtbl->Release(pout_params); pout_params = NULL; } if (psvc != NULL) { psvc->lpVtbl->Release(psvc); psvc = NULL; } if (ploc != NULL) { ploc->lpVtbl->Release(ploc); ploc = NULL; } if (pclass_instance != NULL) { pclass_instance->lpVtbl->Release(pclass_instance); pclass_instance = NULL; } if (penumerate != NULL) { penumerate->lpVtbl->Release(penumerate); penumerate = NULL; } CoUninitialize(); return retval; } /* This function will create an internal port on the switch given a given name * executing the method AddResourceSettings as per documentation: * https://msdn.microsoft.com/en-us/library/hh850019%28v=vs.85%29.aspx. * It will verify if the port is already defined, in which case it will use * the specific port, and if the forwarding extension "Open vSwitch Extension" * is enabled and running only on a single switch. * After the port is created and bound to the switch we will disable the * created net adapter and rename it to match the OVS bridge name .*/ boolean create_wmi_port(char *name) { HRESULT hres = 0; boolean retval = true; BSTR text_object_string = NULL; IWbemLocator *ploc = NULL; IWbemContext *pcontext = NULL; IWbemServices *psvc = NULL; IEnumWbemClassObject *penumerate = NULL; IWbemClassObject *default_settings_data = NULL; IWbemClassObject *default_system = NULL; IWbemClassObject *pcls_obj = NULL; IWbemClassObject *pclass = NULL; IWbemClassObject *pinput_params = NULL; IWbemClassObject *pclass_instance = NULL; IWbemObjectTextSrc *text_object = NULL; IWbemClassObject *pout_params = NULL; wchar_t *wide_name = NULL; VARIANT vt_prop; VARIANT switch_setting_path; VARIANT new_name; SAFEARRAY *psa = SafeArrayCreateVector(VT_BSTR, 0, 1); VARIANT variant_array; LONG count[1]; VariantInit(&vt_prop); VariantInit(&switch_setting_path); sanitize_port_name(name); if (psa == NULL) { VLOG_WARN("Could not allocate memory for a SAFEARRAY"); retval = false; goto error; } if (!initialize_wmi(&ploc, &pcontext)) { VLOG_WARN("Could not initialize DCOM"); retval = false; goto error; } if (!connect_set_security(ploc, pcontext, L"Root\\Virtualization\\v2", &psvc)) { VLOG_WARN("Could not connect and set security for virtualization"); retval = false; goto error; } /* Check if the element already exists on the switch. */ wchar_t internal_port_query[WMI_QUERY_COUNT] = L"SELECT * FROM " L"Msvm_InternalEthernetPort WHERE ElementName = \""; wide_name = malloc((strlen(name) + 1) * sizeof(wchar_t)); if (wide_name == NULL) { VLOG_WARN("Could not allocate memory for wide string"); retval = false; goto error; } if (!tranform_wide(name, wide_name)) { retval = false; goto error; } wcscat_s(internal_port_query, WMI_QUERY_COUNT, wide_name); wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\""); hres = psvc->lpVtbl->ExecQuery(psvc, L"WQL", internal_port_query, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &penumerate); if (FAILED(hres)) { retval = false; goto error; } if (get_first_element(penumerate, &pcls_obj)) { VLOG_WARN("Port with name: %s already defined on the switch", name); goto error; } penumerate->lpVtbl->Release(penumerate); penumerate = NULL; /* Check if the extension is enabled and running. Also check if the * the extension is enabled on more than one switch. */ hres = psvc->lpVtbl->ExecQuery(psvc, L"WQL", L"SELECT * " L"FROM Msvm_EthernetSwitchExtension " L"WHERE " L"ElementName=\"Open vSwitch Extension\" " L"AND EnabledState=2 " L"AND HealthState=5", WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &penumerate); if (FAILED(hres)) { retval = false; goto error; } if (!get_first_element(penumerate, &pcls_obj)) { VLOG_WARN("Open vSwitch Extension is not enabled on any switch"); retval = false; goto error; } wcscpy_s(internal_port_query, WMI_QUERY_COUNT, L"SELECT * FROM Msvm_VirtualEthernetSwitch WHERE Name = \""); hres = pcls_obj->lpVtbl->Get(pcls_obj, L"SystemName", 0, &vt_prop, 0, 0); if (FAILED(hres)) { retval = false; goto error; } wcscat_s(internal_port_query, WMI_QUERY_COUNT, vt_prop.bstrVal); VariantClear(&vt_prop); pcls_obj->lpVtbl->Release(pcls_obj); pcls_obj = NULL; if (get_first_element(penumerate, &pcls_obj)) { VLOG_WARN("The extension is activated on more than one switch, " "aborting operation. Please activate the extension on a " "single switch"); retval = false; goto error; } penumerate->lpVtbl->Release(penumerate); penumerate = NULL; if (pcls_obj != NULL) { pcls_obj->lpVtbl->Release(pcls_obj); pcls_obj = NULL; } /* Get the switch object on which the extension is activated. */ wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\""); hres = psvc->lpVtbl->ExecQuery(psvc, L"WQL", internal_port_query, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &penumerate); if (FAILED(hres)) { retval = false; goto error; } if (!get_first_element(penumerate, &pcls_obj)) { VLOG_WARN("Could not get the switch object on which the extension is" "activated"); retval = false; goto error; } penumerate->lpVtbl->Release(penumerate); penumerate = NULL; hres = pcls_obj->lpVtbl->Get(pcls_obj, L"ElementName", 0, &vt_prop, 0, 0); if (FAILED(hres)) { retval = false; goto error; } wcscpy_s(internal_port_query, WMI_QUERY_COUNT, L"SELECT * FROM Msvm_VirtualEthernetSwitchSettingData WHERE " L"ElementName = \""); wcscat_s(internal_port_query, WMI_QUERY_COUNT, vt_prop.bstrVal); VariantClear(&vt_prop); hres = pcls_obj->lpVtbl->Get(pcls_obj, L"Name", 0, &vt_prop, 0, 0); if (FAILED(hres)) { retval = false; goto error; } pcls_obj->lpVtbl->Release(pcls_obj); pcls_obj = NULL; /* Should be enough to give the InstanceID, from msdn documentation: * Uniquely identifies an instance of this class. This property is * inherited from CIM_SettingData and is always * set to "Microsoft:GUID\DeviceSpecificData". */ wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\" AND InstanceID = \"Microsoft:"); wcscat_s(internal_port_query, WMI_QUERY_COUNT, vt_prop.bstrVal); wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\""); VariantClear(&vt_prop); /* Retrieve the Msvm_VirtualEthernetSwitchSettingData pinned to the switch * object on which the extension is activated. */ hres = psvc->lpVtbl->ExecQuery(psvc, L"WQL", internal_port_query, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &penumerate); if (FAILED(hres)) { retval = false; goto error; } if (!get_first_element(penumerate, &pcls_obj)) { VLOG_WARN("Could not get the first " "Msvm_VirtualEthernetSwitchSettingData object"); retval = false; goto error; } penumerate->lpVtbl->Release(penumerate); penumerate = NULL; hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &switch_setting_path, 0, 0); if (FAILED(hres)) { retval = false; goto error; } pcls_obj->lpVtbl->Release(pcls_obj); pcls_obj = NULL; /* Retrieve a default allocation port. This object will be later filled * with optional data to create an switch internal port. */ hres = psvc->lpVtbl->ExecQuery(psvc, L"WQL", L"SELECT * FROM " L"Msvm_EthernetPortAllocationSettingData " L"WHERE InstanceID LIKE '%%%%\\\\Default' " L"AND ResourceSubType = " L"'Microsoft:Hyper-V:Ethernet Connection'", WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &penumerate); if (FAILED(hres)) { retval = false; goto error; } if (!get_first_element(penumerate, &default_settings_data)) { VLOG_WARN("Could not retrieve default allocation port object"); retval = false; goto error; } penumerate->lpVtbl->Release(penumerate); penumerate = NULL; /* Retrieve the default computer system on which the port allocation will * be hosted. * Instead of querying using Description, we can query using InstallDate. * From MSDN documentation regarding InstallDate: * The date and time the virtual machine configuration was created for * a virtual machine, or Null, for a management operating system. */ hres = psvc->lpVtbl->ExecQuery(psvc, L"WQL", L"SELECT * FROM Msvm_ComputerSystem WHERE " L"InstallDate is NULL", WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &penumerate); if (FAILED(hres)) { retval = false; goto error; } if (!get_first_element(penumerate, &default_system)) { VLOG_WARN("Could not retrieve default computer system object"); retval = false; goto error; } hres = default_system->lpVtbl->Get(default_system, L"__PATH", 0, &vt_prop, 0, 0); if (FAILED(hres)) { retval = false; goto error; } penumerate->lpVtbl->Release(penumerate); penumerate = NULL; count[0] = 0; hres = SafeArrayPutElement(psa, count, vt_prop.bstrVal); if (FAILED(hres)) { retval = false; goto error; } VariantClear(&vt_prop); variant_array.vt = VT_ARRAY | VT_BSTR; variant_array.parray = psa; hres = default_settings_data->lpVtbl->Put(default_settings_data, L"HostResource", 0, &variant_array, 0); if (FAILED(hres)) { retval = false; goto error; } hres = psvc->lpVtbl->GetObject(psvc, L"Msvm_VirtualEthernetSwitchManagementService", 0, NULL, &pclass, NULL); if (FAILED(hres)) { retval = false; goto error; } hres = pclass->lpVtbl->GetMethod(pclass, L"AddResourceSettings", 0, &pinput_params, NULL); if (FAILED(hres)) { retval = false; goto error; } hres = pinput_params->lpVtbl->SpawnInstance(pinput_params, 0, &pclass_instance); if (FAILED(hres)) { retval = false; goto error; } /* Store the switch setting path retrieved above in the affected * configuration field of the class instance. */ hres = pclass_instance->lpVtbl->Put(pclass_instance, L"AffectedConfiguration", 0, &switch_setting_path, 0); if (FAILED(hres)) { retval = false; goto error; } /* Store the port name in the ElementName field of the default allocation * data. */ vt_prop.vt = VT_BSTR; vt_prop.bstrVal = SysAllocString(wide_name); hres = default_settings_data->lpVtbl->Put(default_settings_data, L"ElementName", 0, &vt_prop, 0); VariantClear(&vt_prop); if (FAILED(hres)) { retval = false; goto error; } /* Retrieve and store the serialized data of the modified default switch * settings data. */ hres = CoCreateInstance(&CLSID_WbemObjectTextSrc, NULL, CLSCTX_INPROC_SERVER, &IID_IWbemObjectTextSrc, (void**)&text_object); if (FAILED(hres)) { retval = false; goto error; } hres = text_object->lpVtbl->GetText(text_object, 0, default_settings_data, WMI_OBJ_TEXT_WMI_DTD_2_0, pcontext, &text_object_string); if (FAILED(hres)) { retval = false; goto error; } hres = SafeArrayDestroy(psa); if (FAILED(hres)) { VLOG_WARN("Could not clear the data of the array"); retval = false; goto error; } psa = SafeArrayCreateVector(VT_BSTR, 0, 1); if (psa == NULL) { VLOG_WARN("Could not allocate memory for a SAFEARRAY"); retval = false; goto error; } count[0] = 0; variant_array.parray = psa; hres = SafeArrayPutElement(psa, count, text_object_string); if (FAILED(hres)) { retval = false; goto error; } hres = pclass_instance->lpVtbl->Put(pclass_instance, L"ResourceSettings", 0, &variant_array, 0); if (FAILED(hres)) { retval = false; goto error; } /* Get the object of the switch service. */ hres = psvc->lpVtbl->ExecQuery(psvc, L"WQL", L"SELECT * FROM " L"Msvm_VirtualEthernetSwitchManagementService", WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &penumerate); if (FAILED(hres)) { retval = false; goto error; } if (!get_first_element(penumerate, &pcls_obj)) { VLOG_WARN("Could not get the object of the switch service"); retval = false; goto error; } penumerate->lpVtbl->Release(penumerate); penumerate = NULL; hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0); if (FAILED(hres)) { retval = false; goto error; } pcls_obj->lpVtbl->Release(pcls_obj); pcls_obj = NULL; /* Try to add the port to the switch. */ hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal, L"AddResourceSettings", 0, pcontext, pclass_instance, &pout_params, NULL); if (FAILED(hres)) { retval = false; goto error; } unsigned int retvalue = 0; hres = get_uint_value(pout_params, L"ReturnValue", &retvalue); if (FAILED(hres)) { retval = false; goto error; } if (retvalue != 0 && retvalue != job_wait) { retval = false; goto error; } if (retvalue == job_wait) { WCHAR job_path[2048]; hres = get_str_value(pout_params, L"Job", job_path, sizeof(job_path) / sizeof(WCHAR)); if (FAILED(hres)) { retval = false; goto error; } hres = wait_for_job(psvc, job_path); if (FAILED(hres)) { retval = false; goto error; } } pclass->lpVtbl->Release(pclass); pclass = NULL; pclass_instance->lpVtbl->Release(pclass_instance); pclass_instance = NULL; pinput_params->lpVtbl->Release(pinput_params); pinput_params = NULL; psvc->lpVtbl->Release(psvc); psvc = NULL; VariantClear(&vt_prop); if (!connect_set_security(ploc, pcontext, L"Root\\StandardCimv2", &psvc)) { VLOG_WARN("Could not connect and set security for CIM"); retval = false; goto error; } wcscpy_s(internal_port_query, WMI_QUERY_COUNT, L"SELECT * FROM MSFT_NetAdapter WHERE Name LIKE '%%"); wcscat_s(internal_port_query, WMI_QUERY_COUNT, wide_name); wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"%%'"); /* Get the object with the port name equal to name on the CIM. */ hres = psvc->lpVtbl->ExecQuery(psvc, L"WQL", internal_port_query, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &penumerate); if (!get_first_element(penumerate, &pcls_obj)) { VLOG_WARN("Element name: %s not found in CIM", name); retval = false; goto error; } penumerate->lpVtbl->Release(penumerate); penumerate = NULL; pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0); pcls_obj->lpVtbl->Release(pcls_obj); pcls_obj = NULL; /* Disable the adapter with port name equal with name. */ hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal, L"Disable", 0, pcontext, NULL, NULL, NULL); if (FAILED(hres)) { retval = false; goto error; } hres = psvc->lpVtbl->GetObject(psvc, L"MSFT_NetAdapter", 0, NULL, &pclass, NULL); if (FAILED(hres)) { retval = false; goto error; } hres = pclass->lpVtbl->GetMethod(pclass, L"Rename", 0, &pinput_params, NULL); if (FAILED(hres)) { retval = false; goto error; } hres = pinput_params->lpVtbl->SpawnInstance(pinput_params, 0, &pclass_instance); if (FAILED(hres)) { retval = false; goto error; } VariantInit(&new_name); new_name.vt = VT_BSTR; new_name.bstrVal = wide_name; hres = pclass_instance->lpVtbl->Put(pclass_instance, L"NewName", 0, &new_name, 0); if (FAILED(hres)) { retval = false; goto error; } hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal, L"Rename", 0, pcontext, pclass_instance, NULL, NULL); if (FAILED(hres)) { retval = false; } error: if (text_object_string != NULL) { SysFreeString(text_object_string); text_object_string = NULL; } if (psa != NULL) { SafeArrayDestroy(psa); psa = NULL; } if (ploc != NULL) { ploc->lpVtbl->Release(ploc); ploc = NULL; } if (pcontext != NULL) { pcontext->lpVtbl->Release(pcontext); pcontext = NULL; } if (psvc != NULL) { psvc->lpVtbl->Release(psvc); psvc = NULL; } if (penumerate != NULL) { penumerate->lpVtbl->Release(penumerate); penumerate = NULL; } if (default_settings_data != NULL) { default_settings_data->lpVtbl->Release(default_settings_data); default_settings_data = NULL; } if (default_system != NULL) { default_system->lpVtbl->Release(default_system); default_system = NULL; } if (pcls_obj != NULL) { pcls_obj->lpVtbl->Release(pcls_obj); pcls_obj = NULL; } if (pclass != NULL) { pclass->lpVtbl->Release(pclass); pclass = NULL; } if (pinput_params != NULL) { pinput_params->lpVtbl->Release(pinput_params); pinput_params = NULL; } if (pclass_instance != NULL) { pclass_instance->lpVtbl->Release(pclass_instance); pclass_instance = NULL; } if (text_object != NULL) { text_object->lpVtbl->Release(text_object); text_object = NULL; } if (pout_params != NULL) { pout_params->lpVtbl->Release(pout_params); pout_params = NULL; } if (wide_name != NULL) { free(wide_name); wide_name = NULL; } VariantClear(&vt_prop); VariantClear(&switch_setting_path); if (!retval) { get_hres_error(hres); } CoUninitialize(); return retval; }