]> git.proxmox.com Git - qemu.git/blob - qga/vss-win32/install.cpp
qemu-ga: vss-win32: Install VSS provider COM+ application service
[qemu.git] / qga / vss-win32 / install.cpp
1 /*
2 * QEMU Guest Agent win32 VSS Provider installer
3 *
4 * Copyright Hitachi Data Systems Corp. 2013
5 *
6 * Authors:
7 * Tomoki Sekiyama <tomoki.sekiyama@hds.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
11 */
12
13 #include <stdio.h>
14 #include <string.h>
15
16 #include "vss-common.h"
17 #include "inc/win2003/vscoordint.h"
18
19 #include <comadmin.h>
20 #include <wbemidl.h>
21 #include <comdef.h>
22 #include <comutil.h>
23
24 extern HINSTANCE g_hinstDll;
25
26 const GUID CLSID_COMAdminCatalog = { 0xF618C514, 0xDFB8, 0x11d1,
27 {0xA2, 0xCF, 0x00, 0x80, 0x5F, 0xC7, 0x92, 0x35} };
28 const GUID IID_ICOMAdminCatalog2 = { 0x790C6E0B, 0x9194, 0x4cc9,
29 {0x94, 0x26, 0xA4, 0x8A, 0x63, 0x18, 0x56, 0x96} };
30 const GUID CLSID_WbemLocator = { 0x4590f811, 0x1d3a, 0x11d0,
31 {0x89, 0x1f, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} };
32 const GUID IID_IWbemLocator = { 0xdc12a687, 0x737f, 0x11cf,
33 {0x88, 0x4d, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} };
34
35 void errmsg(DWORD err, const char *text)
36 {
37 /*
38 * `text' contains function call statement when errmsg is called via chk().
39 * To make error message more readable, we cut off the text after '('.
40 * If text doesn't contains '(', negative precision is given, which is
41 * treated as though it were missing.
42 */
43 char *msg = NULL, *nul = strchr(text, '(');
44 int len = nul ? nul - text : -1;
45
46 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
47 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
48 NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
49 (char *)&msg, 0, NULL);
50 fprintf(stderr, "%.*s. (Error: %lx) %s\n", len, text, err, msg);
51 LocalFree(msg);
52 }
53
54 static void errmsg_dialog(DWORD err, const char *text, const char *opt = "")
55 {
56 char *msg, buf[512];
57
58 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
59 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
60 NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
61 (char *)&msg, 0, NULL);
62 snprintf(buf, sizeof(buf), "%s%s. (Error: %lx) %s", text, opt, err, msg);
63 MessageBox(NULL, buf, "Error from " QGA_PROVIDER_NAME, MB_OK|MB_ICONERROR);
64 LocalFree(msg);
65 }
66
67 #define _chk(hr, status, msg, err_label) \
68 do { \
69 hr = (status); \
70 if (FAILED(hr)) { \
71 errmsg(hr, msg); \
72 goto err_label; \
73 } \
74 } while (0)
75
76 #define chk(status) _chk(hr, status, "Failed to " #status, out)
77
78 void __stdcall _com_issue_error(HRESULT hr)
79 {
80 errmsg(hr, "Unexpected error in COM");
81 }
82
83 template<class T>
84 HRESULT put_Value(ICatalogObject *pObj, LPCWSTR name, T val)
85 {
86 return pObj->put_Value(_bstr_t(name), _variant_t(val));
87 }
88
89 /* Lookup Administrators group name from winmgmt */
90 static HRESULT GetAdminName(_bstr_t *name)
91 {
92 HRESULT hr;
93 COMPointer<IWbemLocator> pLoc;
94 COMPointer<IWbemServices> pSvc;
95 COMPointer<IEnumWbemClassObject> pEnum;
96 COMPointer<IWbemClassObject> pWobj;
97 ULONG returned;
98 _variant_t var;
99
100 chk(CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER,
101 IID_IWbemLocator, (LPVOID *)pLoc.replace()));
102 chk(pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, NULL,
103 0, 0, 0, pSvc.replace()));
104 chk(CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
105 NULL, RPC_C_AUTHN_LEVEL_CALL,
106 RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE));
107 chk(pSvc->ExecQuery(_bstr_t(L"WQL"),
108 _bstr_t(L"select * from Win32_Account where "
109 "SID='S-1-5-32-544' and localAccount=TRUE"),
110 WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY,
111 NULL, pEnum.replace()));
112 if (!pEnum) {
113 hr = E_FAIL;
114 errmsg(hr, "Failed to query for Administrators");
115 goto out;
116 }
117 chk(pEnum->Next(WBEM_INFINITE, 1, pWobj.replace(), &returned));
118 if (returned == 0) {
119 hr = E_FAIL;
120 errmsg(hr, "No Administrators found");
121 goto out;
122 }
123
124 chk(pWobj->Get(_bstr_t(L"Name"), 0, &var, 0, 0));
125 try {
126 *name = var;
127 } catch(...) {
128 hr = E_FAIL;
129 errmsg(hr, "Failed to get name of Administrators");
130 goto out;
131 }
132
133 out:
134 return hr;
135 }
136
137 /* Find and iterate QGA VSS provider in COM+ Application Catalog */
138 static HRESULT QGAProviderFind(
139 HRESULT (*found)(ICatalogCollection *, int, void *), void *arg)
140 {
141 HRESULT hr;
142 COMInitializer initializer;
143 COMPointer<IUnknown> pUnknown;
144 COMPointer<ICOMAdminCatalog2> pCatalog;
145 COMPointer<ICatalogCollection> pColl;
146 COMPointer<ICatalogObject> pObj;
147 _variant_t var;
148 long i, n;
149
150 chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
151 IID_IUnknown, (void **)pUnknown.replace()));
152 chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2,
153 (void **)pCatalog.replace()));
154 chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
155 (IDispatch **)pColl.replace()));
156 chk(pColl->Populate());
157
158 chk(pColl->get_Count(&n));
159 for (i = n - 1; i >= 0; i--) {
160 chk(pColl->get_Item(i, (IDispatch **)pObj.replace()));
161 chk(pObj->get_Value(_bstr_t(L"Name"), &var));
162 if (var == _variant_t(QGA_PROVIDER_LNAME)) {
163 if (FAILED(found(pColl, i, arg))) {
164 goto out;
165 }
166 }
167 }
168 chk(pColl->SaveChanges(&n));
169
170 out:
171 return hr;
172 }
173
174 /* Count QGA VSS provider in COM+ Application Catalog */
175 static HRESULT QGAProviderCount(ICatalogCollection *coll, int i, void *arg)
176 {
177 (*(int *)arg)++;
178 return S_OK;
179 }
180
181 /* Remove QGA VSS provider from COM+ Application Catalog Collection */
182 static HRESULT QGAProviderRemove(ICatalogCollection *coll, int i, void *arg)
183 {
184 HRESULT hr;
185
186 fprintf(stderr, "Removing COM+ Application: %s\n", QGA_PROVIDER_NAME);
187 chk(coll->Remove(i));
188 out:
189 return hr;
190 }
191
192 /* Unregister this module from COM+ Applications Catalog */
193 STDAPI COMUnregister(void)
194 {
195 HRESULT hr;
196
197 DllUnregisterServer();
198 chk(QGAProviderFind(QGAProviderRemove, NULL));
199 out:
200 return hr;
201 }
202
203 /* Register this module to COM+ Applications Catalog */
204 STDAPI COMRegister(void)
205 {
206 HRESULT hr;
207 COMInitializer initializer;
208 COMPointer<IUnknown> pUnknown;
209 COMPointer<ICOMAdminCatalog2> pCatalog;
210 COMPointer<ICatalogCollection> pApps, pRoles, pUsersInRole;
211 COMPointer<ICatalogObject> pObj;
212 long n;
213 _bstr_t name;
214 _variant_t key;
215 CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH];
216 bool unregisterOnFailure = false;
217 int count = 0;
218
219 if (!g_hinstDll) {
220 errmsg(E_FAIL, "Failed to initialize DLL");
221 return E_FAIL;
222 }
223
224 chk(QGAProviderFind(QGAProviderCount, (void *)&count));
225 if (count) {
226 errmsg(E_ABORT, "QGA VSS Provider is already installed");
227 return E_ABORT;
228 }
229
230 chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
231 IID_IUnknown, (void **)pUnknown.replace()));
232 chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2,
233 (void **)pCatalog.replace()));
234
235 /* Install COM+ Component */
236
237 chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
238 (IDispatch **)pApps.replace()));
239 chk(pApps->Populate());
240 chk(pApps->Add((IDispatch **)&pObj));
241 chk(put_Value(pObj, L"Name", QGA_PROVIDER_LNAME));
242 chk(put_Value(pObj, L"Description", QGA_PROVIDER_LNAME));
243 chk(put_Value(pObj, L"ApplicationAccessChecksEnabled", true));
244 chk(put_Value(pObj, L"Authentication", short(6)));
245 chk(put_Value(pObj, L"AuthenticationCapability", short(2)));
246 chk(put_Value(pObj, L"ImpersonationLevel", short(2)));
247 chk(pApps->SaveChanges(&n));
248
249 /* The app should be deleted if something fails after SaveChanges */
250 unregisterOnFailure = true;
251
252 chk(pObj->get_Key(&key));
253
254 if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) {
255 hr = HRESULT_FROM_WIN32(GetLastError());
256 errmsg(hr, "GetModuleFileName failed");
257 goto out;
258 }
259 n = strlen(dllPath);
260 if (n < 3) {
261 hr = E_FAIL;
262 errmsg(hr, "Failed to lookup dll");
263 goto out;
264 }
265 strcpy(tlbPath, dllPath);
266 strcpy(tlbPath+n-3, "tlb");
267 fprintf(stderr, "Registering " QGA_PROVIDER_NAME ":\n");
268 fprintf(stderr, " %s\n", dllPath);
269 fprintf(stderr, " %s\n", tlbPath);
270 if (!PathFileExists(tlbPath)) {
271 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
272 errmsg(hr, "Failed to lookup tlb");
273 goto out;
274 }
275
276 chk(pCatalog->CreateServiceForApplication(
277 _bstr_t(QGA_PROVIDER_LNAME), _bstr_t(QGA_PROVIDER_LNAME),
278 _bstr_t(L"SERVICE_AUTO_START"), _bstr_t(L"SERVICE_ERROR_NORMAL"),
279 _bstr_t(L""), _bstr_t(L".\\localsystem"), _bstr_t(L""), FALSE));
280 chk(pCatalog->InstallComponent(_bstr_t(QGA_PROVIDER_LNAME),
281 _bstr_t(dllPath), _bstr_t(tlbPath),
282 _bstr_t("")));
283
284 /* Setup roles of the applicaion */
285
286 chk(pApps->GetCollection(_bstr_t(L"Roles"), key,
287 (IDispatch **)pRoles.replace()));
288 chk(pRoles->Populate());
289 chk(pRoles->Add((IDispatch **)pObj.replace()));
290 chk(put_Value(pObj, L"Name", L"Administrators"));
291 chk(put_Value(pObj, L"Description", L"Administrators group"));
292 chk(pRoles->SaveChanges(&n));
293 chk(pObj->get_Key(&key));
294
295 /* Setup users in the role */
296
297 chk(pRoles->GetCollection(_bstr_t(L"UsersInRole"), key,
298 (IDispatch **)pUsersInRole.replace()));
299 chk(pUsersInRole->Populate());
300
301 chk(pUsersInRole->Add((IDispatch **)pObj.replace()));
302 chk(GetAdminName(&name));
303 chk(put_Value(pObj, L"User", _bstr_t(".\\") + name));
304
305 chk(pUsersInRole->Add((IDispatch **)pObj.replace()));
306 chk(put_Value(pObj, L"User", L"SYSTEM"));
307 chk(pUsersInRole->SaveChanges(&n));
308
309 out:
310 if (unregisterOnFailure && FAILED(hr)) {
311 COMUnregister();
312 }
313
314 return hr;
315 }
316
317
318 static BOOL CreateRegistryKey(LPCTSTR key, LPCTSTR value, LPCTSTR data)
319 {
320 HKEY hKey;
321 LONG ret;
322 DWORD size;
323
324 ret = RegCreateKeyEx(HKEY_CLASSES_ROOT, key, 0, NULL,
325 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL);
326 if (ret != ERROR_SUCCESS) {
327 goto out;
328 }
329
330 if (data != NULL) {
331 size = strlen(data) + 1;
332 } else {
333 size = 0;
334 }
335
336 ret = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, size);
337 RegCloseKey(hKey);
338
339 out:
340 if (ret != ERROR_SUCCESS) {
341 /* As we cannot printf within DllRegisterServer(), show a dialog. */
342 errmsg_dialog(ret, "Cannot add registry", key);
343 return FALSE;
344 }
345 return TRUE;
346 }
347
348 /* Register this dll as a VSS provider */
349 STDAPI DllRegisterServer(void)
350 {
351 COMInitializer initializer;
352 COMPointer<IVssAdmin> pVssAdmin;
353 HRESULT hr = E_FAIL;
354 char dllPath[MAX_PATH];
355 char key[256];
356
357 if (!g_hinstDll) {
358 errmsg_dialog(hr, "Module instance is not available");
359 goto out;
360 }
361
362 /* Add this module to registery */
363
364 sprintf(key, "CLSID\\%s", g_szClsid);
365 if (!CreateRegistryKey(key, NULL, g_szClsid)) {
366 goto out;
367 }
368
369 if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) {
370 errmsg_dialog(GetLastError(), "GetModuleFileName failed");
371 goto out;
372 }
373
374 sprintf(key, "CLSID\\%s\\InprocServer32", g_szClsid);
375 if (!CreateRegistryKey(key, NULL, dllPath)) {
376 goto out;
377 }
378
379 if (!CreateRegistryKey(key, "ThreadingModel", "Apartment")) {
380 goto out;
381 }
382
383 sprintf(key, "CLSID\\%s\\ProgID", g_szClsid);
384 if (!CreateRegistryKey(key, NULL, g_szProgid)) {
385 goto out;
386 }
387
388 if (!CreateRegistryKey(g_szProgid, NULL, QGA_PROVIDER_NAME)) {
389 goto out;
390 }
391
392 sprintf(key, "%s\\CLSID", g_szProgid);
393 if (!CreateRegistryKey(key, NULL, g_szClsid)) {
394 goto out;
395 }
396
397 hr = CoCreateInstance(CLSID_VSSCoordinator, NULL, CLSCTX_ALL,
398 IID_IVssAdmin, (void **)pVssAdmin.replace());
399 if (FAILED(hr)) {
400 errmsg_dialog(hr, "CoCreateInstance(VSSCoordinator) failed");
401 goto out;
402 }
403
404 hr = pVssAdmin->RegisterProvider(g_gProviderId, CLSID_QGAVSSProvider,
405 const_cast<WCHAR*>(QGA_PROVIDER_LNAME),
406 VSS_PROV_SOFTWARE,
407 const_cast<WCHAR*>(QGA_PROVIDER_VERSION),
408 g_gProviderVersion);
409 if (FAILED(hr)) {
410 errmsg_dialog(hr, "RegisterProvider failed");
411 }
412
413 out:
414 if (FAILED(hr)) {
415 DllUnregisterServer();
416 }
417
418 return hr;
419 }
420
421 /* Unregister this VSS hardware provider from the system */
422 STDAPI DllUnregisterServer(void)
423 {
424 TCHAR key[256];
425 COMInitializer initializer;
426 COMPointer<IVssAdmin> pVssAdmin;
427
428 HRESULT hr = CoCreateInstance(CLSID_VSSCoordinator,
429 NULL, CLSCTX_ALL, IID_IVssAdmin,
430 (void **)pVssAdmin.replace());
431 if (SUCCEEDED(hr)) {
432 hr = pVssAdmin->UnregisterProvider(g_gProviderId);
433 } else {
434 errmsg(hr, "CoCreateInstance(VSSCoordinator) failed");
435 }
436
437 sprintf(key, "CLSID\\%s", g_szClsid);
438 SHDeleteKey(HKEY_CLASSES_ROOT, key);
439 SHDeleteKey(HKEY_CLASSES_ROOT, g_szProgid);
440
441 return S_OK; /* Uninstall should never fail */
442 }
443
444
445 /* Support function to convert ASCII string into BSTR (used in _bstr_t) */
446 namespace _com_util
447 {
448 BSTR WINAPI ConvertStringToBSTR(const char *ascii) {
449 int len = strlen(ascii);
450 BSTR bstr = SysAllocStringLen(NULL, len);
451
452 if (!bstr) {
453 return NULL;
454 }
455
456 if (mbstowcs(bstr, ascii, len) == (size_t)-1) {
457 fprintf(stderr, "Failed to convert string '%s' into BSTR", ascii);
458 bstr[0] = 0;
459 }
460 return bstr;
461 }
462 }