VirtualBox

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

Last change on this file since 73768 was 73716, checked in by vboxsync, 6 years ago

Main/CloudProviderManager+CloudProvider+CloudProfile: Introduce CloudProfile as separate interface, and do a big cleanup. Adding synchronization and incomplete support for moving to an extension pack. Updated VBoxManage to list providers and touched up the GUI code slightly to deal with the changed interfaces.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use