VirtualBox

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

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

Main: bugref:1909: Prepared the API translation engine to using in ExtPacks and VBoxManage. Added using API translation engine in ExtPacks. Allowed VBox compilation with NLS enabled and GUI disabled. Allowed ExtPacks only compilation with NLS translation 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 91312 2021-09-20 11:06:57Z 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, tr("%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, tr("%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->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