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