]> git.proxmox.com Git - mirror_ovs.git/blame - lib/wmi.c
cirrus: Use FreeBSD 12.2.
[mirror_ovs.git] / lib / wmi.c
CommitLineData
da467899
AS
1/*
2 * Copyright (c) 2016 Cloudbase Solutions Srl
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <config.h>
18#include "wmi.h"
19#include <stdlib.h>
20#include <stdio.h>
21#include <tchar.h>
22#include "openvswitch/vlog.h"
60db0898 23#include "util.h"
da467899
AS
24
25VLOG_DEFINE_THIS_MODULE(wmi);
26
27/* WMI Job values. */
28enum job_status
29{
30 job_starting = 3,
31 job_running = 4,
32 job_completed = 7,
33 job_wait = 4096
34};
35
36static char *
37sanitize_port_name(char *name)
38{
39 char *p1, *p2;
40 p1 = p2 = name;
41
42 while (*p1) {
43 if ((*p1) == '\'' || (*p1) == '\"') {
44 p1++;
45 } else {
46 *p2 = *p1;
47 p2++;
48 p1++;
49 }
50 }
51 *p2 = '\0';
52 return name;
53}
54
55/* This function will output the appropriate message for a given HRESULT.*/
56static void
57get_hres_error(HRESULT hres)
58{
59 char *error_msg = NULL;
60
61 if (FACILITY_WINDOWS == HRESULT_FACILITY(hres)) {
62 hres = HRESULT_CODE(hres);
63 }
64
65 VLOG_ERR("%s", ovs_format_message(hres));
66}
67
68static boolean
69check_return_value(HRESULT hres)
70{
71 if (FAILED(hres)) {
72 get_hres_error(hres);
73 return false;
74 }
75
76 return true;
77}
78
79static HRESULT
80get_variant_value(IWbemClassObject *pcls_obj, wchar_t *field_name,
81 VARIANT *value)
82{
83 HRESULT hres;
84
85 VariantInit(value);
86
87 hres = pcls_obj->lpVtbl->Get(pcls_obj, field_name, 0, value, 0, 0);
88
89 if (FAILED(hres)) {
90 VariantClear(value);
91 }
92
93 return hres;
94}
95
96/* This function retrieves the uint16_t value from a given class object with
97 * the field name field_name. */
98static HRESULT
99get_uint16_t_value(IWbemClassObject *pcls_obj, wchar_t *field_name,
100 uint16_t *value)
101{
102 VARIANT vt_prop;
103 HRESULT hres = get_variant_value(pcls_obj, field_name, &vt_prop);
104 *value = V_UI2(&vt_prop);
105
106 return hres;
107}
108
109/* This function retrieves the unsigned int values from a given class object
110 * with the field name field_name. */
111static HRESULT
112get_uint_value(IWbemClassObject *pcls_obj, wchar_t *field_name,
113 unsigned int *value)
114{
115 VARIANT vt_prop;
116 HRESULT hres = get_variant_value(pcls_obj, field_name, &vt_prop);
117 *value = V_UI4(&vt_prop);
118
119 return hres;
120}
121
122/* This function retrieves the unsigned short value from a given class object
123 * with the field name field_name. */
124static HRESULT
125get_ushort_value(IWbemClassObject *pcls_obj, wchar_t *field_name,
126 unsigned short *value)
127{
128 VARIANT vt_prop;
129 HRESULT hres = get_variant_value(pcls_obj, field_name, &vt_prop);
130 *value = V_UI2(&vt_prop);
131
132 return hres;
133}
134
135/* This function retrieves the BSTR value from a given class object with
136 * the field name field_name, to a preallocated destination dest and with the
137 * maximum length max_dest_lgth. */
138static HRESULT
139get_str_value(IWbemClassObject *pcls_obj, wchar_t *field_name, wchar_t *dest,
140 int max_dest_lgth)
141{
142 VARIANT vt_prop;
143 HRESULT hres = get_variant_value(pcls_obj, field_name, &vt_prop);
144
145 if (wcscpy_s(dest, max_dest_lgth, vt_prop.bstrVal)) {
146 VariantClear(&vt_prop);
147 VLOG_WARN("get_str_value, wcscpy_s failed :%s", ovs_strerror(errno));
148 return WBEM_E_FAILED;
149 }
150
151 VariantClear(&vt_prop);
152 return S_OK;
153}
154
155/* This function waits for a WMI job to finish and retrieves the error code
156 * if the job failed */
157static HRESULT
158wait_for_job(IWbemServices *psvc, wchar_t *job_path)
159{
160 IWbemClassObject *pcls_obj = NULL;
161 HRESULT retval = 0;
162 uint16_t job_state = 0;
163 uint16_t error = 0;
164
165 do {
166 if(!check_return_value(psvc->lpVtbl->GetObject(psvc, job_path, 0, NULL,
167 &pcls_obj, NULL))) {
168 retval = WBEM_E_FAILED;
169 break;
170 }
171
172 retval = get_uint16_t_value(pcls_obj, L"JobState", &job_state);
173 if (FAILED(retval)) {
174 break;
175 }
176
177 if (job_state == job_starting || job_state == job_running) {
178 Sleep(200);
179 } else if (job_state == job_completed) {
180 break;
181 } else {
182 /* Error occurred. */
183 retval = get_uint16_t_value(pcls_obj, L"ErrorCode", &error);
184 if (FAILED(retval)) {
185 break;
186 }
187 VLOG_WARN("Job failed with error: %d", error);
188 retval = WBEM_E_FAILED;;
189 break;
190 }
191
192 if (pcls_obj != NULL) {
193 pcls_obj->lpVtbl->Release(pcls_obj);
194 pcls_obj = NULL;
195 }
196 } while(TRUE);
197
198 if (pcls_obj != NULL) {
199 pcls_obj->lpVtbl->Release(pcls_obj);
200 pcls_obj = NULL;
201 }
202
203 return retval;
204}
205
206/* This function will initialize DCOM retrieving the WMI locator's ploc and
207 * the context associated to it. */
208static boolean
209initialize_wmi(IWbemLocator **ploc, IWbemContext **pcontext)
210{
211 HRESULT hres = 0;
212
213 /* Initialize COM. */
214 hres = CoInitialize(NULL);
215
216 if (FAILED(hres)) {
217 return false;
218 }
219
220 /* Initialize COM security. */
221 hres = CoInitializeSecurity(NULL,
222 -1,
223 NULL,
224 NULL,
225 RPC_C_AUTHN_LEVEL_DEFAULT,
226 RPC_C_IMP_LEVEL_IMPERSONATE,
227 NULL,
228 EOAC_NONE,
229 NULL);
230
231 if (FAILED(hres)) {
232 return false;
233 }
234
235 /* Fill context. */
236 hres = CoCreateInstance(&CLSID_WbemContext,
237 NULL,
238 CLSCTX_INPROC_SERVER,
239 &IID_IWbemContext,
240 (void**)pcontext);
241
242 if (FAILED(hres)) {
243 return false;
244 }
245
246 fill_context(*pcontext);
247
248 /* Initialize locator's (ploc) to WMI. */
249 hres = CoCreateInstance(&CLSID_WbemLocator,
250 NULL,
251 CLSCTX_INPROC_SERVER,
252 &IID_IWbemLocator,
253 (LPVOID *)ploc);
254
255 if (FAILED(hres)) {
256 return false;
257 }
258
259 return true;
260}
261
262/* This function connects the WMI locator's ploc to a given WMI provider
263 * defined in server and also sets the required security levels for a local
264 * connection to it. */
265static boolean
266connect_set_security(IWbemLocator *ploc, IWbemContext *pcontext,
267 wchar_t *server, IWbemServices **psvc)
268{
269 HRESULT hres = 0;
270
271 /* Connect to server. */
272 hres = ploc->lpVtbl->ConnectServer(ploc,
273 server,
274 NULL,
275 NULL,
276 0,
277 0,
278 0,
279 pcontext,
280 psvc);
281
282 if (FAILED(hres)) {
283 return false;
284 }
285
286 /* Set security levels. */
287 hres = CoSetProxyBlanket((IUnknown *) *psvc,
288 RPC_C_AUTHN_WINNT,
289 RPC_C_AUTHZ_NONE,
290 NULL,
291 RPC_C_AUTHN_LEVEL_CALL,
292 RPC_C_IMP_LEVEL_IMPERSONATE,
293 NULL,
294 EOAC_NONE);
295
296 if (FAILED(hres)) {
297 return false;
298 }
299
300 return true;
301}
302
303/* This function retrieves the first class object of a given enumeration
304 * outputted by a query and fails if it could not retrieve the object or there
305 * was no object to retrieve */
306static boolean
307get_first_element(IEnumWbemClassObject *penumerate,
308 IWbemClassObject **pcls_obj)
309{
310 unsigned long retval = 0;
311
312 if (penumerate == NULL) {
313 VLOG_WARN("Enumeration Class Object is NULL. Cannot get the first"
314 "object");
315 return false;
316 }
317
318 HRESULT hres = penumerate->lpVtbl->Next(penumerate, WBEM_INFINITE, 1,
319 pcls_obj, &retval);
320
321
322 if (!check_return_value(hres) || retval == 0) {
323 return false;
324 }
325
326 return true;
327}
328
329/* This function is a wrapper that transforms a char * into a wchar_t * */
330static boolean
331tranform_wide(char *name, wchar_t *wide_name)
332{
333 unsigned long size = strlen(name) + 1;
334 long long ret = 0;
335
336 if (wide_name == NULL) {
337 VLOG_WARN("Provided wide string is NULL");
338 return false;
339 }
340
341 ret = mbstowcs(wide_name, name, size);
342
343 if (ret == -1) {
344 VLOG_WARN("Invalid multibyte character is encountered");
345 return false;
346 } else if (ret == size) {
347 VLOG_WARN("Returned wide string not NULL terminated");
348 return false;
349 }
350
351 return true;
352}
353
c4129270
SV
354#define WMI_QUERY_COUNT 2048
355
da467899
AS
356/* This function will delete a switch internal port with a given name as input
357 * executing "RemoveResourceSettings" as per documentation:
358 * https://msdn.microsoft.com/en-us/library/hh850277%28v=vs.85%29.aspx
359 * allocating the data and populating the needed fields to execute the
360 * method */
361boolean
362delete_wmi_port(char *name)
363{
364 HRESULT hres = 0;
365 boolean retval = true;
366
367 IWbemLocator *ploc = NULL;
368 IWbemServices *psvc = NULL;
369 IWbemContext *pcontext = NULL;
370 IWbemClassObject *pclass_instance = NULL;
371 IWbemClassObject *pinput_params = NULL;
372 IWbemClassObject *pcls_obj = NULL;
373 IWbemClassObject *pout_params = NULL;
374 IEnumWbemClassObject *penumerate = NULL;
375
376 sanitize_port_name(name);
377 VARIANT vt_prop;
378 VARIANT variant_array;
379 wchar_t *wide_name = NULL;
380 VariantInit(&vt_prop);
381 VariantInit(&variant_array);
382
383 LONG count[1];
384 SAFEARRAY* psa = SafeArrayCreateVector(VT_BSTR, 0, 1);
385 if (psa == NULL) {
386 VLOG_WARN("Could not allocate memory for a SAFEARRAY");
387 retval = false;
388 goto error;
389 }
390
391 if (!initialize_wmi(&ploc, &pcontext)) {
392 VLOG_WARN("Could not initialize DCOM");
393 retval = false;
394 goto error;
395 }
396
397 if (!connect_set_security(ploc, pcontext, L"Root\\Virtualization\\v2",
398 &psvc)) {
399 VLOG_WARN("Could not connect and set security for virtualization");
400 retval = false;
401 goto error;
402 }
403
404
405 /* Get the port with the element name equal to the name input. */
c4129270 406 wchar_t internal_port_query[WMI_QUERY_COUNT] = L"SELECT * from "
da467899
AS
407 L"Msvm_EthernetPortAllocationSettingData WHERE ElementName = \"" ;
408
95c36eb7 409 wide_name = xmalloc((strlen(name) + 1) * sizeof(wchar_t));
da467899
AS
410
411 if (!tranform_wide(name, wide_name)) {
412 retval = false;
413 goto error;
414 }
c4129270 415 wcscat_s(internal_port_query, WMI_QUERY_COUNT, wide_name);
da467899 416
c4129270 417 wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\"");
da467899
AS
418
419 hres = psvc->lpVtbl->ExecQuery(psvc,
420 L"WQL",
421 internal_port_query,
422 WBEM_FLAG_FORWARD_ONLY |
423 WBEM_FLAG_RETURN_IMMEDIATELY,
424 NULL,
425 &penumerate);
426
427 if (FAILED(hres)) {
428 retval = false;
429 goto error;
430 }
431
432 /* Get the element path on the switch which will be deleted. */
433 if (!get_first_element(penumerate, &pcls_obj)) {
434 retval = false;
435 goto error;
436 }
437 penumerate->lpVtbl->Release(penumerate);
438 penumerate = NULL;
439
440 hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0);
441
442 if (FAILED(hres)) {
443 retval = false;
444 goto error;
445 }
446 pcls_obj->lpVtbl->Release(pcls_obj);
447 pcls_obj = NULL;
448
449 /* Get the class object and the parameters it can have. */
450 hres = psvc->lpVtbl->GetObject(psvc,
451 L"Msvm_VirtualEthernetSwitchManagementService", 0, NULL, &pcls_obj,
452 NULL);
453
454 if (FAILED(hres)) {
455 retval = false;
456 goto error;
457 }
458
459 hres = pcls_obj->lpVtbl->GetMethod(pcls_obj, L"RemoveResourceSettings", 0,
460 &pinput_params, NULL);
461
462 if (FAILED(hres)) {
463 retval = false;
464 goto error;
465 }
466 pcls_obj->lpVtbl->Release(pcls_obj);
467 pcls_obj = NULL;
468
469 hres = pinput_params->lpVtbl->SpawnInstance(pinput_params, 0,
470 &pclass_instance);
471 if (FAILED(hres)) {
472 retval = false;
473 goto error;
474 }
475
476 count[0] = 0;
477
478 hres = SafeArrayPutElement(psa, count, vt_prop.bstrVal);
479
480 if (FAILED(hres)) {
481 retval = false;
482 goto error;
483 }
484
485 VariantClear(&vt_prop);
486 VariantInit(&vt_prop);
487 variant_array.vt = VT_ARRAY | VT_BSTR;
488 variant_array.parray = psa;
489
490 hres = pclass_instance->lpVtbl->Put(pclass_instance, L"ResourceSettings", 0,
491 &variant_array, 0);
492 if (FAILED(hres)) {
493 retval = false;
494 goto error;
495 }
496
497 /* Get the object of the Msvm_VirtualEthernetSwitchManagementService which
498 * we need to invoke the port deletion. */
499 hres = psvc->lpVtbl->ExecQuery(psvc,
500 L"WQL",
501 L"SELECT * FROM "
502 L"Msvm_VirtualEthernetSwitchManagementService",
503 WBEM_FLAG_FORWARD_ONLY |
504 WBEM_FLAG_RETURN_IMMEDIATELY,
505 NULL,
506 &penumerate);
507
508 if (FAILED(hres)) {
509 retval = false;
510 goto error;
511 }
512
513 if (!get_first_element(penumerate, &pcls_obj)) {
514 retval = false;
515 goto error;
516 }
517 penumerate->lpVtbl->Release(penumerate);
518 penumerate = NULL;
519
520 hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0);
521
522 if (FAILED(hres)) {
523 retval = false;
524 goto error;
525 }
526
527 pcls_obj->lpVtbl->Release(pcls_obj);
528 pcls_obj = NULL;
529
530 /* Invoke the delete port method. */
531 hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal,
532 L"RemoveResourceSettings", 0,
533 pcontext, pclass_instance, &pout_params,
534 NULL);
535 if (FAILED(hres)) {
536 retval = false;
537 goto error;
538 }
539 VariantClear(&vt_prop);
540 VariantInit(&vt_prop);
541
542 hres = pout_params->lpVtbl->Get(pout_params, L"ReturnValue", 0,
543 &vt_prop, NULL, 0);
544
545 if (FAILED(hres)) {
546 retval = false;
547 goto error;
548 }
549
550 unsigned int retvalue = 0;
551 hres = get_uint_value(pout_params, L"ReturnValue", &retvalue);
552 if (FAILED(hres)) {
553 retval = false;
554 goto error;
555 }
556
557 if (retvalue != 0 && retvalue != job_wait) {
558 retval = false;
559 goto error;
560 }
561
562 if (retvalue == job_wait) {
563 WCHAR job_path[2048];
564 hres = get_str_value(pout_params, L"Job", job_path,
565 sizeof(job_path) / sizeof(WCHAR));
566 if (FAILED(hres)) {
567 retval = false;
568 goto error;
569 }
570 hres = wait_for_job(psvc, job_path);
571 if (FAILED(hres)) {
572 retval = false;
573 }
574 }
575
576error:
577 VariantClear(&vt_prop);
578
579 if (pcontext != NULL) {
580 pcontext->lpVtbl->Release(pcontext);
581 pcontext = NULL;
582 }
583 if (psa != NULL) {
584 SafeArrayDestroy(psa);
585 psa = NULL;
586 }
587 if (pcls_obj != NULL) {
588 pcls_obj->lpVtbl->Release(pcls_obj);
589 pcls_obj = NULL;
590 }
591 if (wide_name != NULL) {
592 free(wide_name);
593 wide_name = NULL;
594 }
595 if (!retval) {
596 get_hres_error(hres);
597 }
598 if (pinput_params != NULL) {
599 pinput_params->lpVtbl->Release(pinput_params);
600 pinput_params = NULL;
601 }
602 if (pout_params != NULL) {
603 pout_params->lpVtbl->Release(pout_params);
604 pout_params = NULL;
605 }
606 if (psvc != NULL) {
607 psvc->lpVtbl->Release(psvc);
608 psvc = NULL;
609 }
610 if (ploc != NULL) {
611 ploc->lpVtbl->Release(ploc);
612 ploc = NULL;
613 }
614 if (pclass_instance != NULL) {
615 pclass_instance->lpVtbl->Release(pclass_instance);
616 pclass_instance = NULL;
617 }
618 if (penumerate != NULL) {
619 penumerate->lpVtbl->Release(penumerate);
620 penumerate = NULL;
621 }
622
623 CoUninitialize();
624 return retval;
625}
626
c4129270 627
da467899
AS
628/* This function will create an internal port on the switch given a given name
629 * executing the method AddResourceSettings as per documentation:
630 * https://msdn.microsoft.com/en-us/library/hh850019%28v=vs.85%29.aspx.
631 * It will verify if the port is already defined, in which case it will use
632 * the specific port, and if the forwarding extension "Open vSwitch Extension"
633 * is enabled and running only on a single switch.
634 * After the port is created and bound to the switch we will disable the
635 * created net adapter and rename it to match the OVS bridge name .*/
636boolean
637create_wmi_port(char *name) {
638 HRESULT hres = 0;
639 boolean retval = true;
640
641 BSTR text_object_string = NULL;
642
643 IWbemLocator *ploc = NULL;
644 IWbemContext *pcontext = NULL;
645 IWbemServices *psvc = NULL;
646 IEnumWbemClassObject *penumerate = NULL;
647 IWbemClassObject *default_settings_data = NULL;
648 IWbemClassObject *default_system = NULL;
649 IWbemClassObject *pcls_obj = NULL;
650 IWbemClassObject *pclass = NULL;
651 IWbemClassObject *pinput_params = NULL;
652 IWbemClassObject *pclass_instance = NULL;
653 IWbemObjectTextSrc *text_object = NULL;
654 IWbemClassObject *pout_params = NULL;
655
656 wchar_t *wide_name = NULL;
657 VARIANT vt_prop;
658 VARIANT switch_setting_path;
659 VARIANT new_name;
660 SAFEARRAY *psa = SafeArrayCreateVector(VT_BSTR, 0, 1);
661 VARIANT variant_array;
662 LONG count[1];
663
664 VariantInit(&vt_prop);
665 VariantInit(&switch_setting_path);
666 sanitize_port_name(name);
667
668 if (psa == NULL) {
669 VLOG_WARN("Could not allocate memory for a SAFEARRAY");
670 retval = false;
671 goto error;
672 }
673
674 if (!initialize_wmi(&ploc, &pcontext)) {
675 VLOG_WARN("Could not initialize DCOM");
676 retval = false;
677 goto error;
678 }
679
680 if (!connect_set_security(ploc, pcontext, L"Root\\Virtualization\\v2",
681 &psvc)) {
682 VLOG_WARN("Could not connect and set security for virtualization");
683 retval = false;
684 goto error;
685 }
686
687 /* Check if the element already exists on the switch. */
c4129270 688 wchar_t internal_port_query[WMI_QUERY_COUNT] = L"SELECT * FROM "
58b34b00 689 L"CIM_EthernetPort WHERE ElementName = \"";
da467899 690
95c36eb7 691 wide_name = xmalloc((strlen(name) + 1) * sizeof(wchar_t));
da467899
AS
692
693 if (!tranform_wide(name, wide_name)) {
694 retval = false;
695 goto error;
696 }
da467899 697
c4129270
SV
698 wcscat_s(internal_port_query, WMI_QUERY_COUNT, wide_name);
699
700 wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\"");
da467899
AS
701 hres = psvc->lpVtbl->ExecQuery(psvc,
702 L"WQL",
703 internal_port_query,
704 WBEM_FLAG_FORWARD_ONLY |
705 WBEM_FLAG_RETURN_IMMEDIATELY,
706 NULL,
707 &penumerate);
708
709 if (FAILED(hres)) {
710 retval = false;
711 goto error;
712 }
713
714 if (get_first_element(penumerate, &pcls_obj)) {
715 VLOG_WARN("Port with name: %s already defined on the switch", name);
716 goto error;
717 }
718 penumerate->lpVtbl->Release(penumerate);
719 penumerate = NULL;
720
721 /* Check if the extension is enabled and running. Also check if the
722 * the extension is enabled on more than one switch. */
723 hres = psvc->lpVtbl->ExecQuery(psvc,
724 L"WQL",
725 L"SELECT * "
726 L"FROM Msvm_EthernetSwitchExtension "
727 L"WHERE "
728 L"ElementName=\"Open vSwitch Extension\" "
729 L"AND EnabledState=2 "
730 L"AND HealthState=5",
731 WBEM_FLAG_FORWARD_ONLY |
732 WBEM_FLAG_RETURN_IMMEDIATELY,
733 NULL,
734 &penumerate);
735
736 if (FAILED(hres)) {
737 retval = false;
738 goto error;
739 }
740
741 if (!get_first_element(penumerate, &pcls_obj)) {
742 VLOG_WARN("Open vSwitch Extension is not enabled on any switch");
743 retval = false;
744 goto error;
745 }
c4129270 746 wcscpy_s(internal_port_query, WMI_QUERY_COUNT,
da467899
AS
747 L"SELECT * FROM Msvm_VirtualEthernetSwitch WHERE Name = \"");
748
749 hres = pcls_obj->lpVtbl->Get(pcls_obj, L"SystemName", 0,
750 &vt_prop, 0, 0);
751 if (FAILED(hres)) {
752 retval = false;
753 goto error;
754 }
755
c4129270 756 wcscat_s(internal_port_query, WMI_QUERY_COUNT,
da467899
AS
757 vt_prop.bstrVal);
758
759 VariantClear(&vt_prop);
760 pcls_obj->lpVtbl->Release(pcls_obj);
761 pcls_obj = NULL;
762
763 if (get_first_element(penumerate, &pcls_obj)) {
764 VLOG_WARN("The extension is activated on more than one switch, "
765 "aborting operation. Please activate the extension on a "
766 "single switch");
767 retval = false;
768 goto error;
769 }
770 penumerate->lpVtbl->Release(penumerate);
771 penumerate = NULL;
772 if (pcls_obj != NULL) {
773 pcls_obj->lpVtbl->Release(pcls_obj);
774 pcls_obj = NULL;
775 }
776
777 /* Get the switch object on which the extension is activated. */
c4129270 778 wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\"");
da467899
AS
779 hres = psvc->lpVtbl->ExecQuery(psvc,
780 L"WQL",
781 internal_port_query,
782 WBEM_FLAG_FORWARD_ONLY |
783 WBEM_FLAG_RETURN_IMMEDIATELY,
784 NULL,
785 &penumerate);
786
787 if (FAILED(hres)) {
788 retval = false;
789 goto error;
790 }
791
792 if (!get_first_element(penumerate, &pcls_obj)) {
793 VLOG_WARN("Could not get the switch object on which the extension is"
794 "activated");
795 retval = false;
796 goto error;
797 }
798 penumerate->lpVtbl->Release(penumerate);
799 penumerate = NULL;
800
801 hres = pcls_obj->lpVtbl->Get(pcls_obj, L"ElementName", 0, &vt_prop, 0, 0);
802
803 if (FAILED(hres)) {
804 retval = false;
805 goto error;
806 }
807
c4129270 808 wcscpy_s(internal_port_query, WMI_QUERY_COUNT,
da467899
AS
809 L"SELECT * FROM Msvm_VirtualEthernetSwitchSettingData WHERE "
810 L"ElementName = \"");
811
c4129270 812 wcscat_s(internal_port_query, WMI_QUERY_COUNT,
da467899
AS
813 vt_prop.bstrVal);
814 VariantClear(&vt_prop);
815
816 hres = pcls_obj->lpVtbl->Get(pcls_obj, L"Name", 0, &vt_prop, 0, 0);
817
818 if (FAILED(hres)) {
819 retval = false;
820 goto error;
821 }
822 pcls_obj->lpVtbl->Release(pcls_obj);
823 pcls_obj = NULL;
824
825 /* Should be enough to give the InstanceID, from msdn documentation:
826 * Uniquely identifies an instance of this class. This property is
827 * inherited from CIM_SettingData and is always
828 * set to "Microsoft:GUID\DeviceSpecificData". */
c4129270 829 wcscat_s(internal_port_query, WMI_QUERY_COUNT,
da467899 830 L"\" AND InstanceID = \"Microsoft:");
c4129270 831 wcscat_s(internal_port_query, WMI_QUERY_COUNT,
da467899 832 vt_prop.bstrVal);
c4129270 833 wcscat_s(internal_port_query, WMI_QUERY_COUNT,
da467899
AS
834 L"\"");
835
836 VariantClear(&vt_prop);
837
838 /* Retrieve the Msvm_VirtualEthernetSwitchSettingData pinned to the switch
839 * object on which the extension is activated. */
840 hres = psvc->lpVtbl->ExecQuery(psvc,
841 L"WQL",
842 internal_port_query,
843 WBEM_FLAG_FORWARD_ONLY |
844 WBEM_FLAG_RETURN_IMMEDIATELY,
845 NULL,
846 &penumerate);
847
848 if (FAILED(hres)) {
849 retval = false;
850 goto error;
851 }
852
853 if (!get_first_element(penumerate, &pcls_obj)) {
854 VLOG_WARN("Could not get the first "
855 "Msvm_VirtualEthernetSwitchSettingData object");
856 retval = false;
857 goto error;
858 }
859 penumerate->lpVtbl->Release(penumerate);
860 penumerate = NULL;
861
862 hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &switch_setting_path,
863 0, 0);
864
865 if (FAILED(hres)) {
866 retval = false;
867 goto error;
868 }
869 pcls_obj->lpVtbl->Release(pcls_obj);
870 pcls_obj = NULL;
871
872 /* Retrieve a default allocation port. This object will be later filled
873 * with optional data to create an switch internal port. */
874 hres = psvc->lpVtbl->ExecQuery(psvc,
875 L"WQL",
876 L"SELECT * FROM "
877 L"Msvm_EthernetPortAllocationSettingData "
878 L"WHERE InstanceID LIKE '%%%%\\\\Default' "
879 L"AND ResourceSubType = "
880 L"'Microsoft:Hyper-V:Ethernet Connection'",
881 WBEM_FLAG_FORWARD_ONLY |
882 WBEM_FLAG_RETURN_IMMEDIATELY,
883 NULL,
884 &penumerate);
885
886 if (FAILED(hres)) {
887 retval = false;
888 goto error;
889 }
890
891 if (!get_first_element(penumerate, &default_settings_data)) {
892 VLOG_WARN("Could not retrieve default allocation port object");
893 retval = false;
894 goto error;
895 }
896 penumerate->lpVtbl->Release(penumerate);
897 penumerate = NULL;
898
899 /* Retrieve the default computer system on which the port allocation will
900 * be hosted.
901 * Instead of querying using Description, we can query using InstallDate.
902 * From MSDN documentation regarding InstallDate:
903 * The date and time the virtual machine configuration was created for
904 * a virtual machine, or Null, for a management operating system. */
905 hres = psvc->lpVtbl->ExecQuery(psvc,
906 L"WQL",
907 L"SELECT * FROM Msvm_ComputerSystem WHERE "
908 L"InstallDate is NULL",
909 WBEM_FLAG_FORWARD_ONLY |
910 WBEM_FLAG_RETURN_IMMEDIATELY,
911 NULL,
912 &penumerate);
913
914 if (FAILED(hres)) {
915 retval = false;
916 goto error;
917 }
918
919 if (!get_first_element(penumerate, &default_system)) {
920 VLOG_WARN("Could not retrieve default computer system object");
921 retval = false;
922 goto error;
923 }
924
925 hres = default_system->lpVtbl->Get(default_system, L"__PATH",
926 0, &vt_prop, 0, 0);
927 if (FAILED(hres)) {
928 retval = false;
929 goto error;
930 }
931 penumerate->lpVtbl->Release(penumerate);
932 penumerate = NULL;
933
934 count[0] = 0;
935 hres = SafeArrayPutElement(psa, count, vt_prop.bstrVal);
936
937 if (FAILED(hres)) {
938 retval = false;
939 goto error;
940 }
941
942 VariantClear(&vt_prop);
943 variant_array.vt = VT_ARRAY | VT_BSTR;
944 variant_array.parray = psa;
945 hres = default_settings_data->lpVtbl->Put(default_settings_data,
946 L"HostResource", 0,
947 &variant_array, 0);
948 if (FAILED(hres)) {
949 retval = false;
950 goto error;
951 }
952
953 hres = psvc->lpVtbl->GetObject(psvc,
954 L"Msvm_VirtualEthernetSwitchManagementService",
955 0, NULL, &pclass, NULL);
956 if (FAILED(hres)) {
957 retval = false;
958 goto error;
959 }
960
961 hres = pclass->lpVtbl->GetMethod(pclass, L"AddResourceSettings", 0,
962 &pinput_params, NULL);
963 if (FAILED(hres)) {
964 retval = false;
965 goto error;
966 }
967
968 hres = pinput_params->lpVtbl->SpawnInstance(pinput_params, 0,
969 &pclass_instance);
970 if (FAILED(hres)) {
971 retval = false;
972 goto error;
973 }
974
975 /* Store the switch setting path retrieved above in the affected
976 * configuration field of the class instance. */
977 hres = pclass_instance->lpVtbl->Put(pclass_instance,
978 L"AffectedConfiguration", 0,
979 &switch_setting_path, 0);
980
981 if (FAILED(hres)) {
982 retval = false;
983 goto error;
984 }
985
986 /* Store the port name in the ElementName field of the default allocation
987 * data. */
988 vt_prop.vt = VT_BSTR;
989 vt_prop.bstrVal = SysAllocString(wide_name);
990 hres = default_settings_data->lpVtbl->Put(default_settings_data,
991 L"ElementName", 0,
992 &vt_prop, 0);
993 VariantClear(&vt_prop);
994 if (FAILED(hres)) {
995 retval = false;
996 goto error;
997 }
998
999 /* Retrieve and store the serialized data of the modified default switch
1000 * settings data. */
1001 hres = CoCreateInstance(&CLSID_WbemObjectTextSrc,
1002 NULL,
1003 CLSCTX_INPROC_SERVER,
1004 &IID_IWbemObjectTextSrc,
1005 (void**)&text_object);
1006 if (FAILED(hres)) {
1007 retval = false;
1008 goto error;
1009 }
1010
1011 hres = text_object->lpVtbl->GetText(text_object, 0,
1012 default_settings_data,
1013 WMI_OBJ_TEXT_WMI_DTD_2_0,
1014 pcontext,
1015 &text_object_string);
1016 if (FAILED(hres)) {
1017 retval = false;
1018 goto error;
1019 }
1020 hres = SafeArrayDestroy(psa);
1021 if (FAILED(hres)) {
1022 VLOG_WARN("Could not clear the data of the array");
1023 retval = false;
1024 goto error;
1025 }
1026
1027 psa = SafeArrayCreateVector(VT_BSTR, 0, 1);
1028
1029 if (psa == NULL) {
1030 VLOG_WARN("Could not allocate memory for a SAFEARRAY");
1031 retval = false;
1032 goto error;
1033 }
1034
1035 count[0] = 0;
1036 variant_array.parray = psa;
1037 hres = SafeArrayPutElement(psa, count, text_object_string);
1038 if (FAILED(hres)) {
1039 retval = false;
1040 goto error;
1041 }
1042 hres = pclass_instance->lpVtbl->Put(pclass_instance, L"ResourceSettings",
1043 0, &variant_array, 0);
1044 if (FAILED(hres)) {
1045 retval = false;
1046 goto error;
1047 }
1048
1049 /* Get the object of the switch service. */
1050 hres = psvc->lpVtbl->ExecQuery(psvc,
1051 L"WQL",
1052 L"SELECT * FROM "
1053 L"Msvm_VirtualEthernetSwitchManagementService",
1054 WBEM_FLAG_FORWARD_ONLY |
1055 WBEM_FLAG_RETURN_IMMEDIATELY,
1056 NULL,
1057 &penumerate);
1058 if (FAILED(hres)) {
1059 retval = false;
1060 goto error;
1061 }
1062
1063 if (!get_first_element(penumerate, &pcls_obj)) {
1064 VLOG_WARN("Could not get the object of the switch service");
1065 retval = false;
1066 goto error;
1067 }
1068 penumerate->lpVtbl->Release(penumerate);
1069 penumerate = NULL;
1070
1071 hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0);
1072 if (FAILED(hres)) {
1073 retval = false;
1074 goto error;
1075 }
1076 pcls_obj->lpVtbl->Release(pcls_obj);
1077 pcls_obj = NULL;
1078
1079 /* Try to add the port to the switch. */
1080 hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal,
1081 L"AddResourceSettings", 0,
1082 pcontext, pclass_instance, &pout_params,
1083 NULL);
1084 if (FAILED(hres)) {
1085 retval = false;
1086 goto error;
1087 }
1088
1089 unsigned int retvalue = 0;
1090 hres = get_uint_value(pout_params, L"ReturnValue", &retvalue);
1091 if (FAILED(hres)) {
1092 retval = false;
1093 goto error;
1094 }
1095
1096 if (retvalue != 0 && retvalue != job_wait) {
1097 retval = false;
1098 goto error;
1099 }
1100
1101 if (retvalue == job_wait) {
1102 WCHAR job_path[2048];
1103 hres = get_str_value(pout_params, L"Job", job_path,
1104 sizeof(job_path) / sizeof(WCHAR));
1105 if (FAILED(hres)) {
1106 retval = false;
1107 goto error;
1108 }
1109 hres = wait_for_job(psvc, job_path);
1110 if (FAILED(hres)) {
1111 retval = false;
1112 goto error;
1113 }
1114 }
1115
1116 pclass->lpVtbl->Release(pclass);
1117 pclass = NULL;
1118 pclass_instance->lpVtbl->Release(pclass_instance);
1119 pclass_instance = NULL;
1120 pinput_params->lpVtbl->Release(pinput_params);
1121 pinput_params = NULL;
1122 psvc->lpVtbl->Release(psvc);
1123 psvc = NULL;
1124 VariantClear(&vt_prop);
1125
1126 if (!connect_set_security(ploc, pcontext, L"Root\\StandardCimv2",
1127 &psvc)) {
1128 VLOG_WARN("Could not connect and set security for CIM");
1129 retval = false;
1130 goto error;
1131 }
1132
c4129270 1133 wcscpy_s(internal_port_query, WMI_QUERY_COUNT,
da467899 1134 L"SELECT * FROM MSFT_NetAdapter WHERE Name LIKE '%%");
c4129270
SV
1135 wcscat_s(internal_port_query, WMI_QUERY_COUNT, wide_name);
1136 wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"%%'");
da467899
AS
1137
1138 /* Get the object with the port name equal to name on the CIM. */
1139 hres = psvc->lpVtbl->ExecQuery(psvc,
1140 L"WQL",
1141 internal_port_query,
1142 WBEM_FLAG_FORWARD_ONLY |
1143 WBEM_FLAG_RETURN_IMMEDIATELY,
1144 NULL,
1145 &penumerate);
1146
1147 if (!get_first_element(penumerate, &pcls_obj)) {
1148 VLOG_WARN("Element name: %s not found in CIM", name);
1149 retval = false;
1150 goto error;
1151 }
1152 penumerate->lpVtbl->Release(penumerate);
1153 penumerate = NULL;
1154 pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0);
1155 pcls_obj->lpVtbl->Release(pcls_obj);
1156 pcls_obj = NULL;
1157
1158 /* Disable the adapter with port name equal with name. */
1159 hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal, L"Disable", 0,
1160 pcontext, NULL, NULL, NULL);
1161
1162 if (FAILED(hres)) {
1163 retval = false;
1164 goto error;
1165 }
1166
1167 hres = psvc->lpVtbl->GetObject(psvc, L"MSFT_NetAdapter", 0, NULL, &pclass,
1168 NULL);
1169 if (FAILED(hres)) {
1170 retval = false;
1171 goto error;
1172 }
1173
1174 hres = pclass->lpVtbl->GetMethod(pclass, L"Rename", 0, &pinput_params,
1175 NULL);
1176 if (FAILED(hres)) {
1177 retval = false;
1178 goto error;
1179 }
1180
1181 hres = pinput_params->lpVtbl->SpawnInstance(pinput_params, 0,
1182 &pclass_instance);
1183 if (FAILED(hres)) {
1184 retval = false;
1185 goto error;
1186 }
1187
1188 VariantInit(&new_name);
1189 new_name.vt = VT_BSTR;
1190 new_name.bstrVal = wide_name;
1191 hres = pclass_instance->lpVtbl->Put(pclass_instance, L"NewName", 0,
1192 &new_name, 0);
1193 if (FAILED(hres)) {
1194 retval = false;
1195 goto error;
1196 }
1197 hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal, L"Rename", 0,
1198 pcontext, pclass_instance, NULL, NULL);
1199 if (FAILED(hres)) {
1200 retval = false;
1201 }
1202
1203error:
1204 if (text_object_string != NULL) {
1205 SysFreeString(text_object_string);
1206 text_object_string = NULL;
1207 }
1208 if (psa != NULL) {
1209 SafeArrayDestroy(psa);
1210 psa = NULL;
1211 }
1212 if (ploc != NULL) {
1213 ploc->lpVtbl->Release(ploc);
1214 ploc = NULL;
1215 }
1216 if (pcontext != NULL) {
1217 pcontext->lpVtbl->Release(pcontext);
1218 pcontext = NULL;
1219 }
1220 if (psvc != NULL) {
1221 psvc->lpVtbl->Release(psvc);
1222 psvc = NULL;
1223 }
1224 if (penumerate != NULL) {
1225 penumerate->lpVtbl->Release(penumerate);
1226 penumerate = NULL;
1227 }
1228 if (default_settings_data != NULL) {
1229 default_settings_data->lpVtbl->Release(default_settings_data);
1230 default_settings_data = NULL;
1231 }
1232 if (default_system != NULL) {
1233 default_system->lpVtbl->Release(default_system);
1234 default_system = NULL;
1235 }
1236 if (pcls_obj != NULL) {
1237 pcls_obj->lpVtbl->Release(pcls_obj);
1238 pcls_obj = NULL;
1239 }
1240 if (pclass != NULL) {
1241 pclass->lpVtbl->Release(pclass);
1242 pclass = NULL;
1243 }
1244 if (pinput_params != NULL) {
1245 pinput_params->lpVtbl->Release(pinput_params);
1246 pinput_params = NULL;
1247 }
1248 if (pclass_instance != NULL) {
1249 pclass_instance->lpVtbl->Release(pclass_instance);
1250 pclass_instance = NULL;
1251 }
1252 if (text_object != NULL) {
1253 text_object->lpVtbl->Release(text_object);
1254 text_object = NULL;
1255 }
1256 if (pout_params != NULL) {
1257 pout_params->lpVtbl->Release(pout_params);
1258 pout_params = NULL;
1259 }
1260 if (wide_name != NULL) {
1261 free(wide_name);
1262 wide_name = NULL;
1263 }
1264 VariantClear(&vt_prop);
1265 VariantClear(&switch_setting_path);
1266
1267 if (!retval) {
1268 get_hres_error(hres);
1269 }
1270 CoUninitialize();
1271 return retval;
1272}