VirtualBox

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

Last change on this file since 106061 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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

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