VirtualBox

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

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

Main/ExtPack: Fix version major/minor order, and add long forgotten placeholder field initialization, putting the end marker value correctly.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use