VirtualBox

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

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

fixed extpack install failure on windows.

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

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