VirtualBox

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

Last change on this file since 35413 was 35413, checked in by vboxsync, 14 years ago

ExtPackManager: typo

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