VirtualBox

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

Last change on this file since 34686 was 34579, checked in by vboxsync, 14 years ago

Completed the extension pack renaming. Some bugfixes.

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