VirtualBox

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

Last change on this file since 101698 was 101293, checked in by vboxsync, 9 months ago

Main/ExtPack*: tidying up. (Please, do NOT use 'm' as a local variable, that's just so confusing.)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 133.3 KB
Line 
1/* $Id: ExtPackManagerImpl.cpp 101293 2023-09-27 14:20:49Z 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 *pData = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1777 AssertPtrReturn(pData, VERR_INVALID_POINTER);
1778 ExtPack *pThis = pData->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/**
1791 * @interface_method_impl{VBOXEXTPACKHLP,pfnGetFilePath}
1792 */
1793/*static*/ DECLCALLBACK(int)
1794ExtPack::i_hlpGetFilePath(PCVBOXEXTPACKHLP pHlp, const char *pszFilename, char *pszPath, size_t cbPath)
1795{
1796 /*
1797 * Validate the input and get our bearings.
1798 */
1799 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
1800 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1801 AssertReturn(cbPath > 0, VERR_BUFFER_OVERFLOW);
1802
1803 AssertPtrReturn(pHlp, VERR_INVALID_POINTER);
1804 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VERR_INVALID_POINTER);
1805 ExtPack::Data *pData = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1806 AssertPtrReturn(pData, VERR_INVALID_POINTER);
1807
1808 /*
1809 * This is a simple RTPathJoin, no checking if things exists or anything.
1810 */
1811 int vrc = RTPathJoin(pszPath, cbPath, pData->strExtPackPath.c_str(), pszFilename);
1812 if (RT_FAILURE(vrc))
1813 RT_BZERO(pszPath, cbPath);
1814 return vrc;
1815}
1816
1817/**
1818 * @interface_method_impl{VBOXEXTPACKHLP,pfnGetContext}
1819 */
1820/*static*/ DECLCALLBACK(VBOXEXTPACKCTX)
1821ExtPack::i_hlpGetContext(PCVBOXEXTPACKHLP pHlp)
1822{
1823 /*
1824 * Validate the input and get our bearings.
1825 */
1826 AssertPtrReturn(pHlp, VBOXEXTPACKCTX_INVALID);
1827 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VBOXEXTPACKCTX_INVALID);
1828 ExtPack::Data *pData = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1829 AssertPtrReturn(pData, VBOXEXTPACKCTX_INVALID);
1830
1831 return pData->enmContext;
1832}
1833
1834/**
1835 * @interface_method_impl{VBOXEXTPACKHLP,pfnLoadHGCMService}
1836 */
1837/*static*/ DECLCALLBACK(int)
1838ExtPack::i_hlpLoadHGCMService(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IConsole) *pConsole,
1839 const char *pszServiceLibrary, const char *pszServiceName)
1840{
1841#ifdef VBOX_COM_INPROC
1842 /*
1843 * Validate the input and get our bearings.
1844 */
1845 AssertPtrReturn(pszServiceLibrary, VERR_INVALID_POINTER);
1846 AssertPtrReturn(pszServiceName, VERR_INVALID_POINTER);
1847
1848 AssertPtrReturn(pHlp, VERR_INVALID_POINTER);
1849 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VERR_INVALID_POINTER);
1850 ExtPack::Data *pData = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1851 AssertPtrReturn(pData, VERR_INVALID_POINTER);
1852 ExtPack *pThis = pData->pThis;
1853 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1854 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
1855
1856 Console *pCon = (Console *)pConsole;
1857 return pCon->i_hgcmLoadService(pszServiceLibrary, pszServiceName);
1858#else
1859 RT_NOREF(pHlp, pConsole, pszServiceLibrary, pszServiceName);
1860 return VERR_INVALID_STATE;
1861#endif
1862}
1863
1864/**
1865 * @interface_method_impl{VBOXEXTPACKHLP,pfnLoadVDPlugin}
1866 */
1867/*static*/ DECLCALLBACK(int)
1868ExtPack::i_hlpLoadVDPlugin(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IVirtualBox) *pVirtualBox, const char *pszPluginLibrary)
1869{
1870#ifndef VBOX_COM_INPROC
1871 /*
1872 * Validate the input and get our bearings.
1873 */
1874 AssertPtrReturn(pszPluginLibrary, VERR_INVALID_POINTER);
1875
1876 AssertPtrReturn(pHlp, VERR_INVALID_POINTER);
1877 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VERR_INVALID_POINTER);
1878 ExtPack::Data *pData = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1879 AssertPtrReturn(pData, VERR_INVALID_POINTER);
1880 ExtPack *pThis = pData->pThis;
1881 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1882 AssertPtrReturn(pVirtualBox, VERR_INVALID_POINTER);
1883
1884 VirtualBox *pVBox = (VirtualBox *)pVirtualBox;
1885 return pVBox->i_loadVDPlugin(pszPluginLibrary);
1886#else
1887 RT_NOREF(pHlp, pVirtualBox, pszPluginLibrary);
1888 return VERR_INVALID_STATE;
1889#endif
1890}
1891
1892/**
1893 * @interface_method_impl{VBOXEXTPACKHLP,pfnUnloadVDPlugin}
1894 */
1895/*static*/ DECLCALLBACK(int)
1896ExtPack::i_hlpUnloadVDPlugin(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IVirtualBox) *pVirtualBox, const char *pszPluginLibrary)
1897{
1898#ifndef VBOX_COM_INPROC
1899 /*
1900 * Validate the input and get our bearings.
1901 */
1902 AssertPtrReturn(pszPluginLibrary, VERR_INVALID_POINTER);
1903
1904 AssertPtrReturn(pHlp, VERR_INVALID_POINTER);
1905 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VERR_INVALID_POINTER);
1906 ExtPack::Data *pData = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1907 AssertPtrReturn(pData, VERR_INVALID_POINTER);
1908 ExtPack *pThis = pData->pThis;
1909 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1910 AssertPtrReturn(pVirtualBox, VERR_INVALID_POINTER);
1911
1912 VirtualBox *pVBox = (VirtualBox *)pVirtualBox;
1913 return pVBox->i_unloadVDPlugin(pszPluginLibrary);
1914#else
1915 RT_NOREF(pHlp, pVirtualBox, pszPluginLibrary);
1916 return VERR_INVALID_STATE;
1917#endif
1918}
1919
1920/**
1921 * @interface_method_impl{VBOXEXTPACKHLP,pfnCreateProgress}
1922 */
1923/*static*/ DECLCALLBACK(uint32_t)
1924ExtPack::i_hlpCreateProgress(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IUnknown) *pInitiator,
1925 const char *pcszDescription, uint32_t cOperations,
1926 uint32_t uTotalOperationsWeight, const char *pcszFirstOperationDescription,
1927 uint32_t uFirstOperationWeight, VBOXEXTPACK_IF_CS(IProgress) **ppProgressOut)
1928{
1929 /*
1930 * Validate the input and get our bearings.
1931 */
1932 AssertPtrReturn(pcszDescription, (uint32_t)E_INVALIDARG);
1933 AssertReturn(cOperations >= 1, (uint32_t)E_INVALIDARG);
1934 AssertReturn(uTotalOperationsWeight >= 1, (uint32_t)E_INVALIDARG);
1935 AssertPtrReturn(pcszFirstOperationDescription, (uint32_t)E_INVALIDARG);
1936 AssertReturn(uFirstOperationWeight >= 1, (uint32_t)E_INVALIDARG);
1937 AssertPtrReturn(ppProgressOut, (uint32_t)E_INVALIDARG);
1938
1939 AssertPtrReturn(pHlp, (uint32_t)E_INVALIDARG);
1940 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
1941#ifndef VBOX_COM_INPROC
1942 ExtPack::Data *pData = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1943#endif
1944
1945 ComObjPtr<Progress> pProgress;
1946 HRESULT hrc = pProgress.createObject();
1947 if (SUCCEEDED(hrc))
1948 {
1949 hrc = pProgress->init(
1950#ifndef VBOX_COM_INPROC
1951 pData->pVirtualBox,
1952#endif
1953 pInitiator, pcszDescription, TRUE /* aCancelable */,
1954 cOperations, uTotalOperationsWeight,
1955 pcszFirstOperationDescription, uFirstOperationWeight);
1956 if (SUCCEEDED(hrc))
1957 hrc = pProgress.queryInterfaceTo(ppProgressOut);
1958 }
1959 return hrc;
1960}
1961
1962/**
1963 * @interface_method_impl{VBOXEXTPACKHLP,pfnGetCanceledProgress}
1964 */
1965/*static*/ DECLCALLBACK(uint32_t)
1966ExtPack::i_hlpGetCanceledProgress(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IProgress) *pProgress,
1967 bool *pfCanceled)
1968{
1969 /*
1970 * Validate the input and get our bearings.
1971 */
1972 AssertPtrReturn(pProgress, (uint32_t)E_INVALIDARG);
1973 AssertPtrReturn(pfCanceled, (uint32_t)E_INVALIDARG);
1974
1975 AssertPtrReturn(pHlp, (uint32_t)E_INVALIDARG);
1976 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
1977
1978 BOOL fCanceled = FALSE;
1979 HRESULT hrc = pProgress->COMGETTER(Canceled)(&fCanceled);
1980 *pfCanceled = !!fCanceled;
1981 return hrc;
1982}
1983
1984/**
1985 * @interface_method_impl{VBOXEXTPACKHLP,pfnUpdateProgress}
1986 */
1987/*static*/ DECLCALLBACK(uint32_t)
1988ExtPack::i_hlpUpdateProgress(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IProgress) *pProgress,
1989 uint32_t uPercent)
1990{
1991 /*
1992 * Validate the input and get our bearings.
1993 */
1994 AssertPtrReturn(pProgress, (uint32_t)E_INVALIDARG);
1995 AssertReturn(uPercent <= 100, (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->SetCurrentOperationProgress(uPercent);
2003}
2004
2005/**
2006 * @interface_method_impl{VBOXEXTPACKHLP,pfnNextOperationProgress}
2007 */
2008/*static*/ DECLCALLBACK(uint32_t)
2009ExtPack::i_hlpNextOperationProgress(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IProgress) *pProgress,
2010 const char *pcszNextOperationDescription,
2011 uint32_t uNextOperationWeight)
2012{
2013 /*
2014 * Validate the input and get our bearings.
2015 */
2016 AssertPtrReturn(pProgress, (uint32_t)E_INVALIDARG);
2017 AssertPtrReturn(pcszNextOperationDescription, (uint32_t)E_INVALIDARG);
2018 AssertReturn(uNextOperationWeight >= 1, (uint32_t)E_INVALIDARG);
2019
2020 AssertPtrReturn(pHlp, (uint32_t)E_INVALIDARG);
2021 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
2022
2023 ComPtr<IInternalProgressControl> pProgressControl(pProgress);
2024 AssertReturn(!!pProgressControl, (uint32_t)E_INVALIDARG);
2025 return pProgressControl->SetNextOperation(Bstr(pcszNextOperationDescription).raw(), uNextOperationWeight);
2026}
2027
2028/**
2029 * @interface_method_impl{VBOXEXTPACKHLP,pfnWaitOtherProgress}
2030 */
2031/*static*/ DECLCALLBACK(uint32_t)
2032ExtPack::i_hlpWaitOtherProgress(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IProgress) *pProgress,
2033 VBOXEXTPACK_IF_CS(IProgress) *pProgressOther, uint32_t cTimeoutMS)
2034{
2035 /*
2036 * Validate the input and get our bearings.
2037 */
2038 AssertPtrReturn(pProgress, (uint32_t)E_INVALIDARG);
2039 AssertPtrReturn(pProgressOther, (uint32_t)E_INVALIDARG);
2040
2041 AssertPtrReturn(pHlp, (uint32_t)E_INVALIDARG);
2042 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
2043
2044 ComPtr<IInternalProgressControl> pProgressControl(pProgress);
2045 AssertReturn(!!pProgressControl, (uint32_t)E_INVALIDARG);
2046 return pProgressControl->WaitForOtherProgressCompletion(pProgressOther, cTimeoutMS);
2047}
2048
2049/**
2050 * @interface_method_impl{VBOXEXTPACKHLP,pfnCompleteProgress}
2051 */
2052/*static*/ DECLCALLBACK(uint32_t)
2053ExtPack::i_hlpCompleteProgress(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IProgress) *pProgress,
2054 uint32_t uResultCode)
2055{
2056 /*
2057 * Validate the input and get our bearings.
2058 */
2059 AssertPtrReturn(pHlp, (uint32_t)E_INVALIDARG);
2060 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
2061 AssertPtrReturn(pProgress, (uint32_t)E_INVALIDARG);
2062
2063 ComPtr<IInternalProgressControl> pProgressControl(pProgress);
2064 AssertReturn(!!pProgressControl, (uint32_t)E_INVALIDARG);
2065
2066 ComPtr<IVirtualBoxErrorInfo> errorInfo;
2067 if (FAILED((HRESULT)uResultCode))
2068 {
2069 ErrorInfoKeeper eik;
2070 eik.getVirtualBoxErrorInfo(errorInfo);
2071 }
2072 return pProgressControl->NotifyComplete((LONG)uResultCode, errorInfo);
2073}
2074
2075/**
2076 * @interface_method_impl{VBOXEXTPACKHLP,pfnCreateEvent}
2077 */
2078/*static*/ DECLCALLBACK(uint32_t)
2079ExtPack::i_hlpCreateEvent(PCVBOXEXTPACKHLP pHlp,
2080 VBOXEXTPACK_IF_CS(IEventSource) *aSource,
2081 /* VBoxEventType_T */ uint32_t aType, bool aWaitable,
2082 VBOXEXTPACK_IF_CS(IEvent) **ppEventOut)
2083{
2084 /*
2085 * Validate the input and get our bearings.
2086 */
2087 AssertPtrReturn(pHlp, (uint32_t)E_INVALIDARG);
2088 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
2089 AssertPtrReturn(ppEventOut, (uint32_t)E_INVALIDARG);
2090
2091 ComObjPtr<VBoxEvent> pEvent;
2092 HRESULT hrc = pEvent.createObject();
2093 if (SUCCEEDED(hrc))
2094 {
2095 /* default aSource to pVirtualBox? */
2096 hrc = pEvent->init(aSource, static_cast<VBoxEventType_T>(aType), aWaitable);
2097 if (SUCCEEDED(hrc))
2098 hrc = pEvent.queryInterfaceTo(ppEventOut);
2099 }
2100 return hrc;
2101}
2102
2103/**
2104 * @interface_method_impl{VBOXEXTPACKHLP,pfnCreateVetoEvent}
2105 */
2106/*static*/ DECLCALLBACK(uint32_t)
2107ExtPack::i_hlpCreateVetoEvent(PCVBOXEXTPACKHLP pHlp,
2108 VBOXEXTPACK_IF_CS(IEventSource) *aSource,
2109 /* VBoxEventType_T */ uint32_t aType,
2110 VBOXEXTPACK_IF_CS(IVetoEvent) **ppEventOut)
2111{
2112 AssertPtrReturn(pHlp, (uint32_t)E_INVALIDARG);
2113 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, (uint32_t)E_INVALIDARG);
2114 AssertPtrReturn(ppEventOut, (uint32_t)E_INVALIDARG);
2115
2116 ComObjPtr<VBoxVetoEvent> pEvent;
2117
2118 HRESULT hrc = pEvent.createObject();
2119 if (SUCCEEDED(hrc))
2120 {
2121 /* default aSource to pVirtualBox? */
2122 hrc = pEvent->init(aSource, static_cast<VBoxEventType_T>(aType));
2123 if (SUCCEEDED(hrc))
2124 hrc = pEvent.queryInterfaceTo(ppEventOut);
2125 }
2126 return hrc;
2127}
2128
2129/**
2130 * @interface_method_impl{VBOXEXTPACKHLP,pfnTranslate}
2131 */
2132/*static*/ DECLCALLBACK(const char *)
2133ExtPack::i_hlpTranslate(PCVBOXEXTPACKHLP pHlp,
2134 const char *pszComponent,
2135 const char *pszSourceText,
2136 const char *pszComment /*=NULL*/,
2137 const size_t uNum /*= ~(size_t)0*/)
2138{
2139 /*
2140 * Validate the input and get our bearings.
2141 */
2142 AssertPtrReturn(pHlp, pszSourceText);
2143 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, pszSourceText);
2144 ExtPack::Data *pData = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
2145 AssertPtrReturn(pData, pszSourceText);
2146
2147#ifdef VBOX_WITH_MAIN_NLS
2148 return VirtualBoxTranslator::translate(pData->pTrComponent, pszComponent, pszSourceText, pszComment, uNum);
2149#else
2150 RT_NOREF(pszComponent, pszComment, uNum);
2151 return pszSourceText;
2152#endif
2153}
2154
2155
2156/*static*/ DECLCALLBACK(int)
2157ExtPack::i_hlpReservedN(PCVBOXEXTPACKHLP pHlp)
2158{
2159 /*
2160 * Validate the input and get our bearings.
2161 */
2162 AssertPtrReturn(pHlp, VERR_INVALID_POINTER);
2163 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VERR_INVALID_POINTER);
2164 ExtPack::Data *pData = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
2165 AssertPtrReturn(pData, VERR_INVALID_POINTER);
2166 ExtPack *pThis = pData->pThis;
2167 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2168
2169 return VERR_NOT_IMPLEMENTED;
2170}
2171
2172
2173
2174
2175HRESULT ExtPack::getName(com::Utf8Str &aName)
2176{
2177 aName = m->Desc.strName;
2178 return S_OK;
2179}
2180
2181HRESULT ExtPack::getDescription(com::Utf8Str &aDescription)
2182{
2183 aDescription = m->Desc.strDescription;
2184 return S_OK;
2185}
2186
2187HRESULT ExtPack::getVersion(com::Utf8Str &aVersion)
2188{
2189 aVersion = m->Desc.strVersion;
2190 return S_OK;
2191}
2192
2193HRESULT ExtPack::getRevision(ULONG *aRevision)
2194{
2195 *aRevision = m->Desc.uRevision;
2196 return S_OK;
2197}
2198
2199HRESULT ExtPack::getEdition(com::Utf8Str &aEdition)
2200{
2201 aEdition = m->Desc.strEdition;
2202 return S_OK;
2203}
2204
2205HRESULT ExtPack::getVRDEModule(com::Utf8Str &aVRDEModule)
2206{
2207 aVRDEModule = m->Desc.strVrdeModule;
2208 return S_OK;
2209}
2210
2211HRESULT ExtPack::getCryptoModule(com::Utf8Str &aCryptoModule)
2212{
2213 aCryptoModule = m->Desc.strCryptoModule;
2214 return S_OK;
2215}
2216
2217HRESULT ExtPack::getPlugIns(std::vector<ComPtr<IExtPackPlugIn> > &aPlugIns)
2218{
2219 /** @todo implement plug-ins. */
2220 NOREF(aPlugIns);
2221 ReturnComNotImplemented();
2222}
2223
2224HRESULT ExtPack::getUsable(BOOL *aUsable)
2225{
2226 *aUsable = m->fUsable;
2227 return S_OK;
2228}
2229
2230HRESULT ExtPack::getWhyUnusable(com::Utf8Str &aWhyUnusable)
2231{
2232 aWhyUnusable = m->strWhyUnusable;
2233 return S_OK;
2234}
2235
2236HRESULT ExtPack::getShowLicense(BOOL *aShowLicense)
2237{
2238 *aShowLicense = m->Desc.fShowLicense;
2239 return S_OK;
2240}
2241
2242HRESULT ExtPack::getLicense(com::Utf8Str &aLicense)
2243{
2244 Utf8Str strHtml("html");
2245 Utf8Str str("");
2246 return queryLicense(str, str, strHtml, aLicense);
2247}
2248
2249HRESULT ExtPack::queryLicense(const com::Utf8Str &aPreferredLocale, const com::Utf8Str &aPreferredLanguage,
2250 const com::Utf8Str &aFormat, com::Utf8Str &aLicenseText)
2251{
2252 HRESULT hrc = S_OK;
2253
2254 /*
2255 * Validate input.
2256 */
2257 if (aPreferredLocale.length() != 2 && aPreferredLocale.length() != 0)
2258 return setError(E_FAIL, tr("The preferred locale is a two character string or empty."));
2259
2260 if (aPreferredLanguage.length() != 2 && aPreferredLanguage.length() != 0)
2261 return setError(E_FAIL, tr("The preferred language is a two character string or empty."));
2262
2263 if ( !aFormat.equals("html")
2264 && !aFormat.equals("rtf")
2265 && !aFormat.equals("txt"))
2266 return setError(E_FAIL, tr("The license format can only have the values 'html', 'rtf' and 'txt'."));
2267
2268 /*
2269 * Combine the options to form a file name before locking down anything.
2270 */
2271 char szName[sizeof(VBOX_EXTPACK_LICENSE_NAME_PREFIX "-de_DE.html") + 2];
2272 if (aPreferredLocale.isNotEmpty() && aPreferredLanguage.isNotEmpty())
2273 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX "-%s_%s.%s",
2274 aPreferredLocale.c_str(), aPreferredLanguage.c_str(), aFormat.c_str());
2275 else if (aPreferredLocale.isNotEmpty())
2276 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX "-%s.%s",
2277 aPreferredLocale.c_str(), aFormat.c_str());
2278 else if (aPreferredLanguage.isNotEmpty())
2279 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX "-_%s.%s",
2280 aPreferredLocale.c_str(), aFormat.c_str());
2281 else
2282 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX ".%s",
2283 aFormat.c_str());
2284
2285 /*
2286 * Effectuate the query.
2287 */
2288 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS); /* paranoia */
2289
2290 if (!m->fUsable)
2291 hrc = setError(E_FAIL, "%s", m->strWhyUnusable.c_str());
2292 else
2293 {
2294 char szPath[RTPATH_MAX];
2295 int vrc = RTPathJoin(szPath, sizeof(szPath), m->strExtPackPath.c_str(), szName);
2296 if (RT_SUCCESS(vrc))
2297 {
2298 void *pvFile;
2299 size_t cbFile;
2300 vrc = RTFileReadAllEx(szPath, 0, RTFOFF_MAX, RTFILE_RDALL_O_DENY_READ, &pvFile, &cbFile);
2301 if (RT_SUCCESS(vrc))
2302 {
2303 Bstr bstrLicense((const char *)pvFile, cbFile);
2304 if (bstrLicense.isNotEmpty())
2305 {
2306 aLicenseText = Utf8Str(bstrLicense);
2307 hrc = S_OK;
2308 }
2309 else
2310 hrc = setError(VBOX_E_IPRT_ERROR, tr("The license file '%s' is empty or contains invalid UTF-8 encoding"),
2311 szPath);
2312 RTFileReadAllFree(pvFile, cbFile);
2313 }
2314 else if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
2315 hrc = setErrorBoth(VBOX_E_OBJECT_NOT_FOUND, vrc, tr("The license file '%s' was not found in extension pack '%s'"),
2316 szName, m->Desc.strName.c_str());
2317 else
2318 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to open the license file '%s': %Rrc"), szPath, vrc);
2319 }
2320 else
2321 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("RTPathJoin failed: %Rrc"), vrc);
2322 }
2323 return hrc;
2324}
2325
2326HRESULT ExtPack::queryObject(const com::Utf8Str &aObjUuid, ComPtr<IUnknown> &aReturnInterface)
2327{
2328 com::Guid ObjectId;
2329 CheckComArgGuid(aObjUuid, ObjectId);
2330
2331 HRESULT hrc = S_OK;
2332
2333 if ( m->pReg
2334 && m->pReg->pfnQueryObject)
2335 {
2336 void *pvUnknown = m->pReg->pfnQueryObject(m->pReg, ObjectId.raw());
2337 if (pvUnknown)
2338 {
2339 aReturnInterface = (IUnknown *)pvUnknown;
2340 /* The above assignment increased the refcount. Since pvUnknown
2341 * is a dumb pointer we have to do the release ourselves. */
2342 ((IUnknown *)pvUnknown)->Release();
2343 }
2344 else
2345 hrc = E_NOINTERFACE;
2346 }
2347 else
2348 hrc = E_NOINTERFACE;
2349 return hrc;
2350}
2351
2352DEFINE_EMPTY_CTOR_DTOR(ExtPackManager)
2353
2354/**
2355 * Called by ComObjPtr::createObject when creating the object.
2356 *
2357 * Just initialize the basic object state, do the rest in init().
2358 *
2359 * @returns S_OK.
2360 */
2361HRESULT ExtPackManager::FinalConstruct()
2362{
2363 m = NULL;
2364 return BaseFinalConstruct();
2365}
2366
2367/**
2368 * Initializes the extension pack manager.
2369 *
2370 * @returns COM status code.
2371 * @param a_pVirtualBox Pointer to the VirtualBox object.
2372 * @param a_enmContext The context we're in.
2373 */
2374HRESULT ExtPackManager::initExtPackManager(VirtualBox *a_pVirtualBox, VBOXEXTPACKCTX a_enmContext)
2375{
2376 AutoInitSpan autoInitSpan(this);
2377 AssertReturn(autoInitSpan.isOk(), E_FAIL);
2378
2379 /*
2380 * Figure some stuff out before creating the instance data.
2381 */
2382 char szBaseDir[RTPATH_MAX];
2383 int vrc = RTPathAppPrivateArchTop(szBaseDir, sizeof(szBaseDir));
2384 AssertLogRelRCReturn(vrc, E_FAIL);
2385 vrc = RTPathAppend(szBaseDir, sizeof(szBaseDir), VBOX_EXTPACK_INSTALL_DIR);
2386 AssertLogRelRCReturn(vrc, E_FAIL);
2387
2388 char szCertificatDir[RTPATH_MAX];
2389 vrc = RTPathAppPrivateNoArch(szCertificatDir, sizeof(szCertificatDir));
2390 AssertLogRelRCReturn(vrc, E_FAIL);
2391 vrc = RTPathAppend(szCertificatDir, sizeof(szCertificatDir), VBOX_EXTPACK_CERT_DIR);
2392 AssertLogRelRCReturn(vrc, E_FAIL);
2393
2394 /*
2395 * Allocate and initialize the instance data.
2396 */
2397 m = new Data;
2398 m->strBaseDir = szBaseDir;
2399 m->strCertificatDirPath = szCertificatDir;
2400 m->enmContext = a_enmContext;
2401#ifndef VBOX_COM_INPROC
2402 m->pVirtualBox = a_pVirtualBox;
2403#else
2404 RT_NOREF_PV(a_pVirtualBox);
2405#endif
2406
2407 /*
2408 * Go looking for extensions. The RTDirOpen may fail if nothing has been
2409 * installed yet, or if root is paranoid and has revoked our access to them.
2410 *
2411 * We ASSUME that there are no files, directories or stuff in the directory
2412 * that exceed the max name length in RTDIRENTRYEX.
2413 */
2414 HRESULT hrc = S_OK;
2415 RTDIR hDir;
2416 vrc = RTDirOpen(&hDir, szBaseDir);
2417 if (RT_SUCCESS(vrc))
2418 {
2419 for (;;)
2420 {
2421 RTDIRENTRYEX Entry;
2422 vrc = RTDirReadEx(hDir, &Entry, NULL /*pcbDirEntry*/, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2423 if (RT_FAILURE(vrc))
2424 {
2425 AssertLogRelMsg(vrc == VERR_NO_MORE_FILES, ("%Rrc\n", vrc));
2426 break;
2427 }
2428 if ( RTFS_IS_DIRECTORY(Entry.Info.Attr.fMode)
2429 && strcmp(Entry.szName, ".") != 0
2430 && strcmp(Entry.szName, "..") != 0
2431 && VBoxExtPackIsValidMangledName(Entry.szName) )
2432 {
2433 /*
2434 * All directories are extensions, the shall be nothing but
2435 * extensions in this subdirectory.
2436 */
2437 char szExtPackDir[RTPATH_MAX];
2438 vrc = RTPathJoin(szExtPackDir, sizeof(szExtPackDir), m->strBaseDir.c_str(), Entry.szName);
2439 AssertLogRelRC(vrc);
2440 if (RT_SUCCESS(vrc))
2441 {
2442 RTCString *pstrName = VBoxExtPackUnmangleName(Entry.szName, RTSTR_MAX);
2443 AssertLogRel(pstrName);
2444 if (pstrName)
2445 {
2446 ComObjPtr<ExtPack> NewExtPack;
2447 HRESULT hrc2 = NewExtPack.createObject();
2448 if (SUCCEEDED(hrc2))
2449 hrc2 = NewExtPack->initWithDir(a_pVirtualBox, a_enmContext, pstrName->c_str(), szExtPackDir);
2450 delete pstrName;
2451 if (SUCCEEDED(hrc2))
2452 {
2453 m->llInstalledExtPacks.push_back(NewExtPack);
2454 /* Paranoia, there should be no API clients before this method is finished. */
2455
2456 m->cUpdate++;
2457 }
2458 else if (SUCCEEDED(hrc))
2459 hrc = hrc2;
2460 }
2461 else
2462 hrc = E_UNEXPECTED;
2463 }
2464 else
2465 hrc = E_UNEXPECTED;
2466 }
2467 }
2468 RTDirClose(hDir);
2469 }
2470 /* else: ignore, the directory probably does not exist or something. */
2471
2472 if (SUCCEEDED(hrc))
2473 autoInitSpan.setSucceeded();
2474 return hrc;
2475}
2476
2477/**
2478 * COM cruft.
2479 */
2480void ExtPackManager::FinalRelease()
2481{
2482 uninit();
2483 BaseFinalRelease();
2484}
2485
2486/**
2487 * Do the actual cleanup.
2488 */
2489void ExtPackManager::uninit()
2490{
2491 /* Enclose the state transition Ready->InUninit->NotReady */
2492 AutoUninitSpan autoUninitSpan(this);
2493 if (!autoUninitSpan.uninitDone() && m != NULL)
2494 {
2495 delete m;
2496 m = NULL;
2497 }
2498}
2499
2500HRESULT ExtPackManager::getInstalledExtPacks(std::vector<ComPtr<IExtPack> > &aInstalledExtPacks)
2501{
2502 Assert(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON);
2503
2504 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
2505
2506 aInstalledExtPacks.resize(m->llInstalledExtPacks.size());
2507 std::copy(m->llInstalledExtPacks.begin(), m->llInstalledExtPacks.end(), aInstalledExtPacks.begin());
2508
2509 return S_OK;
2510}
2511
2512HRESULT ExtPackManager::find(const com::Utf8Str &aName, ComPtr<IExtPack> &aReturnData)
2513{
2514 HRESULT hrc = S_OK;
2515
2516 Assert(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON);
2517
2518 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
2519
2520 ComPtr<ExtPack> ptrExtPack = i_findExtPack(aName.c_str());
2521 if (!ptrExtPack.isNull())
2522 ptrExtPack.queryInterfaceTo(aReturnData.asOutParam());
2523 else
2524 hrc = VBOX_E_OBJECT_NOT_FOUND;
2525
2526 return hrc;
2527}
2528
2529HRESULT ExtPackManager::openExtPackFile(const com::Utf8Str &aPath, ComPtr<IExtPackFile> &aFile)
2530{
2531 AssertReturn(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON, E_UNEXPECTED);
2532
2533#ifndef VBOX_COM_INPROC
2534 /* The API can optionally take a ::SHA-256=<hex-digest> attribute at the
2535 end of the file name. This is just a temporary measure for
2536 backporting, in 4.2 we'll add another parameter to the method. */
2537 Utf8Str strTarball;
2538 Utf8Str strDigest;
2539 size_t offSha256 = aPath.find("::SHA-256=");
2540 if (offSha256 == Utf8Str::npos)
2541 strTarball = aPath;
2542 else
2543 {
2544 strTarball = aPath.substr(0, offSha256);
2545 strDigest = aPath.substr(offSha256 + sizeof("::SHA-256=") - 1);
2546 }
2547
2548 ComObjPtr<ExtPackFile> NewExtPackFile;
2549 HRESULT hrc = NewExtPackFile.createObject();
2550 if (SUCCEEDED(hrc))
2551 hrc = NewExtPackFile->initWithFile(strTarball.c_str(), strDigest.c_str(), this, m->pVirtualBox);
2552 if (SUCCEEDED(hrc))
2553 NewExtPackFile.queryInterfaceTo(aFile.asOutParam());
2554
2555 return hrc;
2556#else
2557 RT_NOREF(aPath, aFile);
2558 return E_NOTIMPL;
2559#endif
2560}
2561
2562HRESULT ExtPackManager::uninstall(const com::Utf8Str &aName, BOOL aForcedRemoval,
2563 const com::Utf8Str &aDisplayInfo, ComPtr<IProgress> &aProgress)
2564{
2565 Assert(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON);
2566
2567#ifndef VBOX_COM_INPROC
2568
2569 HRESULT hrc;
2570 ExtPackUninstallTask *pTask = NULL;
2571 try
2572 {
2573 pTask = new ExtPackUninstallTask();
2574 hrc = pTask->Init(this, aName, aForcedRemoval != FALSE, aDisplayInfo);
2575 if (SUCCEEDED(hrc))
2576 {
2577 ComPtr<Progress> ptrProgress = pTask->ptrProgress;
2578 hrc = pTask->createThreadWithType(RTTHREADTYPE_DEFAULT);
2579 pTask = NULL; /* always consumed by createThread */
2580 if (SUCCEEDED(hrc))
2581 hrc = ptrProgress.queryInterfaceTo(aProgress.asOutParam());
2582 else
2583 hrc = setError(VBOX_E_IPRT_ERROR,
2584 tr("Starting thread for an extension pack uninstallation failed with %Rrc"), hrc);
2585 }
2586 else
2587 hrc = setError(hrc, tr("Looks like creating a progress object for ExtraPackUninstallTask object failed"));
2588 }
2589 catch (std::bad_alloc &)
2590 {
2591 hrc = E_OUTOFMEMORY;
2592 }
2593 catch (HRESULT hrcXcpt)
2594 {
2595 LogFlowThisFunc(("Exception was caught in the function ExtPackManager::uninstall()\n"));
2596 hrc = hrcXcpt;
2597 }
2598 if (pTask)
2599 delete pTask;
2600 return hrc;
2601#else
2602 RT_NOREF(aName, aForcedRemoval, aDisplayInfo, aProgress);
2603 return E_NOTIMPL;
2604#endif
2605}
2606
2607HRESULT ExtPackManager::cleanup(void)
2608{
2609 Assert(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON);
2610
2611 AutoCaller autoCaller(this);
2612 HRESULT hrc = autoCaller.hrc();
2613 if (SUCCEEDED(hrc))
2614 {
2615 /*
2616 * Run the set-uid-to-root binary that performs the cleanup.
2617 *
2618 * Take the write lock to prevent conflicts with other calls to this
2619 * VBoxSVC instance.
2620 */
2621 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
2622 hrc = i_runSetUidToRootHelper(NULL,
2623 "cleanup",
2624 "--base-dir", m->strBaseDir.c_str(),
2625 (const char *)NULL);
2626 }
2627
2628 return hrc;
2629}
2630
2631HRESULT ExtPackManager::queryAllPlugInsForFrontend(const com::Utf8Str &aFrontendName, std::vector<com::Utf8Str> &aPlugInModules)
2632{
2633 NOREF(aFrontendName);
2634 aPlugInModules.resize(0);
2635 return S_OK;
2636}
2637
2638HRESULT ExtPackManager::isExtPackUsable(const com::Utf8Str &aName, BOOL *aUsable)
2639{
2640 *aUsable = i_isExtPackUsable(aName.c_str());
2641 return S_OK;
2642}
2643
2644/**
2645 * Finds the success indicator string in the stderr output ofr hte helper app.
2646 *
2647 * @returns Pointer to the indicator.
2648 * @param psz The stderr output string. Can be NULL.
2649 * @param cch The size of the string.
2650 */
2651static char *findSuccessIndicator(char *psz, size_t cch)
2652{
2653 static const char s_szSuccessInd[] = "rcExit=RTEXITCODE_SUCCESS";
2654 Assert(!cch || strlen(psz) == cch);
2655 if (cch < sizeof(s_szSuccessInd) - 1)
2656 return NULL;
2657 char *pszInd = &psz[cch - sizeof(s_szSuccessInd) + 1];
2658 if (strcmp(s_szSuccessInd, pszInd))
2659 return NULL;
2660 return pszInd;
2661}
2662
2663/**
2664 * Runs the helper application that does the privileged operations.
2665 *
2666 * @returns S_OK or a failure status with error information set.
2667 * @param a_pstrDisplayInfo Platform specific display info hacks.
2668 * @param a_pszCommand The command to execute.
2669 * @param ... The argument strings that goes along with the
2670 * command. Maximum is about 16. Terminated by a
2671 * NULL.
2672 */
2673HRESULT ExtPackManager::i_runSetUidToRootHelper(Utf8Str const *a_pstrDisplayInfo, const char *a_pszCommand, ...)
2674{
2675 /*
2676 * Calculate the path to the helper application.
2677 */
2678 char szExecName[RTPATH_MAX];
2679 int vrc = RTPathAppPrivateArch(szExecName, sizeof(szExecName));
2680 AssertLogRelRCReturn(vrc, E_UNEXPECTED);
2681
2682 vrc = RTPathAppend(szExecName, sizeof(szExecName), VBOX_EXTPACK_HELPER_NAME);
2683 AssertLogRelRCReturn(vrc, E_UNEXPECTED);
2684
2685 /*
2686 * Convert the variable argument list to a RTProcCreate argument vector.
2687 */
2688 const char *apszArgs[20];
2689 unsigned cArgs = 0;
2690
2691 LogRel(("ExtPack: Executing '%s'", szExecName));
2692 apszArgs[cArgs++] = &szExecName[0];
2693
2694 if ( a_pstrDisplayInfo
2695 && a_pstrDisplayInfo->isNotEmpty())
2696 {
2697 LogRel((" '--display-info-hack' '%s'", a_pstrDisplayInfo->c_str()));
2698 apszArgs[cArgs++] = "--display-info-hack";
2699 apszArgs[cArgs++] = a_pstrDisplayInfo->c_str();
2700 }
2701
2702 LogRel((" '%s'", a_pszCommand));
2703 apszArgs[cArgs++] = a_pszCommand;
2704
2705 va_list va;
2706 va_start(va, a_pszCommand);
2707 const char *pszLastArg;
2708 for (;;)
2709 {
2710 AssertReturn(cArgs < RT_ELEMENTS(apszArgs) - 1, E_UNEXPECTED);
2711 pszLastArg = va_arg(va, const char *);
2712 if (!pszLastArg)
2713 break;
2714 LogRel((" '%s'", pszLastArg));
2715 apszArgs[cArgs++] = pszLastArg;
2716 };
2717 va_end(va);
2718
2719 LogRel(("\n"));
2720 apszArgs[cArgs] = NULL;
2721
2722 /*
2723 * Create a PIPE which we attach to stderr so that we can read the error
2724 * message on failure and report it back to the caller.
2725 */
2726 RTPIPE hPipeR;
2727 RTHANDLE hStdErrPipe;
2728 hStdErrPipe.enmType = RTHANDLETYPE_PIPE;
2729 vrc = RTPipeCreate(&hPipeR, &hStdErrPipe.u.hPipe, RTPIPE_C_INHERIT_WRITE);
2730 AssertLogRelRCReturn(vrc, E_UNEXPECTED);
2731
2732 /*
2733 * Spawn the process.
2734 */
2735 HRESULT hrc;
2736 RTPROCESS hProcess;
2737 vrc = RTProcCreateEx(szExecName,
2738 apszArgs,
2739 RTENV_DEFAULT,
2740 0 /*fFlags*/,
2741 NULL /*phStdIn*/,
2742 NULL /*phStdOut*/,
2743 &hStdErrPipe,
2744 NULL /*pszAsUser*/,
2745 NULL /*pszPassword*/,
2746 NULL /*pvExtraData*/,
2747 &hProcess);
2748 if (RT_SUCCESS(vrc))
2749 {
2750 vrc = RTPipeClose(hStdErrPipe.u.hPipe);
2751 hStdErrPipe.u.hPipe = NIL_RTPIPE;
2752
2753 /*
2754 * Read the pipe output until the process completes.
2755 */
2756 RTPROCSTATUS ProcStatus = { -42, RTPROCEXITREASON_ABEND };
2757 size_t cbStdErrBuf = 0;
2758 size_t offStdErrBuf = 0;
2759 char *pszStdErrBuf = NULL;
2760 do
2761 {
2762 /*
2763 * Service the pipe. Block waiting for output or the pipe breaking
2764 * when the process terminates.
2765 */
2766 if (hPipeR != NIL_RTPIPE)
2767 {
2768 char achBuf[1024];
2769 size_t cbRead;
2770 vrc = RTPipeReadBlocking(hPipeR, achBuf, sizeof(achBuf), &cbRead);
2771 if (RT_SUCCESS(vrc))
2772 {
2773 /* grow the buffer? */
2774 size_t cbBufReq = offStdErrBuf + cbRead + 1;
2775 if ( cbBufReq > cbStdErrBuf
2776 && cbBufReq < _256K)
2777 {
2778 size_t cbNew = RT_ALIGN_Z(cbBufReq, 16); // 1024
2779 void *pvNew = RTMemRealloc(pszStdErrBuf, cbNew);
2780 if (pvNew)
2781 {
2782 pszStdErrBuf = (char *)pvNew;
2783 cbStdErrBuf = cbNew;
2784 }
2785 }
2786
2787 /* append if we've got room. */
2788 if (cbBufReq <= cbStdErrBuf)
2789 {
2790 memcpy(&pszStdErrBuf[offStdErrBuf], achBuf, cbRead);
2791 offStdErrBuf = offStdErrBuf + cbRead;
2792 pszStdErrBuf[offStdErrBuf] = '\0';
2793 }
2794 }
2795 else
2796 {
2797 AssertLogRelMsg(vrc == VERR_BROKEN_PIPE, ("%Rrc\n", vrc));
2798 RTPipeClose(hPipeR);
2799 hPipeR = NIL_RTPIPE;
2800 }
2801 }
2802
2803 /*
2804 * Service the process. Block if we have no pipe.
2805 */
2806 if (hProcess != NIL_RTPROCESS)
2807 {
2808 vrc = RTProcWait(hProcess,
2809 hPipeR == NIL_RTPIPE ? RTPROCWAIT_FLAGS_BLOCK : RTPROCWAIT_FLAGS_NOBLOCK,
2810 &ProcStatus);
2811 if (RT_SUCCESS(vrc))
2812 hProcess = NIL_RTPROCESS;
2813 else
2814 AssertLogRelMsgStmt(vrc == VERR_PROCESS_RUNNING, ("%Rrc\n", vrc), hProcess = NIL_RTPROCESS);
2815 }
2816 } while ( hPipeR != NIL_RTPIPE
2817 || hProcess != NIL_RTPROCESS);
2818
2819 LogRel(("ExtPack: enmReason=%d iStatus=%d stderr='%s'\n",
2820 ProcStatus.enmReason, ProcStatus.iStatus, offStdErrBuf ? pszStdErrBuf : ""));
2821
2822 /*
2823 * Look for rcExit=RTEXITCODE_SUCCESS at the end of the error output,
2824 * cut it as it is only there to attest the success.
2825 */
2826 if (offStdErrBuf > 0)
2827 {
2828 RTStrStripR(pszStdErrBuf);
2829 offStdErrBuf = strlen(pszStdErrBuf);
2830 }
2831
2832 char *pszSuccessInd = findSuccessIndicator(pszStdErrBuf, offStdErrBuf);
2833 if (pszSuccessInd)
2834 {
2835 *pszSuccessInd = '\0';
2836 offStdErrBuf = (size_t)(pszSuccessInd - pszStdErrBuf);
2837 }
2838 else if ( ProcStatus.enmReason == RTPROCEXITREASON_NORMAL
2839 && ProcStatus.iStatus == 0)
2840 ProcStatus.iStatus = offStdErrBuf ? 667 : 666;
2841
2842 /*
2843 * Compose the status code and, on failure, error message.
2844 */
2845 if ( ProcStatus.enmReason == RTPROCEXITREASON_NORMAL
2846 && ProcStatus.iStatus == 0)
2847 hrc = S_OK;
2848 else if (ProcStatus.enmReason == RTPROCEXITREASON_NORMAL)
2849 {
2850 AssertMsg(ProcStatus.iStatus != 0, ("%s\n", pszStdErrBuf));
2851 hrc = setError(E_FAIL, tr("The installer failed with exit code %d: %s"),
2852 ProcStatus.iStatus, offStdErrBuf ? pszStdErrBuf : "");
2853 }
2854 else if (ProcStatus.enmReason == RTPROCEXITREASON_SIGNAL)
2855 hrc = setError(E_UNEXPECTED, tr("The installer was killed by signal #d (stderr: %s)"),
2856 ProcStatus.iStatus, offStdErrBuf ? pszStdErrBuf : "");
2857 else if (ProcStatus.enmReason == RTPROCEXITREASON_ABEND)
2858 hrc = setError(E_UNEXPECTED, tr("The installer aborted abnormally (stderr: %s)"),
2859 offStdErrBuf ? pszStdErrBuf : "");
2860 else
2861 hrc = setError(E_UNEXPECTED, tr("internal error: enmReason=%d iStatus=%d stderr='%s'"),
2862 ProcStatus.enmReason, ProcStatus.iStatus, offStdErrBuf ? pszStdErrBuf : "");
2863
2864 RTMemFree(pszStdErrBuf);
2865 }
2866 else
2867 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to launch the helper application '%s' (%Rrc)"), szExecName, vrc);
2868
2869 RTPipeClose(hPipeR);
2870 RTPipeClose(hStdErrPipe.u.hPipe);
2871
2872 return hrc;
2873}
2874
2875/**
2876 * Finds an installed extension pack.
2877 *
2878 * @returns Pointer to the extension pack if found, NULL if not. (No reference
2879 * counting problem here since the caller must be holding the lock.)
2880 * @param a_pszName The name of the extension pack.
2881 */
2882ExtPack *ExtPackManager::i_findExtPack(const char *a_pszName)
2883{
2884 size_t cchName = strlen(a_pszName);
2885
2886 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
2887 it != m->llInstalledExtPacks.end();
2888 ++it)
2889 {
2890 ExtPack::Data *pExtPackData = (*it)->m;
2891 if ( pExtPackData
2892 && pExtPackData->Desc.strName.length() == cchName
2893 && pExtPackData->Desc.strName.equalsIgnoreCase(a_pszName))
2894 return (*it);
2895 }
2896 return NULL;
2897}
2898
2899/**
2900 * Removes an installed extension pack from the internal list.
2901 *
2902 * The package is expected to exist!
2903 *
2904 * @param a_pszName The name of the extension pack.
2905 */
2906void ExtPackManager::i_removeExtPack(const char *a_pszName)
2907{
2908 size_t cchName = strlen(a_pszName);
2909
2910 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
2911 it != m->llInstalledExtPacks.end();
2912 ++it)
2913 {
2914 ExtPack::Data *pExtPackData = (*it)->m;
2915 if ( pExtPackData
2916 && pExtPackData->Desc.strName.length() == cchName
2917 && pExtPackData->Desc.strName.equalsIgnoreCase(a_pszName))
2918 {
2919 m->llInstalledExtPacks.erase(it);
2920 m->cUpdate++;
2921 return;
2922 }
2923 }
2924 AssertMsgFailed(("%s\n", a_pszName));
2925}
2926
2927#ifndef VBOX_COM_INPROC
2928
2929/**
2930 * Refreshes the specified extension pack.
2931 *
2932 * This may remove the extension pack from the list, so any non-smart pointers
2933 * to the extension pack object may become invalid.
2934 *
2935 * @returns S_OK and *a_ppExtPack on success, COM status code and error
2936 * message on failure. Note that *a_ppExtPack can be NULL.
2937 *
2938 * @param a_pszName The extension to update..
2939 * @param a_fUnusableIsError If @c true, report an unusable extension pack
2940 * as an error.
2941 * @param a_ppExtPack Where to store the pointer to the extension
2942 * pack of it is still around after the refresh.
2943 * This is optional.
2944 *
2945 * @remarks Caller holds the extension manager lock.
2946 * @remarks Only called in VBoxSVC.
2947 */
2948HRESULT ExtPackManager::i_refreshExtPack(const char *a_pszName, bool a_fUnusableIsError, ExtPack **a_ppExtPack)
2949{
2950 Assert(m->pVirtualBox != NULL); /* Only called from VBoxSVC. */
2951
2952 HRESULT hrc;
2953 ExtPack *pExtPack = i_findExtPack(a_pszName);
2954 if (pExtPack)
2955 {
2956 /*
2957 * Refresh existing object.
2958 */
2959 bool fCanDelete;
2960 hrc = pExtPack->i_refresh(&fCanDelete);
2961 if (SUCCEEDED(hrc))
2962 {
2963 if (fCanDelete)
2964 {
2965 i_removeExtPack(a_pszName);
2966 pExtPack = NULL;
2967 }
2968 }
2969 }
2970 else
2971 {
2972 /*
2973 * Do this check here, otherwise VBoxExtPackCalcDir() will fail with a strange
2974 * error.
2975 */
2976 bool fValid = VBoxExtPackIsValidName(a_pszName);
2977 if (!fValid)
2978 return setError(E_FAIL, "Invalid extension pack name specified");
2979
2980 /*
2981 * Does the dir exist? Make some special effort to deal with case
2982 * sensitivie file systems (a_pszName is case insensitive and mangled).
2983 */
2984 char szDir[RTPATH_MAX];
2985 int vrc = VBoxExtPackCalcDir(szDir, sizeof(szDir), m->strBaseDir.c_str(), a_pszName);
2986 AssertLogRelRCReturn(vrc, E_FAIL);
2987
2988 RTDIRENTRYEX Entry;
2989 RTFSOBJINFO ObjInfo;
2990 vrc = RTPathQueryInfoEx(szDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2991 bool fExists = RT_SUCCESS(vrc) && RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode);
2992 if (!fExists)
2993 {
2994 RTDIR hDir;
2995 vrc = RTDirOpen(&hDir, m->strBaseDir.c_str());
2996 if (RT_SUCCESS(vrc))
2997 {
2998 const char *pszMangledName = RTPathFilename(szDir);
2999 for (;;)
3000 {
3001 vrc = RTDirReadEx(hDir, &Entry, NULL /*pcbDirEntry*/, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
3002 if (RT_FAILURE(vrc))
3003 {
3004 AssertLogRelMsg(vrc == VERR_NO_MORE_FILES, ("%Rrc\n", vrc));
3005 break;
3006 }
3007 if ( RTFS_IS_DIRECTORY(Entry.Info.Attr.fMode)
3008 && !RTStrICmp(Entry.szName, pszMangledName))
3009 {
3010 /*
3011 * The installed extension pack has a uses different case.
3012 * Update the name and directory variables.
3013 */
3014 vrc = RTPathJoin(szDir, sizeof(szDir), m->strBaseDir.c_str(), Entry.szName); /* not really necessary */
3015 AssertLogRelRCReturnStmt(vrc, RTDirClose(hDir), E_UNEXPECTED);
3016 a_pszName = Entry.szName;
3017 fExists = true;
3018 break;
3019 }
3020 }
3021 RTDirClose(hDir);
3022 }
3023 }
3024 if (fExists)
3025 {
3026 /*
3027 * We've got something, create a new extension pack object for it.
3028 */
3029 ComObjPtr<ExtPack> ptrNewExtPack;
3030 hrc = ptrNewExtPack.createObject();
3031 if (SUCCEEDED(hrc))
3032 hrc = ptrNewExtPack->initWithDir(m->pVirtualBox, m->enmContext, a_pszName, szDir);
3033 if (SUCCEEDED(hrc))
3034 {
3035 m->llInstalledExtPacks.push_back(ptrNewExtPack);
3036 m->cUpdate++;
3037 if (ptrNewExtPack->m->fUsable)
3038 LogRel(("ExtPackManager: Found extension pack '%s'.\n", a_pszName));
3039 else
3040 LogRel(("ExtPackManager: Found bad extension pack '%s': %s\n",
3041 a_pszName, ptrNewExtPack->m->strWhyUnusable.c_str() ));
3042 pExtPack = ptrNewExtPack;
3043 }
3044 }
3045 else
3046 hrc = S_OK;
3047 }
3048
3049 /*
3050 * Report error if not usable, if that is desired.
3051 */
3052 if ( SUCCEEDED(hrc)
3053 && pExtPack
3054 && a_fUnusableIsError
3055 && !pExtPack->m->fUsable)
3056 hrc = setError(E_FAIL, "%s", pExtPack->m->strWhyUnusable.c_str());
3057
3058 if (a_ppExtPack)
3059 *a_ppExtPack = pExtPack;
3060 return hrc;
3061}
3062
3063/**
3064 * Checks if there are any running VMs.
3065 *
3066 * This is called when uninstalling or replacing an extension pack.
3067 *
3068 * @returns true / false
3069 */
3070bool ExtPackManager::i_areThereAnyRunningVMs(void) const
3071{
3072 Assert(m->pVirtualBox != NULL); /* Only called from VBoxSVC. */
3073
3074 /*
3075 * Get list of machines and their states.
3076 */
3077 com::SafeIfaceArray<IMachine> SaMachines;
3078 HRESULT hrc = m->pVirtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(SaMachines));
3079 if (SUCCEEDED(hrc))
3080 {
3081 com::SafeArray<MachineState_T> SaStates;
3082 hrc = m->pVirtualBox->GetMachineStates(ComSafeArrayAsInParam(SaMachines), ComSafeArrayAsOutParam(SaStates));
3083 if (SUCCEEDED(hrc))
3084 {
3085 /*
3086 * Scan the two parallel arrays for machines in the running state.
3087 */
3088 Assert(SaStates.size() == SaMachines.size());
3089 for (size_t i = 0; i < SaMachines.size(); ++i)
3090 if (SaMachines[i] && Global::IsOnline(SaStates[i]))
3091 return true;
3092 }
3093 }
3094 return false;
3095}
3096
3097/**
3098 * Worker for IExtPackFile::Install.
3099 *
3100 * Called on a worker thread via doInstallThreadProc.
3101 *
3102 * @returns COM status code.
3103 * @param a_pExtPackFile The extension pack file, caller checks that
3104 * it's usable.
3105 * @param a_fReplace Whether to replace any existing extpack or just
3106 * fail.
3107 * @param a_pstrDisplayInfo Host specific display information hacks.
3108 * be NULL.
3109 */
3110HRESULT ExtPackManager::i_doInstall(ExtPackFile *a_pExtPackFile, bool a_fReplace, Utf8Str const *a_pstrDisplayInfo)
3111{
3112 AssertReturn(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON, E_UNEXPECTED);
3113 RTCString const * const pStrName = &a_pExtPackFile->m->Desc.strName;
3114 RTCString const * const pStrTarball = &a_pExtPackFile->m->strExtPackFile;
3115 RTCString const * const pStrTarballDigest = &a_pExtPackFile->m->strDigest;
3116
3117 AutoCaller autoCaller(this);
3118 HRESULT hrc = autoCaller.hrc();
3119 if (SUCCEEDED(hrc))
3120 {
3121 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3122
3123 /*
3124 * Refresh the data we have on the extension pack as it
3125 * may be made stale by direct meddling or some other user.
3126 */
3127 ExtPack *pExtPack;
3128 hrc = i_refreshExtPack(pStrName->c_str(), false /*a_fUnusableIsError*/, &pExtPack);
3129 if (SUCCEEDED(hrc))
3130 {
3131 if (pExtPack && a_fReplace)
3132 {
3133 /* We must leave the lock when calling i_areThereAnyRunningVMs,
3134 which means we have to redo the refresh call afterwards. */
3135 autoLock.release();
3136 bool fRunningVMs = i_areThereAnyRunningVMs();
3137 bool fVetoingCP = pExtPack->i_areThereCloudProviderUninstallVetos();
3138 bool fUnloadedCryptoMod = m->pVirtualBox->i_unloadCryptoIfModule() == S_OK;
3139 autoLock.acquire();
3140 hrc = i_refreshExtPack(pStrName->c_str(), false /*a_fUnusableIsError*/, &pExtPack);
3141 if (fRunningVMs)
3142 {
3143 LogRel(("Upgrading extension pack '%s' failed because at least one VM is still running.", pStrName->c_str()));
3144 hrc = setError(E_FAIL, tr("Upgrading extension pack '%s' failed because at least one VM is still running"),
3145 pStrName->c_str());
3146 }
3147 else if (fVetoingCP)
3148 {
3149 LogRel(("Upgrading extension pack '%s' failed because at least one Cloud Provider is still busy.", pStrName->c_str()));
3150 hrc = setError(E_FAIL, tr("Upgrading extension pack '%s' failed because at least one Cloud Provider is still busy"),
3151 pStrName->c_str());
3152 }
3153 else if (!fUnloadedCryptoMod)
3154 {
3155 LogRel(("Upgrading extension pack '%s' failed because the cryptographic support module is still in use.", pStrName->c_str()));
3156 hrc = setError(E_FAIL, tr("Upgrading extension pack '%s' failed because the cryptographic support module is still in use"),
3157 pStrName->c_str());
3158 }
3159 else if (SUCCEEDED(hrc) && pExtPack)
3160 hrc = pExtPack->i_callUninstallHookAndClose(m->pVirtualBox, false /*a_ForcedRemoval*/);
3161 }
3162 else if (pExtPack)
3163 hrc = setError(E_FAIL,
3164 tr("Extension pack '%s' is already installed."
3165 " In case of a reinstallation, please uninstall it first"),
3166 pStrName->c_str());
3167 }
3168 if (SUCCEEDED(hrc))
3169 {
3170 /*
3171 * Run the privileged helper binary that performs the actual
3172 * installation. Then create an object for the packet (we do this
3173 * even on failure, to be on the safe side).
3174 */
3175 hrc = i_runSetUidToRootHelper(a_pstrDisplayInfo,
3176 "install",
3177 "--base-dir", m->strBaseDir.c_str(),
3178 "--cert-dir", m->strCertificatDirPath.c_str(),
3179 "--name", pStrName->c_str(),
3180 "--tarball", pStrTarball->c_str(),
3181 "--sha-256", pStrTarballDigest->c_str(),
3182 pExtPack ? "--replace" : (const char *)NULL,
3183 (const char *)NULL);
3184 if (SUCCEEDED(hrc))
3185 {
3186 hrc = i_refreshExtPack(pStrName->c_str(), true /*a_fUnusableIsError*/, &pExtPack);
3187 if (SUCCEEDED(hrc) && pExtPack)
3188 {
3189 RTERRINFOSTATIC ErrInfo;
3190 RTErrInfoInitStatic(&ErrInfo);
3191 pExtPack->i_callInstalledHook(m->pVirtualBox, &autoLock, &ErrInfo.Core);
3192 if (RT_SUCCESS(ErrInfo.Core.rc))
3193 {
3194 LogRel(("ExtPackManager: Successfully installed extension pack '%s'.\n", pStrName->c_str()));
3195 m->pVirtualBox->i_onExtPackInstalled(*pStrName);
3196 }
3197 else
3198 {
3199 LogRel(("ExtPackManager: Installed hook for '%s' failed: %Rrc - %s\n",
3200 pStrName->c_str(), ErrInfo.Core.rc, ErrInfo.Core.pszMsg));
3201
3202 /*
3203 * Uninstall the extpack if the error indicates that.
3204 */
3205 if (ErrInfo.Core.rc == VERR_EXTPACK_UNSUPPORTED_HOST_UNINSTALL)
3206 i_runSetUidToRootHelper(a_pstrDisplayInfo,
3207 "uninstall",
3208 "--base-dir", m->strBaseDir.c_str(),
3209 "--name", pStrName->c_str(),
3210 "--forced",
3211 (const char *)NULL);
3212 hrc = setErrorBoth(E_FAIL, ErrInfo.Core.rc, tr("The installation hook failed: %Rrc - %s"),
3213 ErrInfo.Core.rc, ErrInfo.Core.pszMsg);
3214 }
3215 }
3216 else if (SUCCEEDED(hrc))
3217 hrc = setError(E_FAIL, tr("Installing extension pack '%s' failed under mysterious circumstances"),
3218 pStrName->c_str());
3219 }
3220 else
3221 {
3222 ErrorInfoKeeper Eik;
3223 i_refreshExtPack(pStrName->c_str(), false /*a_fUnusableIsError*/, NULL);
3224 }
3225 }
3226
3227 /*
3228 * Do VirtualBoxReady callbacks now for any freshly installed
3229 * extension pack (old ones will not be called).
3230 */
3231 if (m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON)
3232 {
3233 autoLock.release();
3234 i_callAllVirtualBoxReadyHooks();
3235 }
3236 }
3237
3238 return hrc;
3239}
3240
3241/**
3242 * Worker for IExtPackManager::Uninstall.
3243 *
3244 * Called on a worker thread via doUninstallThreadProc.
3245 *
3246 * @returns COM status code.
3247 * @param a_pstrName The name of the extension pack to uninstall.
3248 * @param a_fForcedRemoval Whether to be skip and ignore certain bits of
3249 * the extpack feedback. To deal with misbehaving
3250 * extension pack hooks.
3251 * @param a_pstrDisplayInfo Host specific display information hacks.
3252 */
3253HRESULT ExtPackManager::i_doUninstall(Utf8Str const *a_pstrName, bool a_fForcedRemoval, Utf8Str const *a_pstrDisplayInfo)
3254{
3255 Assert(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON);
3256
3257 AutoCaller autoCaller(this);
3258 HRESULT hrc = autoCaller.hrc();
3259 if (SUCCEEDED(hrc))
3260 {
3261 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3262
3263 /*
3264 * Refresh the data we have on the extension pack as it
3265 * may be made stale by direct meddling or some other user.
3266 */
3267 ExtPack *pExtPack;
3268 hrc = i_refreshExtPack(a_pstrName->c_str(), false /*a_fUnusableIsError*/, &pExtPack);
3269 if (SUCCEEDED(hrc) && pExtPack)
3270 {
3271 /* We must leave the lock when calling i_areThereAnyRunningVMs,
3272 which means we have to redo the refresh call afterwards. */
3273 autoLock.release();
3274 bool fRunningVMs = i_areThereAnyRunningVMs();
3275 bool fVetoingCP = pExtPack->i_areThereCloudProviderUninstallVetos();
3276 bool fUnloadedCryptoMod = m->pVirtualBox->i_unloadCryptoIfModule() == S_OK;
3277 autoLock.acquire();
3278 if (a_fForcedRemoval || (!fRunningVMs && !fVetoingCP && fUnloadedCryptoMod))
3279 {
3280 hrc = i_refreshExtPack(a_pstrName->c_str(), false /*a_fUnusableIsError*/, &pExtPack);
3281 if (SUCCEEDED(hrc))
3282 {
3283 if (!pExtPack)
3284 {
3285 LogRel(("ExtPackManager: Extension pack '%s' is not installed, so nothing to uninstall.\n", a_pstrName->c_str()));
3286 hrc = S_OK; /* nothing to uninstall */
3287 }
3288 else
3289 {
3290 /*
3291 * Call the uninstall hook and unload the main dll.
3292 */
3293 hrc = pExtPack->i_callUninstallHookAndClose(m->pVirtualBox, a_fForcedRemoval);
3294 if (SUCCEEDED(hrc))
3295 {
3296 /*
3297 * Run the set-uid-to-root binary that performs the
3298 * uninstallation. Then refresh the object.
3299 *
3300 * This refresh is theorically subject to races, but it's of
3301 * the don't-do-that variety.
3302 */
3303 const char *pszForcedOpt = a_fForcedRemoval ? "--forced" : NULL;
3304 hrc = i_runSetUidToRootHelper(a_pstrDisplayInfo,
3305 "uninstall",
3306 "--base-dir", m->strBaseDir.c_str(),
3307 "--name", a_pstrName->c_str(),
3308 pszForcedOpt, /* Last as it may be NULL. */
3309 (const char *)NULL);
3310 if (SUCCEEDED(hrc))
3311 {
3312 hrc = i_refreshExtPack(a_pstrName->c_str(), false /*a_fUnusableIsError*/, &pExtPack);
3313 if (SUCCEEDED(hrc))
3314 {
3315 if (!pExtPack)
3316 {
3317 LogRel(("ExtPackManager: Successfully uninstalled extension pack '%s'.\n", a_pstrName->c_str()));
3318 m->pVirtualBox->i_onExtPackInstalled(*a_pstrName);
3319 }
3320 else
3321 hrc = setError(E_FAIL,
3322 tr("Uninstall extension pack '%s' failed under mysterious circumstances"),
3323 a_pstrName->c_str());
3324 }
3325 }
3326 else
3327 {
3328 ErrorInfoKeeper Eik;
3329 i_refreshExtPack(a_pstrName->c_str(), false /*a_fUnusableIsError*/, NULL);
3330 }
3331 }
3332 }
3333 }
3334 }
3335 else
3336 {
3337 if (fRunningVMs)
3338 {
3339 LogRel(("Uninstall extension pack '%s' failed because at least one VM is still running.", a_pstrName->c_str()));
3340 hrc = setError(E_FAIL, tr("Uninstall extension pack '%s' failed because at least one VM is still running"),
3341 a_pstrName->c_str());
3342 }
3343 else if (fVetoingCP)
3344 {
3345 LogRel(("Uninstall extension pack '%s' failed because at least one Cloud Provider is still busy.", a_pstrName->c_str()));
3346 hrc = setError(E_FAIL, tr("Uninstall extension pack '%s' failed because at least one Cloud Provider is still busy"),
3347 a_pstrName->c_str());
3348 }
3349 else if (!fUnloadedCryptoMod)
3350 {
3351 LogRel(("Uninstall extension pack '%s' failed because the cryptographic support module is still in use.", a_pstrName->c_str()));
3352 hrc = setError(E_FAIL, tr("Uninstall extension pack '%s' failed because the cryptographic support module is still in use"),
3353 a_pstrName->c_str());
3354 }
3355 else
3356 {
3357 LogRel(("Uninstall extension pack '%s' failed for an unknown reason.", a_pstrName->c_str()));
3358 hrc = setError(E_FAIL, tr("Uninstall extension pack '%s' failed for an unknown reason"),
3359 a_pstrName->c_str());
3360
3361 }
3362 }
3363 }
3364 else if (SUCCEEDED(hrc) && !pExtPack)
3365 {
3366 hrc = setError(E_FAIL, tr("Extension pack '%s' is not installed.\n"), a_pstrName->c_str());
3367 }
3368
3369 /*
3370 * Do VirtualBoxReady callbacks now for any freshly installed
3371 * extension pack (old ones will not be called).
3372 */
3373 if (m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON)
3374 {
3375 autoLock.release();
3376 i_callAllVirtualBoxReadyHooks();
3377 }
3378 }
3379
3380 return hrc;
3381}
3382
3383
3384/**
3385 * Calls the pfnVirtualBoxReady hook for all working extension packs.
3386 *
3387 * @remarks The caller must not hold any locks.
3388 */
3389void ExtPackManager::i_callAllVirtualBoxReadyHooks(void)
3390{
3391 AutoCaller autoCaller(this);
3392 HRESULT hrc = autoCaller.hrc();
3393 if (FAILED(hrc))
3394 return;
3395 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3396 ComPtr<ExtPackManager> ptrSelfRef = this;
3397
3398 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
3399 it != m->llInstalledExtPacks.end();
3400 /* advancing below */)
3401 {
3402 if ((*it)->i_callVirtualBoxReadyHook(m->pVirtualBox, &autoLock))
3403 it = m->llInstalledExtPacks.begin();
3404 else
3405 ++it;
3406 }
3407}
3408
3409
3410/**
3411 * Queries objects of type @a aObjUuid from all the extension packs.
3412 *
3413 * @returns COM status code.
3414 * @param aObjUuid The UUID of the kind of objects we're querying.
3415 * @param aObjects Where to return the objects.
3416 * @param a_pstrExtPackNames Where to return the corresponding extpack names (may be NULL).
3417 *
3418 * @remarks The caller must not hold any locks.
3419 */
3420HRESULT ExtPackManager::i_queryObjects(const com::Utf8Str &aObjUuid, std::vector<ComPtr<IUnknown> > &aObjects, std::vector<com::Utf8Str> *a_pstrExtPackNames)
3421{
3422 aObjects.clear();
3423 if (a_pstrExtPackNames)
3424 a_pstrExtPackNames->clear();
3425
3426 AutoCaller autoCaller(this);
3427 HRESULT hrc = autoCaller.hrc();
3428 if (SUCCEEDED(hrc))
3429 {
3430 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3431 ComPtr<ExtPackManager> ptrSelfRef = this;
3432
3433 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
3434 it != m->llInstalledExtPacks.end();
3435 ++it)
3436 {
3437 ComPtr<IUnknown> ptrIf;
3438 HRESULT hrc2 = (*it)->queryObject(aObjUuid, ptrIf);
3439 if (SUCCEEDED(hrc2))
3440 {
3441 aObjects.push_back(ptrIf);
3442 if (a_pstrExtPackNames)
3443 a_pstrExtPackNames->push_back((*it)->m->Desc.strName);
3444 }
3445 else if (hrc2 != E_NOINTERFACE)
3446 hrc = hrc2;
3447 }
3448
3449 if (aObjects.size() > 0)
3450 hrc = S_OK;
3451 }
3452 return hrc;
3453}
3454
3455#endif /* !VBOX_COM_INPROC */
3456
3457#ifdef VBOX_COM_INPROC
3458/**
3459 * Calls the pfnConsoleReady hook for all working extension packs.
3460 *
3461 * @param a_pConsole The console interface.
3462 * @remarks The caller must not hold any locks.
3463 */
3464void ExtPackManager::i_callAllConsoleReadyHooks(IConsole *a_pConsole)
3465{
3466 AutoCaller autoCaller(this);
3467 HRESULT hrc = autoCaller.hrc();
3468 if (FAILED(hrc))
3469 return;
3470 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3471 ComPtr<ExtPackManager> ptrSelfRef = this;
3472
3473 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
3474 it != m->llInstalledExtPacks.end();
3475 /* advancing below */)
3476 {
3477 if ((*it)->i_callConsoleReadyHook(a_pConsole, &autoLock))
3478 it = m->llInstalledExtPacks.begin();
3479 else
3480 ++it;
3481 }
3482}
3483#endif
3484
3485#ifndef VBOX_COM_INPROC
3486/**
3487 * Calls the pfnVMCreated hook for all working extension packs.
3488 *
3489 * @param a_pMachine The machine interface of the new VM.
3490 */
3491void ExtPackManager::i_callAllVmCreatedHooks(IMachine *a_pMachine)
3492{
3493 AutoCaller autoCaller(this);
3494 HRESULT hrc = autoCaller.hrc();
3495 if (FAILED(hrc))
3496 return;
3497 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3498 ComPtr<ExtPackManager> ptrSelfRef = this; /* paranoia */
3499 ExtPackList llExtPacks = m->llInstalledExtPacks;
3500
3501 for (ExtPackList::iterator it = llExtPacks.begin(); it != llExtPacks.end(); ++it)
3502 (*it)->i_callVmCreatedHook(m->pVirtualBox, a_pMachine, &autoLock);
3503}
3504#endif
3505
3506#ifdef VBOX_COM_INPROC
3507
3508/**
3509 * Calls the pfnVMConfigureVMM hook for all working extension packs.
3510 *
3511 * @returns VBox status code. Stops on the first failure, expecting the caller
3512 * to signal this to the caller of the CFGM constructor.
3513 * @param a_pConsole The console interface for the VM.
3514 * @param a_pVM The VM handle.
3515 * @param a_pVMM The VMM function table.
3516 */
3517int ExtPackManager::i_callAllVmConfigureVmmHooks(IConsole *a_pConsole, PVM a_pVM, PCVMMR3VTABLE a_pVMM)
3518{
3519 AutoCaller autoCaller(this);
3520 HRESULT hrc = autoCaller.hrc();
3521 if (FAILED(hrc))
3522 return Global::vboxStatusCodeFromCOM(hrc);
3523 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3524 ComPtr<ExtPackManager> ptrSelfRef = this; /* paranoia */
3525 ExtPackList llExtPacks = m->llInstalledExtPacks;
3526
3527 for (ExtPackList::iterator it = llExtPacks.begin(); it != llExtPacks.end(); ++it)
3528 {
3529 int vrc;
3530 (*it)->i_callVmConfigureVmmHook(a_pConsole, a_pVM, a_pVMM, &autoLock, &vrc);
3531 if (RT_FAILURE(vrc))
3532 return vrc;
3533 }
3534
3535 return VINF_SUCCESS;
3536}
3537
3538/**
3539 * Calls the pfnVMPowerOn hook for all working extension packs.
3540 *
3541 * @returns VBox status code. Stops on the first failure, expecting the caller
3542 * to not power on the VM.
3543 * @param a_pConsole The console interface for the VM.
3544 * @param a_pVM The VM handle.
3545 * @param a_pVMM The VMM function table.
3546 */
3547int ExtPackManager::i_callAllVmPowerOnHooks(IConsole *a_pConsole, PVM a_pVM, PCVMMR3VTABLE a_pVMM)
3548{
3549 AutoCaller autoCaller(this);
3550 HRESULT hrc = autoCaller.hrc();
3551 if (FAILED(hrc))
3552 return Global::vboxStatusCodeFromCOM(hrc);
3553 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3554 ComPtr<ExtPackManager> ptrSelfRef = this; /* paranoia */
3555 ExtPackList llExtPacks = m->llInstalledExtPacks;
3556
3557 for (ExtPackList::iterator it = llExtPacks.begin(); it != llExtPacks.end(); ++it)
3558 {
3559 int vrc;
3560 (*it)->i_callVmPowerOnHook(a_pConsole, a_pVM, a_pVMM, &autoLock, &vrc);
3561 if (RT_FAILURE(vrc))
3562 return vrc;
3563 }
3564
3565 return VINF_SUCCESS;
3566}
3567
3568/**
3569 * Calls the pfnVMPowerOff hook for all working extension packs.
3570 *
3571 * @param a_pConsole The console interface for the VM.
3572 * @param a_pVM The VM handle. Can be NULL.
3573 * @param a_pVMM The VMM function table.
3574 */
3575void ExtPackManager::i_callAllVmPowerOffHooks(IConsole *a_pConsole, PVM a_pVM, PCVMMR3VTABLE a_pVMM)
3576{
3577 AutoCaller autoCaller(this);
3578 HRESULT hrc = autoCaller.hrc();
3579 if (FAILED(hrc))
3580 return;
3581 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3582 ComPtr<ExtPackManager> ptrSelfRef = this; /* paranoia */
3583 ExtPackList llExtPacks = m->llInstalledExtPacks;
3584
3585 for (ExtPackList::iterator it = llExtPacks.begin(); it != llExtPacks.end(); ++it)
3586 (*it)->i_callVmPowerOffHook(a_pConsole, a_pVM, a_pVMM, &autoLock);
3587}
3588
3589#endif /* VBOX_COM_INPROC */
3590
3591/**
3592 * Checks that the specified extension pack contains a VRDE module and that it
3593 * is shipshape.
3594 *
3595 * @returns S_OK if ok, appropriate failure status code with details.
3596 * @param a_pstrExtPack The name of the extension pack.
3597 */
3598HRESULT ExtPackManager::i_checkVrdeExtPack(Utf8Str const *a_pstrExtPack)
3599{
3600 AutoCaller autoCaller(this);
3601 HRESULT hrc = autoCaller.hrc();
3602 if (SUCCEEDED(hrc))
3603 {
3604 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3605
3606 ExtPack *pExtPack = i_findExtPack(a_pstrExtPack->c_str());
3607 if (pExtPack)
3608 hrc = pExtPack->i_checkVrde();
3609 else
3610 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("No extension pack by the name '%s' was found"), a_pstrExtPack->c_str());
3611 }
3612
3613 return hrc;
3614}
3615
3616/**
3617 * Gets the full path to the VRDE library of the specified extension pack.
3618 *
3619 * This will do extacly the same as checkVrdeExtPack and then resolve the
3620 * library path.
3621 *
3622 * @returns VINF_SUCCESS if a path is returned, VBox error status and message
3623 * return if not.
3624 * @param a_pstrExtPack The extension pack.
3625 * @param a_pstrVrdeLibrary Where to return the path.
3626 */
3627int ExtPackManager::i_getVrdeLibraryPathForExtPack(Utf8Str const *a_pstrExtPack, Utf8Str *a_pstrVrdeLibrary)
3628{
3629 AutoCaller autoCaller(this);
3630 HRESULT hrc = autoCaller.hrc();
3631 if (SUCCEEDED(hrc))
3632 {
3633 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3634
3635 ExtPack *pExtPack = i_findExtPack(a_pstrExtPack->c_str());
3636 if (pExtPack)
3637 hrc = pExtPack->i_getVrdpLibraryName(a_pstrVrdeLibrary);
3638 else
3639 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("No extension pack by the name '%s' was found"),
3640 a_pstrExtPack->c_str());
3641 }
3642
3643 return Global::vboxStatusCodeFromCOM(hrc);
3644}
3645
3646/**
3647 * Checks that the specified extension pack contains a cryptographic module and that it
3648 * is shipshape.
3649 *
3650 * @returns S_OK if ok, appropriate failure status code with details.
3651 * @param a_pstrExtPack The name of the extension pack.
3652 */
3653HRESULT ExtPackManager::i_checkCryptoExtPack(Utf8Str const *a_pstrExtPack)
3654{
3655 AutoCaller autoCaller(this);
3656 HRESULT hrc = autoCaller.hrc();
3657 if (SUCCEEDED(hrc))
3658 {
3659 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3660
3661 ExtPack *pExtPack = i_findExtPack(a_pstrExtPack->c_str());
3662 if (pExtPack)
3663 hrc = pExtPack->i_checkCrypto();
3664 else
3665 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("No extension pack by the name '%s' was found"), a_pstrExtPack->c_str());
3666 }
3667
3668 return hrc;
3669}
3670
3671/**
3672 * Gets the full path to the cryptographic library of the specified extension pack.
3673 *
3674 * This will do extacly the same as checkCryptoExtPack and then resolve the
3675 * library path.
3676 *
3677 * @returns VINF_SUCCESS if a path is returned, VBox error status and message
3678 * return if not.
3679 * @param a_pstrExtPack The extension pack.
3680 * @param a_pstrCryptoLibrary Where to return the path.
3681 */
3682int ExtPackManager::i_getCryptoLibraryPathForExtPack(Utf8Str const *a_pstrExtPack, Utf8Str *a_pstrCryptoLibrary)
3683{
3684 AutoCaller autoCaller(this);
3685 HRESULT hrc = autoCaller.hrc();
3686 if (SUCCEEDED(hrc))
3687 {
3688 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3689
3690 ExtPack *pExtPack = i_findExtPack(a_pstrExtPack->c_str());
3691 if (pExtPack)
3692 hrc = pExtPack->i_getCryptoLibraryName(a_pstrCryptoLibrary);
3693 else
3694 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("No extension pack by the name '%s' was found"),
3695 a_pstrExtPack->c_str());
3696 }
3697
3698 return Global::vboxStatusCodeFromCOM(hrc);
3699}
3700
3701
3702/**
3703 * Gets the full path to the specified library of the specified extension pack.
3704 *
3705 * @returns S_OK if a path is returned, COM error status and message return if
3706 * not.
3707 * @param a_pszModuleName The library.
3708 * @param a_pszExtPack The extension pack.
3709 * @param a_pstrLibrary Where to return the path.
3710 */
3711HRESULT ExtPackManager::i_getLibraryPathForExtPack(const char *a_pszModuleName, const char *a_pszExtPack, Utf8Str *a_pstrLibrary)
3712{
3713 AutoCaller autoCaller(this);
3714 HRESULT hrc = autoCaller.hrc();
3715 if (SUCCEEDED(hrc))
3716 {
3717 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3718
3719 ExtPack *pExtPack = i_findExtPack(a_pszExtPack);
3720 if (pExtPack)
3721 hrc = pExtPack->i_getLibraryName(a_pszModuleName, a_pstrLibrary);
3722 else
3723 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("No extension pack by the name '%s' was found"), a_pszExtPack);
3724 }
3725
3726 return hrc;
3727}
3728
3729/**
3730 * Gets the name of the default VRDE extension pack.
3731 *
3732 * @returns S_OK or some COM error status on red tape failure.
3733 * @param a_pstrExtPack Where to return the extension pack name. Returns
3734 * empty if no extension pack wishes to be the default
3735 * VRDP provider.
3736 */
3737HRESULT ExtPackManager::i_getDefaultVrdeExtPack(Utf8Str *a_pstrExtPack)
3738{
3739 a_pstrExtPack->setNull();
3740
3741 AutoCaller autoCaller(this);
3742 HRESULT hrc = autoCaller.hrc();
3743 if (SUCCEEDED(hrc))
3744 {
3745 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3746
3747 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
3748 it != m->llInstalledExtPacks.end();
3749 ++it)
3750 {
3751 if ((*it)->i_wantsToBeDefaultVrde())
3752 {
3753 *a_pstrExtPack = (*it)->m->Desc.strName;
3754 break;
3755 }
3756 }
3757 }
3758 return hrc;
3759}
3760
3761/**
3762 * Gets the name of the default cryptographic extension pack.
3763 *
3764 * @returns S_OK or some COM error status on red tape failure.
3765 * @param a_pstrExtPack Where to return the extension pack name. Returns
3766 * empty if no extension pack wishes to be the default
3767 * VRDP provider.
3768 */
3769HRESULT ExtPackManager::i_getDefaultCryptoExtPack(Utf8Str *a_pstrExtPack)
3770{
3771 a_pstrExtPack->setNull();
3772
3773 AutoCaller autoCaller(this);
3774 HRESULT hrc = autoCaller.hrc();
3775 if (SUCCEEDED(hrc))
3776 {
3777 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3778
3779 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
3780 it != m->llInstalledExtPacks.end();
3781 ++it)
3782 {
3783 if ((*it)->i_wantsToBeDefaultCrypto())
3784 {
3785 *a_pstrExtPack = (*it)->m->Desc.strName;
3786 break;
3787 }
3788 }
3789 }
3790 return hrc;
3791}
3792
3793/**
3794 * Checks if an extension pack is (present and) usable.
3795 *
3796 * @returns @c true if it is, otherwise @c false.
3797 * @param a_pszExtPack The name of the extension pack.
3798 */
3799bool ExtPackManager::i_isExtPackUsable(const char *a_pszExtPack)
3800{
3801 AutoCaller autoCaller(this);
3802 HRESULT hrc = autoCaller.hrc();
3803 if (FAILED(hrc))
3804 return false;
3805 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3806
3807 ExtPack *pExtPack = i_findExtPack(a_pszExtPack);
3808 return pExtPack != NULL
3809 && pExtPack->m->fUsable;
3810}
3811
3812/**
3813 * Dumps all extension packs to the release log.
3814 */
3815void ExtPackManager::i_dumpAllToReleaseLog(void)
3816{
3817 AutoCaller autoCaller(this);
3818 HRESULT hrc = autoCaller.hrc();
3819 if (FAILED(hrc))
3820 return;
3821 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3822
3823 LogRel(("Installed Extension Packs:\n"));
3824 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
3825 it != m->llInstalledExtPacks.end();
3826 ++it)
3827 {
3828 ExtPack::Data *pExtPackData = (*it)->m;
3829 if (pExtPackData)
3830 {
3831 if (pExtPackData->fUsable)
3832 LogRel((" %s (Version: %s r%u%s%s; VRDE Module: %s; Crypto Module: %s)\n",
3833 pExtPackData->Desc.strName.c_str(),
3834 pExtPackData->Desc.strVersion.c_str(),
3835 pExtPackData->Desc.uRevision,
3836 pExtPackData->Desc.strEdition.isEmpty() ? "" : " ",
3837 pExtPackData->Desc.strEdition.c_str(),
3838 pExtPackData->Desc.strVrdeModule.c_str(),
3839 pExtPackData->Desc.strCryptoModule.c_str() ));
3840 else
3841 LogRel((" %s (Version: %s r%u%s%s; VRDE Module: %s; Crypto Module: %s unusable because of '%s')\n",
3842 pExtPackData->Desc.strName.c_str(),
3843 pExtPackData->Desc.strVersion.c_str(),
3844 pExtPackData->Desc.uRevision,
3845 pExtPackData->Desc.strEdition.isEmpty() ? "" : " ",
3846 pExtPackData->Desc.strEdition.c_str(),
3847 pExtPackData->Desc.strVrdeModule.c_str(),
3848 pExtPackData->Desc.strCryptoModule.c_str(),
3849 pExtPackData->strWhyUnusable.c_str() ));
3850 }
3851 else
3852 LogRel((" pExtPackData is NULL\n"));
3853 }
3854
3855 if (!m->llInstalledExtPacks.size())
3856 LogRel((" None installed!\n"));
3857}
3858
3859/**
3860 * Gets the update counter (reflecting extpack list updates).
3861 */
3862uint64_t ExtPackManager::i_getUpdateCounter(void)
3863{
3864 AutoCaller autoCaller(this);
3865 HRESULT hrc = autoCaller.hrc();
3866 if (FAILED(hrc))
3867 return 0;
3868 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3869 return m->cUpdate;
3870}
3871
3872/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use