VirtualBox

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

Last change on this file since 47148 was 46823, checked in by vboxsync, 12 years ago

Main: fix breakage in previous client/server separation cleanup

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

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