VirtualBox

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

Last change on this file since 95294 was 95115, checked in by vboxsync, 3 years ago

Main/ExtPackManagerImpl: Don't make an extra copy of the llInstalledExtPacks, use std::copy to do the copying instead (c++17).

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