VirtualBox

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

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

Main,VBoxManage,FE/Qt: Implemented IExtPackFile and dropped IExtPackManager::Install.

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