VirtualBox

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

Last change on this file since 91322 was 91322, checked in by vboxsync, 4 years ago

Main: bugref:1909: Fixed SEGFAULT when extpack plugin is not loaded but NLS is enabled

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

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