VirtualBox

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

Last change on this file since 94134 was 93449, checked in by vboxsync, 3 years ago

Main,ExtPacks: Don't like with the VMM anymore, use the function table. bugref:10074

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