VirtualBox

source: vbox/trunk/src/VBox/Main/src-all/ExtPackManagerImpl.cpp@ 90441

Last change on this file since 90441 was 86058, checked in by vboxsync, 4 years ago

Main/ExtPack: belatedly bump VBOXEXTPACKHLP_VERSION for the event
creation callback. While here, as a fore/after-thought add another,
so far not used, callback for creation of IVetoEvent.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 123.3 KB
Line 
1/* $Id: ExtPackManagerImpl.cpp 86058 2020-09-07 23:33:30Z vboxsync $ */
2/** @file
3 * VirtualBox Main - interface for Extension Packs, VBoxSVC & VBoxC.
4 */
5
6/*
7 * Copyright (C) 2010-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include "ExtPackManagerImpl.h"
23#include "CloudProviderManagerImpl.h"
24#include "ExtPackUtil.h"
25#include "ThreadTask.h"
26
27#include <iprt/buildconfig.h>
28#include <iprt/ctype.h>
29#include <iprt/dir.h>
30#include <iprt/env.h>
31#include <iprt/file.h>
32#include <iprt/ldr.h>
33#include <iprt/manifest.h>
34#include <iprt/param.h>
35#include <iprt/path.h>
36#include <iprt/pipe.h>
37#include <iprt/process.h>
38#include <iprt/string.h>
39
40#include <VBox/com/array.h>
41#include <VBox/com/ErrorInfo.h>
42#include <VBox/err.h>
43#include <VBox/log.h>
44#include <VBox/sup.h>
45#include <VBox/version.h>
46#include "AutoCaller.h"
47#include "Global.h"
48#include "ProgressImpl.h"
49#ifdef VBOX_COM_INPROC
50# include "ConsoleImpl.h"
51#else
52# include "VirtualBoxImpl.h"
53#endif
54
55
56/*********************************************************************************************************************************
57* Defined Constants And Macros *
58*********************************************************************************************************************************/
59/** @def VBOX_EXTPACK_HELPER_NAME
60 * The name of the utility application we employ to install and uninstall the
61 * extension packs. This is a set-uid-to-root binary on unixy platforms, which
62 * is why it has to be a separate application.
63 */
64#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
65# define VBOX_EXTPACK_HELPER_NAME "VBoxExtPackHelperApp.exe"
66#else
67# define VBOX_EXTPACK_HELPER_NAME "VBoxExtPackHelperApp"
68#endif
69
70
71/*********************************************************************************************************************************
72* Structures and Typedefs *
73*********************************************************************************************************************************/
74struct ExtPackBaseData
75{
76public:
77 /** The extension pack descriptor (loaded from the XML, mostly). */
78 VBOXEXTPACKDESC Desc;
79 /** The file system object info of the XML file.
80 * This is for detecting changes and save time in refresh(). */
81 RTFSOBJINFO ObjInfoDesc;
82 /** Whether it's usable or not. */
83 bool fUsable;
84 /** Why it is unusable. */
85 Utf8Str strWhyUnusable;
86};
87
88#ifndef VBOX_COM_INPROC
89/**
90 * Private extension pack data.
91 */
92struct ExtPackFile::Data : public ExtPackBaseData
93{
94public:
95 /** The path to the tarball. */
96 Utf8Str strExtPackFile;
97 /** The SHA-256 hash of the file (as string). */
98 Utf8Str strDigest;
99 /** The file handle of the extension pack file. */
100 RTFILE hExtPackFile;
101 /** Our manifest for the tarball. */
102 RTMANIFEST hOurManifest;
103 /** Pointer to the extension pack manager. */
104 ComObjPtr<ExtPackManager> ptrExtPackMgr;
105 /** Pointer to the VirtualBox object so we can create a progress object. */
106 VirtualBox *pVirtualBox;
107
108 RTMEMEF_NEW_AND_DELETE_OPERATORS();
109};
110#endif
111
112/**
113 * Private extension pack data.
114 */
115struct ExtPack::Data : public ExtPackBaseData
116{
117public:
118 /** Where the extension pack is located. */
119 Utf8Str strExtPackPath;
120 /** The file system object info of the extension pack directory.
121 * This is for detecting changes and save time in refresh(). */
122 RTFSOBJINFO ObjInfoExtPack;
123 /** The full path to the main module. */
124 Utf8Str strMainModPath;
125 /** The file system object info of the main module.
126 * This is used to determin whether to bother try reload it. */
127 RTFSOBJINFO ObjInfoMainMod;
128 /** The module handle of the main extension pack module. */
129 RTLDRMOD hMainMod;
130
131 /** The helper callbacks for the extension pack. */
132 VBOXEXTPACKHLP Hlp;
133 /** Pointer back to the extension pack object (for Hlp methods). */
134 ExtPack *pThis;
135#ifndef VBOX_COM_INPROC
136 /** The extension pack main registration structure. */
137 PCVBOXEXTPACKREG pReg;
138#else
139 /** The extension pack main VM registration structure. */
140 PCVBOXEXTPACKVMREG pReg;
141#endif
142 /** The current context. */
143 VBOXEXTPACKCTX enmContext;
144 /** Set if we've made the pfnVirtualBoxReady or pfnConsoleReady call. */
145 bool fMadeReadyCall;
146#ifndef VBOX_COM_INPROC
147 /** Pointer to the VirtualBox object so we can create a progress object. */
148 VirtualBox *pVirtualBox;
149#endif
150
151 RTMEMEF_NEW_AND_DELETE_OPERATORS();
152};
153
154/** List of extension packs. */
155typedef std::list< ComObjPtr<ExtPack> > ExtPackList;
156
157/**
158 * Private extension pack manager data.
159 */
160struct ExtPackManager::Data
161{
162 Data()
163 : cUpdate(0)
164 {}
165
166 /** The directory where the extension packs are installed. */
167 Utf8Str strBaseDir;
168 /** The directory where the certificates this installation recognizes are
169 * stored. */
170 Utf8Str strCertificatDirPath;
171 /** The list of installed extension packs. */
172 ExtPackList llInstalledExtPacks;
173#ifndef VBOX_COM_INPROC
174 /** Pointer to the VirtualBox object, our parent. */
175 VirtualBox *pVirtualBox;
176#endif
177 /** The current context. */
178 VBOXEXTPACKCTX enmContext;
179 /** Update counter for the installed extension packs, increased in every list update. */
180 uint64_t cUpdate;
181
182 RTMEMEF_NEW_AND_DELETE_OPERATORS();
183};
184
185#ifndef VBOX_COM_INPROC
186
187/**
188 * Extension pack installation job.
189 */
190class ExtPackInstallTask : public ThreadTask
191{
192public:
193 explicit ExtPackInstallTask() : ThreadTask("ExtPackInst") { }
194 ~ExtPackInstallTask() { }
195
196 void handler()
197 {
198 HRESULT hrc = ptrExtPackMgr->i_doInstall(ptrExtPackFile, fReplace, &strDisplayInfo);
199 ptrProgress->i_notifyComplete(hrc);
200 }
201
202 HRESULT Init(const ComPtr<ExtPackFile> &a_strExtPackFile, bool a_fReplace,
203 const Utf8Str &strDispInfo, const ComPtr<ExtPackManager> &a_ptrExtPackMgr)
204 {
205 ptrExtPackFile = a_strExtPackFile;
206 fReplace = a_fReplace;
207 strDisplayInfo = strDispInfo;
208 ptrExtPackMgr = a_ptrExtPackMgr;
209
210 HRESULT hrc = ptrProgress.createObject();
211 if (SUCCEEDED(hrc))
212 {
213 Bstr bstrDescription("Installing extension pack");
214 hrc = ptrProgress->init(ptrExtPackFile->m->pVirtualBox,
215 static_cast<IExtPackFile *>(ptrExtPackFile),
216 bstrDescription.raw(),
217 FALSE /*aCancelable*/);
218 }
219
220 return hrc;
221 }
222
223 /** Smart pointer to the progress object for this job. */
224 ComObjPtr<Progress> ptrProgress;
225private:
226 /** Smart pointer to the extension pack file. */
227 ComPtr<ExtPackFile> ptrExtPackFile;
228 /** The replace argument. */
229 bool fReplace;
230 /** The display info argument. */
231 Utf8Str strDisplayInfo;
232 /** Smart pointer to the extension manager. */
233 ComPtr<ExtPackManager> ptrExtPackMgr;
234};
235
236/**
237 * Extension pack uninstallation job.
238 */
239class ExtPackUninstallTask : public ThreadTask
240{
241public:
242 explicit ExtPackUninstallTask() : ThreadTask("ExtPackUninst") { }
243 ~ExtPackUninstallTask() { }
244
245 void handler()
246 {
247 HRESULT hrc = ptrExtPackMgr->i_doUninstall(&strName, fForcedRemoval, &strDisplayInfo);
248 ptrProgress->i_notifyComplete(hrc);
249 }
250
251 HRESULT Init(const ComPtr<ExtPackManager> &a_ptrExtPackMgr, const Utf8Str &a_strName,
252 bool a_fForcedRemoval, const Utf8Str &a_strDisplayInfo)
253 {
254 ptrExtPackMgr = a_ptrExtPackMgr;
255 strName = a_strName;
256 fForcedRemoval = a_fForcedRemoval;
257 strDisplayInfo = a_strDisplayInfo;
258
259 HRESULT hrc = ptrProgress.createObject();
260 if (SUCCEEDED(hrc))
261 {
262 Bstr bstrDescription("Uninstalling extension pack");
263 hrc = ptrProgress->init(ptrExtPackMgr->m->pVirtualBox,
264 static_cast<IExtPackManager *>(ptrExtPackMgr),
265 bstrDescription.raw(),
266 FALSE /*aCancelable*/);
267 }
268
269 return hrc;
270 }
271
272 /** Smart pointer to the progress object for this job. */
273 ComObjPtr<Progress> ptrProgress;
274private:
275 /** Smart pointer to the extension manager. */
276 ComPtr<ExtPackManager> ptrExtPackMgr;
277 /** The name of the extension pack. */
278 Utf8Str strName;
279 /** The replace argument. */
280 bool fForcedRemoval;
281 /** The display info argument. */
282 Utf8Str strDisplayInfo;
283};
284
285DEFINE_EMPTY_CTOR_DTOR(ExtPackFile)
286
287/**
288 * Called by ComObjPtr::createObject when creating the object.
289 *
290 * Just initialize the basic object state, do the rest in initWithDir().
291 *
292 * @returns S_OK.
293 */
294HRESULT ExtPackFile::FinalConstruct()
295{
296 m = NULL;
297 return BaseFinalConstruct();
298}
299
300/**
301 * Initializes the extension pack by reading its file.
302 *
303 * @returns COM status code.
304 * @param a_pszFile The path to the extension pack file.
305 * @param a_pszDigest The SHA-256 digest of the file. Or an empty string.
306 * @param a_pExtPackMgr Pointer to the extension pack manager.
307 * @param a_pVirtualBox Pointer to the VirtualBox object.
308 */
309HRESULT ExtPackFile::initWithFile(const char *a_pszFile, const char *a_pszDigest, ExtPackManager *a_pExtPackMgr,
310 VirtualBox *a_pVirtualBox)
311{
312 AutoInitSpan autoInitSpan(this);
313 AssertReturn(autoInitSpan.isOk(), E_FAIL);
314
315 /*
316 * Allocate + initialize our private data.
317 */
318 m = new ExtPackFile::Data;
319 VBoxExtPackInitDesc(&m->Desc);
320 RT_ZERO(m->ObjInfoDesc);
321 m->fUsable = false;
322 m->strWhyUnusable = tr("ExtPack::init failed");
323 m->strExtPackFile = a_pszFile;
324 m->strDigest = a_pszDigest;
325 m->hExtPackFile = NIL_RTFILE;
326 m->hOurManifest = NIL_RTMANIFEST;
327 m->ptrExtPackMgr = a_pExtPackMgr;
328 m->pVirtualBox = a_pVirtualBox;
329
330 RTCString *pstrTarName = VBoxExtPackExtractNameFromTarballPath(a_pszFile);
331 if (pstrTarName)
332 {
333 m->Desc.strName = *pstrTarName;
334 delete pstrTarName;
335 pstrTarName = NULL;
336 }
337
338 autoInitSpan.setSucceeded();
339
340 /*
341 * Try open the extension pack and check that it is a regular file.
342 */
343 int vrc = RTFileOpen(&m->hExtPackFile, a_pszFile,
344 RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN);
345 if (RT_FAILURE(vrc))
346 {
347 if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
348 return initFailed(tr("'%s' file not found"), a_pszFile);
349 return initFailed(tr("RTFileOpen('%s',,) failed with %Rrc"), a_pszFile, vrc);
350 }
351
352 RTFSOBJINFO ObjInfo;
353 vrc = RTFileQueryInfo(m->hExtPackFile, &ObjInfo, RTFSOBJATTRADD_UNIX);
354 if (RT_FAILURE(vrc))
355 return initFailed(tr("RTFileQueryInfo failed with %Rrc on '%s'"), vrc, a_pszFile);
356 if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
357 return initFailed(tr("Not a regular file: %s"), a_pszFile);
358
359 /*
360 * Validate the tarball and extract the XML file.
361 */
362 char szError[8192];
363 RTVFSFILE hXmlFile;
364 vrc = VBoxExtPackValidateTarball(m->hExtPackFile, NULL /*pszExtPackName*/, a_pszFile, a_pszDigest,
365 szError, sizeof(szError), &m->hOurManifest, &hXmlFile, &m->strDigest);
366 if (RT_FAILURE(vrc))
367 return initFailed(tr("%s"), szError);
368
369 /*
370 * Parse the XML.
371 */
372 RTCString strSavedName(m->Desc.strName);
373 RTCString *pStrLoadErr = VBoxExtPackLoadDescFromVfsFile(hXmlFile, &m->Desc, &m->ObjInfoDesc);
374 RTVfsFileRelease(hXmlFile);
375 if (pStrLoadErr != NULL)
376 {
377 m->strWhyUnusable.printf(tr("Failed to the xml file: %s"), pStrLoadErr->c_str());
378 m->Desc.strName = strSavedName;
379 delete pStrLoadErr;
380 return S_OK;
381 }
382
383 /*
384 * Match the tarball name with the name from the XML.
385 */
386 /** @todo drop this restriction after the old install interface is
387 * dropped. */
388 if (!strSavedName.equalsIgnoreCase(m->Desc.strName))
389 return initFailed(tr("Extension pack name mismatch between the downloaded file and the XML inside it (xml='%s' file='%s')"),
390 m->Desc.strName.c_str(), strSavedName.c_str());
391
392
393 m->fUsable = true;
394 m->strWhyUnusable.setNull();
395 return S_OK;
396}
397
398/**
399 * Protected helper that formats the strWhyUnusable value.
400 *
401 * @returns S_OK
402 * @param a_pszWhyFmt Why it failed, format string.
403 * @param ... The format arguments.
404 */
405HRESULT ExtPackFile::initFailed(const char *a_pszWhyFmt, ...)
406{
407 va_list va;
408 va_start(va, a_pszWhyFmt);
409 m->strWhyUnusable.printfV(a_pszWhyFmt, va);
410 va_end(va);
411 return S_OK;
412}
413
414/**
415 * COM cruft.
416 */
417void ExtPackFile::FinalRelease()
418{
419 uninit();
420 BaseFinalRelease();
421}
422
423/**
424 * Do the actual cleanup.
425 */
426void ExtPackFile::uninit()
427{
428 /* Enclose the state transition Ready->InUninit->NotReady */
429 AutoUninitSpan autoUninitSpan(this);
430 if (!autoUninitSpan.uninitDone() && m != NULL)
431 {
432 VBoxExtPackFreeDesc(&m->Desc);
433 RTFileClose(m->hExtPackFile);
434 m->hExtPackFile = NIL_RTFILE;
435 RTManifestRelease(m->hOurManifest);
436 m->hOurManifest = NIL_RTMANIFEST;
437
438 delete m;
439 m = NULL;
440 }
441}
442
443HRESULT ExtPackFile::getName(com::Utf8Str &aName)
444{
445 aName = m->Desc.strName;
446 return S_OK;
447}
448
449HRESULT ExtPackFile::getDescription(com::Utf8Str &aDescription)
450{
451 aDescription = m->Desc.strDescription;
452 return S_OK;
453}
454
455HRESULT ExtPackFile::getVersion(com::Utf8Str &aVersion)
456{
457 aVersion = m->Desc.strVersion;
458 return S_OK;
459}
460
461HRESULT ExtPackFile::getEdition(com::Utf8Str &aEdition)
462{
463 aEdition = m->Desc.strEdition;
464 return S_OK;
465}
466
467HRESULT ExtPackFile::getRevision(ULONG *aRevision)
468{
469 *aRevision = m->Desc.uRevision;
470 return S_OK;
471}
472
473HRESULT ExtPackFile::getVRDEModule(com::Utf8Str &aVRDEModule)
474{
475 aVRDEModule = m->Desc.strVrdeModule;
476 return S_OK;
477}
478
479HRESULT ExtPackFile::getPlugIns(std::vector<ComPtr<IExtPackPlugIn> > &aPlugIns)
480{
481 /** @todo implement plug-ins. */
482#ifdef VBOX_WITH_XPCOM
483 NOREF(aPlugIns);
484#endif
485 NOREF(aPlugIns);
486 ReturnComNotImplemented();
487}
488
489HRESULT ExtPackFile::getUsable(BOOL *aUsable)
490{
491 *aUsable = m->fUsable;
492 return S_OK;
493}
494
495HRESULT ExtPackFile::getWhyUnusable(com::Utf8Str &aWhyUnusable)
496{
497 aWhyUnusable = m->strWhyUnusable;
498 return S_OK;
499}
500
501HRESULT ExtPackFile::getShowLicense(BOOL *aShowLicense)
502{
503 *aShowLicense = m->Desc.fShowLicense;
504 return S_OK;
505}
506
507HRESULT ExtPackFile::getLicense(com::Utf8Str &aLicense)
508{
509 Utf8Str strHtml("html");
510 Utf8Str str("");
511 return queryLicense(str, str, strHtml, aLicense);
512}
513
514/* Same as ExtPack::QueryLicense, should really explore the subject of base classes here... */
515HRESULT ExtPackFile::queryLicense(const com::Utf8Str &aPreferredLocale, const com::Utf8Str &aPreferredLanguage,
516 const com::Utf8Str &aFormat, com::Utf8Str &aLicenseText)
517{
518 HRESULT hrc = S_OK;
519
520 /*
521 * Validate input.
522 */
523
524 if (aPreferredLocale.length() != 2 && aPreferredLocale.length() != 0)
525 return setError(E_FAIL, tr("The preferred locale is a two character string or empty."));
526
527 if (aPreferredLanguage.length() != 2 && aPreferredLanguage.length() != 0)
528 return setError(E_FAIL, tr("The preferred lanuage is a two character string or empty."));
529
530 if ( !aFormat.equals("html")
531 && !aFormat.equals("rtf")
532 && !aFormat.equals("txt"))
533 return setError(E_FAIL, tr("The license format can only have the values 'html', 'rtf' and 'txt'."));
534
535 /*
536 * Combine the options to form a file name before locking down anything.
537 */
538 char szName[sizeof(VBOX_EXTPACK_LICENSE_NAME_PREFIX "-de_DE.html") + 2];
539 if (aPreferredLocale.isNotEmpty() && aPreferredLanguage.isNotEmpty())
540 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX "-%s_%s.%s",
541 aPreferredLocale.c_str(), aPreferredLanguage.c_str(), aFormat.c_str());
542 else if (aPreferredLocale.isNotEmpty())
543 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX "-%s.%s",
544 aPreferredLocale.c_str(), aFormat.c_str());
545 else if (aPreferredLanguage.isNotEmpty())
546 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX "-_%s.%s",
547 aPreferredLocale.c_str(), aFormat.c_str());
548 else
549 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX ".%s",
550 aFormat.c_str());
551 /*
552 * Lock the extension pack. We need a write lock here as there must not be
553 * concurrent accesses to the tar file handle.
554 */
555 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
556
557 /*
558 * Do not permit this query on a pack that isn't considered usable (could
559 * be marked so because of bad license files).
560 */
561 if (!m->fUsable)
562 hrc = setError(E_FAIL, tr("%s"), m->strWhyUnusable.c_str());
563 else
564 {
565 /*
566 * Look it up in the manifest before scanning the tarball for it
567 */
568 if (RTManifestEntryExists(m->hOurManifest, szName))
569 {
570 RTVFSFSSTREAM hTarFss;
571 char szError[8192];
572 int vrc = VBoxExtPackOpenTarFss(m->hExtPackFile, szError, sizeof(szError), &hTarFss, NULL);
573 if (RT_SUCCESS(vrc))
574 {
575 for (;;)
576 {
577 /* Get the first/next. */
578 char *pszName;
579 RTVFSOBJ hVfsObj;
580 RTVFSOBJTYPE enmType;
581 vrc = RTVfsFsStrmNext(hTarFss, &pszName, &enmType, &hVfsObj);
582 if (RT_FAILURE(vrc))
583 {
584 if (vrc != VERR_EOF)
585 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("RTVfsFsStrmNext failed: %Rrc"), vrc);
586 else
587 hrc = setErrorBoth(E_UNEXPECTED, vrc, tr("'%s' was found in the manifest but not in the tarball"), szName);
588 break;
589 }
590
591 /* Is this it? */
592 const char *pszAdjName = pszName[0] == '.' && pszName[1] == '/' ? &pszName[2] : pszName;
593 if ( !strcmp(pszAdjName, szName)
594 && ( enmType == RTVFSOBJTYPE_IO_STREAM
595 || enmType == RTVFSOBJTYPE_FILE))
596 {
597 RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
598 RTVfsObjRelease(hVfsObj);
599 RTStrFree(pszName);
600
601 /* Load the file into memory. */
602 RTFSOBJINFO ObjInfo;
603 vrc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_NOTHING);
604 if (RT_SUCCESS(vrc))
605 {
606 size_t cbFile = (size_t)ObjInfo.cbObject;
607 void *pvFile = RTMemAllocZ(cbFile + 1);
608 if (pvFile)
609 {
610 vrc = RTVfsIoStrmRead(hVfsIos, pvFile, cbFile, true /*fBlocking*/, NULL);
611 if (RT_SUCCESS(vrc))
612 {
613 /* try translate it into a string we can return. */
614 Bstr bstrLicense((const char *)pvFile, cbFile);
615 if (bstrLicense.isNotEmpty())
616 {
617 aLicenseText = Utf8Str(bstrLicense);
618 hrc = S_OK;
619 }
620 else
621 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
622 tr("The license file '%s' is empty or contains invalid UTF-8 encoding"),
623 szName);
624 }
625 else
626 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to read '%s': %Rrc"), szName, vrc);
627 RTMemFree(pvFile);
628 }
629 else
630 hrc = setError(E_OUTOFMEMORY, tr("Failed to allocate %zu bytes for '%s'"), cbFile, szName);
631 }
632 else
633 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("RTVfsIoStrmQueryInfo on '%s': %Rrc"), szName, vrc);
634 RTVfsIoStrmRelease(hVfsIos);
635 break;
636 }
637
638 /* Release current. */
639 RTVfsObjRelease(hVfsObj);
640 RTStrFree(pszName);
641 }
642 RTVfsFsStrmRelease(hTarFss);
643 }
644 else
645 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("%s"), szError);
646 }
647 else
648 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("The license file '%s' was not found in '%s'"),
649 szName, m->strExtPackFile.c_str());
650 }
651 return hrc;
652}
653
654HRESULT ExtPackFile::getFilePath(com::Utf8Str &aFilePath)
655{
656
657 aFilePath = m->strExtPackFile;
658 return S_OK;
659}
660
661HRESULT ExtPackFile::install(BOOL aReplace, const com::Utf8Str &aDisplayInfo, ComPtr<IProgress> &aProgress)
662{
663 HRESULT hrc;
664 if (m->fUsable)
665 {
666 ExtPackInstallTask *pTask = NULL;
667 try
668 {
669 pTask = new ExtPackInstallTask();
670 hrc = pTask->Init(this, aReplace != FALSE, aDisplayInfo, m->ptrExtPackMgr);
671 if (SUCCEEDED(hrc))
672 {
673 ComPtr<Progress> ptrProgress = pTask->ptrProgress;
674 hrc = pTask->createThreadWithType(RTTHREADTYPE_DEFAULT);
675 pTask = NULL; /* The _completely_ _undocumented_ createThread method always consumes pTask. */
676 if (SUCCEEDED(hrc))
677 hrc = ptrProgress.queryInterfaceTo(aProgress.asOutParam());
678 else
679 hrc = setError(VBOX_E_IPRT_ERROR,
680 tr("Starting thread for an extension pack installation failed with %Rrc"), hrc);
681 }
682 else
683 hrc = setError(VBOX_E_IPRT_ERROR,
684 tr("Looks like creating a progress object for ExtraPackInstallTask object failed"));
685 }
686 catch (std::bad_alloc &)
687 {
688 hrc = E_OUTOFMEMORY;
689 }
690 catch (HRESULT hrcXcpt)
691 {
692 LogFlowThisFunc(("Exception was caught in the function ExtPackFile::install() \n"));
693 hrc = hrcXcpt;
694 }
695 if (pTask)
696 delete pTask;
697 }
698 else
699 hrc = setError(E_FAIL, "%s", m->strWhyUnusable.c_str());
700 return hrc;
701}
702
703#endif /* !VBOX_COM_INPROC */
704
705
706
707
708DEFINE_EMPTY_CTOR_DTOR(ExtPack)
709
710/**
711 * Called by ComObjPtr::createObject when creating the object.
712 *
713 * Just initialize the basic object state, do the rest in initWithDir().
714 *
715 * @returns S_OK.
716 */
717HRESULT ExtPack::FinalConstruct()
718{
719 m = NULL;
720 return BaseFinalConstruct();
721}
722
723/**
724 * Initializes the extension pack by reading its file.
725 *
726 * @returns COM status code.
727 * @param a_pVirtualBox The VirtualBox object.
728 * @param a_enmContext The context we're in.
729 * @param a_pszName The name of the extension pack. This is also the
730 * name of the subdirector under @a a_pszParentDir
731 * where the extension pack is installed.
732 * @param a_pszDir The extension pack directory name.
733 */
734HRESULT ExtPack::initWithDir(VirtualBox *a_pVirtualBox, VBOXEXTPACKCTX a_enmContext, const char *a_pszName, const char *a_pszDir)
735{
736 AutoInitSpan autoInitSpan(this);
737 AssertReturn(autoInitSpan.isOk(), E_FAIL);
738
739 static const VBOXEXTPACKHLP s_HlpTmpl =
740 {
741 /* u32Version = */ VBOXEXTPACKHLP_VERSION,
742 /* uVBoxFullVersion = */ VBOX_FULL_VERSION,
743 /* uVBoxVersionRevision = */ 0,
744 /* u32Padding = */ 0,
745 /* pszVBoxVersion = */ "",
746 /* pfnFindModule = */ ExtPack::i_hlpFindModule,
747 /* pfnGetFilePath = */ ExtPack::i_hlpGetFilePath,
748 /* pfnGetContext = */ ExtPack::i_hlpGetContext,
749 /* pfnLoadHGCMService = */ ExtPack::i_hlpLoadHGCMService,
750 /* pfnLoadVDPlugin = */ ExtPack::i_hlpLoadVDPlugin,
751 /* pfnUnloadVDPlugin = */ ExtPack::i_hlpUnloadVDPlugin,
752 /* pfnCreateProgress = */ ExtPack::i_hlpCreateProgress,
753 /* pfnGetCanceledProgress = */ ExtPack::i_hlpGetCanceledProgress,
754 /* pfnUpdateProgress = */ ExtPack::i_hlpUpdateProgress,
755 /* pfnNextOperationProgress = */ ExtPack::i_hlpNextOperationProgress,
756 /* pfnWaitOtherProgress = */ ExtPack::i_hlpWaitOtherProgress,
757 /* pfnCompleteProgress = */ ExtPack::i_hlpCompleteProgress,
758 /* pfnCreateEvent = */ ExtPack::i_hlpCreateEvent,
759 /* pfnCreateVetoEvent = */ ExtPack::i_hlpCreateVetoEvent,
760 /* pfnReserved1 = */ ExtPack::i_hlpReservedN,
761 /* pfnReserved2 = */ ExtPack::i_hlpReservedN,
762 /* pfnReserved3 = */ ExtPack::i_hlpReservedN,
763 /* pfnReserved4 = */ ExtPack::i_hlpReservedN,
764 /* pfnReserved5 = */ ExtPack::i_hlpReservedN,
765 /* pfnReserved6 = */ ExtPack::i_hlpReservedN,
766 /* u32EndMarker = */ VBOXEXTPACKHLP_VERSION
767 };
768
769 /*
770 * Allocate + initialize our private data.
771 */
772 m = new Data;
773 VBoxExtPackInitDesc(&m->Desc);
774 m->Desc.strName = a_pszName;
775 RT_ZERO(m->ObjInfoDesc);
776 m->fUsable = false;
777 m->strWhyUnusable = tr("ExtPack::init failed");
778 m->strExtPackPath = a_pszDir;
779 RT_ZERO(m->ObjInfoExtPack);
780 m->strMainModPath.setNull();
781 RT_ZERO(m->ObjInfoMainMod);
782 m->hMainMod = NIL_RTLDRMOD;
783 m->Hlp = s_HlpTmpl;
784 m->Hlp.pszVBoxVersion = RTBldCfgVersion();
785 m->Hlp.uVBoxInternalRevision = RTBldCfgRevision();
786 m->pThis = this;
787 m->pReg = NULL;
788 m->enmContext = a_enmContext;
789 m->fMadeReadyCall = false;
790#ifndef VBOX_COM_INPROC
791 m->pVirtualBox = a_pVirtualBox;
792#else
793 RT_NOREF(a_pVirtualBox);
794#endif
795
796 /*
797 * Make sure the SUPR3Hardened API works (ignoring errors for now).
798 */
799 int rc = SUPR3HardenedVerifyInit();
800 if (RT_FAILURE(rc))
801 LogRel(("SUPR3HardenedVerifyInit failed: %Rrc\n", rc));
802
803 /*
804 * Probe the extension pack (this code is shared with refresh()).
805 */
806 i_probeAndLoad();
807
808 autoInitSpan.setSucceeded();
809 return S_OK;
810}
811
812/**
813 * COM cruft.
814 */
815void ExtPack::FinalRelease()
816{
817 uninit();
818 BaseFinalRelease();
819}
820
821/**
822 * Do the actual cleanup.
823 */
824void ExtPack::uninit()
825{
826 /* Enclose the state transition Ready->InUninit->NotReady */
827 AutoUninitSpan autoUninitSpan(this);
828 if (!autoUninitSpan.uninitDone() && m != NULL)
829 {
830 if (m->hMainMod != NIL_RTLDRMOD)
831 {
832 AssertPtr(m->pReg);
833 if (m->pReg->pfnUnload != NULL)
834 m->pReg->pfnUnload(m->pReg);
835
836 RTLdrClose(m->hMainMod);
837 m->hMainMod = NIL_RTLDRMOD;
838 m->pReg = NULL;
839 }
840
841 VBoxExtPackFreeDesc(&m->Desc);
842
843 delete m;
844 m = NULL;
845 }
846}
847
848
849#ifndef VBOX_COM_INPROC
850/**
851 * Calls the installed hook.
852 *
853 * @returns true if we left the lock, false if we didn't.
854 * @param a_pVirtualBox The VirtualBox interface.
855 * @param a_pLock The write lock held by the caller.
856 * @param pErrInfo Where to return error information.
857 */
858bool ExtPack::i_callInstalledHook(IVirtualBox *a_pVirtualBox, AutoWriteLock *a_pLock, PRTERRINFO pErrInfo)
859{
860 if ( m != NULL
861 && m->hMainMod != NIL_RTLDRMOD)
862 {
863 if (m->pReg->pfnInstalled)
864 {
865 ComPtr<ExtPack> ptrSelfRef = this;
866 a_pLock->release();
867 pErrInfo->rc = m->pReg->pfnInstalled(m->pReg, a_pVirtualBox, pErrInfo);
868 a_pLock->acquire();
869 return true;
870 }
871 }
872 pErrInfo->rc = VINF_SUCCESS;
873 return false;
874}
875
876/**
877 * Calls the uninstall hook and closes the module.
878 *
879 * @returns S_OK or COM error status with error information.
880 * @param a_pVirtualBox The VirtualBox interface.
881 * @param a_fForcedRemoval When set, we'll ignore complaints from the
882 * uninstall hook.
883 * @remarks The caller holds the manager's write lock, not released.
884 */
885HRESULT ExtPack::i_callUninstallHookAndClose(IVirtualBox *a_pVirtualBox, bool a_fForcedRemoval)
886{
887 HRESULT hrc = S_OK;
888
889 if ( m != NULL
890 && m->hMainMod != NIL_RTLDRMOD)
891 {
892 if (m->pReg->pfnUninstall && !a_fForcedRemoval)
893 {
894 int vrc = m->pReg->pfnUninstall(m->pReg, a_pVirtualBox);
895 if (RT_FAILURE(vrc))
896 {
897 LogRel(("ExtPack pfnUninstall returned %Rrc for %s\n", vrc, m->Desc.strName.c_str()));
898 if (!a_fForcedRemoval)
899 hrc = setErrorBoth(E_FAIL, vrc, tr("pfnUninstall returned %Rrc"), vrc);
900 }
901 }
902 if (SUCCEEDED(hrc))
903 {
904 RTLdrClose(m->hMainMod);
905 m->hMainMod = NIL_RTLDRMOD;
906 m->pReg = NULL;
907 }
908 }
909
910 return hrc;
911}
912
913/**
914 * Calls the pfnVirtualBoxReady hook.
915 *
916 * @returns true if we left the lock, false if we didn't.
917 * @param a_pVirtualBox The VirtualBox interface.
918 * @param a_pLock The write lock held by the caller.
919 */
920bool ExtPack::i_callVirtualBoxReadyHook(IVirtualBox *a_pVirtualBox, AutoWriteLock *a_pLock)
921{
922 if ( m != NULL
923 && m->fUsable
924 && m->hMainMod != NIL_RTLDRMOD
925 && !m->fMadeReadyCall)
926 {
927 m->fMadeReadyCall = true;
928 if (m->pReg->pfnVirtualBoxReady)
929 {
930 ComPtr<ExtPack> ptrSelfRef = this;
931 a_pLock->release();
932 m->pReg->pfnVirtualBoxReady(m->pReg, a_pVirtualBox);
933 i_notifyCloudProviderManager();
934 a_pLock->acquire();
935 return true;
936 }
937 }
938 return false;
939}
940#endif /* !VBOX_COM_INPROC */
941
942#ifdef VBOX_COM_INPROC
943/**
944 * Calls the pfnConsoleReady hook.
945 *
946 * @returns true if we left the lock, false if we didn't.
947 * @param a_pConsole The Console interface.
948 * @param a_pLock The write lock held by the caller.
949 */
950bool ExtPack::i_callConsoleReadyHook(IConsole *a_pConsole, AutoWriteLock *a_pLock)
951{
952 if ( m != NULL
953 && m->fUsable
954 && m->hMainMod != NIL_RTLDRMOD
955 && !m->fMadeReadyCall)
956 {
957 m->fMadeReadyCall = true;
958 if (m->pReg->pfnConsoleReady)
959 {
960 ComPtr<ExtPack> ptrSelfRef = this;
961 a_pLock->release();
962 m->pReg->pfnConsoleReady(m->pReg, a_pConsole);
963 a_pLock->acquire();
964 return true;
965 }
966 }
967 return false;
968}
969#endif /* VBOX_COM_INPROC */
970
971#ifndef VBOX_COM_INPROC
972/**
973 * Calls the pfnVMCreate hook.
974 *
975 * @returns true if we left the lock, false if we didn't.
976 * @param a_pVirtualBox The VirtualBox interface.
977 * @param a_pMachine The machine interface of the new VM.
978 * @param a_pLock The write lock held by the caller.
979 */
980bool ExtPack::i_callVmCreatedHook(IVirtualBox *a_pVirtualBox, IMachine *a_pMachine, AutoWriteLock *a_pLock)
981{
982 if ( m != NULL
983 && m->hMainMod != NIL_RTLDRMOD
984 && m->fUsable)
985 {
986 if (m->pReg->pfnVMCreated)
987 {
988 ComPtr<ExtPack> ptrSelfRef = this;
989 a_pLock->release();
990 m->pReg->pfnVMCreated(m->pReg, a_pVirtualBox, a_pMachine);
991 a_pLock->acquire();
992 return true;
993 }
994 }
995 return false;
996}
997#endif /* !VBOX_COM_INPROC */
998
999#ifdef VBOX_COM_INPROC
1000/**
1001 * Calls the pfnVMConfigureVMM hook.
1002 *
1003 * @returns true if we left the lock, false if we didn't.
1004 * @param a_pConsole The console interface.
1005 * @param a_pVM The VM handle.
1006 * @param a_pLock The write lock held by the caller.
1007 * @param a_pvrc Where to return the status code of the
1008 * callback. This is always set. LogRel is
1009 * called on if a failure status is returned.
1010 */
1011bool ExtPack::i_callVmConfigureVmmHook(IConsole *a_pConsole, PVM a_pVM, AutoWriteLock *a_pLock, int *a_pvrc)
1012{
1013 *a_pvrc = VINF_SUCCESS;
1014 if ( m != NULL
1015 && m->hMainMod != NIL_RTLDRMOD
1016 && m->fUsable)
1017 {
1018 if (m->pReg->pfnVMConfigureVMM)
1019 {
1020 ComPtr<ExtPack> ptrSelfRef = this;
1021 a_pLock->release();
1022 int vrc = m->pReg->pfnVMConfigureVMM(m->pReg, a_pConsole, a_pVM);
1023 *a_pvrc = vrc;
1024 a_pLock->acquire();
1025 if (RT_FAILURE(vrc))
1026 LogRel(("ExtPack pfnVMConfigureVMM returned %Rrc for %s\n", vrc, m->Desc.strName.c_str()));
1027 return true;
1028 }
1029 }
1030 return false;
1031}
1032
1033/**
1034 * Calls the pfnVMPowerOn hook.
1035 *
1036 * @returns true if we left the lock, false if we didn't.
1037 * @param a_pConsole The console interface.
1038 * @param a_pVM The VM handle.
1039 * @param a_pLock The write lock held by the caller.
1040 * @param a_pvrc Where to return the status code of the
1041 * callback. This is always set. LogRel is
1042 * called on if a failure status is returned.
1043 */
1044bool ExtPack::i_callVmPowerOnHook(IConsole *a_pConsole, PVM a_pVM, AutoWriteLock *a_pLock, int *a_pvrc)
1045{
1046 *a_pvrc = VINF_SUCCESS;
1047 if ( m != NULL
1048 && m->hMainMod != NIL_RTLDRMOD
1049 && m->fUsable)
1050 {
1051 if (m->pReg->pfnVMPowerOn)
1052 {
1053 ComPtr<ExtPack> ptrSelfRef = this;
1054 a_pLock->release();
1055 int vrc = m->pReg->pfnVMPowerOn(m->pReg, a_pConsole, a_pVM);
1056 *a_pvrc = vrc;
1057 a_pLock->acquire();
1058 if (RT_FAILURE(vrc))
1059 LogRel(("ExtPack pfnVMPowerOn returned %Rrc for %s\n", vrc, m->Desc.strName.c_str()));
1060 return true;
1061 }
1062 }
1063 return false;
1064}
1065
1066/**
1067 * Calls the pfnVMPowerOff hook.
1068 *
1069 * @returns true if we left the lock, false if we didn't.
1070 * @param a_pConsole The console interface.
1071 * @param a_pVM The VM handle.
1072 * @param a_pLock The write lock held by the caller.
1073 */
1074bool ExtPack::i_callVmPowerOffHook(IConsole *a_pConsole, PVM a_pVM, AutoWriteLock *a_pLock)
1075{
1076 if ( m != NULL
1077 && m->hMainMod != NIL_RTLDRMOD
1078 && m->fUsable)
1079 {
1080 if (m->pReg->pfnVMPowerOff)
1081 {
1082 ComPtr<ExtPack> ptrSelfRef = this;
1083 a_pLock->release();
1084 m->pReg->pfnVMPowerOff(m->pReg, a_pConsole, a_pVM);
1085 a_pLock->acquire();
1086 return true;
1087 }
1088 }
1089 return false;
1090}
1091#endif /* VBOX_COM_INPROC */
1092
1093/**
1094 * Check if the extension pack is usable and has an VRDE module.
1095 *
1096 * @returns S_OK or COM error status with error information.
1097 *
1098 * @remarks Caller holds the extension manager lock for reading, no locking
1099 * necessary.
1100 */
1101HRESULT ExtPack::i_checkVrde(void)
1102{
1103 HRESULT hrc;
1104 if ( m != NULL
1105 && m->fUsable)
1106 {
1107 if (m->Desc.strVrdeModule.isNotEmpty())
1108 hrc = S_OK;
1109 else
1110 hrc = setError(E_FAIL, tr("The extension pack '%s' does not include a VRDE module"), m->Desc.strName.c_str());
1111 }
1112 else
1113 hrc = setError(E_FAIL, tr("%s"), m->strWhyUnusable.c_str());
1114 return hrc;
1115}
1116
1117/**
1118 * Same as checkVrde(), except that it also resolves the path to the module.
1119 *
1120 * @returns S_OK or COM error status with error information.
1121 * @param a_pstrVrdeLibrary Where to return the path on success.
1122 *
1123 * @remarks Caller holds the extension manager lock for reading, no locking
1124 * necessary.
1125 */
1126HRESULT ExtPack::i_getVrdpLibraryName(Utf8Str *a_pstrVrdeLibrary)
1127{
1128 HRESULT hrc = i_checkVrde();
1129 if (SUCCEEDED(hrc))
1130 {
1131 if (i_findModule(m->Desc.strVrdeModule.c_str(), NULL, VBOXEXTPACKMODKIND_R3,
1132 a_pstrVrdeLibrary, NULL /*a_pfNative*/, NULL /*a_pObjInfo*/))
1133 hrc = S_OK;
1134 else
1135 hrc = setError(E_FAIL, tr("Failed to locate the VRDE module '%s' in extension pack '%s'"),
1136 m->Desc.strVrdeModule.c_str(), m->Desc.strName.c_str());
1137 }
1138 return hrc;
1139}
1140
1141/**
1142 * Resolves the path to the module.
1143 *
1144 * @returns S_OK or COM error status with error information.
1145 * @param a_pszModuleName The library.
1146 * @param a_pstrLibrary Where to return the path on success.
1147 *
1148 * @remarks Caller holds the extension manager lock for reading, no locking
1149 * necessary.
1150 */
1151HRESULT ExtPack::i_getLibraryName(const char *a_pszModuleName, Utf8Str *a_pstrLibrary)
1152{
1153 HRESULT hrc;
1154 if (i_findModule(a_pszModuleName, NULL, VBOXEXTPACKMODKIND_R3,
1155 a_pstrLibrary, NULL /*a_pfNative*/, NULL /*a_pObjInfo*/))
1156 hrc = S_OK;
1157 else
1158 hrc = setError(E_FAIL, tr("Failed to locate the module '%s' in extension pack '%s'"),
1159 a_pszModuleName, m->Desc.strName.c_str());
1160 return hrc;
1161}
1162
1163/**
1164 * Check if this extension pack wishes to be the default VRDE provider.
1165 *
1166 * @returns @c true if it wants to and it is in a usable state, otherwise
1167 * @c false.
1168 *
1169 * @remarks Caller holds the extension manager lock for reading, no locking
1170 * necessary.
1171 */
1172bool ExtPack::i_wantsToBeDefaultVrde(void) const
1173{
1174 return m->fUsable
1175 && m->Desc.strVrdeModule.isNotEmpty();
1176}
1177
1178/**
1179 * Refreshes the extension pack state.
1180 *
1181 * This is called by the manager so that the on disk changes are picked up.
1182 *
1183 * @returns S_OK or COM error status with error information.
1184 *
1185 * @param a_pfCanDelete Optional can-delete-this-object output indicator.
1186 *
1187 * @remarks Caller holds the extension manager lock for writing.
1188 * @remarks Only called in VBoxSVC.
1189 */
1190HRESULT ExtPack::i_refresh(bool *a_pfCanDelete)
1191{
1192 if (a_pfCanDelete)
1193 *a_pfCanDelete = false;
1194
1195 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS); /* for the COMGETTERs */
1196
1197 /*
1198 * Has the module been deleted?
1199 */
1200 RTFSOBJINFO ObjInfoExtPack;
1201 int vrc = RTPathQueryInfoEx(m->strExtPackPath.c_str(), &ObjInfoExtPack, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
1202 if ( RT_FAILURE(vrc)
1203 || !RTFS_IS_DIRECTORY(ObjInfoExtPack.Attr.fMode))
1204 {
1205 if (a_pfCanDelete)
1206 *a_pfCanDelete = true;
1207 return S_OK;
1208 }
1209
1210 /*
1211 * We've got a directory, so try query file system object info for the
1212 * files we are interested in as well.
1213 */
1214 RTFSOBJINFO ObjInfoDesc;
1215 char szDescFilePath[RTPATH_MAX];
1216 vrc = RTPathJoin(szDescFilePath, sizeof(szDescFilePath), m->strExtPackPath.c_str(), VBOX_EXTPACK_DESCRIPTION_NAME);
1217 if (RT_SUCCESS(vrc))
1218 vrc = RTPathQueryInfoEx(szDescFilePath, &ObjInfoDesc, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
1219 if (RT_FAILURE(vrc))
1220 RT_ZERO(ObjInfoDesc);
1221
1222 RTFSOBJINFO ObjInfoMainMod;
1223 if (m->strMainModPath.isNotEmpty())
1224 vrc = RTPathQueryInfoEx(m->strMainModPath.c_str(), &ObjInfoMainMod, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
1225 if (m->strMainModPath.isEmpty() || RT_FAILURE(vrc))
1226 RT_ZERO(ObjInfoMainMod);
1227
1228 /*
1229 * If we have a usable module already, just verify that things haven't
1230 * changed since we loaded it.
1231 */
1232 if (m->fUsable)
1233 {
1234 if (m->hMainMod == NIL_RTLDRMOD)
1235 i_probeAndLoad();
1236 else if ( !i_objinfoIsEqual(&ObjInfoDesc, &m->ObjInfoDesc)
1237 || !i_objinfoIsEqual(&ObjInfoMainMod, &m->ObjInfoMainMod)
1238 || !i_objinfoIsEqual(&ObjInfoExtPack, &m->ObjInfoExtPack) )
1239 {
1240 /** @todo not important, so it can wait. */
1241 }
1242 }
1243 /*
1244 * Ok, it is currently not usable. If anything has changed since last time
1245 * reprobe the extension pack.
1246 */
1247 else if ( !i_objinfoIsEqual(&ObjInfoDesc, &m->ObjInfoDesc)
1248 || !i_objinfoIsEqual(&ObjInfoMainMod, &m->ObjInfoMainMod)
1249 || !i_objinfoIsEqual(&ObjInfoExtPack, &m->ObjInfoExtPack) )
1250 i_probeAndLoad();
1251
1252 return S_OK;
1253}
1254
1255#ifndef VBOX_COM_INPROC
1256/**
1257 * Checks if there are cloud providers vetoing extension pack uninstall.
1258 *
1259 * This needs going through the cloud provider singleton in VirtualBox,
1260 * the job cannot be done purely by using the code in the extension pack).
1261 * It cannot be integrated into i_callUninstallHookAndClose, because it
1262 * can only do its job when the extpack lock is not held, whereas the
1263 * actual uninstall must be done with the lock held all the time for
1264 * consistency reasons.
1265 *
1266 * This is called when uninstalling or replacing an extension pack.
1267 *
1268 * @returns true / false
1269 */
1270bool ExtPack::i_areThereCloudProviderUninstallVetos()
1271{
1272 Assert(m->pVirtualBox != NULL); /* Only called from VBoxSVC. */
1273
1274 ComObjPtr<CloudProviderManager> cpm(m->pVirtualBox->i_getCloudProviderManager());
1275 AssertReturn(!cpm.isNull(), false);
1276
1277 return !cpm->i_canRemoveExtPack(static_cast<IExtPack *>(this));
1278}
1279
1280/**
1281 * Notifies the Cloud Provider Manager that there is a new extension pack.
1282 *
1283 * This is called when installing an extension pack.
1284 */
1285void ExtPack::i_notifyCloudProviderManager()
1286{
1287 Assert(m->pVirtualBox != NULL); /* Only called from VBoxSVC. */
1288
1289 ComObjPtr<CloudProviderManager> cpm(m->pVirtualBox->i_getCloudProviderManager());
1290 AssertReturnVoid(!cpm.isNull());
1291
1292 cpm->i_addExtPack(static_cast<IExtPack *>(this));
1293}
1294
1295#endif /* !VBOX_COM_INPROC */
1296
1297/**
1298 * Probes the extension pack, loading the main dll and calling its registration
1299 * entry point.
1300 *
1301 * This updates the state accordingly, the strWhyUnusable and fUnusable members
1302 * being the most important ones.
1303 */
1304void ExtPack::i_probeAndLoad(void)
1305{
1306 m->fUsable = false;
1307 m->fMadeReadyCall = false;
1308
1309 /*
1310 * Query the file system info for the extension pack directory. This and
1311 * all other file system info we save is for the benefit of refresh().
1312 */
1313 int vrc = RTPathQueryInfoEx(m->strExtPackPath.c_str(), &m->ObjInfoExtPack, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
1314 if (RT_FAILURE(vrc))
1315 {
1316 m->strWhyUnusable.printf(tr("RTPathQueryInfoEx on '%s' failed: %Rrc"), m->strExtPackPath.c_str(), vrc);
1317 return;
1318 }
1319 if (!RTFS_IS_DIRECTORY(m->ObjInfoExtPack.Attr.fMode))
1320 {
1321 if (RTFS_IS_SYMLINK(m->ObjInfoExtPack.Attr.fMode))
1322 m->strWhyUnusable.printf(tr("'%s' is a symbolic link, this is not allowed"),
1323 m->strExtPackPath.c_str(), vrc);
1324 else if (RTFS_IS_FILE(m->ObjInfoExtPack.Attr.fMode))
1325 m->strWhyUnusable.printf(tr("'%s' is a symbolic file, not a directory"),
1326 m->strExtPackPath.c_str(), vrc);
1327 else
1328 m->strWhyUnusable.printf(tr("'%s' is not a directory (fMode=%#x)"),
1329 m->strExtPackPath.c_str(), m->ObjInfoExtPack.Attr.fMode);
1330 return;
1331 }
1332
1333 RTERRINFOSTATIC ErrInfo;
1334 RTErrInfoInitStatic(&ErrInfo);
1335 vrc = SUPR3HardenedVerifyDir(m->strExtPackPath.c_str(), true /*fRecursive*/, true /*fCheckFiles*/, &ErrInfo.Core);
1336 if (RT_FAILURE(vrc))
1337 {
1338 m->strWhyUnusable.printf(tr("%s (rc=%Rrc)"), ErrInfo.Core.pszMsg, vrc);
1339 return;
1340 }
1341
1342 /*
1343 * Read the description file.
1344 */
1345 RTCString strSavedName(m->Desc.strName);
1346 RTCString *pStrLoadErr = VBoxExtPackLoadDesc(m->strExtPackPath.c_str(), &m->Desc, &m->ObjInfoDesc);
1347 if (pStrLoadErr != NULL)
1348 {
1349 m->strWhyUnusable.printf(tr("Failed to load '%s/%s': %s"),
1350 m->strExtPackPath.c_str(), VBOX_EXTPACK_DESCRIPTION_NAME, pStrLoadErr->c_str());
1351 m->Desc.strName = strSavedName;
1352 delete pStrLoadErr;
1353 return;
1354 }
1355
1356 /*
1357 * Make sure the XML name and directory matches.
1358 */
1359 if (!m->Desc.strName.equalsIgnoreCase(strSavedName))
1360 {
1361 m->strWhyUnusable.printf(tr("The description name ('%s') and directory name ('%s') does not match"),
1362 m->Desc.strName.c_str(), strSavedName.c_str());
1363 m->Desc.strName = strSavedName;
1364 return;
1365 }
1366
1367 /*
1368 * Load the main DLL and call the predefined entry point.
1369 */
1370#ifndef VBOX_COM_INPROC
1371 const char *pszMainModule = m->Desc.strMainModule.c_str();
1372#else
1373 const char *pszMainModule = m->Desc.strMainVMModule.c_str();
1374 if (m->Desc.strMainVMModule.isEmpty())
1375 {
1376 /*
1377 * We're good! The main module for VM processes is optional.
1378 */
1379 m->fUsable = true;
1380 m->strWhyUnusable.setNull();
1381 return;
1382 }
1383#endif
1384 bool fIsNative;
1385 if (!i_findModule(pszMainModule, NULL /* default extension */, VBOXEXTPACKMODKIND_R3,
1386 &m->strMainModPath, &fIsNative, &m->ObjInfoMainMod))
1387 {
1388 m->strWhyUnusable.printf(tr("Failed to locate the main module ('%s')"), pszMainModule);
1389 return;
1390 }
1391
1392 vrc = SUPR3HardenedVerifyPlugIn(m->strMainModPath.c_str(), &ErrInfo.Core);
1393 if (RT_FAILURE(vrc))
1394 {
1395 m->strWhyUnusable.printf(tr("%s"), ErrInfo.Core.pszMsg);
1396 return;
1397 }
1398
1399 if (fIsNative)
1400 {
1401 vrc = SUPR3HardenedLdrLoadPlugIn(m->strMainModPath.c_str(), &m->hMainMod, &ErrInfo.Core);
1402 if (RT_FAILURE(vrc))
1403 {
1404 m->hMainMod = NIL_RTLDRMOD;
1405 m->strWhyUnusable.printf(tr("Failed to load the main module ('%s'): %Rrc - %s"),
1406 m->strMainModPath.c_str(), vrc, ErrInfo.Core.pszMsg);
1407 return;
1408 }
1409 }
1410 else
1411 {
1412 m->strWhyUnusable.printf(tr("Only native main modules are currently supported"));
1413 return;
1414 }
1415
1416 /*
1417 * Resolve the predefined entry point.
1418 */
1419#ifndef VBOX_COM_INPROC
1420 const char *pszMainEntryPoint = VBOX_EXTPACK_MAIN_MOD_ENTRY_POINT;
1421 PFNVBOXEXTPACKREGISTER pfnRegistration;
1422 uint32_t uVersion = VBOXEXTPACKREG_VERSION;
1423#else
1424 const char *pszMainEntryPoint = VBOX_EXTPACK_MAIN_VM_MOD_ENTRY_POINT;
1425 PFNVBOXEXTPACKVMREGISTER pfnRegistration;
1426 uint32_t uVersion = VBOXEXTPACKVMREG_VERSION;
1427#endif
1428 vrc = RTLdrGetSymbol(m->hMainMod, pszMainEntryPoint, (void **)&pfnRegistration);
1429 if (RT_SUCCESS(vrc))
1430 {
1431 RTErrInfoClear(&ErrInfo.Core);
1432 vrc = pfnRegistration(&m->Hlp, &m->pReg, &ErrInfo.Core);
1433 if ( RT_SUCCESS(vrc)
1434 && !RTErrInfoIsSet(&ErrInfo.Core)
1435 && VALID_PTR(m->pReg))
1436 {
1437 if ( VBOXEXTPACK_IS_MAJOR_VER_EQUAL(m->pReg->u32Version, uVersion)
1438 && m->pReg->u32EndMarker == m->pReg->u32Version)
1439 {
1440#ifndef VBOX_COM_INPROC
1441 if ( (!m->pReg->pfnInstalled || RT_VALID_PTR(m->pReg->pfnInstalled))
1442 && (!m->pReg->pfnUninstall || RT_VALID_PTR(m->pReg->pfnUninstall))
1443 && (!m->pReg->pfnVirtualBoxReady || RT_VALID_PTR(m->pReg->pfnVirtualBoxReady))
1444 && (!m->pReg->pfnUnload || RT_VALID_PTR(m->pReg->pfnUnload))
1445 && (!m->pReg->pfnVMCreated || RT_VALID_PTR(m->pReg->pfnVMCreated))
1446 && (!m->pReg->pfnQueryObject || RT_VALID_PTR(m->pReg->pfnQueryObject))
1447 )
1448 {
1449 /*
1450 * We're good!
1451 */
1452 m->fUsable = true;
1453 m->strWhyUnusable.setNull();
1454 return;
1455 }
1456#else
1457 if ( (!m->pReg->pfnConsoleReady || RT_VALID_PTR(m->pReg->pfnConsoleReady))
1458 && (!m->pReg->pfnUnload || RT_VALID_PTR(m->pReg->pfnUnload))
1459 && (!m->pReg->pfnVMConfigureVMM || RT_VALID_PTR(m->pReg->pfnVMConfigureVMM))
1460 && (!m->pReg->pfnVMPowerOn || RT_VALID_PTR(m->pReg->pfnVMPowerOn))
1461 && (!m->pReg->pfnVMPowerOff || RT_VALID_PTR(m->pReg->pfnVMPowerOff))
1462 && (!m->pReg->pfnQueryObject || RT_VALID_PTR(m->pReg->pfnQueryObject))
1463 )
1464 {
1465 /*
1466 * We're good!
1467 */
1468 m->fUsable = true;
1469 m->strWhyUnusable.setNull();
1470 return;
1471 }
1472#endif
1473
1474 m->strWhyUnusable = tr("The registration structure contains one or more invalid function pointers");
1475 }
1476 else
1477 m->strWhyUnusable.printf(tr("Unsupported registration structure version %u.%u"),
1478 RT_HIWORD(m->pReg->u32Version), RT_LOWORD(m->pReg->u32Version));
1479 }
1480 else
1481 m->strWhyUnusable.printf(tr("%s returned %Rrc, pReg=%p ErrInfo='%s'"),
1482 pszMainEntryPoint, vrc, m->pReg, ErrInfo.Core.pszMsg);
1483 m->pReg = NULL;
1484 }
1485 else
1486 m->strWhyUnusable.printf(tr("Failed to resolve exported symbol '%s' in the main module: %Rrc"),
1487 pszMainEntryPoint, vrc);
1488
1489 RTLdrClose(m->hMainMod);
1490 m->hMainMod = NIL_RTLDRMOD;
1491}
1492
1493/**
1494 * Finds a module.
1495 *
1496 * @returns true if found, false if not.
1497 * @param a_pszName The module base name (no extension).
1498 * @param a_pszExt The extension. If NULL we use default
1499 * extensions.
1500 * @param a_enmKind The kind of module to locate.
1501 * @param a_pStrFound Where to return the path to the module we've
1502 * found.
1503 * @param a_pfNative Where to return whether this is a native module
1504 * or an agnostic one. Optional.
1505 * @param a_pObjInfo Where to return the file system object info for
1506 * the module. Optional.
1507 */
1508bool ExtPack::i_findModule(const char *a_pszName, const char *a_pszExt, VBOXEXTPACKMODKIND a_enmKind,
1509 Utf8Str *a_pStrFound, bool *a_pfNative, PRTFSOBJINFO a_pObjInfo) const
1510{
1511 /*
1512 * Try the native path first.
1513 */
1514 char szPath[RTPATH_MAX];
1515 int vrc = RTPathJoin(szPath, sizeof(szPath), m->strExtPackPath.c_str(), RTBldCfgTargetDotArch());
1516 AssertLogRelRCReturn(vrc, false);
1517 vrc = RTPathAppend(szPath, sizeof(szPath), a_pszName);
1518 AssertLogRelRCReturn(vrc, false);
1519 if (!a_pszExt)
1520 {
1521 const char *pszDefExt;
1522 switch (a_enmKind)
1523 {
1524 case VBOXEXTPACKMODKIND_RC: pszDefExt = ".rc"; break;
1525 case VBOXEXTPACKMODKIND_R0: pszDefExt = ".r0"; break;
1526 case VBOXEXTPACKMODKIND_R3: pszDefExt = RTLdrGetSuff(); break;
1527 default:
1528 AssertFailedReturn(false);
1529 }
1530 vrc = RTStrCat(szPath, sizeof(szPath), pszDefExt);
1531 AssertLogRelRCReturn(vrc, false);
1532 }
1533
1534 RTFSOBJINFO ObjInfo;
1535 if (!a_pObjInfo)
1536 a_pObjInfo = &ObjInfo;
1537 vrc = RTPathQueryInfo(szPath, a_pObjInfo, RTFSOBJATTRADD_UNIX);
1538 if (RT_SUCCESS(vrc) && RTFS_IS_FILE(a_pObjInfo->Attr.fMode))
1539 {
1540 if (a_pfNative)
1541 *a_pfNative = true;
1542 *a_pStrFound = szPath;
1543 return true;
1544 }
1545
1546 /*
1547 * Try the platform agnostic modules.
1548 */
1549 /* gcc.x86/module.rel */
1550 char szSubDir[32];
1551 RTStrPrintf(szSubDir, sizeof(szSubDir), "%s.%s", RTBldCfgCompiler(), RTBldCfgTargetArch());
1552 vrc = RTPathJoin(szPath, sizeof(szPath), m->strExtPackPath.c_str(), szSubDir);
1553 AssertLogRelRCReturn(vrc, false);
1554 vrc = RTPathAppend(szPath, sizeof(szPath), a_pszName);
1555 AssertLogRelRCReturn(vrc, false);
1556 if (!a_pszExt)
1557 {
1558 vrc = RTStrCat(szPath, sizeof(szPath), ".rel");
1559 AssertLogRelRCReturn(vrc, false);
1560 }
1561 vrc = RTPathQueryInfo(szPath, a_pObjInfo, RTFSOBJATTRADD_UNIX);
1562 if (RT_SUCCESS(vrc) && RTFS_IS_FILE(a_pObjInfo->Attr.fMode))
1563 {
1564 if (a_pfNative)
1565 *a_pfNative = false;
1566 *a_pStrFound = szPath;
1567 return true;
1568 }
1569
1570 /* x86/module.rel */
1571 vrc = RTPathJoin(szPath, sizeof(szPath), m->strExtPackPath.c_str(), RTBldCfgTargetArch());
1572 AssertLogRelRCReturn(vrc, false);
1573 vrc = RTPathAppend(szPath, sizeof(szPath), a_pszName);
1574 AssertLogRelRCReturn(vrc, false);
1575 if (!a_pszExt)
1576 {
1577 vrc = RTStrCat(szPath, sizeof(szPath), ".rel");
1578 AssertLogRelRCReturn(vrc, false);
1579 }
1580 vrc = RTPathQueryInfo(szPath, a_pObjInfo, RTFSOBJATTRADD_UNIX);
1581 if (RT_SUCCESS(vrc) && RTFS_IS_FILE(a_pObjInfo->Attr.fMode))
1582 {
1583 if (a_pfNative)
1584 *a_pfNative = false;
1585 *a_pStrFound = szPath;
1586 return true;
1587 }
1588
1589 return false;
1590}
1591
1592/**
1593 * Compares two file system object info structures.
1594 *
1595 * @returns true if equal, false if not.
1596 * @param pObjInfo1 The first.
1597 * @param pObjInfo2 The second.
1598 * @todo IPRT should do this, really.
1599 */
1600/* static */ bool ExtPack::i_objinfoIsEqual(PCRTFSOBJINFO pObjInfo1, PCRTFSOBJINFO pObjInfo2)
1601{
1602 if (!RTTimeSpecIsEqual(&pObjInfo1->ModificationTime, &pObjInfo2->ModificationTime))
1603 return false;
1604 if (!RTTimeSpecIsEqual(&pObjInfo1->ChangeTime, &pObjInfo2->ChangeTime))
1605 return false;
1606 if (!RTTimeSpecIsEqual(&pObjInfo1->BirthTime, &pObjInfo2->BirthTime))
1607 return false;
1608 if (pObjInfo1->cbObject != pObjInfo2->cbObject)
1609 return false;
1610 if (pObjInfo1->Attr.fMode != pObjInfo2->Attr.fMode)
1611 return false;
1612 if (pObjInfo1->Attr.enmAdditional == pObjInfo2->Attr.enmAdditional)
1613 {
1614 switch (pObjInfo1->Attr.enmAdditional)
1615 {
1616 case RTFSOBJATTRADD_UNIX:
1617 if (pObjInfo1->Attr.u.Unix.uid != pObjInfo2->Attr.u.Unix.uid)
1618 return false;
1619 if (pObjInfo1->Attr.u.Unix.gid != pObjInfo2->Attr.u.Unix.gid)
1620 return false;
1621 if (pObjInfo1->Attr.u.Unix.INodeIdDevice != pObjInfo2->Attr.u.Unix.INodeIdDevice)
1622 return false;
1623 if (pObjInfo1->Attr.u.Unix.INodeId != pObjInfo2->Attr.u.Unix.INodeId)
1624 return false;
1625 if (pObjInfo1->Attr.u.Unix.GenerationId != pObjInfo2->Attr.u.Unix.GenerationId)
1626 return false;
1627 break;
1628 default:
1629 break;
1630 }
1631 }
1632 return true;
1633}
1634
1635
1636/**
1637 * @interface_method_impl{VBOXEXTPACKHLP,pfnFindModule}
1638 */
1639/*static*/ DECLCALLBACK(int)
1640ExtPack::i_hlpFindModule(PCVBOXEXTPACKHLP pHlp, const char *pszName, const char *pszExt, VBOXEXTPACKMODKIND enmKind,
1641 char *pszFound, size_t cbFound, bool *pfNative)
1642{
1643 /*
1644 * Validate the input and get our bearings.
1645 */
1646 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
1647 AssertPtrNullReturn(pszExt, VERR_INVALID_POINTER);
1648 AssertPtrReturn(pszFound, VERR_INVALID_POINTER);
1649 AssertPtrNullReturn(pfNative, VERR_INVALID_POINTER);
1650 AssertReturn(enmKind > VBOXEXTPACKMODKIND_INVALID && enmKind < VBOXEXTPACKMODKIND_END, VERR_INVALID_PARAMETER);
1651
1652 AssertPtrReturn(pHlp, VERR_INVALID_POINTER);
1653 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VERR_INVALID_POINTER);
1654 ExtPack::Data *m = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1655 AssertPtrReturn(m, VERR_INVALID_POINTER);
1656 ExtPack *pThis = m->pThis;
1657 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1658
1659 /*
1660 * This is just a wrapper around findModule.
1661 */
1662 Utf8Str strFound;
1663 if (pThis->i_findModule(pszName, pszExt, enmKind, &strFound, pfNative, NULL))
1664 return RTStrCopy(pszFound, cbFound, strFound.c_str());
1665 return VERR_FILE_NOT_FOUND;
1666}
1667
1668/*static*/ DECLCALLBACK(int)
1669ExtPack::i_hlpGetFilePath(PCVBOXEXTPACKHLP pHlp, const char *pszFilename, char *pszPath, size_t cbPath)
1670{
1671 /*
1672 * Validate the input and get our bearings.
1673 */
1674 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
1675 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1676 AssertReturn(cbPath > 0, VERR_BUFFER_OVERFLOW);
1677
1678 AssertPtrReturn(pHlp, VERR_INVALID_POINTER);
1679 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VERR_INVALID_POINTER);
1680 ExtPack::Data *m = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1681 AssertPtrReturn(m, VERR_INVALID_POINTER);
1682 ExtPack *pThis = m->pThis;
1683 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1684
1685 /*
1686 * This is a simple RTPathJoin, no checking if things exists or anything.
1687 */
1688 int vrc = RTPathJoin(pszPath, cbPath, pThis->m->strExtPackPath.c_str(), pszFilename);
1689 if (RT_FAILURE(vrc))
1690 RT_BZERO(pszPath, cbPath);
1691 return vrc;
1692}
1693
1694/*static*/ DECLCALLBACK(VBOXEXTPACKCTX)
1695ExtPack::i_hlpGetContext(PCVBOXEXTPACKHLP pHlp)
1696{
1697 /*
1698 * Validate the input and get our bearings.
1699 */
1700 AssertPtrReturn(pHlp, VBOXEXTPACKCTX_INVALID);
1701 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VBOXEXTPACKCTX_INVALID);
1702 ExtPack::Data *m = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1703 AssertPtrReturn(m, VBOXEXTPACKCTX_INVALID);
1704 ExtPack *pThis = m->pThis;
1705 AssertPtrReturn(pThis, VBOXEXTPACKCTX_INVALID);
1706
1707 return pThis->m->enmContext;
1708}
1709
1710/*static*/ DECLCALLBACK(int)
1711ExtPack::i_hlpLoadHGCMService(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IConsole) *pConsole,
1712 const char *pszServiceLibrary, const char *pszServiceName)
1713{
1714#ifdef VBOX_COM_INPROC
1715 /*
1716 * Validate the input and get our bearings.
1717 */
1718 AssertPtrReturn(pszServiceLibrary, VERR_INVALID_POINTER);
1719 AssertPtrReturn(pszServiceName, VERR_INVALID_POINTER);
1720
1721 AssertPtrReturn(pHlp, VERR_INVALID_POINTER);
1722 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VERR_INVALID_POINTER);
1723 ExtPack::Data *m = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1724 AssertPtrReturn(m, VERR_INVALID_POINTER);
1725 ExtPack *pThis = m->pThis;
1726 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1727 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
1728
1729 Console *pCon = (Console *)pConsole;
1730 return pCon->i_hgcmLoadService(pszServiceLibrary, pszServiceName);
1731#else
1732 NOREF(pHlp); NOREF(pConsole); NOREF(pszServiceLibrary); NOREF(pszServiceName);
1733 return VERR_INVALID_STATE;
1734#endif
1735}
1736
1737/*static*/ DECLCALLBACK(int)
1738ExtPack::i_hlpLoadVDPlugin(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IVirtualBox) *pVirtualBox, const char *pszPluginLibrary)
1739{
1740#ifndef VBOX_COM_INPROC
1741 /*
1742 * Validate the input and get our bearings.
1743 */
1744 AssertPtrReturn(pszPluginLibrary, VERR_INVALID_POINTER);
1745
1746 AssertPtrReturn(pHlp, VERR_INVALID_POINTER);
1747 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VERR_INVALID_POINTER);
1748 ExtPack::Data *m = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1749 AssertPtrReturn(m, VERR_INVALID_POINTER);
1750 ExtPack *pThis = m->pThis;
1751 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1752 AssertPtrReturn(pVirtualBox, VERR_INVALID_POINTER);
1753
1754 VirtualBox *pVBox = (VirtualBox *)pVirtualBox;
1755 return pVBox->i_loadVDPlugin(pszPluginLibrary);
1756#else
1757 NOREF(pHlp); NOREF(pVirtualBox); NOREF(pszPluginLibrary);
1758 return VERR_INVALID_STATE;
1759#endif
1760}
1761
1762/*static*/ DECLCALLBACK(int)
1763ExtPack::i_hlpUnloadVDPlugin(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IVirtualBox) *pVirtualBox, const char *pszPluginLibrary)
1764{
1765#ifndef VBOX_COM_INPROC
1766 /*
1767 * Validate the input and get our bearings.
1768 */
1769 AssertPtrReturn(pszPluginLibrary, VERR_INVALID_POINTER);
1770
1771 AssertPtrReturn(pHlp, VERR_INVALID_POINTER);
1772 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VERR_INVALID_POINTER);
1773 ExtPack::Data *m = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1774 AssertPtrReturn(m, VERR_INVALID_POINTER);
1775 ExtPack *pThis = m->pThis;
1776 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1777 AssertPtrReturn(pVirtualBox, VERR_INVALID_POINTER);
1778
1779 VirtualBox *pVBox = (VirtualBox *)pVirtualBox;
1780 return pVBox->i_unloadVDPlugin(pszPluginLibrary);
1781#else
1782 NOREF(pHlp); NOREF(pVirtualBox); NOREF(pszPluginLibrary);
1783 return VERR_INVALID_STATE;
1784#endif
1785}
1786
1787/*static*/ DECLCALLBACK(uint32_t)
1788ExtPack::i_hlpCreateProgress(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IUnknown) *pInitiator,
1789 const char *pcszDescription, uint32_t cOperations,
1790 uint32_t uTotalOperationsWeight, const char *pcszFirstOperationDescription,
1791 uint32_t uFirstOperationWeight, VBOXEXTPACK_IF_CS(IProgress) **ppProgressOut)
1792{
1793 /*
1794 * Validate the input and get our bearings.
1795 */
1796 AssertPtrReturn(pcszDescription, (uint32_t)E_INVALIDARG);
1797 AssertReturn(cOperations >= 1, (uint32_t)E_INVALIDARG);
1798 AssertReturn(uTotalOperationsWeight >= 1, (uint32_t)E_INVALIDARG);
1799 AssertPtrReturn(pcszFirstOperationDescription, (uint32_t)E_INVALIDARG);
1800 AssertReturn(uFirstOperationWeight >= 1, (uint32_t)E_INVALIDARG);
1801 AssertPtrReturn(ppProgressOut, (uint32_t)E_INVALIDARG);
1802
1803 AssertPtrReturn(pHlp, (uint32_t)E_INVALIDARG);
1804 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
1805#ifndef VBOX_COM_INPROC
1806 ExtPack::Data *m = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1807#endif
1808
1809 ComObjPtr<Progress> pProgress;
1810 HRESULT hrc = pProgress.createObject();
1811 if (FAILED(hrc))
1812 return hrc;
1813 hrc = pProgress->init(
1814#ifndef VBOX_COM_INPROC
1815 m->pVirtualBox,
1816#endif
1817 pInitiator, pcszDescription, TRUE /* aCancelable */,
1818 cOperations, uTotalOperationsWeight,
1819 pcszFirstOperationDescription, uFirstOperationWeight);
1820 if (FAILED(hrc))
1821 return hrc;
1822
1823 return pProgress.queryInterfaceTo(ppProgressOut);
1824}
1825
1826/*static*/ DECLCALLBACK(uint32_t)
1827ExtPack::i_hlpGetCanceledProgress(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IProgress) *pProgress,
1828 bool *pfCanceled)
1829{
1830 /*
1831 * Validate the input and get our bearings.
1832 */
1833 AssertPtrReturn(pProgress, (uint32_t)E_INVALIDARG);
1834 AssertPtrReturn(pfCanceled, (uint32_t)E_INVALIDARG);
1835
1836 AssertPtrReturn(pHlp, (uint32_t)E_INVALIDARG);
1837 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
1838
1839 BOOL fCanceled = FALSE;
1840 HRESULT hrc = pProgress->COMGETTER(Canceled)(&fCanceled);
1841 *pfCanceled = !!fCanceled;
1842 return hrc;
1843}
1844
1845/*static*/ DECLCALLBACK(uint32_t)
1846ExtPack::i_hlpUpdateProgress(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IProgress) *pProgress,
1847 uint32_t uPercent)
1848{
1849 /*
1850 * Validate the input and get our bearings.
1851 */
1852 AssertPtrReturn(pProgress, (uint32_t)E_INVALIDARG);
1853 AssertReturn(uPercent <= 100, (uint32_t)E_INVALIDARG);
1854
1855 AssertPtrReturn(pHlp, (uint32_t)E_INVALIDARG);
1856 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
1857
1858 ComPtr<IInternalProgressControl> pProgressControl(pProgress);
1859 AssertReturn(!!pProgressControl, (uint32_t)E_INVALIDARG);
1860 return pProgressControl->SetCurrentOperationProgress(uPercent);
1861}
1862
1863/*static*/ DECLCALLBACK(uint32_t)
1864ExtPack::i_hlpNextOperationProgress(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IProgress) *pProgress,
1865 const char *pcszNextOperationDescription,
1866 uint32_t uNextOperationWeight)
1867{
1868 /*
1869 * Validate the input and get our bearings.
1870 */
1871 AssertPtrReturn(pProgress, (uint32_t)E_INVALIDARG);
1872 AssertPtrReturn(pcszNextOperationDescription, (uint32_t)E_INVALIDARG);
1873 AssertReturn(uNextOperationWeight >= 1, (uint32_t)E_INVALIDARG);
1874
1875 AssertPtrReturn(pHlp, (uint32_t)E_INVALIDARG);
1876 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
1877
1878 ComPtr<IInternalProgressControl> pProgressControl(pProgress);
1879 AssertReturn(!!pProgressControl, (uint32_t)E_INVALIDARG);
1880 return pProgressControl->SetNextOperation(Bstr(pcszNextOperationDescription).raw(), uNextOperationWeight);
1881}
1882
1883/*static*/ DECLCALLBACK(uint32_t)
1884ExtPack::i_hlpWaitOtherProgress(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IProgress) *pProgress,
1885 VBOXEXTPACK_IF_CS(IProgress) *pProgressOther, uint32_t cTimeoutMS)
1886{
1887 /*
1888 * Validate the input and get our bearings.
1889 */
1890 AssertPtrReturn(pProgress, (uint32_t)E_INVALIDARG);
1891 AssertPtrReturn(pProgressOther, (uint32_t)E_INVALIDARG);
1892
1893 AssertPtrReturn(pHlp, (uint32_t)E_INVALIDARG);
1894 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
1895
1896 ComPtr<IInternalProgressControl> pProgressControl(pProgress);
1897 AssertReturn(!!pProgressControl, (uint32_t)E_INVALIDARG);
1898 return pProgressControl->WaitForOtherProgressCompletion(pProgressOther, cTimeoutMS);
1899}
1900
1901/*static*/ DECLCALLBACK(uint32_t)
1902ExtPack::i_hlpCompleteProgress(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IProgress) *pProgress,
1903 uint32_t uResultCode)
1904{
1905 /*
1906 * Validate the input and get our bearings.
1907 */
1908 AssertPtrReturn(pProgress, (uint32_t)E_INVALIDARG);
1909
1910 AssertPtrReturn(pHlp, (uint32_t)E_INVALIDARG);
1911 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
1912
1913 ComPtr<IInternalProgressControl> pProgressControl(pProgress);
1914 AssertReturn(!!pProgressControl, (uint32_t)E_INVALIDARG);
1915
1916 ComPtr<IVirtualBoxErrorInfo> errorInfo;
1917 if (FAILED((HRESULT)uResultCode))
1918 {
1919 ErrorInfoKeeper eik;
1920 eik.getVirtualBoxErrorInfo(errorInfo);
1921 }
1922 return pProgressControl->NotifyComplete((LONG)uResultCode, errorInfo);
1923}
1924
1925
1926/*static*/ DECLCALLBACK(uint32_t)
1927ExtPack::i_hlpCreateEvent(PCVBOXEXTPACKHLP pHlp,
1928 VBOXEXTPACK_IF_CS(IEventSource) *aSource,
1929 /* VBoxEventType_T */ uint32_t aType, bool aWaitable,
1930 VBOXEXTPACK_IF_CS(IEvent) **ppEventOut)
1931{
1932 HRESULT hrc;
1933
1934 AssertPtrReturn(pHlp, (uint32_t)E_INVALIDARG);
1935 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
1936 AssertPtrReturn(ppEventOut, (uint32_t)E_INVALIDARG);
1937
1938 ComObjPtr<VBoxEvent> pEvent;
1939
1940 hrc = pEvent.createObject();
1941 if (FAILED(hrc))
1942 return hrc;
1943
1944 /* default aSource to pVirtualBox? */
1945 hrc = pEvent->init(aSource, static_cast<VBoxEventType_T>(aType), aWaitable);
1946 if (FAILED(hrc))
1947 return hrc;
1948
1949 return pEvent.queryInterfaceTo(ppEventOut);
1950}
1951
1952
1953/*static*/ DECLCALLBACK(uint32_t)
1954ExtPack::i_hlpCreateVetoEvent(PCVBOXEXTPACKHLP pHlp,
1955 VBOXEXTPACK_IF_CS(IEventSource) *aSource,
1956 /* VBoxEventType_T */ uint32_t aType,
1957 VBOXEXTPACK_IF_CS(IVetoEvent) **ppEventOut)
1958{
1959 HRESULT hrc;
1960
1961 AssertPtrReturn(pHlp, (uint32_t)E_INVALIDARG);
1962 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
1963 AssertPtrReturn(ppEventOut, (uint32_t)E_INVALIDARG);
1964
1965 ComObjPtr<VBoxVetoEvent> pEvent;
1966
1967 hrc = pEvent.createObject();
1968 if (FAILED(hrc))
1969 return hrc;
1970
1971 /* default aSource to pVirtualBox? */
1972 hrc = pEvent->init(aSource, static_cast<VBoxEventType_T>(aType));
1973 if (FAILED(hrc))
1974 return hrc;
1975
1976 return pEvent.queryInterfaceTo(ppEventOut);
1977}
1978
1979
1980/*static*/ DECLCALLBACK(int)
1981ExtPack::i_hlpReservedN(PCVBOXEXTPACKHLP pHlp)
1982{
1983 /*
1984 * Validate the input and get our bearings.
1985 */
1986 AssertPtrReturn(pHlp, VERR_INVALID_POINTER);
1987 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VERR_INVALID_POINTER);
1988 ExtPack::Data *m = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1989 AssertPtrReturn(m, VERR_INVALID_POINTER);
1990 ExtPack *pThis = m->pThis;
1991 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1992
1993 return VERR_NOT_IMPLEMENTED;
1994}
1995
1996
1997
1998
1999HRESULT ExtPack::getName(com::Utf8Str &aName)
2000{
2001 aName = m->Desc.strName;
2002 return S_OK;
2003}
2004
2005HRESULT ExtPack::getDescription(com::Utf8Str &aDescription)
2006{
2007 aDescription = m->Desc.strDescription;
2008 return S_OK;
2009}
2010
2011HRESULT ExtPack::getVersion(com::Utf8Str &aVersion)
2012{
2013 aVersion = m->Desc.strVersion;
2014 return S_OK;
2015}
2016
2017HRESULT ExtPack::getRevision(ULONG *aRevision)
2018{
2019 *aRevision = m->Desc.uRevision;
2020 return S_OK;
2021}
2022
2023HRESULT ExtPack::getEdition(com::Utf8Str &aEdition)
2024{
2025 aEdition = m->Desc.strEdition;
2026 return S_OK;
2027}
2028
2029HRESULT ExtPack::getVRDEModule(com::Utf8Str &aVRDEModule)
2030{
2031 aVRDEModule = m->Desc.strVrdeModule;
2032 return S_OK;
2033}
2034
2035HRESULT ExtPack::getPlugIns(std::vector<ComPtr<IExtPackPlugIn> > &aPlugIns)
2036{
2037 /** @todo implement plug-ins. */
2038 NOREF(aPlugIns);
2039 ReturnComNotImplemented();
2040}
2041
2042HRESULT ExtPack::getUsable(BOOL *aUsable)
2043{
2044 *aUsable = m->fUsable;
2045 return S_OK;
2046}
2047
2048HRESULT ExtPack::getWhyUnusable(com::Utf8Str &aWhyUnusable)
2049{
2050 aWhyUnusable = m->strWhyUnusable;
2051 return S_OK;
2052}
2053
2054HRESULT ExtPack::getShowLicense(BOOL *aShowLicense)
2055{
2056 *aShowLicense = m->Desc.fShowLicense;
2057 return S_OK;
2058}
2059
2060HRESULT ExtPack::getLicense(com::Utf8Str &aLicense)
2061{
2062 Utf8Str strHtml("html");
2063 Utf8Str str("");
2064 return queryLicense(str, str, strHtml, aLicense);
2065}
2066
2067HRESULT ExtPack::queryLicense(const com::Utf8Str &aPreferredLocale, const com::Utf8Str &aPreferredLanguage,
2068 const com::Utf8Str &aFormat, com::Utf8Str &aLicenseText)
2069{
2070 HRESULT hrc = S_OK;
2071
2072 /*
2073 * Validate input.
2074 */
2075 if (aPreferredLocale.length() != 2 && aPreferredLocale.length() != 0)
2076 return setError(E_FAIL, tr("The preferred locale is a two character string or empty."));
2077
2078 if (aPreferredLanguage.length() != 2 && aPreferredLanguage.length() != 0)
2079 return setError(E_FAIL, tr("The preferred lanuage is a two character string or empty."));
2080
2081 if ( !aFormat.equals("html")
2082 && !aFormat.equals("rtf")
2083 && !aFormat.equals("txt"))
2084 return setError(E_FAIL, tr("The license format can only have the values 'html', 'rtf' and 'txt'."));
2085
2086 /*
2087 * Combine the options to form a file name before locking down anything.
2088 */
2089 char szName[sizeof(VBOX_EXTPACK_LICENSE_NAME_PREFIX "-de_DE.html") + 2];
2090 if (aPreferredLocale.isNotEmpty() && aPreferredLanguage.isNotEmpty())
2091 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX "-%s_%s.%s",
2092 aPreferredLocale.c_str(), aPreferredLanguage.c_str(), aFormat.c_str());
2093 else if (aPreferredLocale.isNotEmpty())
2094 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX "-%s.%s",
2095 aPreferredLocale.c_str(), aFormat.c_str());
2096 else if (aPreferredLanguage.isNotEmpty())
2097 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX "-_%s.%s",
2098 aPreferredLocale.c_str(), aFormat.c_str());
2099 else
2100 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX ".%s",
2101 aFormat.c_str());
2102
2103 /*
2104 * Effectuate the query.
2105 */
2106 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS); /* paranoia */
2107
2108 if (!m->fUsable)
2109 hrc = setError(E_FAIL, tr("%s"), m->strWhyUnusable.c_str());
2110 else
2111 {
2112 char szPath[RTPATH_MAX];
2113 int vrc = RTPathJoin(szPath, sizeof(szPath), m->strExtPackPath.c_str(), szName);
2114 if (RT_SUCCESS(vrc))
2115 {
2116 void *pvFile;
2117 size_t cbFile;
2118 vrc = RTFileReadAllEx(szPath, 0, RTFOFF_MAX, RTFILE_RDALL_O_DENY_READ, &pvFile, &cbFile);
2119 if (RT_SUCCESS(vrc))
2120 {
2121 Bstr bstrLicense((const char *)pvFile, cbFile);
2122 if (bstrLicense.isNotEmpty())
2123 {
2124 aLicenseText = Utf8Str(bstrLicense);
2125 hrc = S_OK;
2126 }
2127 else
2128 hrc = setError(VBOX_E_IPRT_ERROR, tr("The license file '%s' is empty or contains invalid UTF-8 encoding"),
2129 szPath);
2130 RTFileReadAllFree(pvFile, cbFile);
2131 }
2132 else if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
2133 hrc = setErrorBoth(VBOX_E_OBJECT_NOT_FOUND, vrc, tr("The license file '%s' was not found in extension pack '%s'"),
2134 szName, m->Desc.strName.c_str());
2135 else
2136 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to open the license file '%s': %Rrc"), szPath, vrc);
2137 }
2138 else
2139 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("RTPathJoin failed: %Rrc"), vrc);
2140 }
2141 return hrc;
2142}
2143
2144HRESULT ExtPack::queryObject(const com::Utf8Str &aObjUuid, ComPtr<IUnknown> &aReturnInterface)
2145{
2146 com::Guid ObjectId;
2147 CheckComArgGuid(aObjUuid, ObjectId);
2148
2149 HRESULT hrc = S_OK;
2150
2151 if ( m->pReg
2152 && m->pReg->pfnQueryObject)
2153 {
2154 void *pvUnknown = m->pReg->pfnQueryObject(m->pReg, ObjectId.raw());
2155 if (pvUnknown)
2156 {
2157 aReturnInterface = (IUnknown *)pvUnknown;
2158 /* The above assignment increased the refcount. Since pvUnknown
2159 * is a dumb pointer we have to do the release ourselves. */
2160 ((IUnknown *)pvUnknown)->Release();
2161 }
2162 else
2163 hrc = E_NOINTERFACE;
2164 }
2165 else
2166 hrc = E_NOINTERFACE;
2167 return hrc;
2168}
2169
2170DEFINE_EMPTY_CTOR_DTOR(ExtPackManager)
2171
2172/**
2173 * Called by ComObjPtr::createObject when creating the object.
2174 *
2175 * Just initialize the basic object state, do the rest in init().
2176 *
2177 * @returns S_OK.
2178 */
2179HRESULT ExtPackManager::FinalConstruct()
2180{
2181 m = NULL;
2182 return BaseFinalConstruct();
2183}
2184
2185/**
2186 * Initializes the extension pack manager.
2187 *
2188 * @returns COM status code.
2189 * @param a_pVirtualBox Pointer to the VirtualBox object.
2190 * @param a_enmContext The context we're in.
2191 */
2192HRESULT ExtPackManager::initExtPackManager(VirtualBox *a_pVirtualBox, VBOXEXTPACKCTX a_enmContext)
2193{
2194 AutoInitSpan autoInitSpan(this);
2195 AssertReturn(autoInitSpan.isOk(), E_FAIL);
2196
2197 /*
2198 * Figure some stuff out before creating the instance data.
2199 */
2200 char szBaseDir[RTPATH_MAX];
2201 int rc = RTPathAppPrivateArchTop(szBaseDir, sizeof(szBaseDir));
2202 AssertLogRelRCReturn(rc, E_FAIL);
2203 rc = RTPathAppend(szBaseDir, sizeof(szBaseDir), VBOX_EXTPACK_INSTALL_DIR);
2204 AssertLogRelRCReturn(rc, E_FAIL);
2205
2206 char szCertificatDir[RTPATH_MAX];
2207 rc = RTPathAppPrivateNoArch(szCertificatDir, sizeof(szCertificatDir));
2208 AssertLogRelRCReturn(rc, E_FAIL);
2209 rc = RTPathAppend(szCertificatDir, sizeof(szCertificatDir), VBOX_EXTPACK_CERT_DIR);
2210 AssertLogRelRCReturn(rc, E_FAIL);
2211
2212 /*
2213 * Allocate and initialize the instance data.
2214 */
2215 m = new Data;
2216 m->strBaseDir = szBaseDir;
2217 m->strCertificatDirPath = szCertificatDir;
2218 m->enmContext = a_enmContext;
2219#ifndef VBOX_COM_INPROC
2220 m->pVirtualBox = a_pVirtualBox;
2221#else
2222 RT_NOREF_PV(a_pVirtualBox);
2223#endif
2224
2225 /*
2226 * Go looking for extensions. The RTDirOpen may fail if nothing has been
2227 * installed yet, or if root is paranoid and has revoked our access to them.
2228 *
2229 * We ASSUME that there are no files, directories or stuff in the directory
2230 * that exceed the max name length in RTDIRENTRYEX.
2231 */
2232 HRESULT hrc = S_OK;
2233 RTDIR hDir;
2234 int vrc = RTDirOpen(&hDir, szBaseDir);
2235 if (RT_SUCCESS(vrc))
2236 {
2237 for (;;)
2238 {
2239 RTDIRENTRYEX Entry;
2240 vrc = RTDirReadEx(hDir, &Entry, NULL /*pcbDirEntry*/, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2241 if (RT_FAILURE(vrc))
2242 {
2243 AssertLogRelMsg(vrc == VERR_NO_MORE_FILES, ("%Rrc\n", vrc));
2244 break;
2245 }
2246 if ( RTFS_IS_DIRECTORY(Entry.Info.Attr.fMode)
2247 && strcmp(Entry.szName, ".") != 0
2248 && strcmp(Entry.szName, "..") != 0
2249 && VBoxExtPackIsValidMangledName(Entry.szName) )
2250 {
2251 /*
2252 * All directories are extensions, the shall be nothing but
2253 * extensions in this subdirectory.
2254 */
2255 char szExtPackDir[RTPATH_MAX];
2256 vrc = RTPathJoin(szExtPackDir, sizeof(szExtPackDir), m->strBaseDir.c_str(), Entry.szName);
2257 AssertLogRelRC(vrc);
2258 if (RT_SUCCESS(vrc))
2259 {
2260 RTCString *pstrName = VBoxExtPackUnmangleName(Entry.szName, RTSTR_MAX);
2261 AssertLogRel(pstrName);
2262 if (pstrName)
2263 {
2264 ComObjPtr<ExtPack> NewExtPack;
2265 HRESULT hrc2 = NewExtPack.createObject();
2266 if (SUCCEEDED(hrc2))
2267 hrc2 = NewExtPack->initWithDir(a_pVirtualBox, a_enmContext, pstrName->c_str(), szExtPackDir);
2268 delete pstrName;
2269 if (SUCCEEDED(hrc2))
2270 {
2271 m->llInstalledExtPacks.push_back(NewExtPack);
2272 /* Paranoia, there should be no API clients before this method is finished. */
2273
2274 m->cUpdate++;
2275 }
2276 else if (SUCCEEDED(hrc))
2277 hrc = hrc2;
2278 }
2279 else
2280 hrc = E_UNEXPECTED;
2281 }
2282 else
2283 hrc = E_UNEXPECTED;
2284 }
2285 }
2286 RTDirClose(hDir);
2287 }
2288 /* else: ignore, the directory probably does not exist or something. */
2289
2290 if (SUCCEEDED(hrc))
2291 autoInitSpan.setSucceeded();
2292 return hrc;
2293}
2294
2295/**
2296 * COM cruft.
2297 */
2298void ExtPackManager::FinalRelease()
2299{
2300 uninit();
2301 BaseFinalRelease();
2302}
2303
2304/**
2305 * Do the actual cleanup.
2306 */
2307void ExtPackManager::uninit()
2308{
2309 /* Enclose the state transition Ready->InUninit->NotReady */
2310 AutoUninitSpan autoUninitSpan(this);
2311 if (!autoUninitSpan.uninitDone() && m != NULL)
2312 {
2313 delete m;
2314 m = NULL;
2315 }
2316}
2317
2318HRESULT ExtPackManager::getInstalledExtPacks(std::vector<ComPtr<IExtPack> > &aInstalledExtPacks)
2319{
2320 Assert(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON);
2321
2322 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
2323
2324
2325 SafeIfaceArray<IExtPack> SaExtPacks(m->llInstalledExtPacks);
2326 aInstalledExtPacks.resize(SaExtPacks.size());
2327 for(size_t i = 0; i < SaExtPacks.size(); ++i)
2328 aInstalledExtPacks[i] = SaExtPacks[i];
2329
2330 return S_OK;
2331}
2332
2333HRESULT ExtPackManager::find(const com::Utf8Str &aName, ComPtr<IExtPack> &aReturnData)
2334{
2335 HRESULT hrc = S_OK;
2336
2337 Assert(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON);
2338
2339 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
2340
2341 ComPtr<ExtPack> ptrExtPack = i_findExtPack(aName.c_str());
2342 if (!ptrExtPack.isNull())
2343 ptrExtPack.queryInterfaceTo(aReturnData.asOutParam());
2344 else
2345 hrc = VBOX_E_OBJECT_NOT_FOUND;
2346
2347 return hrc;
2348}
2349
2350HRESULT ExtPackManager::openExtPackFile(const com::Utf8Str &aPath, ComPtr<IExtPackFile> &aFile)
2351{
2352 AssertReturn(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON, E_UNEXPECTED);
2353
2354#ifndef VBOX_COM_INPROC
2355 /* The API can optionally take a ::SHA-256=<hex-digest> attribute at the
2356 end of the file name. This is just a temporary measure for
2357 backporting, in 4.2 we'll add another parameter to the method. */
2358 Utf8Str strTarball;
2359 Utf8Str strDigest;
2360 size_t offSha256 = aPath.find("::SHA-256=");
2361 if (offSha256 == Utf8Str::npos)
2362 strTarball = aPath;
2363 else
2364 {
2365 strTarball = aPath.substr(0, offSha256);
2366 strDigest = aPath.substr(offSha256 + sizeof("::SHA-256=") - 1);
2367 }
2368
2369 ComObjPtr<ExtPackFile> NewExtPackFile;
2370 HRESULT hrc = NewExtPackFile.createObject();
2371 if (SUCCEEDED(hrc))
2372 hrc = NewExtPackFile->initWithFile(strTarball.c_str(), strDigest.c_str(), this, m->pVirtualBox);
2373 if (SUCCEEDED(hrc))
2374 NewExtPackFile.queryInterfaceTo(aFile.asOutParam());
2375
2376 return hrc;
2377#else
2378 RT_NOREF(aPath, aFile);
2379 return E_NOTIMPL;
2380#endif
2381}
2382
2383HRESULT ExtPackManager::uninstall(const com::Utf8Str &aName, BOOL aForcedRemoval,
2384 const com::Utf8Str &aDisplayInfo, ComPtr<IProgress> &aProgress)
2385{
2386 Assert(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON);
2387
2388#ifndef VBOX_COM_INPROC
2389
2390 HRESULT hrc;
2391 ExtPackUninstallTask *pTask = NULL;
2392 try
2393 {
2394 pTask = new ExtPackUninstallTask();
2395 hrc = pTask->Init(this, aName, aForcedRemoval != FALSE, aDisplayInfo);
2396 if (SUCCEEDED(hrc))
2397 {
2398 ComPtr<Progress> ptrProgress = pTask->ptrProgress;
2399 hrc = pTask->createThreadWithType(RTTHREADTYPE_DEFAULT);
2400 pTask = NULL; /* always consumed by createThread */
2401 if (SUCCEEDED(hrc))
2402 hrc = ptrProgress.queryInterfaceTo(aProgress.asOutParam());
2403 else
2404 hrc = setError(VBOX_E_IPRT_ERROR,
2405 tr("Starting thread for an extension pack uninstallation failed with %Rrc"), hrc);
2406 }
2407 else
2408 hrc = setError(hrc, tr("Looks like creating a progress object for ExtraPackUninstallTask object failed"));
2409 }
2410 catch (std::bad_alloc &)
2411 {
2412 hrc = E_OUTOFMEMORY;
2413 }
2414 catch (HRESULT hrcXcpt)
2415 {
2416 LogFlowThisFunc(("Exception was caught in the function ExtPackManager::uninstall()\n"));
2417 hrc = hrcXcpt;
2418 }
2419 if (pTask)
2420 delete pTask;
2421 return hrc;
2422#else
2423 RT_NOREF(aName, aForcedRemoval, aDisplayInfo, aProgress);
2424 return E_NOTIMPL;
2425#endif
2426}
2427
2428HRESULT ExtPackManager::cleanup(void)
2429{
2430 Assert(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON);
2431
2432 AutoCaller autoCaller(this);
2433 HRESULT hrc = autoCaller.rc();
2434 if (SUCCEEDED(hrc))
2435 {
2436 /*
2437 * Run the set-uid-to-root binary that performs the cleanup.
2438 *
2439 * Take the write lock to prevent conflicts with other calls to this
2440 * VBoxSVC instance.
2441 */
2442 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
2443 hrc = i_runSetUidToRootHelper(NULL,
2444 "cleanup",
2445 "--base-dir", m->strBaseDir.c_str(),
2446 (const char *)NULL);
2447 }
2448
2449 return hrc;
2450}
2451
2452HRESULT ExtPackManager::queryAllPlugInsForFrontend(const com::Utf8Str &aFrontendName, std::vector<com::Utf8Str> &aPlugInModules)
2453{
2454 NOREF(aFrontendName);
2455 aPlugInModules.resize(0);
2456 return S_OK;
2457}
2458
2459HRESULT ExtPackManager::isExtPackUsable(const com::Utf8Str &aName, BOOL *aUsable)
2460{
2461 *aUsable = i_isExtPackUsable(aName.c_str());
2462 return S_OK;
2463}
2464
2465/**
2466 * Finds the success indicator string in the stderr output ofr hte helper app.
2467 *
2468 * @returns Pointer to the indicator.
2469 * @param psz The stderr output string. Can be NULL.
2470 * @param cch The size of the string.
2471 */
2472static char *findSuccessIndicator(char *psz, size_t cch)
2473{
2474 static const char s_szSuccessInd[] = "rcExit=RTEXITCODE_SUCCESS";
2475 Assert(!cch || strlen(psz) == cch);
2476 if (cch < sizeof(s_szSuccessInd) - 1)
2477 return NULL;
2478 char *pszInd = &psz[cch - sizeof(s_szSuccessInd) + 1];
2479 if (strcmp(s_szSuccessInd, pszInd))
2480 return NULL;
2481 return pszInd;
2482}
2483
2484/**
2485 * Runs the helper application that does the privileged operations.
2486 *
2487 * @returns S_OK or a failure status with error information set.
2488 * @param a_pstrDisplayInfo Platform specific display info hacks.
2489 * @param a_pszCommand The command to execute.
2490 * @param ... The argument strings that goes along with the
2491 * command. Maximum is about 16. Terminated by a
2492 * NULL.
2493 */
2494HRESULT ExtPackManager::i_runSetUidToRootHelper(Utf8Str const *a_pstrDisplayInfo, const char *a_pszCommand, ...)
2495{
2496 /*
2497 * Calculate the path to the helper application.
2498 */
2499 char szExecName[RTPATH_MAX];
2500 int vrc = RTPathAppPrivateArch(szExecName, sizeof(szExecName));
2501 AssertLogRelRCReturn(vrc, E_UNEXPECTED);
2502
2503 vrc = RTPathAppend(szExecName, sizeof(szExecName), VBOX_EXTPACK_HELPER_NAME);
2504 AssertLogRelRCReturn(vrc, E_UNEXPECTED);
2505
2506 /*
2507 * Convert the variable argument list to a RTProcCreate argument vector.
2508 */
2509 const char *apszArgs[20];
2510 unsigned cArgs = 0;
2511
2512 LogRel(("ExtPack: Executing '%s'", szExecName));
2513 apszArgs[cArgs++] = &szExecName[0];
2514
2515 if ( a_pstrDisplayInfo
2516 && a_pstrDisplayInfo->isNotEmpty())
2517 {
2518 LogRel((" '--display-info-hack' '%s'", a_pstrDisplayInfo->c_str()));
2519 apszArgs[cArgs++] = "--display-info-hack";
2520 apszArgs[cArgs++] = a_pstrDisplayInfo->c_str();
2521 }
2522
2523 LogRel((" '%s'", a_pszCommand));
2524 apszArgs[cArgs++] = a_pszCommand;
2525
2526 va_list va;
2527 va_start(va, a_pszCommand);
2528 const char *pszLastArg;
2529 for (;;)
2530 {
2531 AssertReturn(cArgs < RT_ELEMENTS(apszArgs) - 1, E_UNEXPECTED);
2532 pszLastArg = va_arg(va, const char *);
2533 if (!pszLastArg)
2534 break;
2535 LogRel((" '%s'", pszLastArg));
2536 apszArgs[cArgs++] = pszLastArg;
2537 };
2538 va_end(va);
2539
2540 LogRel(("\n"));
2541 apszArgs[cArgs] = NULL;
2542
2543 /*
2544 * Create a PIPE which we attach to stderr so that we can read the error
2545 * message on failure and report it back to the caller.
2546 */
2547 RTPIPE hPipeR;
2548 RTHANDLE hStdErrPipe;
2549 hStdErrPipe.enmType = RTHANDLETYPE_PIPE;
2550 vrc = RTPipeCreate(&hPipeR, &hStdErrPipe.u.hPipe, RTPIPE_C_INHERIT_WRITE);
2551 AssertLogRelRCReturn(vrc, E_UNEXPECTED);
2552
2553 /*
2554 * Spawn the process.
2555 */
2556 HRESULT hrc;
2557 RTPROCESS hProcess;
2558 vrc = RTProcCreateEx(szExecName,
2559 apszArgs,
2560 RTENV_DEFAULT,
2561 0 /*fFlags*/,
2562 NULL /*phStdIn*/,
2563 NULL /*phStdOut*/,
2564 &hStdErrPipe,
2565 NULL /*pszAsUser*/,
2566 NULL /*pszPassword*/,
2567 NULL /*pvExtraData*/,
2568 &hProcess);
2569 if (RT_SUCCESS(vrc))
2570 {
2571 vrc = RTPipeClose(hStdErrPipe.u.hPipe);
2572 hStdErrPipe.u.hPipe = NIL_RTPIPE;
2573
2574 /*
2575 * Read the pipe output until the process completes.
2576 */
2577 RTPROCSTATUS ProcStatus = { -42, RTPROCEXITREASON_ABEND };
2578 size_t cbStdErrBuf = 0;
2579 size_t offStdErrBuf = 0;
2580 char *pszStdErrBuf = NULL;
2581 do
2582 {
2583 /*
2584 * Service the pipe. Block waiting for output or the pipe breaking
2585 * when the process terminates.
2586 */
2587 if (hPipeR != NIL_RTPIPE)
2588 {
2589 char achBuf[1024];
2590 size_t cbRead;
2591 vrc = RTPipeReadBlocking(hPipeR, achBuf, sizeof(achBuf), &cbRead);
2592 if (RT_SUCCESS(vrc))
2593 {
2594 /* grow the buffer? */
2595 size_t cbBufReq = offStdErrBuf + cbRead + 1;
2596 if ( cbBufReq > cbStdErrBuf
2597 && cbBufReq < _256K)
2598 {
2599 size_t cbNew = RT_ALIGN_Z(cbBufReq, 16); // 1024
2600 void *pvNew = RTMemRealloc(pszStdErrBuf, cbNew);
2601 if (pvNew)
2602 {
2603 pszStdErrBuf = (char *)pvNew;
2604 cbStdErrBuf = cbNew;
2605 }
2606 }
2607
2608 /* append if we've got room. */
2609 if (cbBufReq <= cbStdErrBuf)
2610 {
2611 memcpy(&pszStdErrBuf[offStdErrBuf], achBuf, cbRead);
2612 offStdErrBuf = offStdErrBuf + cbRead;
2613 pszStdErrBuf[offStdErrBuf] = '\0';
2614 }
2615 }
2616 else
2617 {
2618 AssertLogRelMsg(vrc == VERR_BROKEN_PIPE, ("%Rrc\n", vrc));
2619 RTPipeClose(hPipeR);
2620 hPipeR = NIL_RTPIPE;
2621 }
2622 }
2623
2624 /*
2625 * Service the process. Block if we have no pipe.
2626 */
2627 if (hProcess != NIL_RTPROCESS)
2628 {
2629 vrc = RTProcWait(hProcess,
2630 hPipeR == NIL_RTPIPE ? RTPROCWAIT_FLAGS_BLOCK : RTPROCWAIT_FLAGS_NOBLOCK,
2631 &ProcStatus);
2632 if (RT_SUCCESS(vrc))
2633 hProcess = NIL_RTPROCESS;
2634 else
2635 AssertLogRelMsgStmt(vrc == VERR_PROCESS_RUNNING, ("%Rrc\n", vrc), hProcess = NIL_RTPROCESS);
2636 }
2637 } while ( hPipeR != NIL_RTPIPE
2638 || hProcess != NIL_RTPROCESS);
2639
2640 LogRel(("ExtPack: enmReason=%d iStatus=%d stderr='%s'\n",
2641 ProcStatus.enmReason, ProcStatus.iStatus, offStdErrBuf ? pszStdErrBuf : ""));
2642
2643 /*
2644 * Look for rcExit=RTEXITCODE_SUCCESS at the end of the error output,
2645 * cut it as it is only there to attest the success.
2646 */
2647 if (offStdErrBuf > 0)
2648 {
2649 RTStrStripR(pszStdErrBuf);
2650 offStdErrBuf = strlen(pszStdErrBuf);
2651 }
2652
2653 char *pszSuccessInd = findSuccessIndicator(pszStdErrBuf, offStdErrBuf);
2654 if (pszSuccessInd)
2655 {
2656 *pszSuccessInd = '\0';
2657 offStdErrBuf = (size_t)(pszSuccessInd - pszStdErrBuf);
2658 }
2659 else if ( ProcStatus.enmReason == RTPROCEXITREASON_NORMAL
2660 && ProcStatus.iStatus == 0)
2661 ProcStatus.iStatus = offStdErrBuf ? 667 : 666;
2662
2663 /*
2664 * Compose the status code and, on failure, error message.
2665 */
2666 if ( ProcStatus.enmReason == RTPROCEXITREASON_NORMAL
2667 && ProcStatus.iStatus == 0)
2668 hrc = S_OK;
2669 else if (ProcStatus.enmReason == RTPROCEXITREASON_NORMAL)
2670 {
2671 AssertMsg(ProcStatus.iStatus != 0, ("%s\n", pszStdErrBuf));
2672 hrc = setError(E_FAIL, tr("The installer failed with exit code %d: %s"),
2673 ProcStatus.iStatus, offStdErrBuf ? pszStdErrBuf : "");
2674 }
2675 else if (ProcStatus.enmReason == RTPROCEXITREASON_SIGNAL)
2676 hrc = setError(E_UNEXPECTED, tr("The installer was killed by signal #d (stderr: %s)"),
2677 ProcStatus.iStatus, offStdErrBuf ? pszStdErrBuf : "");
2678 else if (ProcStatus.enmReason == RTPROCEXITREASON_ABEND)
2679 hrc = setError(E_UNEXPECTED, tr("The installer aborted abnormally (stderr: %s)"),
2680 offStdErrBuf ? pszStdErrBuf : "");
2681 else
2682 hrc = setError(E_UNEXPECTED, tr("internal error: enmReason=%d iStatus=%d stderr='%s'"),
2683 ProcStatus.enmReason, ProcStatus.iStatus, offStdErrBuf ? pszStdErrBuf : "");
2684
2685 RTMemFree(pszStdErrBuf);
2686 }
2687 else
2688 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to launch the helper application '%s' (%Rrc)"), szExecName, vrc);
2689
2690 RTPipeClose(hPipeR);
2691 RTPipeClose(hStdErrPipe.u.hPipe);
2692
2693 return hrc;
2694}
2695
2696/**
2697 * Finds an installed extension pack.
2698 *
2699 * @returns Pointer to the extension pack if found, NULL if not. (No reference
2700 * counting problem here since the caller must be holding the lock.)
2701 * @param a_pszName The name of the extension pack.
2702 */
2703ExtPack *ExtPackManager::i_findExtPack(const char *a_pszName)
2704{
2705 size_t cchName = strlen(a_pszName);
2706
2707 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
2708 it != m->llInstalledExtPacks.end();
2709 ++it)
2710 {
2711 ExtPack::Data *pExtPackData = (*it)->m;
2712 if ( pExtPackData
2713 && pExtPackData->Desc.strName.length() == cchName
2714 && pExtPackData->Desc.strName.equalsIgnoreCase(a_pszName))
2715 return (*it);
2716 }
2717 return NULL;
2718}
2719
2720/**
2721 * Removes an installed extension pack from the internal list.
2722 *
2723 * The package is expected to exist!
2724 *
2725 * @param a_pszName The name of the extension pack.
2726 */
2727void ExtPackManager::i_removeExtPack(const char *a_pszName)
2728{
2729 size_t cchName = strlen(a_pszName);
2730
2731 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
2732 it != m->llInstalledExtPacks.end();
2733 ++it)
2734 {
2735 ExtPack::Data *pExtPackData = (*it)->m;
2736 if ( pExtPackData
2737 && pExtPackData->Desc.strName.length() == cchName
2738 && pExtPackData->Desc.strName.equalsIgnoreCase(a_pszName))
2739 {
2740 m->llInstalledExtPacks.erase(it);
2741 m->cUpdate++;
2742 return;
2743 }
2744 }
2745 AssertMsgFailed(("%s\n", a_pszName));
2746}
2747
2748#ifndef VBOX_COM_INPROC
2749
2750/**
2751 * Refreshes the specified extension pack.
2752 *
2753 * This may remove the extension pack from the list, so any non-smart pointers
2754 * to the extension pack object may become invalid.
2755 *
2756 * @returns S_OK and *a_ppExtPack on success, COM status code and error
2757 * message on failure. Note that *a_ppExtPack can be NULL.
2758 *
2759 * @param a_pszName The extension to update..
2760 * @param a_fUnusableIsError If @c true, report an unusable extension pack
2761 * as an error.
2762 * @param a_ppExtPack Where to store the pointer to the extension
2763 * pack of it is still around after the refresh.
2764 * This is optional.
2765 *
2766 * @remarks Caller holds the extension manager lock.
2767 * @remarks Only called in VBoxSVC.
2768 */
2769HRESULT ExtPackManager::i_refreshExtPack(const char *a_pszName, bool a_fUnusableIsError, ExtPack **a_ppExtPack)
2770{
2771 Assert(m->pVirtualBox != NULL); /* Only called from VBoxSVC. */
2772
2773 HRESULT hrc;
2774 ExtPack *pExtPack = i_findExtPack(a_pszName);
2775 if (pExtPack)
2776 {
2777 /*
2778 * Refresh existing object.
2779 */
2780 bool fCanDelete;
2781 hrc = pExtPack->i_refresh(&fCanDelete);
2782 if (SUCCEEDED(hrc))
2783 {
2784 if (fCanDelete)
2785 {
2786 i_removeExtPack(a_pszName);
2787 pExtPack = NULL;
2788 }
2789 }
2790 }
2791 else
2792 {
2793 /*
2794 * Do this check here, otherwise VBoxExtPackCalcDir() will fail with a strange
2795 * error.
2796 */
2797 bool fValid = VBoxExtPackIsValidName(a_pszName);
2798 if (!fValid)
2799 return setError(E_FAIL, "Invalid extension pack name specified");
2800
2801 /*
2802 * Does the dir exist? Make some special effort to deal with case
2803 * sensitivie file systems (a_pszName is case insensitive and mangled).
2804 */
2805 char szDir[RTPATH_MAX];
2806 int vrc = VBoxExtPackCalcDir(szDir, sizeof(szDir), m->strBaseDir.c_str(), a_pszName);
2807 AssertLogRelRCReturn(vrc, E_FAIL);
2808
2809 RTDIRENTRYEX Entry;
2810 RTFSOBJINFO ObjInfo;
2811 vrc = RTPathQueryInfoEx(szDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2812 bool fExists = RT_SUCCESS(vrc) && RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode);
2813 if (!fExists)
2814 {
2815 RTDIR hDir;
2816 vrc = RTDirOpen(&hDir, m->strBaseDir.c_str());
2817 if (RT_SUCCESS(vrc))
2818 {
2819 const char *pszMangledName = RTPathFilename(szDir);
2820 for (;;)
2821 {
2822 vrc = RTDirReadEx(hDir, &Entry, NULL /*pcbDirEntry*/, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2823 if (RT_FAILURE(vrc))
2824 {
2825 AssertLogRelMsg(vrc == VERR_NO_MORE_FILES, ("%Rrc\n", vrc));
2826 break;
2827 }
2828 if ( RTFS_IS_DIRECTORY(Entry.Info.Attr.fMode)
2829 && !RTStrICmp(Entry.szName, pszMangledName))
2830 {
2831 /*
2832 * The installed extension pack has a uses different case.
2833 * Update the name and directory variables.
2834 */
2835 vrc = RTPathJoin(szDir, sizeof(szDir), m->strBaseDir.c_str(), Entry.szName); /* not really necessary */
2836 AssertLogRelRCReturnStmt(vrc, RTDirClose(hDir), E_UNEXPECTED);
2837 a_pszName = Entry.szName;
2838 fExists = true;
2839 break;
2840 }
2841 }
2842 RTDirClose(hDir);
2843 }
2844 }
2845 if (fExists)
2846 {
2847 /*
2848 * We've got something, create a new extension pack object for it.
2849 */
2850 ComObjPtr<ExtPack> ptrNewExtPack;
2851 hrc = ptrNewExtPack.createObject();
2852 if (SUCCEEDED(hrc))
2853 hrc = ptrNewExtPack->initWithDir(m->pVirtualBox, m->enmContext, a_pszName, szDir);
2854 if (SUCCEEDED(hrc))
2855 {
2856 m->llInstalledExtPacks.push_back(ptrNewExtPack);
2857 m->cUpdate++;
2858 if (ptrNewExtPack->m->fUsable)
2859 LogRel(("ExtPackManager: Found extension pack '%s'.\n", a_pszName));
2860 else
2861 LogRel(("ExtPackManager: Found bad extension pack '%s': %s\n",
2862 a_pszName, ptrNewExtPack->m->strWhyUnusable.c_str() ));
2863 pExtPack = ptrNewExtPack;
2864 }
2865 }
2866 else
2867 hrc = S_OK;
2868 }
2869
2870 /*
2871 * Report error if not usable, if that is desired.
2872 */
2873 if ( SUCCEEDED(hrc)
2874 && pExtPack
2875 && a_fUnusableIsError
2876 && !pExtPack->m->fUsable)
2877 hrc = setError(E_FAIL, "%s", pExtPack->m->strWhyUnusable.c_str());
2878
2879 if (a_ppExtPack)
2880 *a_ppExtPack = pExtPack;
2881 return hrc;
2882}
2883
2884/**
2885 * Checks if there are any running VMs.
2886 *
2887 * This is called when uninstalling or replacing an extension pack.
2888 *
2889 * @returns true / false
2890 */
2891bool ExtPackManager::i_areThereAnyRunningVMs(void) const
2892{
2893 Assert(m->pVirtualBox != NULL); /* Only called from VBoxSVC. */
2894
2895 /*
2896 * Get list of machines and their states.
2897 */
2898 com::SafeIfaceArray<IMachine> SaMachines;
2899 HRESULT hrc = m->pVirtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(SaMachines));
2900 if (SUCCEEDED(hrc))
2901 {
2902 com::SafeArray<MachineState_T> SaStates;
2903 hrc = m->pVirtualBox->GetMachineStates(ComSafeArrayAsInParam(SaMachines), ComSafeArrayAsOutParam(SaStates));
2904 if (SUCCEEDED(hrc))
2905 {
2906 /*
2907 * Scan the two parallel arrays for machines in the running state.
2908 */
2909 Assert(SaStates.size() == SaMachines.size());
2910 for (size_t i = 0; i < SaMachines.size(); ++i)
2911 if (SaMachines[i] && Global::IsOnline(SaStates[i]))
2912 return true;
2913 }
2914 }
2915 return false;
2916}
2917
2918/**
2919 * Worker for IExtPackFile::Install.
2920 *
2921 * Called on a worker thread via doInstallThreadProc.
2922 *
2923 * @returns COM status code.
2924 * @param a_pExtPackFile The extension pack file, caller checks that
2925 * it's usable.
2926 * @param a_fReplace Whether to replace any existing extpack or just
2927 * fail.
2928 * @param a_pstrDisplayInfo Host specific display information hacks.
2929 * be NULL.
2930 */
2931HRESULT ExtPackManager::i_doInstall(ExtPackFile *a_pExtPackFile, bool a_fReplace, Utf8Str const *a_pstrDisplayInfo)
2932{
2933 AssertReturn(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON, E_UNEXPECTED);
2934 RTCString const * const pStrName = &a_pExtPackFile->m->Desc.strName;
2935 RTCString const * const pStrTarball = &a_pExtPackFile->m->strExtPackFile;
2936 RTCString const * const pStrTarballDigest = &a_pExtPackFile->m->strDigest;
2937
2938 AutoCaller autoCaller(this);
2939 HRESULT hrc = autoCaller.rc();
2940 if (SUCCEEDED(hrc))
2941 {
2942 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
2943
2944 /*
2945 * Refresh the data we have on the extension pack as it
2946 * may be made stale by direct meddling or some other user.
2947 */
2948 ExtPack *pExtPack;
2949 hrc = i_refreshExtPack(pStrName->c_str(), false /*a_fUnusableIsError*/, &pExtPack);
2950 if (SUCCEEDED(hrc))
2951 {
2952 if (pExtPack && a_fReplace)
2953 {
2954 /* We must leave the lock when calling i_areThereAnyRunningVMs,
2955 which means we have to redo the refresh call afterwards. */
2956 autoLock.release();
2957 bool fRunningVMs = i_areThereAnyRunningVMs();
2958 bool fVetoingCP = pExtPack->i_areThereCloudProviderUninstallVetos();
2959 autoLock.acquire();
2960 hrc = i_refreshExtPack(pStrName->c_str(), false /*a_fUnusableIsError*/, &pExtPack);
2961 if (fRunningVMs)
2962 {
2963 LogRel(("Upgrading extension pack '%s' failed because at least one VM is still running.", pStrName->c_str()));
2964 hrc = setError(E_FAIL, tr("Upgrading extension pack '%s' failed because at least one VM is still running"),
2965 pStrName->c_str());
2966 }
2967 else if (fVetoingCP)
2968 {
2969 LogRel(("Upgrading extension pack '%s' failed because at least one Cloud Provider is still busy.", pStrName->c_str()));
2970 hrc = setError(E_FAIL, tr("Upgrading extension pack '%s' failed because at least one Cloud Provider is still busy"),
2971 pStrName->c_str());
2972 }
2973 else if (SUCCEEDED(hrc) && pExtPack)
2974 hrc = pExtPack->i_callUninstallHookAndClose(m->pVirtualBox, false /*a_ForcedRemoval*/);
2975 }
2976 else if (pExtPack)
2977 hrc = setError(E_FAIL,
2978 tr("Extension pack '%s' is already installed."
2979 " In case of a reinstallation, please uninstall it first"),
2980 pStrName->c_str());
2981 }
2982 if (SUCCEEDED(hrc))
2983 {
2984 /*
2985 * Run the privileged helper binary that performs the actual
2986 * installation. Then create an object for the packet (we do this
2987 * even on failure, to be on the safe side).
2988 */
2989 hrc = i_runSetUidToRootHelper(a_pstrDisplayInfo,
2990 "install",
2991 "--base-dir", m->strBaseDir.c_str(),
2992 "--cert-dir", m->strCertificatDirPath.c_str(),
2993 "--name", pStrName->c_str(),
2994 "--tarball", pStrTarball->c_str(),
2995 "--sha-256", pStrTarballDigest->c_str(),
2996 pExtPack ? "--replace" : (const char *)NULL,
2997 (const char *)NULL);
2998 if (SUCCEEDED(hrc))
2999 {
3000 hrc = i_refreshExtPack(pStrName->c_str(), true /*a_fUnusableIsError*/, &pExtPack);
3001 if (SUCCEEDED(hrc) && pExtPack)
3002 {
3003 RTERRINFOSTATIC ErrInfo;
3004 RTErrInfoInitStatic(&ErrInfo);
3005 pExtPack->i_callInstalledHook(m->pVirtualBox, &autoLock, &ErrInfo.Core);
3006 if (RT_SUCCESS(ErrInfo.Core.rc))
3007 LogRel(("ExtPackManager: Successfully installed extension pack '%s'.\n", pStrName->c_str()));
3008 else
3009 {
3010 LogRel(("ExtPackManager: Installed hook for '%s' failed: %Rrc - %s\n",
3011 pStrName->c_str(), ErrInfo.Core.rc, ErrInfo.Core.pszMsg));
3012
3013 /*
3014 * Uninstall the extpack if the error indicates that.
3015 */
3016 if (ErrInfo.Core.rc == VERR_EXTPACK_UNSUPPORTED_HOST_UNINSTALL)
3017 i_runSetUidToRootHelper(a_pstrDisplayInfo,
3018 "uninstall",
3019 "--base-dir", m->strBaseDir.c_str(),
3020 "--name", pStrName->c_str(),
3021 "--forced",
3022 (const char *)NULL);
3023 hrc = setErrorBoth(E_FAIL, ErrInfo.Core.rc, tr("The installation hook failed: %Rrc - %s"),
3024 ErrInfo.Core.rc, ErrInfo.Core.pszMsg);
3025 }
3026 }
3027 else if (SUCCEEDED(hrc))
3028 hrc = setError(E_FAIL, tr("Installing extension pack '%s' failed under mysterious circumstances"),
3029 pStrName->c_str());
3030 }
3031 else
3032 {
3033 ErrorInfoKeeper Eik;
3034 i_refreshExtPack(pStrName->c_str(), false /*a_fUnusableIsError*/, NULL);
3035 }
3036 }
3037
3038 /*
3039 * Do VirtualBoxReady callbacks now for any freshly installed
3040 * extension pack (old ones will not be called).
3041 */
3042 if (m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON)
3043 {
3044 autoLock.release();
3045 i_callAllVirtualBoxReadyHooks();
3046 }
3047 }
3048
3049 return hrc;
3050}
3051
3052/**
3053 * Worker for IExtPackManager::Uninstall.
3054 *
3055 * Called on a worker thread via doUninstallThreadProc.
3056 *
3057 * @returns COM status code.
3058 * @param a_pstrName The name of the extension pack to uninstall.
3059 * @param a_fForcedRemoval Whether to be skip and ignore certain bits of
3060 * the extpack feedback. To deal with misbehaving
3061 * extension pack hooks.
3062 * @param a_pstrDisplayInfo Host specific display information hacks.
3063 */
3064HRESULT ExtPackManager::i_doUninstall(Utf8Str const *a_pstrName, bool a_fForcedRemoval, Utf8Str const *a_pstrDisplayInfo)
3065{
3066 Assert(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON);
3067
3068 AutoCaller autoCaller(this);
3069 HRESULT hrc = autoCaller.rc();
3070 if (SUCCEEDED(hrc))
3071 {
3072 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3073
3074 /*
3075 * Refresh the data we have on the extension pack as it
3076 * may be made stale by direct meddling or some other user.
3077 */
3078 ExtPack *pExtPack;
3079 hrc = i_refreshExtPack(a_pstrName->c_str(), false /*a_fUnusableIsError*/, &pExtPack);
3080 if (SUCCEEDED(hrc) && pExtPack)
3081 {
3082 /* We must leave the lock when calling i_areThereAnyRunningVMs,
3083 which means we have to redo the refresh call afterwards. */
3084 autoLock.release();
3085 bool fRunningVMs = i_areThereAnyRunningVMs();
3086 bool fVetoingCP = pExtPack->i_areThereCloudProviderUninstallVetos();
3087 autoLock.acquire();
3088 if (a_fForcedRemoval || (!fRunningVMs && !fVetoingCP))
3089 {
3090 hrc = i_refreshExtPack(a_pstrName->c_str(), false /*a_fUnusableIsError*/, &pExtPack);
3091 if (SUCCEEDED(hrc))
3092 {
3093 if (!pExtPack)
3094 {
3095 LogRel(("ExtPackManager: Extension pack '%s' is not installed, so nothing to uninstall.\n", a_pstrName->c_str()));
3096 hrc = S_OK; /* nothing to uninstall */
3097 }
3098 else
3099 {
3100 /*
3101 * Call the uninstall hook and unload the main dll.
3102 */
3103 hrc = pExtPack->i_callUninstallHookAndClose(m->pVirtualBox, a_fForcedRemoval);
3104 if (SUCCEEDED(hrc))
3105 {
3106 /*
3107 * Run the set-uid-to-root binary that performs the
3108 * uninstallation. Then refresh the object.
3109 *
3110 * This refresh is theorically subject to races, but it's of
3111 * the don't-do-that variety.
3112 */
3113 const char *pszForcedOpt = a_fForcedRemoval ? "--forced" : NULL;
3114 hrc = i_runSetUidToRootHelper(a_pstrDisplayInfo,
3115 "uninstall",
3116 "--base-dir", m->strBaseDir.c_str(),
3117 "--name", a_pstrName->c_str(),
3118 pszForcedOpt, /* Last as it may be NULL. */
3119 (const char *)NULL);
3120 if (SUCCEEDED(hrc))
3121 {
3122 hrc = i_refreshExtPack(a_pstrName->c_str(), false /*a_fUnusableIsError*/, &pExtPack);
3123 if (SUCCEEDED(hrc))
3124 {
3125 if (!pExtPack)
3126 LogRel(("ExtPackManager: Successfully uninstalled extension pack '%s'.\n", a_pstrName->c_str()));
3127 else
3128 hrc = setError(E_FAIL,
3129 tr("Uninstall extension pack '%s' failed under mysterious circumstances"),
3130 a_pstrName->c_str());
3131 }
3132 }
3133 else
3134 {
3135 ErrorInfoKeeper Eik;
3136 i_refreshExtPack(a_pstrName->c_str(), false /*a_fUnusableIsError*/, NULL);
3137 }
3138 }
3139 }
3140 }
3141 }
3142 else
3143 {
3144 if (fRunningVMs)
3145 {
3146 LogRel(("Uninstall extension pack '%s' failed because at least one VM is still running.", a_pstrName->c_str()));
3147 hrc = setError(E_FAIL, tr("Uninstall extension pack '%s' failed because at least one VM is still running"),
3148 a_pstrName->c_str());
3149 }
3150 else if (fVetoingCP)
3151 {
3152 LogRel(("Uninstall extension pack '%s' failed because at least one Cloud Provider is still busy.", a_pstrName->c_str()));
3153 hrc = setError(E_FAIL, tr("Uninstall extension pack '%s' failed because at least one Cloud Provider is still busy"),
3154 a_pstrName->c_str());
3155 }
3156 else
3157 {
3158 LogRel(("Uninstall extension pack '%s' failed for an unknown reason.", a_pstrName->c_str()));
3159 hrc = setError(E_FAIL, tr("Uninstall extension pack '%s' failed for an unknown reason"),
3160 a_pstrName->c_str());
3161
3162 }
3163 }
3164 }
3165
3166 /*
3167 * Do VirtualBoxReady callbacks now for any freshly installed
3168 * extension pack (old ones will not be called).
3169 */
3170 if (m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON)
3171 {
3172 autoLock.release();
3173 i_callAllVirtualBoxReadyHooks();
3174 }
3175 }
3176
3177 return hrc;
3178}
3179
3180
3181/**
3182 * Calls the pfnVirtualBoxReady hook for all working extension packs.
3183 *
3184 * @remarks The caller must not hold any locks.
3185 */
3186void ExtPackManager::i_callAllVirtualBoxReadyHooks(void)
3187{
3188 AutoCaller autoCaller(this);
3189 HRESULT hrc = autoCaller.rc();
3190 if (FAILED(hrc))
3191 return;
3192 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3193 ComPtr<ExtPackManager> ptrSelfRef = this;
3194
3195 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
3196 it != m->llInstalledExtPacks.end();
3197 /* advancing below */)
3198 {
3199 if ((*it)->i_callVirtualBoxReadyHook(m->pVirtualBox, &autoLock))
3200 it = m->llInstalledExtPacks.begin();
3201 else
3202 ++it;
3203 }
3204}
3205
3206
3207/**
3208 * Queries objects of type @a aObjUuid from all the extension packs.
3209 *
3210 * @returns COM status code.
3211 * @param aObjUuid The UUID of the kind of objects we're querying.
3212 * @param aObjects Where to return the objects.
3213 * @param a_pstrExtPackNames Where to return the corresponding extpack names (may be NULL).
3214 *
3215 * @remarks The caller must not hold any locks.
3216 */
3217HRESULT ExtPackManager::i_queryObjects(const com::Utf8Str &aObjUuid, std::vector<ComPtr<IUnknown> > &aObjects, std::vector<com::Utf8Str> *a_pstrExtPackNames)
3218{
3219 aObjects.clear();
3220 if (a_pstrExtPackNames)
3221 a_pstrExtPackNames->clear();
3222
3223 AutoCaller autoCaller(this);
3224 HRESULT hrc = autoCaller.rc();
3225 if (SUCCEEDED(hrc))
3226 {
3227 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3228 ComPtr<ExtPackManager> ptrSelfRef = this;
3229
3230 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
3231 it != m->llInstalledExtPacks.end();
3232 ++it)
3233 {
3234 ComPtr<IUnknown> ptrIf;
3235 HRESULT hrc2 = (*it)->queryObject(aObjUuid, ptrIf);
3236 if (SUCCEEDED(hrc2))
3237 {
3238 aObjects.push_back(ptrIf);
3239 if (a_pstrExtPackNames)
3240 a_pstrExtPackNames->push_back((*it)->m->Desc.strName);
3241 }
3242 else if (hrc2 != E_NOINTERFACE)
3243 hrc = hrc2;
3244 }
3245
3246 if (aObjects.size() > 0)
3247 hrc = S_OK;
3248 }
3249 return hrc;
3250}
3251
3252#endif /* !VBOX_COM_INPROC */
3253
3254#ifdef VBOX_COM_INPROC
3255/**
3256 * Calls the pfnConsoleReady hook for all working extension packs.
3257 *
3258 * @param a_pConsole The console interface.
3259 * @remarks The caller must not hold any locks.
3260 */
3261void ExtPackManager::i_callAllConsoleReadyHooks(IConsole *a_pConsole)
3262{
3263 AutoCaller autoCaller(this);
3264 HRESULT hrc = autoCaller.rc();
3265 if (FAILED(hrc))
3266 return;
3267 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3268 ComPtr<ExtPackManager> ptrSelfRef = this;
3269
3270 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
3271 it != m->llInstalledExtPacks.end();
3272 /* advancing below */)
3273 {
3274 if ((*it)->i_callConsoleReadyHook(a_pConsole, &autoLock))
3275 it = m->llInstalledExtPacks.begin();
3276 else
3277 ++it;
3278 }
3279}
3280#endif
3281
3282#ifndef VBOX_COM_INPROC
3283/**
3284 * Calls the pfnVMCreated hook for all working extension packs.
3285 *
3286 * @param a_pMachine The machine interface of the new VM.
3287 */
3288void ExtPackManager::i_callAllVmCreatedHooks(IMachine *a_pMachine)
3289{
3290 AutoCaller autoCaller(this);
3291 HRESULT hrc = autoCaller.rc();
3292 if (FAILED(hrc))
3293 return;
3294 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3295 ComPtr<ExtPackManager> ptrSelfRef = this; /* paranoia */
3296 ExtPackList llExtPacks = m->llInstalledExtPacks;
3297
3298 for (ExtPackList::iterator it = llExtPacks.begin(); it != llExtPacks.end(); ++it)
3299 (*it)->i_callVmCreatedHook(m->pVirtualBox, a_pMachine, &autoLock);
3300}
3301#endif
3302
3303#ifdef VBOX_COM_INPROC
3304/**
3305 * Calls the pfnVMConfigureVMM hook for all working extension packs.
3306 *
3307 * @returns VBox status code. Stops on the first failure, expecting the caller
3308 * to signal this to the caller of the CFGM constructor.
3309 * @param a_pConsole The console interface for the VM.
3310 * @param a_pVM The VM handle.
3311 */
3312int ExtPackManager::i_callAllVmConfigureVmmHooks(IConsole *a_pConsole, PVM a_pVM)
3313{
3314 AutoCaller autoCaller(this);
3315 HRESULT hrc = autoCaller.rc();
3316 if (FAILED(hrc))
3317 return Global::vboxStatusCodeFromCOM(hrc);
3318 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3319 ComPtr<ExtPackManager> ptrSelfRef = this; /* paranoia */
3320 ExtPackList llExtPacks = m->llInstalledExtPacks;
3321
3322 for (ExtPackList::iterator it = llExtPacks.begin(); it != llExtPacks.end(); ++it)
3323 {
3324 int vrc;
3325 (*it)->i_callVmConfigureVmmHook(a_pConsole, a_pVM, &autoLock, &vrc);
3326 if (RT_FAILURE(vrc))
3327 return vrc;
3328 }
3329
3330 return VINF_SUCCESS;
3331}
3332
3333/**
3334 * Calls the pfnVMPowerOn hook for all working extension packs.
3335 *
3336 * @returns VBox status code. Stops on the first failure, expecting the caller
3337 * to not power on the VM.
3338 * @param a_pConsole The console interface for the VM.
3339 * @param a_pVM The VM handle.
3340 */
3341int ExtPackManager::i_callAllVmPowerOnHooks(IConsole *a_pConsole, PVM a_pVM)
3342{
3343 AutoCaller autoCaller(this);
3344 HRESULT hrc = autoCaller.rc();
3345 if (FAILED(hrc))
3346 return Global::vboxStatusCodeFromCOM(hrc);
3347 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3348 ComPtr<ExtPackManager> ptrSelfRef = this; /* paranoia */
3349 ExtPackList llExtPacks = m->llInstalledExtPacks;
3350
3351 for (ExtPackList::iterator it = llExtPacks.begin(); it != llExtPacks.end(); ++it)
3352 {
3353 int vrc;
3354 (*it)->i_callVmPowerOnHook(a_pConsole, a_pVM, &autoLock, &vrc);
3355 if (RT_FAILURE(vrc))
3356 return vrc;
3357 }
3358
3359 return VINF_SUCCESS;
3360}
3361
3362/**
3363 * Calls the pfnVMPowerOff hook for all working extension packs.
3364 *
3365 * @param a_pConsole The console interface for the VM.
3366 * @param a_pVM The VM handle. Can be NULL.
3367 */
3368void ExtPackManager::i_callAllVmPowerOffHooks(IConsole *a_pConsole, PVM a_pVM)
3369{
3370 AutoCaller autoCaller(this);
3371 HRESULT hrc = autoCaller.rc();
3372 if (FAILED(hrc))
3373 return;
3374 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3375 ComPtr<ExtPackManager> ptrSelfRef = this; /* paranoia */
3376 ExtPackList llExtPacks = m->llInstalledExtPacks;
3377
3378 for (ExtPackList::iterator it = llExtPacks.begin(); it != llExtPacks.end(); ++it)
3379 (*it)->i_callVmPowerOffHook(a_pConsole, a_pVM, &autoLock);
3380}
3381#endif
3382
3383
3384/**
3385 * Checks that the specified extension pack contains a VRDE module and that it
3386 * is shipshape.
3387 *
3388 * @returns S_OK if ok, appropriate failure status code with details.
3389 * @param a_pstrExtPack The name of the extension pack.
3390 */
3391HRESULT ExtPackManager::i_checkVrdeExtPack(Utf8Str const *a_pstrExtPack)
3392{
3393 AutoCaller autoCaller(this);
3394 HRESULT hrc = autoCaller.rc();
3395 if (SUCCEEDED(hrc))
3396 {
3397 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3398
3399 ExtPack *pExtPack = i_findExtPack(a_pstrExtPack->c_str());
3400 if (pExtPack)
3401 hrc = pExtPack->i_checkVrde();
3402 else
3403 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("No extension pack by the name '%s' was found"), a_pstrExtPack->c_str());
3404 }
3405
3406 return hrc;
3407}
3408
3409/**
3410 * Gets the full path to the VRDE library of the specified extension pack.
3411 *
3412 * This will do extacly the same as checkVrdeExtPack and then resolve the
3413 * library path.
3414 *
3415 * @returns VINF_SUCCESS if a path is returned, VBox error status and message
3416 * return if not.
3417 * @param a_pstrExtPack The extension pack.
3418 * @param a_pstrVrdeLibrary Where to return the path.
3419 */
3420int ExtPackManager::i_getVrdeLibraryPathForExtPack(Utf8Str const *a_pstrExtPack, Utf8Str *a_pstrVrdeLibrary)
3421{
3422 AutoCaller autoCaller(this);
3423 HRESULT hrc = autoCaller.rc();
3424 if (SUCCEEDED(hrc))
3425 {
3426 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3427
3428 ExtPack *pExtPack = i_findExtPack(a_pstrExtPack->c_str());
3429 if (pExtPack)
3430 hrc = pExtPack->i_getVrdpLibraryName(a_pstrVrdeLibrary);
3431 else
3432 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("No extension pack by the name '%s' was found"),
3433 a_pstrExtPack->c_str());
3434 }
3435
3436 return Global::vboxStatusCodeFromCOM(hrc);
3437}
3438
3439/**
3440 * Gets the full path to the specified library of the specified extension pack.
3441 *
3442 * @returns S_OK if a path is returned, COM error status and message return if
3443 * not.
3444 * @param a_pszModuleName The library.
3445 * @param a_pszExtPack The extension pack.
3446 * @param a_pstrLibrary Where to return the path.
3447 */
3448HRESULT ExtPackManager::i_getLibraryPathForExtPack(const char *a_pszModuleName, const char *a_pszExtPack, Utf8Str *a_pstrLibrary)
3449{
3450 AutoCaller autoCaller(this);
3451 HRESULT hrc = autoCaller.rc();
3452 if (SUCCEEDED(hrc))
3453 {
3454 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3455
3456 ExtPack *pExtPack = i_findExtPack(a_pszExtPack);
3457 if (pExtPack)
3458 hrc = pExtPack->i_getLibraryName(a_pszModuleName, a_pstrLibrary);
3459 else
3460 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("No extension pack by the name '%s' was found"), a_pszExtPack);
3461 }
3462
3463 return hrc;
3464}
3465
3466/**
3467 * Gets the name of the default VRDE extension pack.
3468 *
3469 * @returns S_OK or some COM error status on red tape failure.
3470 * @param a_pstrExtPack Where to return the extension pack name. Returns
3471 * empty if no extension pack wishes to be the default
3472 * VRDP provider.
3473 */
3474HRESULT ExtPackManager::i_getDefaultVrdeExtPack(Utf8Str *a_pstrExtPack)
3475{
3476 a_pstrExtPack->setNull();
3477
3478 AutoCaller autoCaller(this);
3479 HRESULT hrc = autoCaller.rc();
3480 if (SUCCEEDED(hrc))
3481 {
3482 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3483
3484 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
3485 it != m->llInstalledExtPacks.end();
3486 ++it)
3487 {
3488 if ((*it)->i_wantsToBeDefaultVrde())
3489 {
3490 *a_pstrExtPack = (*it)->m->Desc.strName;
3491 break;
3492 }
3493 }
3494 }
3495 return hrc;
3496}
3497
3498/**
3499 * Checks if an extension pack is (present and) usable.
3500 *
3501 * @returns @c true if it is, otherwise @c false.
3502 * @param a_pszExtPack The name of the extension pack.
3503 */
3504bool ExtPackManager::i_isExtPackUsable(const char *a_pszExtPack)
3505{
3506 AutoCaller autoCaller(this);
3507 HRESULT hrc = autoCaller.rc();
3508 if (FAILED(hrc))
3509 return false;
3510 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3511
3512 ExtPack *pExtPack = i_findExtPack(a_pszExtPack);
3513 return pExtPack != NULL
3514 && pExtPack->m->fUsable;
3515}
3516
3517/**
3518 * Dumps all extension packs to the release log.
3519 */
3520void ExtPackManager::i_dumpAllToReleaseLog(void)
3521{
3522 AutoCaller autoCaller(this);
3523 HRESULT hrc = autoCaller.rc();
3524 if (FAILED(hrc))
3525 return;
3526 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3527
3528 LogRel(("Installed Extension Packs:\n"));
3529 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
3530 it != m->llInstalledExtPacks.end();
3531 ++it)
3532 {
3533 ExtPack::Data *pExtPackData = (*it)->m;
3534 if (pExtPackData)
3535 {
3536 if (pExtPackData->fUsable)
3537 LogRel((" %s (Version: %s r%u%s%s; VRDE Module: %s)\n",
3538 pExtPackData->Desc.strName.c_str(),
3539 pExtPackData->Desc.strVersion.c_str(),
3540 pExtPackData->Desc.uRevision,
3541 pExtPackData->Desc.strEdition.isEmpty() ? "" : " ",
3542 pExtPackData->Desc.strEdition.c_str(),
3543 pExtPackData->Desc.strVrdeModule.c_str() ));
3544 else
3545 LogRel((" %s (Version: %s r%u%s%s; VRDE Module: %s unusable because of '%s')\n",
3546 pExtPackData->Desc.strName.c_str(),
3547 pExtPackData->Desc.strVersion.c_str(),
3548 pExtPackData->Desc.uRevision,
3549 pExtPackData->Desc.strEdition.isEmpty() ? "" : " ",
3550 pExtPackData->Desc.strEdition.c_str(),
3551 pExtPackData->Desc.strVrdeModule.c_str(),
3552 pExtPackData->strWhyUnusable.c_str() ));
3553 }
3554 else
3555 LogRel((" pExtPackData is NULL\n"));
3556 }
3557
3558 if (!m->llInstalledExtPacks.size())
3559 LogRel((" None installed!\n"));
3560}
3561
3562/**
3563 * Gets the update counter (reflecting extpack list updates).
3564 */
3565uint64_t ExtPackManager::i_getUpdateCounter(void)
3566{
3567 AutoCaller autoCaller(this);
3568 HRESULT hrc = autoCaller.rc();
3569 if (FAILED(hrc))
3570 return 0;
3571 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3572 return m->cUpdate;
3573}
3574
3575/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette