VirtualBox

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

Last change on this file since 51498 was 50914, checked in by vboxsync, 11 years ago

6813 src-all/ExtPackManagerImpl.cpp

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