VirtualBox

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

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

scm --update-copyright-year

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