VirtualBox

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

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

Main/FE/Qt: don't enable USB 2.0 support for new VMs if the proper ExtPack is not installed

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