VirtualBox

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

Last change on this file since 91406 was 91390, checked in by vboxsync, 3 years ago

Main/NLS: TRCOMPONENT -> PTRCOMPONENT w/ some added type safety. unregisterTranslation should mostly ignore NULL. Docs. bugref:1909

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