VirtualBox

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

Last change on this file since 34079 was 34074, checked in by vboxsync, 14 years ago

build fix

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