VirtualBox

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

Last change on this file since 34141 was 34086, checked in by vboxsync, 14 years ago

Main: Pass more IVirtualBox pointers to the extension pack. VRDE registration hook, VirtualBoxReady hook.

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