VirtualBox

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

Last change on this file since 54053 was 52596, checked in by vboxsync, 10 years ago

Storage/VD + Main/ExtPackManager+VirtualBox+SystemProperties: restructure previous change to fit better into the extpack design (using existing hooks) by using two new extpack helper functions

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