VirtualBox

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

Last change on this file since 99991 was 98262, checked in by vboxsync, 23 months ago

Main: rc() -> hrc()/vrc(). bugref:10223

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