VirtualBox

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

Last change on this file since 107513 was 107513, checked in by vboxsync, 4 months ago

src/VBox/Main/src-all/ExtPackManagerImpl.cpp: Fixed warning found by Parfait (uninitialized attributes). jiraref:VBP-1424

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

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