VirtualBox

source: vbox/trunk/src/VBox/Main/ExtPackManagerImpl.cpp@ 35171

最後變更 在這個檔案從35171是 35152,由 vboxsync 提交於 14 年 前

*: added fFlags parameter to SUPR3HardenedLdrLoadAppPriv(), SUPR3HardenedLdrLoad() and RTLdrLoadEx(). VBoxSVC: slurp in VBoxVMM because it is required by the extension packs

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

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