]>
Commit | Line | Data |
---|---|---|
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 | |
25 | VLOG_DEFINE_THIS_MODULE(wmi); | |
26 | ||
27 | /* WMI Job values. */ | |
28 | enum job_status | |
29 | { | |
30 | job_starting = 3, | |
31 | job_running = 4, | |
32 | job_completed = 7, | |
33 | job_wait = 4096 | |
34 | }; | |
35 | ||
36 | static char * | |
37 | sanitize_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.*/ | |
56 | static void | |
57 | get_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 | ||
68 | static boolean | |
69 | check_return_value(HRESULT hres) | |
70 | { | |
71 | if (FAILED(hres)) { | |
72 | get_hres_error(hres); | |
73 | return false; | |
74 | } | |
75 | ||
76 | return true; | |
77 | } | |
78 | ||
79 | static HRESULT | |
80 | get_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. */ | |
98 | static HRESULT | |
99 | get_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. */ | |
111 | static HRESULT | |
112 | get_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. */ | |
124 | static HRESULT | |
125 | get_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. */ | |
138 | static HRESULT | |
139 | get_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 */ | |
157 | static HRESULT | |
158 | wait_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. */ | |
208 | static boolean | |
209 | initialize_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. */ | |
265 | static boolean | |
266 | connect_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 */ | |
306 | static boolean | |
307 | get_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 * */ | |
330 | static boolean | |
331 | tranform_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 */ | |
361 | boolean | |
362 | delete_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 | ||
409 | wide_name = malloc((strlen(name) + 1) * sizeof(wchar_t)); | |
410 | if (wide_name == NULL) { | |
411 | VLOG_WARN("Could not allocate memory for wide string"); | |
412 | retval = false; | |
413 | goto error; | |
414 | } | |
415 | ||
416 | if (!tranform_wide(name, wide_name)) { | |
417 | retval = false; | |
418 | goto error; | |
419 | } | |
c4129270 | 420 | wcscat_s(internal_port_query, WMI_QUERY_COUNT, wide_name); |
da467899 | 421 | |
c4129270 | 422 | wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\""); |
da467899 AS |
423 | |
424 | hres = psvc->lpVtbl->ExecQuery(psvc, | |
425 | L"WQL", | |
426 | internal_port_query, | |
427 | WBEM_FLAG_FORWARD_ONLY | | |
428 | WBEM_FLAG_RETURN_IMMEDIATELY, | |
429 | NULL, | |
430 | &penumerate); | |
431 | ||
432 | if (FAILED(hres)) { | |
433 | retval = false; | |
434 | goto error; | |
435 | } | |
436 | ||
437 | /* Get the element path on the switch which will be deleted. */ | |
438 | if (!get_first_element(penumerate, &pcls_obj)) { | |
439 | retval = false; | |
440 | goto error; | |
441 | } | |
442 | penumerate->lpVtbl->Release(penumerate); | |
443 | penumerate = NULL; | |
444 | ||
445 | hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0); | |
446 | ||
447 | if (FAILED(hres)) { | |
448 | retval = false; | |
449 | goto error; | |
450 | } | |
451 | pcls_obj->lpVtbl->Release(pcls_obj); | |
452 | pcls_obj = NULL; | |
453 | ||
454 | /* Get the class object and the parameters it can have. */ | |
455 | hres = psvc->lpVtbl->GetObject(psvc, | |
456 | L"Msvm_VirtualEthernetSwitchManagementService", 0, NULL, &pcls_obj, | |
457 | NULL); | |
458 | ||
459 | if (FAILED(hres)) { | |
460 | retval = false; | |
461 | goto error; | |
462 | } | |
463 | ||
464 | hres = pcls_obj->lpVtbl->GetMethod(pcls_obj, L"RemoveResourceSettings", 0, | |
465 | &pinput_params, NULL); | |
466 | ||
467 | if (FAILED(hres)) { | |
468 | retval = false; | |
469 | goto error; | |
470 | } | |
471 | pcls_obj->lpVtbl->Release(pcls_obj); | |
472 | pcls_obj = NULL; | |
473 | ||
474 | hres = pinput_params->lpVtbl->SpawnInstance(pinput_params, 0, | |
475 | &pclass_instance); | |
476 | if (FAILED(hres)) { | |
477 | retval = false; | |
478 | goto error; | |
479 | } | |
480 | ||
481 | count[0] = 0; | |
482 | ||
483 | hres = SafeArrayPutElement(psa, count, vt_prop.bstrVal); | |
484 | ||
485 | if (FAILED(hres)) { | |
486 | retval = false; | |
487 | goto error; | |
488 | } | |
489 | ||
490 | VariantClear(&vt_prop); | |
491 | VariantInit(&vt_prop); | |
492 | variant_array.vt = VT_ARRAY | VT_BSTR; | |
493 | variant_array.parray = psa; | |
494 | ||
495 | hres = pclass_instance->lpVtbl->Put(pclass_instance, L"ResourceSettings", 0, | |
496 | &variant_array, 0); | |
497 | if (FAILED(hres)) { | |
498 | retval = false; | |
499 | goto error; | |
500 | } | |
501 | ||
502 | /* Get the object of the Msvm_VirtualEthernetSwitchManagementService which | |
503 | * we need to invoke the port deletion. */ | |
504 | hres = psvc->lpVtbl->ExecQuery(psvc, | |
505 | L"WQL", | |
506 | L"SELECT * FROM " | |
507 | L"Msvm_VirtualEthernetSwitchManagementService", | |
508 | WBEM_FLAG_FORWARD_ONLY | | |
509 | WBEM_FLAG_RETURN_IMMEDIATELY, | |
510 | NULL, | |
511 | &penumerate); | |
512 | ||
513 | if (FAILED(hres)) { | |
514 | retval = false; | |
515 | goto error; | |
516 | } | |
517 | ||
518 | if (!get_first_element(penumerate, &pcls_obj)) { | |
519 | retval = false; | |
520 | goto error; | |
521 | } | |
522 | penumerate->lpVtbl->Release(penumerate); | |
523 | penumerate = NULL; | |
524 | ||
525 | hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0); | |
526 | ||
527 | if (FAILED(hres)) { | |
528 | retval = false; | |
529 | goto error; | |
530 | } | |
531 | ||
532 | pcls_obj->lpVtbl->Release(pcls_obj); | |
533 | pcls_obj = NULL; | |
534 | ||
535 | /* Invoke the delete port method. */ | |
536 | hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal, | |
537 | L"RemoveResourceSettings", 0, | |
538 | pcontext, pclass_instance, &pout_params, | |
539 | NULL); | |
540 | if (FAILED(hres)) { | |
541 | retval = false; | |
542 | goto error; | |
543 | } | |
544 | VariantClear(&vt_prop); | |
545 | VariantInit(&vt_prop); | |
546 | ||
547 | hres = pout_params->lpVtbl->Get(pout_params, L"ReturnValue", 0, | |
548 | &vt_prop, NULL, 0); | |
549 | ||
550 | if (FAILED(hres)) { | |
551 | retval = false; | |
552 | goto error; | |
553 | } | |
554 | ||
555 | unsigned int retvalue = 0; | |
556 | hres = get_uint_value(pout_params, L"ReturnValue", &retvalue); | |
557 | if (FAILED(hres)) { | |
558 | retval = false; | |
559 | goto error; | |
560 | } | |
561 | ||
562 | if (retvalue != 0 && retvalue != job_wait) { | |
563 | retval = false; | |
564 | goto error; | |
565 | } | |
566 | ||
567 | if (retvalue == job_wait) { | |
568 | WCHAR job_path[2048]; | |
569 | hres = get_str_value(pout_params, L"Job", job_path, | |
570 | sizeof(job_path) / sizeof(WCHAR)); | |
571 | if (FAILED(hres)) { | |
572 | retval = false; | |
573 | goto error; | |
574 | } | |
575 | hres = wait_for_job(psvc, job_path); | |
576 | if (FAILED(hres)) { | |
577 | retval = false; | |
578 | } | |
579 | } | |
580 | ||
581 | error: | |
582 | VariantClear(&vt_prop); | |
583 | ||
584 | if (pcontext != NULL) { | |
585 | pcontext->lpVtbl->Release(pcontext); | |
586 | pcontext = NULL; | |
587 | } | |
588 | if (psa != NULL) { | |
589 | SafeArrayDestroy(psa); | |
590 | psa = NULL; | |
591 | } | |
592 | if (pcls_obj != NULL) { | |
593 | pcls_obj->lpVtbl->Release(pcls_obj); | |
594 | pcls_obj = NULL; | |
595 | } | |
596 | if (wide_name != NULL) { | |
597 | free(wide_name); | |
598 | wide_name = NULL; | |
599 | } | |
600 | if (!retval) { | |
601 | get_hres_error(hres); | |
602 | } | |
603 | if (pinput_params != NULL) { | |
604 | pinput_params->lpVtbl->Release(pinput_params); | |
605 | pinput_params = NULL; | |
606 | } | |
607 | if (pout_params != NULL) { | |
608 | pout_params->lpVtbl->Release(pout_params); | |
609 | pout_params = NULL; | |
610 | } | |
611 | if (psvc != NULL) { | |
612 | psvc->lpVtbl->Release(psvc); | |
613 | psvc = NULL; | |
614 | } | |
615 | if (ploc != NULL) { | |
616 | ploc->lpVtbl->Release(ploc); | |
617 | ploc = NULL; | |
618 | } | |
619 | if (pclass_instance != NULL) { | |
620 | pclass_instance->lpVtbl->Release(pclass_instance); | |
621 | pclass_instance = NULL; | |
622 | } | |
623 | if (penumerate != NULL) { | |
624 | penumerate->lpVtbl->Release(penumerate); | |
625 | penumerate = NULL; | |
626 | } | |
627 | ||
628 | CoUninitialize(); | |
629 | return retval; | |
630 | } | |
631 | ||
c4129270 | 632 | |
da467899 AS |
633 | /* This function will create an internal port on the switch given a given name |
634 | * executing the method AddResourceSettings as per documentation: | |
635 | * https://msdn.microsoft.com/en-us/library/hh850019%28v=vs.85%29.aspx. | |
636 | * It will verify if the port is already defined, in which case it will use | |
637 | * the specific port, and if the forwarding extension "Open vSwitch Extension" | |
638 | * is enabled and running only on a single switch. | |
639 | * After the port is created and bound to the switch we will disable the | |
640 | * created net adapter and rename it to match the OVS bridge name .*/ | |
641 | boolean | |
642 | create_wmi_port(char *name) { | |
643 | HRESULT hres = 0; | |
644 | boolean retval = true; | |
645 | ||
646 | BSTR text_object_string = NULL; | |
647 | ||
648 | IWbemLocator *ploc = NULL; | |
649 | IWbemContext *pcontext = NULL; | |
650 | IWbemServices *psvc = NULL; | |
651 | IEnumWbemClassObject *penumerate = NULL; | |
652 | IWbemClassObject *default_settings_data = NULL; | |
653 | IWbemClassObject *default_system = NULL; | |
654 | IWbemClassObject *pcls_obj = NULL; | |
655 | IWbemClassObject *pclass = NULL; | |
656 | IWbemClassObject *pinput_params = NULL; | |
657 | IWbemClassObject *pclass_instance = NULL; | |
658 | IWbemObjectTextSrc *text_object = NULL; | |
659 | IWbemClassObject *pout_params = NULL; | |
660 | ||
661 | wchar_t *wide_name = NULL; | |
662 | VARIANT vt_prop; | |
663 | VARIANT switch_setting_path; | |
664 | VARIANT new_name; | |
665 | SAFEARRAY *psa = SafeArrayCreateVector(VT_BSTR, 0, 1); | |
666 | VARIANT variant_array; | |
667 | LONG count[1]; | |
668 | ||
669 | VariantInit(&vt_prop); | |
670 | VariantInit(&switch_setting_path); | |
671 | sanitize_port_name(name); | |
672 | ||
673 | if (psa == NULL) { | |
674 | VLOG_WARN("Could not allocate memory for a SAFEARRAY"); | |
675 | retval = false; | |
676 | goto error; | |
677 | } | |
678 | ||
679 | if (!initialize_wmi(&ploc, &pcontext)) { | |
680 | VLOG_WARN("Could not initialize DCOM"); | |
681 | retval = false; | |
682 | goto error; | |
683 | } | |
684 | ||
685 | if (!connect_set_security(ploc, pcontext, L"Root\\Virtualization\\v2", | |
686 | &psvc)) { | |
687 | VLOG_WARN("Could not connect and set security for virtualization"); | |
688 | retval = false; | |
689 | goto error; | |
690 | } | |
691 | ||
692 | /* Check if the element already exists on the switch. */ | |
c4129270 | 693 | wchar_t internal_port_query[WMI_QUERY_COUNT] = L"SELECT * FROM " |
da467899 AS |
694 | L"Msvm_InternalEthernetPort WHERE ElementName = \""; |
695 | ||
696 | wide_name = malloc((strlen(name) + 1) * sizeof(wchar_t)); | |
697 | if (wide_name == NULL) { | |
698 | VLOG_WARN("Could not allocate memory for wide string"); | |
699 | retval = false; | |
700 | goto error; | |
701 | } | |
702 | ||
703 | if (!tranform_wide(name, wide_name)) { | |
704 | retval = false; | |
705 | goto error; | |
706 | } | |
da467899 | 707 | |
c4129270 SV |
708 | wcscat_s(internal_port_query, WMI_QUERY_COUNT, wide_name); |
709 | ||
710 | wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\""); | |
da467899 AS |
711 | hres = psvc->lpVtbl->ExecQuery(psvc, |
712 | L"WQL", | |
713 | internal_port_query, | |
714 | WBEM_FLAG_FORWARD_ONLY | | |
715 | WBEM_FLAG_RETURN_IMMEDIATELY, | |
716 | NULL, | |
717 | &penumerate); | |
718 | ||
719 | if (FAILED(hres)) { | |
720 | retval = false; | |
721 | goto error; | |
722 | } | |
723 | ||
724 | if (get_first_element(penumerate, &pcls_obj)) { | |
725 | VLOG_WARN("Port with name: %s already defined on the switch", name); | |
726 | goto error; | |
727 | } | |
728 | penumerate->lpVtbl->Release(penumerate); | |
729 | penumerate = NULL; | |
730 | ||
731 | /* Check if the extension is enabled and running. Also check if the | |
732 | * the extension is enabled on more than one switch. */ | |
733 | hres = psvc->lpVtbl->ExecQuery(psvc, | |
734 | L"WQL", | |
735 | L"SELECT * " | |
736 | L"FROM Msvm_EthernetSwitchExtension " | |
737 | L"WHERE " | |
738 | L"ElementName=\"Open vSwitch Extension\" " | |
739 | L"AND EnabledState=2 " | |
740 | L"AND HealthState=5", | |
741 | WBEM_FLAG_FORWARD_ONLY | | |
742 | WBEM_FLAG_RETURN_IMMEDIATELY, | |
743 | NULL, | |
744 | &penumerate); | |
745 | ||
746 | if (FAILED(hres)) { | |
747 | retval = false; | |
748 | goto error; | |
749 | } | |
750 | ||
751 | if (!get_first_element(penumerate, &pcls_obj)) { | |
752 | VLOG_WARN("Open vSwitch Extension is not enabled on any switch"); | |
753 | retval = false; | |
754 | goto error; | |
755 | } | |
c4129270 | 756 | wcscpy_s(internal_port_query, WMI_QUERY_COUNT, |
da467899 AS |
757 | L"SELECT * FROM Msvm_VirtualEthernetSwitch WHERE Name = \""); |
758 | ||
759 | hres = pcls_obj->lpVtbl->Get(pcls_obj, L"SystemName", 0, | |
760 | &vt_prop, 0, 0); | |
761 | if (FAILED(hres)) { | |
762 | retval = false; | |
763 | goto error; | |
764 | } | |
765 | ||
c4129270 | 766 | wcscat_s(internal_port_query, WMI_QUERY_COUNT, |
da467899 AS |
767 | vt_prop.bstrVal); |
768 | ||
769 | VariantClear(&vt_prop); | |
770 | pcls_obj->lpVtbl->Release(pcls_obj); | |
771 | pcls_obj = NULL; | |
772 | ||
773 | if (get_first_element(penumerate, &pcls_obj)) { | |
774 | VLOG_WARN("The extension is activated on more than one switch, " | |
775 | "aborting operation. Please activate the extension on a " | |
776 | "single switch"); | |
777 | retval = false; | |
778 | goto error; | |
779 | } | |
780 | penumerate->lpVtbl->Release(penumerate); | |
781 | penumerate = NULL; | |
782 | if (pcls_obj != NULL) { | |
783 | pcls_obj->lpVtbl->Release(pcls_obj); | |
784 | pcls_obj = NULL; | |
785 | } | |
786 | ||
787 | /* Get the switch object on which the extension is activated. */ | |
c4129270 | 788 | wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\""); |
da467899 AS |
789 | hres = psvc->lpVtbl->ExecQuery(psvc, |
790 | L"WQL", | |
791 | internal_port_query, | |
792 | WBEM_FLAG_FORWARD_ONLY | | |
793 | WBEM_FLAG_RETURN_IMMEDIATELY, | |
794 | NULL, | |
795 | &penumerate); | |
796 | ||
797 | if (FAILED(hres)) { | |
798 | retval = false; | |
799 | goto error; | |
800 | } | |
801 | ||
802 | if (!get_first_element(penumerate, &pcls_obj)) { | |
803 | VLOG_WARN("Could not get the switch object on which the extension is" | |
804 | "activated"); | |
805 | retval = false; | |
806 | goto error; | |
807 | } | |
808 | penumerate->lpVtbl->Release(penumerate); | |
809 | penumerate = NULL; | |
810 | ||
811 | hres = pcls_obj->lpVtbl->Get(pcls_obj, L"ElementName", 0, &vt_prop, 0, 0); | |
812 | ||
813 | if (FAILED(hres)) { | |
814 | retval = false; | |
815 | goto error; | |
816 | } | |
817 | ||
c4129270 | 818 | wcscpy_s(internal_port_query, WMI_QUERY_COUNT, |
da467899 AS |
819 | L"SELECT * FROM Msvm_VirtualEthernetSwitchSettingData WHERE " |
820 | L"ElementName = \""); | |
821 | ||
c4129270 | 822 | wcscat_s(internal_port_query, WMI_QUERY_COUNT, |
da467899 AS |
823 | vt_prop.bstrVal); |
824 | VariantClear(&vt_prop); | |
825 | ||
826 | hres = pcls_obj->lpVtbl->Get(pcls_obj, L"Name", 0, &vt_prop, 0, 0); | |
827 | ||
828 | if (FAILED(hres)) { | |
829 | retval = false; | |
830 | goto error; | |
831 | } | |
832 | pcls_obj->lpVtbl->Release(pcls_obj); | |
833 | pcls_obj = NULL; | |
834 | ||
835 | /* Should be enough to give the InstanceID, from msdn documentation: | |
836 | * Uniquely identifies an instance of this class. This property is | |
837 | * inherited from CIM_SettingData and is always | |
838 | * set to "Microsoft:GUID\DeviceSpecificData". */ | |
c4129270 | 839 | wcscat_s(internal_port_query, WMI_QUERY_COUNT, |
da467899 | 840 | L"\" AND InstanceID = \"Microsoft:"); |
c4129270 | 841 | wcscat_s(internal_port_query, WMI_QUERY_COUNT, |
da467899 | 842 | vt_prop.bstrVal); |
c4129270 | 843 | wcscat_s(internal_port_query, WMI_QUERY_COUNT, |
da467899 AS |
844 | L"\""); |
845 | ||
846 | VariantClear(&vt_prop); | |
847 | ||
848 | /* Retrieve the Msvm_VirtualEthernetSwitchSettingData pinned to the switch | |
849 | * object on which the extension is activated. */ | |
850 | hres = psvc->lpVtbl->ExecQuery(psvc, | |
851 | L"WQL", | |
852 | internal_port_query, | |
853 | WBEM_FLAG_FORWARD_ONLY | | |
854 | WBEM_FLAG_RETURN_IMMEDIATELY, | |
855 | NULL, | |
856 | &penumerate); | |
857 | ||
858 | if (FAILED(hres)) { | |
859 | retval = false; | |
860 | goto error; | |
861 | } | |
862 | ||
863 | if (!get_first_element(penumerate, &pcls_obj)) { | |
864 | VLOG_WARN("Could not get the first " | |
865 | "Msvm_VirtualEthernetSwitchSettingData object"); | |
866 | retval = false; | |
867 | goto error; | |
868 | } | |
869 | penumerate->lpVtbl->Release(penumerate); | |
870 | penumerate = NULL; | |
871 | ||
872 | hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &switch_setting_path, | |
873 | 0, 0); | |
874 | ||
875 | if (FAILED(hres)) { | |
876 | retval = false; | |
877 | goto error; | |
878 | } | |
879 | pcls_obj->lpVtbl->Release(pcls_obj); | |
880 | pcls_obj = NULL; | |
881 | ||
882 | /* Retrieve a default allocation port. This object will be later filled | |
883 | * with optional data to create an switch internal port. */ | |
884 | hres = psvc->lpVtbl->ExecQuery(psvc, | |
885 | L"WQL", | |
886 | L"SELECT * FROM " | |
887 | L"Msvm_EthernetPortAllocationSettingData " | |
888 | L"WHERE InstanceID LIKE '%%%%\\\\Default' " | |
889 | L"AND ResourceSubType = " | |
890 | L"'Microsoft:Hyper-V:Ethernet Connection'", | |
891 | WBEM_FLAG_FORWARD_ONLY | | |
892 | WBEM_FLAG_RETURN_IMMEDIATELY, | |
893 | NULL, | |
894 | &penumerate); | |
895 | ||
896 | if (FAILED(hres)) { | |
897 | retval = false; | |
898 | goto error; | |
899 | } | |
900 | ||
901 | if (!get_first_element(penumerate, &default_settings_data)) { | |
902 | VLOG_WARN("Could not retrieve default allocation port object"); | |
903 | retval = false; | |
904 | goto error; | |
905 | } | |
906 | penumerate->lpVtbl->Release(penumerate); | |
907 | penumerate = NULL; | |
908 | ||
909 | /* Retrieve the default computer system on which the port allocation will | |
910 | * be hosted. | |
911 | * Instead of querying using Description, we can query using InstallDate. | |
912 | * From MSDN documentation regarding InstallDate: | |
913 | * The date and time the virtual machine configuration was created for | |
914 | * a virtual machine, or Null, for a management operating system. */ | |
915 | hres = psvc->lpVtbl->ExecQuery(psvc, | |
916 | L"WQL", | |
917 | L"SELECT * FROM Msvm_ComputerSystem WHERE " | |
918 | L"InstallDate is NULL", | |
919 | WBEM_FLAG_FORWARD_ONLY | | |
920 | WBEM_FLAG_RETURN_IMMEDIATELY, | |
921 | NULL, | |
922 | &penumerate); | |
923 | ||
924 | if (FAILED(hres)) { | |
925 | retval = false; | |
926 | goto error; | |
927 | } | |
928 | ||
929 | if (!get_first_element(penumerate, &default_system)) { | |
930 | VLOG_WARN("Could not retrieve default computer system object"); | |
931 | retval = false; | |
932 | goto error; | |
933 | } | |
934 | ||
935 | hres = default_system->lpVtbl->Get(default_system, L"__PATH", | |
936 | 0, &vt_prop, 0, 0); | |
937 | if (FAILED(hres)) { | |
938 | retval = false; | |
939 | goto error; | |
940 | } | |
941 | penumerate->lpVtbl->Release(penumerate); | |
942 | penumerate = NULL; | |
943 | ||
944 | count[0] = 0; | |
945 | hres = SafeArrayPutElement(psa, count, vt_prop.bstrVal); | |
946 | ||
947 | if (FAILED(hres)) { | |
948 | retval = false; | |
949 | goto error; | |
950 | } | |
951 | ||
952 | VariantClear(&vt_prop); | |
953 | variant_array.vt = VT_ARRAY | VT_BSTR; | |
954 | variant_array.parray = psa; | |
955 | hres = default_settings_data->lpVtbl->Put(default_settings_data, | |
956 | L"HostResource", 0, | |
957 | &variant_array, 0); | |
958 | if (FAILED(hres)) { | |
959 | retval = false; | |
960 | goto error; | |
961 | } | |
962 | ||
963 | hres = psvc->lpVtbl->GetObject(psvc, | |
964 | L"Msvm_VirtualEthernetSwitchManagementService", | |
965 | 0, NULL, &pclass, NULL); | |
966 | if (FAILED(hres)) { | |
967 | retval = false; | |
968 | goto error; | |
969 | } | |
970 | ||
971 | hres = pclass->lpVtbl->GetMethod(pclass, L"AddResourceSettings", 0, | |
972 | &pinput_params, NULL); | |
973 | if (FAILED(hres)) { | |
974 | retval = false; | |
975 | goto error; | |
976 | } | |
977 | ||
978 | hres = pinput_params->lpVtbl->SpawnInstance(pinput_params, 0, | |
979 | &pclass_instance); | |
980 | if (FAILED(hres)) { | |
981 | retval = false; | |
982 | goto error; | |
983 | } | |
984 | ||
985 | /* Store the switch setting path retrieved above in the affected | |
986 | * configuration field of the class instance. */ | |
987 | hres = pclass_instance->lpVtbl->Put(pclass_instance, | |
988 | L"AffectedConfiguration", 0, | |
989 | &switch_setting_path, 0); | |
990 | ||
991 | if (FAILED(hres)) { | |
992 | retval = false; | |
993 | goto error; | |
994 | } | |
995 | ||
996 | /* Store the port name in the ElementName field of the default allocation | |
997 | * data. */ | |
998 | vt_prop.vt = VT_BSTR; | |
999 | vt_prop.bstrVal = SysAllocString(wide_name); | |
1000 | hres = default_settings_data->lpVtbl->Put(default_settings_data, | |
1001 | L"ElementName", 0, | |
1002 | &vt_prop, 0); | |
1003 | VariantClear(&vt_prop); | |
1004 | if (FAILED(hres)) { | |
1005 | retval = false; | |
1006 | goto error; | |
1007 | } | |
1008 | ||
1009 | /* Retrieve and store the serialized data of the modified default switch | |
1010 | * settings data. */ | |
1011 | hres = CoCreateInstance(&CLSID_WbemObjectTextSrc, | |
1012 | NULL, | |
1013 | CLSCTX_INPROC_SERVER, | |
1014 | &IID_IWbemObjectTextSrc, | |
1015 | (void**)&text_object); | |
1016 | if (FAILED(hres)) { | |
1017 | retval = false; | |
1018 | goto error; | |
1019 | } | |
1020 | ||
1021 | hres = text_object->lpVtbl->GetText(text_object, 0, | |
1022 | default_settings_data, | |
1023 | WMI_OBJ_TEXT_WMI_DTD_2_0, | |
1024 | pcontext, | |
1025 | &text_object_string); | |
1026 | if (FAILED(hres)) { | |
1027 | retval = false; | |
1028 | goto error; | |
1029 | } | |
1030 | hres = SafeArrayDestroy(psa); | |
1031 | if (FAILED(hres)) { | |
1032 | VLOG_WARN("Could not clear the data of the array"); | |
1033 | retval = false; | |
1034 | goto error; | |
1035 | } | |
1036 | ||
1037 | psa = SafeArrayCreateVector(VT_BSTR, 0, 1); | |
1038 | ||
1039 | if (psa == NULL) { | |
1040 | VLOG_WARN("Could not allocate memory for a SAFEARRAY"); | |
1041 | retval = false; | |
1042 | goto error; | |
1043 | } | |
1044 | ||
1045 | count[0] = 0; | |
1046 | variant_array.parray = psa; | |
1047 | hres = SafeArrayPutElement(psa, count, text_object_string); | |
1048 | if (FAILED(hres)) { | |
1049 | retval = false; | |
1050 | goto error; | |
1051 | } | |
1052 | hres = pclass_instance->lpVtbl->Put(pclass_instance, L"ResourceSettings", | |
1053 | 0, &variant_array, 0); | |
1054 | if (FAILED(hres)) { | |
1055 | retval = false; | |
1056 | goto error; | |
1057 | } | |
1058 | ||
1059 | /* Get the object of the switch service. */ | |
1060 | hres = psvc->lpVtbl->ExecQuery(psvc, | |
1061 | L"WQL", | |
1062 | L"SELECT * FROM " | |
1063 | L"Msvm_VirtualEthernetSwitchManagementService", | |
1064 | WBEM_FLAG_FORWARD_ONLY | | |
1065 | WBEM_FLAG_RETURN_IMMEDIATELY, | |
1066 | NULL, | |
1067 | &penumerate); | |
1068 | if (FAILED(hres)) { | |
1069 | retval = false; | |
1070 | goto error; | |
1071 | } | |
1072 | ||
1073 | if (!get_first_element(penumerate, &pcls_obj)) { | |
1074 | VLOG_WARN("Could not get the object of the switch service"); | |
1075 | retval = false; | |
1076 | goto error; | |
1077 | } | |
1078 | penumerate->lpVtbl->Release(penumerate); | |
1079 | penumerate = NULL; | |
1080 | ||
1081 | hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0); | |
1082 | if (FAILED(hres)) { | |
1083 | retval = false; | |
1084 | goto error; | |
1085 | } | |
1086 | pcls_obj->lpVtbl->Release(pcls_obj); | |
1087 | pcls_obj = NULL; | |
1088 | ||
1089 | /* Try to add the port to the switch. */ | |
1090 | hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal, | |
1091 | L"AddResourceSettings", 0, | |
1092 | pcontext, pclass_instance, &pout_params, | |
1093 | NULL); | |
1094 | if (FAILED(hres)) { | |
1095 | retval = false; | |
1096 | goto error; | |
1097 | } | |
1098 | ||
1099 | unsigned int retvalue = 0; | |
1100 | hres = get_uint_value(pout_params, L"ReturnValue", &retvalue); | |
1101 | if (FAILED(hres)) { | |
1102 | retval = false; | |
1103 | goto error; | |
1104 | } | |
1105 | ||
1106 | if (retvalue != 0 && retvalue != job_wait) { | |
1107 | retval = false; | |
1108 | goto error; | |
1109 | } | |
1110 | ||
1111 | if (retvalue == job_wait) { | |
1112 | WCHAR job_path[2048]; | |
1113 | hres = get_str_value(pout_params, L"Job", job_path, | |
1114 | sizeof(job_path) / sizeof(WCHAR)); | |
1115 | if (FAILED(hres)) { | |
1116 | retval = false; | |
1117 | goto error; | |
1118 | } | |
1119 | hres = wait_for_job(psvc, job_path); | |
1120 | if (FAILED(hres)) { | |
1121 | retval = false; | |
1122 | goto error; | |
1123 | } | |
1124 | } | |
1125 | ||
1126 | pclass->lpVtbl->Release(pclass); | |
1127 | pclass = NULL; | |
1128 | pclass_instance->lpVtbl->Release(pclass_instance); | |
1129 | pclass_instance = NULL; | |
1130 | pinput_params->lpVtbl->Release(pinput_params); | |
1131 | pinput_params = NULL; | |
1132 | psvc->lpVtbl->Release(psvc); | |
1133 | psvc = NULL; | |
1134 | VariantClear(&vt_prop); | |
1135 | ||
1136 | if (!connect_set_security(ploc, pcontext, L"Root\\StandardCimv2", | |
1137 | &psvc)) { | |
1138 | VLOG_WARN("Could not connect and set security for CIM"); | |
1139 | retval = false; | |
1140 | goto error; | |
1141 | } | |
1142 | ||
c4129270 | 1143 | wcscpy_s(internal_port_query, WMI_QUERY_COUNT, |
da467899 | 1144 | L"SELECT * FROM MSFT_NetAdapter WHERE Name LIKE '%%"); |
c4129270 SV |
1145 | wcscat_s(internal_port_query, WMI_QUERY_COUNT, wide_name); |
1146 | wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"%%'"); | |
da467899 AS |
1147 | |
1148 | /* Get the object with the port name equal to name on the CIM. */ | |
1149 | hres = psvc->lpVtbl->ExecQuery(psvc, | |
1150 | L"WQL", | |
1151 | internal_port_query, | |
1152 | WBEM_FLAG_FORWARD_ONLY | | |
1153 | WBEM_FLAG_RETURN_IMMEDIATELY, | |
1154 | NULL, | |
1155 | &penumerate); | |
1156 | ||
1157 | if (!get_first_element(penumerate, &pcls_obj)) { | |
1158 | VLOG_WARN("Element name: %s not found in CIM", name); | |
1159 | retval = false; | |
1160 | goto error; | |
1161 | } | |
1162 | penumerate->lpVtbl->Release(penumerate); | |
1163 | penumerate = NULL; | |
1164 | pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0); | |
1165 | pcls_obj->lpVtbl->Release(pcls_obj); | |
1166 | pcls_obj = NULL; | |
1167 | ||
1168 | /* Disable the adapter with port name equal with name. */ | |
1169 | hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal, L"Disable", 0, | |
1170 | pcontext, NULL, NULL, NULL); | |
1171 | ||
1172 | if (FAILED(hres)) { | |
1173 | retval = false; | |
1174 | goto error; | |
1175 | } | |
1176 | ||
1177 | hres = psvc->lpVtbl->GetObject(psvc, L"MSFT_NetAdapter", 0, NULL, &pclass, | |
1178 | NULL); | |
1179 | if (FAILED(hres)) { | |
1180 | retval = false; | |
1181 | goto error; | |
1182 | } | |
1183 | ||
1184 | hres = pclass->lpVtbl->GetMethod(pclass, L"Rename", 0, &pinput_params, | |
1185 | NULL); | |
1186 | if (FAILED(hres)) { | |
1187 | retval = false; | |
1188 | goto error; | |
1189 | } | |
1190 | ||
1191 | hres = pinput_params->lpVtbl->SpawnInstance(pinput_params, 0, | |
1192 | &pclass_instance); | |
1193 | if (FAILED(hres)) { | |
1194 | retval = false; | |
1195 | goto error; | |
1196 | } | |
1197 | ||
1198 | VariantInit(&new_name); | |
1199 | new_name.vt = VT_BSTR; | |
1200 | new_name.bstrVal = wide_name; | |
1201 | hres = pclass_instance->lpVtbl->Put(pclass_instance, L"NewName", 0, | |
1202 | &new_name, 0); | |
1203 | if (FAILED(hres)) { | |
1204 | retval = false; | |
1205 | goto error; | |
1206 | } | |
1207 | hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal, L"Rename", 0, | |
1208 | pcontext, pclass_instance, NULL, NULL); | |
1209 | if (FAILED(hres)) { | |
1210 | retval = false; | |
1211 | } | |
1212 | ||
1213 | error: | |
1214 | if (text_object_string != NULL) { | |
1215 | SysFreeString(text_object_string); | |
1216 | text_object_string = NULL; | |
1217 | } | |
1218 | if (psa != NULL) { | |
1219 | SafeArrayDestroy(psa); | |
1220 | psa = NULL; | |
1221 | } | |
1222 | if (ploc != NULL) { | |
1223 | ploc->lpVtbl->Release(ploc); | |
1224 | ploc = NULL; | |
1225 | } | |
1226 | if (pcontext != NULL) { | |
1227 | pcontext->lpVtbl->Release(pcontext); | |
1228 | pcontext = NULL; | |
1229 | } | |
1230 | if (psvc != NULL) { | |
1231 | psvc->lpVtbl->Release(psvc); | |
1232 | psvc = NULL; | |
1233 | } | |
1234 | if (penumerate != NULL) { | |
1235 | penumerate->lpVtbl->Release(penumerate); | |
1236 | penumerate = NULL; | |
1237 | } | |
1238 | if (default_settings_data != NULL) { | |
1239 | default_settings_data->lpVtbl->Release(default_settings_data); | |
1240 | default_settings_data = NULL; | |
1241 | } | |
1242 | if (default_system != NULL) { | |
1243 | default_system->lpVtbl->Release(default_system); | |
1244 | default_system = NULL; | |
1245 | } | |
1246 | if (pcls_obj != NULL) { | |
1247 | pcls_obj->lpVtbl->Release(pcls_obj); | |
1248 | pcls_obj = NULL; | |
1249 | } | |
1250 | if (pclass != NULL) { | |
1251 | pclass->lpVtbl->Release(pclass); | |
1252 | pclass = NULL; | |
1253 | } | |
1254 | if (pinput_params != NULL) { | |
1255 | pinput_params->lpVtbl->Release(pinput_params); | |
1256 | pinput_params = NULL; | |
1257 | } | |
1258 | if (pclass_instance != NULL) { | |
1259 | pclass_instance->lpVtbl->Release(pclass_instance); | |
1260 | pclass_instance = NULL; | |
1261 | } | |
1262 | if (text_object != NULL) { | |
1263 | text_object->lpVtbl->Release(text_object); | |
1264 | text_object = NULL; | |
1265 | } | |
1266 | if (pout_params != NULL) { | |
1267 | pout_params->lpVtbl->Release(pout_params); | |
1268 | pout_params = NULL; | |
1269 | } | |
1270 | if (wide_name != NULL) { | |
1271 | free(wide_name); | |
1272 | wide_name = NULL; | |
1273 | } | |
1274 | VariantClear(&vt_prop); | |
1275 | VariantClear(&switch_setting_path); | |
1276 | ||
1277 | if (!retval) { | |
1278 | get_hres_error(hres); | |
1279 | } | |
1280 | CoUninitialize(); | |
1281 | return retval; | |
1282 | } |