VirtualBox

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

Last change on this file since 80862 was 80569, checked in by vboxsync, 5 years ago

Main: bugref:9341: Added VM autostart during boot support for windows host

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

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