VirtualBox

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

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

IExtPackBase: Added two attributes: license and showLicense.

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