VirtualBox

source: vbox/trunk/src/VBox/Main/src-all/ExtPackManagerImpl.cpp@ 49498

Last change on this file since 49498 was 49130, checked in by vboxsync, 11 years ago

Main/ExtPackManagerImpl: internal method to get the full path of a module.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 109.3 KB
Line 
1/* $Id: ExtPackManagerImpl.cpp 49130 2013-10-16 11:57:15Z vboxsync $ */
2/** @file
3 * VirtualBox Main - interface for Extension Packs, VBoxSVC & VBoxC.
4 */
5
6/*
7 * Copyright (C) 2010-2013 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/manifest.h>
32#include <iprt/param.h>
33#include <iprt/path.h>
34#include <iprt/pipe.h>
35#include <iprt/process.h>
36#include <iprt/string.h>
37
38#include <VBox/com/array.h>
39#include <VBox/com/ErrorInfo.h>
40#include <VBox/err.h>
41#include <VBox/log.h>
42#include <VBox/sup.h>
43#include <VBox/version.h>
44#include "AutoCaller.h"
45#include "Global.h"
46#include "ProgressImpl.h"
47#if defined(VBOX_COM_INPROC)
48# include "ConsoleImpl.h"
49#else
50# include "VirtualBoxImpl.h"
51#endif
52
53
54/*******************************************************************************
55* Defined Constants And Macros *
56*******************************************************************************/
57/** @name VBOX_EXTPACK_HELPER_NAME
58 * The name of the utility application we employ to install and uninstall the
59 * extension packs. This is a set-uid-to-root binary on unixy platforms, which
60 * is why it has to be a separate application.
61 */
62#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
63# define VBOX_EXTPACK_HELPER_NAME "VBoxExtPackHelperApp.exe"
64#else
65# define VBOX_EXTPACK_HELPER_NAME "VBoxExtPackHelperApp"
66#endif
67
68
69/*******************************************************************************
70* Structures and Typedefs *
71*******************************************************************************/
72struct ExtPackBaseData
73{
74public:
75 /** The extension pack descriptor (loaded from the XML, mostly). */
76 VBOXEXTPACKDESC Desc;
77 /** The file system object info of the XML file.
78 * This is for detecting changes and save time in refresh(). */
79 RTFSOBJINFO ObjInfoDesc;
80 /** Whether it's usable or not. */
81 bool fUsable;
82 /** Why it is unusable. */
83 Utf8Str strWhyUnusable;
84};
85
86#if !defined(VBOX_COM_INPROC)
87/**
88 * Private extension pack data.
89 */
90struct ExtPackFile::Data : public ExtPackBaseData
91{
92public:
93 /** The path to the tarball. */
94 Utf8Str strExtPackFile;
95 /** The SHA-256 hash of the file (as string). */
96 Utf8Str strDigest;
97 /** The file handle of the extension pack file. */
98 RTFILE hExtPackFile;
99 /** Our manifest for the tarball. */
100 RTMANIFEST hOurManifest;
101 /** Pointer to the extension pack manager. */
102 ComObjPtr<ExtPackManager> ptrExtPackMgr;
103 /** Pointer to the VirtualBox object so we can create a progress object. */
104 VirtualBox *pVirtualBox;
105
106 RTMEMEF_NEW_AND_DELETE_OPERATORS();
107};
108#endif
109
110/**
111 * Private extension pack data.
112 */
113struct ExtPack::Data : public ExtPackBaseData
114{
115public:
116 /** Where the extension pack is located. */
117 Utf8Str strExtPackPath;
118 /** The file system object info of the extension pack directory.
119 * This is for detecting changes and save time in refresh(). */
120 RTFSOBJINFO ObjInfoExtPack;
121 /** The full path to the main module. */
122 Utf8Str strMainModPath;
123 /** The file system object info of the main module.
124 * This is used to determin whether to bother try reload it. */
125 RTFSOBJINFO ObjInfoMainMod;
126 /** The module handle of the main extension pack module. */
127 RTLDRMOD hMainMod;
128
129 /** The helper callbacks for the extension pack. */
130 VBOXEXTPACKHLP Hlp;
131 /** Pointer back to the extension pack object (for Hlp methods). */
132 ExtPack *pThis;
133 /** The extension pack registration structure. */
134 PCVBOXEXTPACKREG pReg;
135 /** The current context. */
136 VBOXEXTPACKCTX enmContext;
137 /** Set if we've made the pfnVirtualBoxReady or pfnConsoleReady call. */
138 bool fMadeReadyCall;
139
140 RTMEMEF_NEW_AND_DELETE_OPERATORS();
141};
142
143/** List of extension packs. */
144typedef std::list< ComObjPtr<ExtPack> > ExtPackList;
145
146/**
147 * Private extension pack manager data.
148 */
149struct ExtPackManager::Data
150{
151 /** The directory where the extension packs are installed. */
152 Utf8Str strBaseDir;
153 /** The directory where the certificates this installation recognizes are
154 * stored. */
155 Utf8Str strCertificatDirPath;
156 /** The list of installed extension packs. */
157 ExtPackList llInstalledExtPacks;
158#if !defined(VBOX_COM_INPROC)
159 /** Pointer to the VirtualBox object, our parent. */
160 VirtualBox *pVirtualBox;
161#endif
162 /** The current context. */
163 VBOXEXTPACKCTX enmContext;
164#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_DARWIN)
165 /** File handle for the VBoxVMM libary which we slurp because ExtPacks depend on it. */
166 RTLDRMOD hVBoxVMM;
167#endif
168
169 RTMEMEF_NEW_AND_DELETE_OPERATORS();
170};
171
172#if !defined(VBOX_COM_INPROC)
173/**
174 * Extension pack installation job.
175 */
176typedef struct EXTPACKINSTALLJOB
177{
178 /** Smart pointer to the extension pack file. */
179 ComPtr<ExtPackFile> ptrExtPackFile;
180 /** The replace argument. */
181 bool fReplace;
182 /** The display info argument. */
183 Utf8Str strDisplayInfo;
184 /** Smart pointer to the extension manager. */
185 ComPtr<ExtPackManager> ptrExtPackMgr;
186 /** Smart pointer to the progress object for this job. */
187 ComObjPtr<Progress> ptrProgress;
188} EXTPACKINSTALLJOB;
189/** Pointer to an extension pack installation job. */
190typedef EXTPACKINSTALLJOB *PEXTPACKINSTALLJOB;
191
192/**
193 * Extension pack uninstallation job.
194 */
195typedef struct EXTPACKUNINSTALLJOB
196{
197 /** Smart pointer to the extension manager. */
198 ComPtr<ExtPackManager> ptrExtPackMgr;
199 /** The name of the extension pack. */
200 Utf8Str strName;
201 /** The replace argument. */
202 bool fForcedRemoval;
203 /** The display info argument. */
204 Utf8Str strDisplayInfo;
205 /** Smart pointer to the progress object for this job. */
206 ComObjPtr<Progress> ptrProgress;
207} EXTPACKUNINSTALLJOB;
208/** Pointer to an extension pack uninstallation job. */
209typedef EXTPACKUNINSTALLJOB *PEXTPACKUNINSTALLJOB;
210
211
212DEFINE_EMPTY_CTOR_DTOR(ExtPackFile)
213
214/**
215 * Called by ComObjPtr::createObject when creating the object.
216 *
217 * Just initialize the basic object state, do the rest in initWithDir().
218 *
219 * @returns S_OK.
220 */
221HRESULT ExtPackFile::FinalConstruct()
222{
223 m = NULL;
224 return BaseFinalConstruct();
225}
226
227/**
228 * Initializes the extension pack by reading its file.
229 *
230 * @returns COM status code.
231 * @param a_pszFile The path to the extension pack file.
232 * @param a_pszDigest The SHA-256 digest of the file. Or an empty string.
233 * @param a_pExtPackMgr Pointer to the extension pack manager.
234 * @param a_pVirtualBox Pointer to the VirtualBox object.
235 */
236HRESULT ExtPackFile::initWithFile(const char *a_pszFile, const char *a_pszDigest, ExtPackManager *a_pExtPackMgr, VirtualBox *a_pVirtualBox)
237{
238 AutoInitSpan autoInitSpan(this);
239 AssertReturn(autoInitSpan.isOk(), E_FAIL);
240
241 /*
242 * Allocate + initialize our private data.
243 */
244 m = new ExtPackFile::Data;
245 VBoxExtPackInitDesc(&m->Desc);
246 RT_ZERO(m->ObjInfoDesc);
247 m->fUsable = false;
248 m->strWhyUnusable = tr("ExtPack::init failed");
249 m->strExtPackFile = a_pszFile;
250 m->strDigest = a_pszDigest;
251 m->hExtPackFile = NIL_RTFILE;
252 m->hOurManifest = NIL_RTMANIFEST;
253 m->ptrExtPackMgr = a_pExtPackMgr;
254 m->pVirtualBox = a_pVirtualBox;
255
256 RTCString *pstrTarName = VBoxExtPackExtractNameFromTarballPath(a_pszFile);
257 if (pstrTarName)
258 {
259 m->Desc.strName = *pstrTarName;
260 delete pstrTarName;
261 pstrTarName = NULL;
262 }
263
264 autoInitSpan.setSucceeded();
265
266 /*
267 * Try open the extension pack and check that it is a regular file.
268 */
269 int vrc = RTFileOpen(&m->hExtPackFile, a_pszFile,
270 RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN);
271 if (RT_FAILURE(vrc))
272 {
273 if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
274 return initFailed(tr("'%s' file not found"), a_pszFile);
275 return initFailed(tr("RTFileOpen('%s',,) failed with %Rrc"), a_pszFile, vrc);
276 }
277
278 RTFSOBJINFO ObjInfo;
279 vrc = RTFileQueryInfo(m->hExtPackFile, &ObjInfo, RTFSOBJATTRADD_UNIX);
280 if (RT_FAILURE(vrc))
281 return initFailed(tr("RTFileQueryInfo failed with %Rrc on '%s'"), vrc, a_pszFile);
282 if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
283 return initFailed(tr("Not a regular file: %s"), a_pszFile);
284
285 /*
286 * Validate the tarball and extract the XML file.
287 */
288 char szError[8192];
289 RTVFSFILE hXmlFile;
290 vrc = VBoxExtPackValidateTarball(m->hExtPackFile, NULL /*pszExtPackName*/, a_pszFile, a_pszDigest,
291 szError, sizeof(szError), &m->hOurManifest, &hXmlFile, &m->strDigest);
292 if (RT_FAILURE(vrc))
293 return initFailed(tr("%s"), szError);
294
295 /*
296 * Parse the XML.
297 */
298 RTCString strSavedName(m->Desc.strName);
299 RTCString *pStrLoadErr = VBoxExtPackLoadDescFromVfsFile(hXmlFile, &m->Desc, &m->ObjInfoDesc);
300 RTVfsFileRelease(hXmlFile);
301 if (pStrLoadErr != NULL)
302 {
303 m->strWhyUnusable.printf(tr("Failed to the xml file: %s"), pStrLoadErr->c_str());
304 m->Desc.strName = strSavedName;
305 delete pStrLoadErr;
306 return S_OK;
307 }
308
309 /*
310 * Match the tarball name with the name from the XML.
311 */
312 /** @todo drop this restriction after the old install interface is
313 * dropped. */
314 if (!strSavedName.equalsIgnoreCase(m->Desc.strName))
315 return initFailed(tr("Extension pack name mismatch between the downloaded file and the XML inside it (xml='%s' file='%s')"),
316 m->Desc.strName.c_str(), strSavedName.c_str());
317
318 m->fUsable = true;
319 m->strWhyUnusable.setNull();
320 return S_OK;
321}
322
323/**
324 * Protected helper that formats the strWhyUnusable value.
325 *
326 * @returns S_OK
327 * @param a_pszWhyFmt Why it failed, format string.
328 * @param ... The format arguments.
329 */
330HRESULT ExtPackFile::initFailed(const char *a_pszWhyFmt, ...)
331{
332 va_list va;
333 va_start(va, a_pszWhyFmt);
334 m->strWhyUnusable.printfV(a_pszWhyFmt, va);
335 va_end(va);
336 return S_OK;
337}
338
339/**
340 * COM cruft.
341 */
342void ExtPackFile::FinalRelease()
343{
344 uninit();
345 BaseFinalRelease();
346}
347
348/**
349 * Do the actual cleanup.
350 */
351void ExtPackFile::uninit()
352{
353 /* Enclose the state transition Ready->InUninit->NotReady */
354 AutoUninitSpan autoUninitSpan(this);
355 if (!autoUninitSpan.uninitDone() && m != NULL)
356 {
357 VBoxExtPackFreeDesc(&m->Desc);
358 RTFileClose(m->hExtPackFile);
359 m->hExtPackFile = NIL_RTFILE;
360 RTManifestRelease(m->hOurManifest);
361 m->hOurManifest = NIL_RTMANIFEST;
362
363 delete m;
364 m = NULL;
365 }
366}
367
368STDMETHODIMP ExtPackFile::COMGETTER(Name)(BSTR *a_pbstrName)
369{
370 CheckComArgOutPointerValid(a_pbstrName);
371
372 AutoCaller autoCaller(this);
373 HRESULT hrc = autoCaller.rc();
374 if (SUCCEEDED(hrc))
375 {
376 Bstr str(m->Desc.strName);
377 str.cloneTo(a_pbstrName);
378 }
379 return hrc;
380}
381
382STDMETHODIMP ExtPackFile::COMGETTER(Description)(BSTR *a_pbstrDescription)
383{
384 CheckComArgOutPointerValid(a_pbstrDescription);
385
386 AutoCaller autoCaller(this);
387 HRESULT hrc = autoCaller.rc();
388 if (SUCCEEDED(hrc))
389 {
390 Bstr str(m->Desc.strDescription);
391 str.cloneTo(a_pbstrDescription);
392 }
393 return hrc;
394}
395
396STDMETHODIMP ExtPackFile::COMGETTER(Version)(BSTR *a_pbstrVersion)
397{
398 CheckComArgOutPointerValid(a_pbstrVersion);
399
400 AutoCaller autoCaller(this);
401 HRESULT hrc = autoCaller.rc();
402 if (SUCCEEDED(hrc))
403 {
404 Bstr str(m->Desc.strVersion);
405 str.cloneTo(a_pbstrVersion);
406 }
407 return hrc;
408}
409
410STDMETHODIMP ExtPackFile::COMGETTER(Edition)(BSTR *a_pbstrEdition)
411{
412 CheckComArgOutPointerValid(a_pbstrEdition);
413
414 AutoCaller autoCaller(this);
415 HRESULT hrc = autoCaller.rc();
416 if (SUCCEEDED(hrc))
417 {
418 Bstr str(m->Desc.strEdition);
419 str.cloneTo(a_pbstrEdition);
420 }
421 return hrc;
422}
423
424STDMETHODIMP ExtPackFile::COMGETTER(Revision)(ULONG *a_puRevision)
425{
426 CheckComArgOutPointerValid(a_puRevision);
427
428 AutoCaller autoCaller(this);
429 HRESULT hrc = autoCaller.rc();
430 if (SUCCEEDED(hrc))
431 *a_puRevision = m->Desc.uRevision;
432 return hrc;
433}
434
435STDMETHODIMP ExtPackFile::COMGETTER(VRDEModule)(BSTR *a_pbstrVrdeModule)
436{
437 CheckComArgOutPointerValid(a_pbstrVrdeModule);
438
439 AutoCaller autoCaller(this);
440 HRESULT hrc = autoCaller.rc();
441 if (SUCCEEDED(hrc))
442 {
443 Bstr str(m->Desc.strVrdeModule);
444 str.cloneTo(a_pbstrVrdeModule);
445 }
446 return hrc;
447}
448
449STDMETHODIMP ExtPackFile::COMGETTER(PlugIns)(ComSafeArrayOut(IExtPackPlugIn *, a_paPlugIns))
450{
451 /** @todo implement plug-ins. */
452#ifdef VBOX_WITH_XPCOM
453 NOREF(a_paPlugIns);
454 NOREF(a_paPlugInsSize);
455#endif
456 ReturnComNotImplemented();
457}
458
459STDMETHODIMP ExtPackFile::COMGETTER(Usable)(BOOL *a_pfUsable)
460{
461 CheckComArgOutPointerValid(a_pfUsable);
462
463 AutoCaller autoCaller(this);
464 HRESULT hrc = autoCaller.rc();
465 if (SUCCEEDED(hrc))
466 *a_pfUsable = m->fUsable;
467 return hrc;
468}
469
470STDMETHODIMP ExtPackFile::COMGETTER(WhyUnusable)(BSTR *a_pbstrWhy)
471{
472 CheckComArgOutPointerValid(a_pbstrWhy);
473
474 AutoCaller autoCaller(this);
475 HRESULT hrc = autoCaller.rc();
476 if (SUCCEEDED(hrc))
477 m->strWhyUnusable.cloneTo(a_pbstrWhy);
478 return hrc;
479}
480
481STDMETHODIMP ExtPackFile::COMGETTER(ShowLicense)(BOOL *a_pfShowIt)
482{
483 CheckComArgOutPointerValid(a_pfShowIt);
484
485 AutoCaller autoCaller(this);
486 HRESULT hrc = autoCaller.rc();
487 if (SUCCEEDED(hrc))
488 *a_pfShowIt = m->Desc.fShowLicense;
489 return hrc;
490}
491
492STDMETHODIMP ExtPackFile::COMGETTER(License)(BSTR *a_pbstrHtmlLicense)
493{
494 Bstr bstrHtml("html");
495 return QueryLicense(Bstr::Empty.raw(), Bstr::Empty.raw(), bstrHtml.raw(), a_pbstrHtmlLicense);
496}
497
498/* Same as ExtPack::QueryLicense, should really explore the subject of base classes here... */
499STDMETHODIMP ExtPackFile::QueryLicense(IN_BSTR a_bstrPreferredLocale, IN_BSTR a_bstrPreferredLanguage, IN_BSTR a_bstrFormat,
500 BSTR *a_pbstrLicense)
501{
502 /*
503 * Validate input.
504 */
505 CheckComArgOutPointerValid(a_pbstrLicense);
506 CheckComArgNotNull(a_bstrPreferredLocale);
507 CheckComArgNotNull(a_bstrPreferredLanguage);
508 CheckComArgNotNull(a_bstrFormat);
509
510 Utf8Str strPreferredLocale(a_bstrPreferredLocale);
511 if (strPreferredLocale.length() != 2 && strPreferredLocale.length() != 0)
512 return setError(E_FAIL, tr("The preferred locale is a two character string or empty."));
513
514 Utf8Str strPreferredLanguage(a_bstrPreferredLanguage);
515 if (strPreferredLanguage.length() != 2 && strPreferredLanguage.length() != 0)
516 return setError(E_FAIL, tr("The preferred lanuage is a two character string or empty."));
517
518 Utf8Str strFormat(a_bstrFormat);
519 if ( !strFormat.equals("html")
520 && !strFormat.equals("rtf")
521 && !strFormat.equals("txt"))
522 return setError(E_FAIL, tr("The license format can only have the values 'html', 'rtf' and 'txt'."));
523
524 /*
525 * Combine the options to form a file name before locking down anything.
526 */
527 char szName[sizeof(VBOX_EXTPACK_LICENSE_NAME_PREFIX "-de_DE.html") + 2];
528 if (strPreferredLocale.isNotEmpty() && strPreferredLanguage.isNotEmpty())
529 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX "-%s_%s.%s",
530 strPreferredLocale.c_str(), strPreferredLanguage.c_str(), strFormat.c_str());
531 else if (strPreferredLocale.isNotEmpty())
532 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX "-%s.%s", strPreferredLocale.c_str(), strFormat.c_str());
533 else if (strPreferredLanguage.isNotEmpty())
534 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX "-_%s.%s", strPreferredLocale.c_str(), strFormat.c_str());
535 else
536 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX ".%s", strFormat.c_str());
537
538 /*
539 * Lock the extension pack. We need a write lock here as there must not be
540 * concurrent accesses to the tar file handle.
541 */
542 AutoCaller autoCaller(this);
543 HRESULT hrc = autoCaller.rc();
544 if (SUCCEEDED(hrc))
545 {
546 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
547
548 /*
549 * Do not permit this query on a pack that isn't considered usable (could
550 * be marked so because of bad license files).
551 */
552 if (!m->fUsable)
553 hrc = setError(E_FAIL, tr("%s"), m->strWhyUnusable.c_str());
554 else
555 {
556 /*
557 * Look it up in the manifest before scanning the tarball for it
558 */
559 if (RTManifestEntryExists(m->hOurManifest, szName))
560 {
561 RTVFSFSSTREAM hTarFss;
562 char szError[8192];
563 int vrc = VBoxExtPackOpenTarFss(m->hExtPackFile, szError, sizeof(szError), &hTarFss, NULL);
564 if (RT_SUCCESS(vrc))
565 {
566 for (;;)
567 {
568 /* Get the first/next. */
569 char *pszName;
570 RTVFSOBJ hVfsObj;
571 RTVFSOBJTYPE enmType;
572 vrc = RTVfsFsStrmNext(hTarFss, &pszName, &enmType, &hVfsObj);
573 if (RT_FAILURE(vrc))
574 {
575 if (vrc != VERR_EOF)
576 hrc = setError(VBOX_E_IPRT_ERROR, tr("RTVfsFsStrmNext failed: %Rrc"), vrc);
577 else
578 hrc = setError(E_UNEXPECTED, tr("'%s' was found in the manifest but not in the tarball"), szName);
579 break;
580 }
581
582 /* Is this it? */
583 const char *pszAdjName = pszName[0] == '.' && pszName[1] == '/' ? &pszName[2] : pszName;
584 if ( !strcmp(pszAdjName, szName)
585 && ( enmType == RTVFSOBJTYPE_IO_STREAM
586 || enmType == RTVFSOBJTYPE_FILE))
587 {
588 RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
589 RTVfsObjRelease(hVfsObj);
590 RTStrFree(pszName);
591
592 /* Load the file into memory. */
593 RTFSOBJINFO ObjInfo;
594 vrc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_NOTHING);
595 if (RT_SUCCESS(vrc))
596 {
597 size_t cbFile = (size_t)ObjInfo.cbObject;
598 void *pvFile = RTMemAllocZ(cbFile + 1);
599 if (pvFile)
600 {
601 vrc = RTVfsIoStrmRead(hVfsIos, pvFile, cbFile, true /*fBlocking*/, NULL);
602 if (RT_SUCCESS(vrc))
603 {
604 /* try translate it into a string we can return. */
605 Bstr bstrLicense((const char *)pvFile, cbFile);
606 if (bstrLicense.isNotEmpty())
607 {
608 bstrLicense.detachTo(a_pbstrLicense);
609 hrc = S_OK;
610 }
611 else
612 hrc = setError(VBOX_E_IPRT_ERROR,
613 tr("The license file '%s' is empty or contains invalid UTF-8 encoding"),
614 szName);
615 }
616 else
617 hrc = setError(VBOX_E_IPRT_ERROR, tr("Failed to read '%s': %Rrc"), szName, vrc);
618 RTMemFree(pvFile);
619 }
620 else
621 hrc = setError(E_OUTOFMEMORY, tr("Failed to allocate %zu bytes for '%s'"), cbFile, szName);
622 }
623 else
624 hrc = setError(VBOX_E_IPRT_ERROR, tr("RTVfsIoStrmQueryInfo on '%s': %Rrc"), szName, vrc);
625 RTVfsIoStrmRelease(hVfsIos);
626 break;
627 }
628
629 /* Release current. */
630 RTVfsObjRelease(hVfsObj);
631 RTStrFree(pszName);
632 }
633 RTVfsFsStrmRelease(hTarFss);
634 }
635 else
636 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("%s"), szError);
637 }
638 else
639 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("The license file '%s' was not found in '%s'"),
640 szName, m->strExtPackFile.c_str());
641 }
642 }
643 return hrc;
644}
645
646STDMETHODIMP ExtPackFile::COMGETTER(FilePath)(BSTR *a_pbstrPath)
647{
648 CheckComArgOutPointerValid(a_pbstrPath);
649
650 AutoCaller autoCaller(this);
651 HRESULT hrc = autoCaller.rc();
652 if (SUCCEEDED(hrc))
653 m->strExtPackFile.cloneTo(a_pbstrPath);
654 return hrc;
655}
656
657STDMETHODIMP ExtPackFile::Install(BOOL a_fReplace, IN_BSTR a_bstrDisplayInfo, IProgress **a_ppProgress)
658{
659 if (a_ppProgress)
660 *a_ppProgress = NULL;
661
662 AutoCaller autoCaller(this);
663 HRESULT hrc = autoCaller.rc();
664 if (SUCCEEDED(hrc))
665 {
666 if (m->fUsable)
667 {
668 PEXTPACKINSTALLJOB pJob = NULL;
669 try
670 {
671 pJob = new EXTPACKINSTALLJOB;
672 pJob->ptrExtPackFile = this;
673 pJob->fReplace = a_fReplace != FALSE;
674 pJob->strDisplayInfo = a_bstrDisplayInfo;
675 pJob->ptrExtPackMgr = m->ptrExtPackMgr;
676 hrc = pJob->ptrProgress.createObject();
677 if (SUCCEEDED(hrc))
678 {
679 Bstr bstrDescription = tr("Installing extension pack");
680 hrc = pJob->ptrProgress->init(
681#ifndef VBOX_COM_INPROC
682 m->pVirtualBox,
683#endif
684 static_cast<IExtPackFile *>(this),
685 bstrDescription.raw(),
686 FALSE /*aCancelable*/,
687 NULL /*aId*/);
688 }
689 if (SUCCEEDED(hrc))
690 {
691 ComPtr<Progress> ptrProgress = pJob->ptrProgress;
692 int vrc = RTThreadCreate(NULL /*phThread*/, ExtPackManager::doInstallThreadProc, pJob, 0,
693 RTTHREADTYPE_DEFAULT, 0 /*fFlags*/, "ExtPackInst");
694 if (RT_SUCCESS(vrc))
695 {
696 pJob = NULL; /* the thread deletes it */
697 ptrProgress.queryInterfaceTo(a_ppProgress);
698 }
699 else
700 hrc = setError(VBOX_E_IPRT_ERROR, tr("RTThreadCreate failed with %Rrc"), vrc);
701 }
702 }
703 catch (std::bad_alloc)
704 {
705 hrc = E_OUTOFMEMORY;
706 }
707 if (pJob)
708 delete pJob;
709 }
710 else
711 hrc = setError(E_FAIL, "%s", m->strWhyUnusable.c_str());
712 }
713 return hrc;
714}
715
716#endif
717
718
719
720
721DEFINE_EMPTY_CTOR_DTOR(ExtPack)
722
723/**
724 * Called by ComObjPtr::createObject when creating the object.
725 *
726 * Just initialize the basic object state, do the rest in initWithDir().
727 *
728 * @returns S_OK.
729 */
730HRESULT ExtPack::FinalConstruct()
731{
732 m = NULL;
733 return S_OK;
734}
735
736/**
737 * Initializes the extension pack by reading its file.
738 *
739 * @returns COM status code.
740 * @param a_enmContext The context we're in.
741 * @param a_pszName The name of the extension pack. This is also the
742 * name of the subdirector under @a a_pszParentDir
743 * where the extension pack is installed.
744 * @param a_pszDir The extension pack directory name.
745 */
746HRESULT ExtPack::initWithDir(VBOXEXTPACKCTX a_enmContext, const char *a_pszName, const char *a_pszDir)
747{
748 AutoInitSpan autoInitSpan(this);
749 AssertReturn(autoInitSpan.isOk(), E_FAIL);
750
751 static const VBOXEXTPACKHLP s_HlpTmpl =
752 {
753 /* u32Version = */ VBOXEXTPACKHLP_VERSION,
754 /* uVBoxFullVersion = */ VBOX_FULL_VERSION,
755 /* uVBoxVersionRevision = */ 0,
756 /* u32Padding = */ 0,
757 /* pszVBoxVersion = */ "",
758 /* pfnFindModule = */ ExtPack::hlpFindModule,
759 /* pfnGetFilePath = */ ExtPack::hlpGetFilePath,
760 /* pfnGetContext = */ ExtPack::hlpGetContext,
761 /* pfnLoadHGCMService = */ ExtPack::hlpLoadHGCMService,
762 /* pfnReserved1 = */ ExtPack::hlpReservedN,
763 /* pfnReserved2 = */ ExtPack::hlpReservedN,
764 /* pfnReserved3 = */ ExtPack::hlpReservedN,
765 /* pfnReserved4 = */ ExtPack::hlpReservedN,
766 /* pfnReserved5 = */ ExtPack::hlpReservedN,
767 /* pfnReserved6 = */ ExtPack::hlpReservedN,
768 /* pfnReserved7 = */ ExtPack::hlpReservedN,
769 /* pfnReserved8 = */ ExtPack::hlpReservedN,
770 /* u32EndMarker = */ VBOXEXTPACKHLP_VERSION
771 };
772
773 /*
774 * Allocate + initialize our private data.
775 */
776 m = new Data;
777 VBoxExtPackInitDesc(&m->Desc);
778 m->Desc.strName = a_pszName;
779 RT_ZERO(m->ObjInfoDesc);
780 m->fUsable = false;
781 m->strWhyUnusable = tr("ExtPack::init failed");
782 m->strExtPackPath = a_pszDir;
783 RT_ZERO(m->ObjInfoExtPack);
784 m->strMainModPath.setNull();
785 RT_ZERO(m->ObjInfoMainMod);
786 m->hMainMod = NIL_RTLDRMOD;
787 m->Hlp = s_HlpTmpl;
788 m->Hlp.pszVBoxVersion = RTBldCfgVersion();
789 m->Hlp.uVBoxInternalRevision = RTBldCfgRevision();
790 m->pThis = this;
791 m->pReg = NULL;
792 m->enmContext = a_enmContext;
793 m->fMadeReadyCall = false;
794
795 /*
796 * Probe the extension pack (this code is shared with refresh()).
797 */
798 probeAndLoad();
799
800 autoInitSpan.setSucceeded();
801 return S_OK;
802}
803
804/**
805 * COM cruft.
806 */
807void ExtPack::FinalRelease()
808{
809 uninit();
810}
811
812/**
813 * Do the actual cleanup.
814 */
815void ExtPack::uninit()
816{
817 /* Enclose the state transition Ready->InUninit->NotReady */
818 AutoUninitSpan autoUninitSpan(this);
819 if (!autoUninitSpan.uninitDone() && m != NULL)
820 {
821 if (m->hMainMod != NIL_RTLDRMOD)
822 {
823 AssertPtr(m->pReg);
824 if (m->pReg->pfnUnload != NULL)
825 m->pReg->pfnUnload(m->pReg);
826
827 RTLdrClose(m->hMainMod);
828 m->hMainMod = NIL_RTLDRMOD;
829 m->pReg = NULL;
830 }
831
832 VBoxExtPackFreeDesc(&m->Desc);
833
834 delete m;
835 m = NULL;
836 }
837}
838
839
840/**
841 * Calls the installed hook.
842 *
843 * @returns true if we left the lock, false if we didn't.
844 * @param a_pVirtualBox The VirtualBox interface.
845 * @param a_pLock The write lock held by the caller.
846 * @param pErrInfo Where to return error information.
847 */
848bool ExtPack::callInstalledHook(IVirtualBox *a_pVirtualBox, AutoWriteLock *a_pLock, PRTERRINFO pErrInfo)
849{
850 if ( m != NULL
851 && m->hMainMod != NIL_RTLDRMOD)
852 {
853 if (m->pReg->pfnInstalled)
854 {
855 ComPtr<ExtPack> ptrSelfRef = this;
856 a_pLock->release();
857 pErrInfo->rc = m->pReg->pfnInstalled(m->pReg, a_pVirtualBox, pErrInfo);
858 a_pLock->acquire();
859 return true;
860 }
861 }
862 pErrInfo->rc = VINF_SUCCESS;
863 return false;
864}
865
866/**
867 * Calls the uninstall hook and closes the module.
868 *
869 * @returns S_OK or COM error status with error information.
870 * @param a_pVirtualBox The VirtualBox interface.
871 * @param a_fForcedRemoval When set, we'll ignore complaints from the
872 * uninstall hook.
873 * @remarks The caller holds the manager's write lock, not released.
874 */
875HRESULT ExtPack::callUninstallHookAndClose(IVirtualBox *a_pVirtualBox, bool a_fForcedRemoval)
876{
877 HRESULT hrc = S_OK;
878
879 if ( m != NULL
880 && m->hMainMod != NIL_RTLDRMOD)
881 {
882 if (m->pReg->pfnUninstall && !a_fForcedRemoval)
883 {
884 int vrc = m->pReg->pfnUninstall(m->pReg, a_pVirtualBox);
885 if (RT_FAILURE(vrc))
886 {
887 LogRel(("ExtPack pfnUninstall returned %Rrc for %s\n", vrc, m->Desc.strName.c_str()));
888 if (!a_fForcedRemoval)
889 hrc = setError(E_FAIL, tr("pfnUninstall returned %Rrc"), vrc);
890 }
891 }
892 if (SUCCEEDED(hrc))
893 {
894 RTLdrClose(m->hMainMod);
895 m->hMainMod = NIL_RTLDRMOD;
896 m->pReg = NULL;
897 }
898 }
899
900 return hrc;
901}
902
903/**
904 * Calls the pfnVirtualBoxReady hook.
905 *
906 * @returns true if we left the lock, false if we didn't.
907 * @param a_pVirtualBox The VirtualBox interface.
908 * @param a_pLock The write lock held by the caller.
909 */
910bool ExtPack::callVirtualBoxReadyHook(IVirtualBox *a_pVirtualBox, AutoWriteLock *a_pLock)
911{
912 if ( m != NULL
913 && m->fUsable
914 && !m->fMadeReadyCall)
915 {
916 m->fMadeReadyCall = true;
917 if (m->pReg->pfnVirtualBoxReady)
918 {
919 ComPtr<ExtPack> ptrSelfRef = this;
920 a_pLock->release();
921 m->pReg->pfnVirtualBoxReady(m->pReg, a_pVirtualBox);
922 a_pLock->acquire();
923 return true;
924 }
925 }
926 return false;
927}
928
929/**
930 * Calls the pfnConsoleReady hook.
931 *
932 * @returns true if we left the lock, false if we didn't.
933 * @param a_pConsole The Console interface.
934 * @param a_pLock The write lock held by the caller.
935 */
936bool ExtPack::callConsoleReadyHook(IConsole *a_pConsole, AutoWriteLock *a_pLock)
937{
938 if ( m != NULL
939 && m->fUsable
940 && !m->fMadeReadyCall)
941 {
942 m->fMadeReadyCall = true;
943 if (m->pReg->pfnConsoleReady)
944 {
945 ComPtr<ExtPack> ptrSelfRef = this;
946 a_pLock->release();
947 m->pReg->pfnConsoleReady(m->pReg, a_pConsole);
948 a_pLock->acquire();
949 return true;
950 }
951 }
952 return false;
953}
954
955/**
956 * Calls the pfnVMCreate hook.
957 *
958 * @returns true if we left the lock, false if we didn't.
959 * @param a_pVirtualBox The VirtualBox interface.
960 * @param a_pMachine The machine interface of the new VM.
961 * @param a_pLock The write lock held by the caller.
962 */
963bool ExtPack::callVmCreatedHook(IVirtualBox *a_pVirtualBox, IMachine *a_pMachine, AutoWriteLock *a_pLock)
964{
965 if ( m != NULL
966 && m->fUsable)
967 {
968 if (m->pReg->pfnVMCreated)
969 {
970 ComPtr<ExtPack> ptrSelfRef = this;
971 a_pLock->release();
972 m->pReg->pfnVMCreated(m->pReg, a_pVirtualBox, a_pMachine);
973 a_pLock->acquire();
974 return true;
975 }
976 }
977 return false;
978}
979
980/**
981 * Calls the pfnVMConfigureVMM hook.
982 *
983 * @returns true if we left the lock, false if we didn't.
984 * @param a_pConsole The console interface.
985 * @param a_pVM The VM handle.
986 * @param a_pLock The write lock held by the caller.
987 * @param a_pvrc Where to return the status code of the
988 * callback. This is always set. LogRel is
989 * called on if a failure status is returned.
990 */
991bool ExtPack::callVmConfigureVmmHook(IConsole *a_pConsole, PVM a_pVM, AutoWriteLock *a_pLock, int *a_pvrc)
992{
993 *a_pvrc = VINF_SUCCESS;
994 if ( m != NULL
995 && m->fUsable)
996 {
997 if (m->pReg->pfnVMConfigureVMM)
998 {
999 ComPtr<ExtPack> ptrSelfRef = this;
1000 a_pLock->release();
1001 int vrc = m->pReg->pfnVMConfigureVMM(m->pReg, a_pConsole, a_pVM);
1002 *a_pvrc = vrc;
1003 a_pLock->acquire();
1004 if (RT_FAILURE(vrc))
1005 LogRel(("ExtPack pfnVMConfigureVMM returned %Rrc for %s\n", vrc, m->Desc.strName.c_str()));
1006 return true;
1007 }
1008 }
1009 return false;
1010}
1011
1012/**
1013 * Calls the pfnVMPowerOn hook.
1014 *
1015 * @returns true if we left the lock, false if we didn't.
1016 * @param a_pConsole The console interface.
1017 * @param a_pVM The VM handle.
1018 * @param a_pLock The write lock held by the caller.
1019 * @param a_pvrc Where to return the status code of the
1020 * callback. This is always set. LogRel is
1021 * called on if a failure status is returned.
1022 */
1023bool ExtPack::callVmPowerOnHook(IConsole *a_pConsole, PVM a_pVM, AutoWriteLock *a_pLock, int *a_pvrc)
1024{
1025 *a_pvrc = VINF_SUCCESS;
1026 if ( m != NULL
1027 && m->fUsable)
1028 {
1029 if (m->pReg->pfnVMPowerOn)
1030 {
1031 ComPtr<ExtPack> ptrSelfRef = this;
1032 a_pLock->release();
1033 int vrc = m->pReg->pfnVMPowerOn(m->pReg, a_pConsole, a_pVM);
1034 *a_pvrc = vrc;
1035 a_pLock->acquire();
1036 if (RT_FAILURE(vrc))
1037 LogRel(("ExtPack pfnVMPowerOn returned %Rrc for %s\n", vrc, m->Desc.strName.c_str()));
1038 return true;
1039 }
1040 }
1041 return false;
1042}
1043
1044/**
1045 * Calls the pfnVMPowerOff hook.
1046 *
1047 * @returns true if we left the lock, false if we didn't.
1048 * @param a_pConsole The console interface.
1049 * @param a_pVM The VM handle.
1050 * @param a_pLock The write lock held by the caller.
1051 */
1052bool ExtPack::callVmPowerOffHook(IConsole *a_pConsole, PVM a_pVM, AutoWriteLock *a_pLock)
1053{
1054 if ( m != NULL
1055 && m->fUsable)
1056 {
1057 if (m->pReg->pfnVMPowerOff)
1058 {
1059 ComPtr<ExtPack> ptrSelfRef = this;
1060 a_pLock->release();
1061 m->pReg->pfnVMPowerOff(m->pReg, a_pConsole, a_pVM);
1062 a_pLock->acquire();
1063 return true;
1064 }
1065 }
1066 return false;
1067}
1068
1069/**
1070 * Check if the extension pack is usable and has an VRDE module.
1071 *
1072 * @returns S_OK or COM error status with error information.
1073 *
1074 * @remarks Caller holds the extension manager lock for reading, no locking
1075 * necessary.
1076 */
1077HRESULT ExtPack::checkVrde(void)
1078{
1079 HRESULT hrc;
1080 if ( m != NULL
1081 && m->fUsable)
1082 {
1083 if (m->Desc.strVrdeModule.isNotEmpty())
1084 hrc = S_OK;
1085 else
1086 hrc = setError(E_FAIL, tr("The extension pack '%s' does not include a VRDE module"), m->Desc.strName.c_str());
1087 }
1088 else
1089 hrc = setError(E_FAIL, tr("%s"), m->strWhyUnusable.c_str());
1090 return hrc;
1091}
1092
1093/**
1094 * Same as checkVrde(), except that it also resolves the path to the module.
1095 *
1096 * @returns S_OK or COM error status with error information.
1097 * @param a_pstrVrdeLibrary Where to return the path on success.
1098 *
1099 * @remarks Caller holds the extension manager lock for reading, no locking
1100 * necessary.
1101 */
1102HRESULT ExtPack::getVrdpLibraryName(Utf8Str *a_pstrVrdeLibrary)
1103{
1104 HRESULT hrc = checkVrde();
1105 if (SUCCEEDED(hrc))
1106 {
1107 if (findModule(m->Desc.strVrdeModule.c_str(), NULL, VBOXEXTPACKMODKIND_R3,
1108 a_pstrVrdeLibrary, NULL /*a_pfNative*/, NULL /*a_pObjInfo*/))
1109 hrc = S_OK;
1110 else
1111 hrc = setError(E_FAIL, tr("Failed to locate the VRDE module '%s' in extension pack '%s'"),
1112 m->Desc.strVrdeModule.c_str(), m->Desc.strName.c_str());
1113 }
1114 return hrc;
1115}
1116
1117/**
1118 * Resolves the path to the module.
1119 *
1120 * @returns S_OK or COM error status with error information.
1121 * @param a_pszModuleName The library.
1122 * @param a_pstrLibrary Where to return the path on success.
1123 *
1124 * @remarks Caller holds the extension manager lock for reading, no locking
1125 * necessary.
1126 */
1127HRESULT ExtPack::getLibraryName(const char *a_pszModuleName, Utf8Str *a_pstrLibrary)
1128{
1129 HRESULT hrc;
1130 if (findModule(a_pszModuleName, NULL, VBOXEXTPACKMODKIND_R3,
1131 a_pstrLibrary, NULL /*a_pfNative*/, NULL /*a_pObjInfo*/))
1132 hrc = S_OK;
1133 else
1134 hrc = setError(E_FAIL, tr("Failed to locate the module '%s' in extension pack '%s'"),
1135 a_pszModuleName, m->Desc.strName.c_str());
1136 return hrc;
1137}
1138
1139/**
1140 * Check if this extension pack wishes to be the default VRDE provider.
1141 *
1142 * @returns @c true if it wants to and it is in a usable state, otherwise
1143 * @c false.
1144 *
1145 * @remarks Caller holds the extension manager lock for reading, no locking
1146 * necessary.
1147 */
1148bool ExtPack::wantsToBeDefaultVrde(void) const
1149{
1150 return m->fUsable
1151 && m->Desc.strVrdeModule.isNotEmpty();
1152}
1153
1154/**
1155 * Refreshes the extension pack state.
1156 *
1157 * This is called by the manager so that the on disk changes are picked up.
1158 *
1159 * @returns S_OK or COM error status with error information.
1160 *
1161 * @param a_pfCanDelete Optional can-delete-this-object output indicator.
1162 *
1163 * @remarks Caller holds the extension manager lock for writing.
1164 * @remarks Only called in VBoxSVC.
1165 */
1166HRESULT ExtPack::refresh(bool *a_pfCanDelete)
1167{
1168 if (a_pfCanDelete)
1169 *a_pfCanDelete = false;
1170
1171 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS); /* for the COMGETTERs */
1172
1173 /*
1174 * Has the module been deleted?
1175 */
1176 RTFSOBJINFO ObjInfoExtPack;
1177 int vrc = RTPathQueryInfoEx(m->strExtPackPath.c_str(), &ObjInfoExtPack, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
1178 if ( RT_FAILURE(vrc)
1179 || !RTFS_IS_DIRECTORY(ObjInfoExtPack.Attr.fMode))
1180 {
1181 if (a_pfCanDelete)
1182 *a_pfCanDelete = true;
1183 return S_OK;
1184 }
1185
1186 /*
1187 * We've got a directory, so try query file system object info for the
1188 * files we are interested in as well.
1189 */
1190 RTFSOBJINFO ObjInfoDesc;
1191 char szDescFilePath[RTPATH_MAX];
1192 vrc = RTPathJoin(szDescFilePath, sizeof(szDescFilePath), m->strExtPackPath.c_str(), VBOX_EXTPACK_DESCRIPTION_NAME);
1193 if (RT_SUCCESS(vrc))
1194 vrc = RTPathQueryInfoEx(szDescFilePath, &ObjInfoDesc, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
1195 if (RT_FAILURE(vrc))
1196 RT_ZERO(ObjInfoDesc);
1197
1198 RTFSOBJINFO ObjInfoMainMod;
1199 if (m->strMainModPath.isNotEmpty())
1200 vrc = RTPathQueryInfoEx(m->strMainModPath.c_str(), &ObjInfoMainMod, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
1201 if (m->strMainModPath.isEmpty() || RT_FAILURE(vrc))
1202 RT_ZERO(ObjInfoMainMod);
1203
1204 /*
1205 * If we have a usable module already, just verify that things haven't
1206 * changed since we loaded it.
1207 */
1208 if (m->fUsable)
1209 {
1210 if (m->hMainMod == NIL_RTLDRMOD)
1211 probeAndLoad();
1212 else if ( !objinfoIsEqual(&ObjInfoDesc, &m->ObjInfoDesc)
1213 || !objinfoIsEqual(&ObjInfoMainMod, &m->ObjInfoMainMod)
1214 || !objinfoIsEqual(&ObjInfoExtPack, &m->ObjInfoExtPack) )
1215 {
1216 /** @todo not important, so it can wait. */
1217 }
1218 }
1219 /*
1220 * Ok, it is currently not usable. If anything has changed since last time
1221 * reprobe the extension pack.
1222 */
1223 else if ( !objinfoIsEqual(&ObjInfoDesc, &m->ObjInfoDesc)
1224 || !objinfoIsEqual(&ObjInfoMainMod, &m->ObjInfoMainMod)
1225 || !objinfoIsEqual(&ObjInfoExtPack, &m->ObjInfoExtPack) )
1226 probeAndLoad();
1227
1228 return S_OK;
1229}
1230
1231/**
1232 * Probes the extension pack, loading the main dll and calling its registration
1233 * entry point.
1234 *
1235 * This updates the state accordingly, the strWhyUnusable and fUnusable members
1236 * being the most important ones.
1237 */
1238void ExtPack::probeAndLoad(void)
1239{
1240 m->fUsable = false;
1241 m->fMadeReadyCall = false;
1242
1243 /*
1244 * Query the file system info for the extension pack directory. This and
1245 * all other file system info we save is for the benefit of refresh().
1246 */
1247 int vrc = RTPathQueryInfoEx(m->strExtPackPath.c_str(), &m->ObjInfoExtPack, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
1248 if (RT_FAILURE(vrc))
1249 {
1250 m->strWhyUnusable.printf(tr("RTPathQueryInfoEx on '%s' failed: %Rrc"), m->strExtPackPath.c_str(), vrc);
1251 return;
1252 }
1253 if (!RTFS_IS_DIRECTORY(m->ObjInfoExtPack.Attr.fMode))
1254 {
1255 if (RTFS_IS_SYMLINK(m->ObjInfoExtPack.Attr.fMode))
1256 m->strWhyUnusable.printf(tr("'%s' is a symbolic link, this is not allowed"), m->strExtPackPath.c_str(), vrc);
1257 else if (RTFS_IS_FILE(m->ObjInfoExtPack.Attr.fMode))
1258 m->strWhyUnusable.printf(tr("'%s' is a symbolic file, not a directory"), m->strExtPackPath.c_str(), vrc);
1259 else
1260 m->strWhyUnusable.printf(tr("'%s' is not a directory (fMode=%#x)"), m->strExtPackPath.c_str(), m->ObjInfoExtPack.Attr.fMode);
1261 return;
1262 }
1263
1264 RTERRINFOSTATIC ErrInfo;
1265 RTErrInfoInitStatic(&ErrInfo);
1266 vrc = SUPR3HardenedVerifyDir(m->strExtPackPath.c_str(), true /*fRecursive*/, true /*fCheckFiles*/, &ErrInfo.Core);
1267 if (RT_FAILURE(vrc))
1268 {
1269 m->strWhyUnusable.printf(tr("%s (rc=%Rrc)"), ErrInfo.Core.pszMsg, vrc);
1270 return;
1271 }
1272
1273 /*
1274 * Read the description file.
1275 */
1276 RTCString strSavedName(m->Desc.strName);
1277 RTCString *pStrLoadErr = VBoxExtPackLoadDesc(m->strExtPackPath.c_str(), &m->Desc, &m->ObjInfoDesc);
1278 if (pStrLoadErr != NULL)
1279 {
1280 m->strWhyUnusable.printf(tr("Failed to load '%s/%s': %s"),
1281 m->strExtPackPath.c_str(), VBOX_EXTPACK_DESCRIPTION_NAME, pStrLoadErr->c_str());
1282 m->Desc.strName = strSavedName;
1283 delete pStrLoadErr;
1284 return;
1285 }
1286
1287 /*
1288 * Make sure the XML name and directory matches.
1289 */
1290 if (!m->Desc.strName.equalsIgnoreCase(strSavedName))
1291 {
1292 m->strWhyUnusable.printf(tr("The description name ('%s') and directory name ('%s') does not match"),
1293 m->Desc.strName.c_str(), strSavedName.c_str());
1294 m->Desc.strName = strSavedName;
1295 return;
1296 }
1297
1298 /*
1299 * Load the main DLL and call the predefined entry point.
1300 */
1301 bool fIsNative;
1302 if (!findModule(m->Desc.strMainModule.c_str(), NULL /* default extension */, VBOXEXTPACKMODKIND_R3,
1303 &m->strMainModPath, &fIsNative, &m->ObjInfoMainMod))
1304 {
1305 m->strWhyUnusable.printf(tr("Failed to locate the main module ('%s')"), m->Desc.strMainModule.c_str());
1306 return;
1307 }
1308
1309 vrc = SUPR3HardenedVerifyPlugIn(m->strMainModPath.c_str(), &ErrInfo.Core);
1310 if (RT_FAILURE(vrc))
1311 {
1312 m->strWhyUnusable.printf(tr("%s"), ErrInfo.Core.pszMsg);
1313 return;
1314 }
1315
1316 if (fIsNative)
1317 {
1318 vrc = SUPR3HardenedLdrLoadPlugIn(m->strMainModPath.c_str(), &m->hMainMod, &ErrInfo.Core);
1319 if (RT_FAILURE(vrc))
1320 {
1321 m->hMainMod = NIL_RTLDRMOD;
1322 m->strWhyUnusable.printf(tr("Failed to load the main module ('%s'): %Rrc - %s"),
1323 m->strMainModPath.c_str(), vrc, ErrInfo.Core.pszMsg);
1324 return;
1325 }
1326 }
1327 else
1328 {
1329 m->strWhyUnusable.printf(tr("Only native main modules are currently supported"));
1330 return;
1331 }
1332
1333 /*
1334 * Resolve the predefined entry point.
1335 */
1336 PFNVBOXEXTPACKREGISTER pfnRegistration;
1337 vrc = RTLdrGetSymbol(m->hMainMod, VBOX_EXTPACK_MAIN_MOD_ENTRY_POINT, (void **)&pfnRegistration);
1338 if (RT_SUCCESS(vrc))
1339 {
1340 RTErrInfoClear(&ErrInfo.Core);
1341 vrc = pfnRegistration(&m->Hlp, &m->pReg, &ErrInfo.Core);
1342 if ( RT_SUCCESS(vrc)
1343 && !RTErrInfoIsSet(&ErrInfo.Core)
1344 && VALID_PTR(m->pReg))
1345 {
1346 if ( VBOXEXTPACK_IS_MAJOR_VER_EQUAL(m->pReg->u32Version, VBOXEXTPACKREG_VERSION)
1347 && m->pReg->u32EndMarker == m->pReg->u32Version)
1348 {
1349 if ( (!m->pReg->pfnInstalled || RT_VALID_PTR(m->pReg->pfnInstalled))
1350 && (!m->pReg->pfnUninstall || RT_VALID_PTR(m->pReg->pfnUninstall))
1351 && (!m->pReg->pfnVirtualBoxReady || RT_VALID_PTR(m->pReg->pfnVirtualBoxReady))
1352 && (!m->pReg->pfnConsoleReady || RT_VALID_PTR(m->pReg->pfnConsoleReady))
1353 && (!m->pReg->pfnUnload || RT_VALID_PTR(m->pReg->pfnUnload))
1354 && (!m->pReg->pfnVMCreated || RT_VALID_PTR(m->pReg->pfnVMCreated))
1355 && (!m->pReg->pfnVMConfigureVMM || RT_VALID_PTR(m->pReg->pfnVMConfigureVMM))
1356 && (!m->pReg->pfnVMPowerOn || RT_VALID_PTR(m->pReg->pfnVMPowerOn))
1357 && (!m->pReg->pfnVMPowerOff || RT_VALID_PTR(m->pReg->pfnVMPowerOff))
1358 && (!m->pReg->pfnQueryObject || RT_VALID_PTR(m->pReg->pfnQueryObject))
1359 )
1360 {
1361 /*
1362 * We're good!
1363 */
1364 m->fUsable = true;
1365 m->strWhyUnusable.setNull();
1366 return;
1367 }
1368
1369 m->strWhyUnusable = tr("The registration structure contains on or more invalid function pointers");
1370 }
1371 else
1372 m->strWhyUnusable.printf(tr("Unsupported registration structure version %u.%u"),
1373 RT_HIWORD(m->pReg->u32Version), RT_LOWORD(m->pReg->u32Version));
1374 }
1375 else
1376 m->strWhyUnusable.printf(tr("%s returned %Rrc, pReg=%p ErrInfo='%s'"),
1377 VBOX_EXTPACK_MAIN_MOD_ENTRY_POINT, vrc, m->pReg, ErrInfo.Core.pszMsg);
1378 m->pReg = NULL;
1379 }
1380 else
1381 m->strWhyUnusable.printf(tr("Failed to resolve exported symbol '%s' in the main module: %Rrc"),
1382 VBOX_EXTPACK_MAIN_MOD_ENTRY_POINT, vrc);
1383
1384 RTLdrClose(m->hMainMod);
1385 m->hMainMod = NIL_RTLDRMOD;
1386}
1387
1388/**
1389 * Finds a module.
1390 *
1391 * @returns true if found, false if not.
1392 * @param a_pszName The module base name (no extension).
1393 * @param a_pszExt The extension. If NULL we use default
1394 * extensions.
1395 * @param a_enmKind The kind of module to locate.
1396 * @param a_pStrFound Where to return the path to the module we've
1397 * found.
1398 * @param a_pfNative Where to return whether this is a native module
1399 * or an agnostic one. Optional.
1400 * @param a_pObjInfo Where to return the file system object info for
1401 * the module. Optional.
1402 */
1403bool ExtPack::findModule(const char *a_pszName, const char *a_pszExt, VBOXEXTPACKMODKIND a_enmKind,
1404 Utf8Str *a_pStrFound, bool *a_pfNative, PRTFSOBJINFO a_pObjInfo) const
1405{
1406 /*
1407 * Try the native path first.
1408 */
1409 char szPath[RTPATH_MAX];
1410 int vrc = RTPathJoin(szPath, sizeof(szPath), m->strExtPackPath.c_str(), RTBldCfgTargetDotArch());
1411 AssertLogRelRCReturn(vrc, false);
1412 vrc = RTPathAppend(szPath, sizeof(szPath), a_pszName);
1413 AssertLogRelRCReturn(vrc, false);
1414 if (!a_pszExt)
1415 {
1416 const char *pszDefExt;
1417 switch (a_enmKind)
1418 {
1419 case VBOXEXTPACKMODKIND_RC: pszDefExt = ".rc"; break;
1420 case VBOXEXTPACKMODKIND_R0: pszDefExt = ".r0"; break;
1421 case VBOXEXTPACKMODKIND_R3: pszDefExt = RTLdrGetSuff(); break;
1422 default:
1423 AssertFailedReturn(false);
1424 }
1425 vrc = RTStrCat(szPath, sizeof(szPath), pszDefExt);
1426 AssertLogRelRCReturn(vrc, false);
1427 }
1428
1429 RTFSOBJINFO ObjInfo;
1430 if (!a_pObjInfo)
1431 a_pObjInfo = &ObjInfo;
1432 vrc = RTPathQueryInfo(szPath, a_pObjInfo, RTFSOBJATTRADD_UNIX);
1433 if (RT_SUCCESS(vrc) && RTFS_IS_FILE(a_pObjInfo->Attr.fMode))
1434 {
1435 if (a_pfNative)
1436 *a_pfNative = true;
1437 *a_pStrFound = szPath;
1438 return true;
1439 }
1440
1441 /*
1442 * Try the platform agnostic modules.
1443 */
1444 /* gcc.x86/module.rel */
1445 char szSubDir[32];
1446 RTStrPrintf(szSubDir, sizeof(szSubDir), "%s.%s", RTBldCfgCompiler(), RTBldCfgTargetArch());
1447 vrc = RTPathJoin(szPath, sizeof(szPath), m->strExtPackPath.c_str(), szSubDir);
1448 AssertLogRelRCReturn(vrc, false);
1449 vrc = RTPathAppend(szPath, sizeof(szPath), a_pszName);
1450 AssertLogRelRCReturn(vrc, false);
1451 if (!a_pszExt)
1452 {
1453 vrc = RTStrCat(szPath, sizeof(szPath), ".rel");
1454 AssertLogRelRCReturn(vrc, false);
1455 }
1456 vrc = RTPathQueryInfo(szPath, a_pObjInfo, RTFSOBJATTRADD_UNIX);
1457 if (RT_SUCCESS(vrc) && RTFS_IS_FILE(a_pObjInfo->Attr.fMode))
1458 {
1459 if (a_pfNative)
1460 *a_pfNative = false;
1461 *a_pStrFound = szPath;
1462 return true;
1463 }
1464
1465 /* x86/module.rel */
1466 vrc = RTPathJoin(szPath, sizeof(szPath), m->strExtPackPath.c_str(), RTBldCfgTargetArch());
1467 AssertLogRelRCReturn(vrc, false);
1468 vrc = RTPathAppend(szPath, sizeof(szPath), a_pszName);
1469 AssertLogRelRCReturn(vrc, false);
1470 if (!a_pszExt)
1471 {
1472 vrc = RTStrCat(szPath, sizeof(szPath), ".rel");
1473 AssertLogRelRCReturn(vrc, false);
1474 }
1475 vrc = RTPathQueryInfo(szPath, a_pObjInfo, RTFSOBJATTRADD_UNIX);
1476 if (RT_SUCCESS(vrc) && RTFS_IS_FILE(a_pObjInfo->Attr.fMode))
1477 {
1478 if (a_pfNative)
1479 *a_pfNative = false;
1480 *a_pStrFound = szPath;
1481 return true;
1482 }
1483
1484 return false;
1485}
1486
1487/**
1488 * Compares two file system object info structures.
1489 *
1490 * @returns true if equal, false if not.
1491 * @param pObjInfo1 The first.
1492 * @param pObjInfo2 The second.
1493 * @todo IPRT should do this, really.
1494 */
1495/* static */ bool ExtPack::objinfoIsEqual(PCRTFSOBJINFO pObjInfo1, PCRTFSOBJINFO pObjInfo2)
1496{
1497 if (!RTTimeSpecIsEqual(&pObjInfo1->ModificationTime, &pObjInfo2->ModificationTime))
1498 return false;
1499 if (!RTTimeSpecIsEqual(&pObjInfo1->ChangeTime, &pObjInfo2->ChangeTime))
1500 return false;
1501 if (!RTTimeSpecIsEqual(&pObjInfo1->BirthTime, &pObjInfo2->BirthTime))
1502 return false;
1503 if (pObjInfo1->cbObject != pObjInfo2->cbObject)
1504 return false;
1505 if (pObjInfo1->Attr.fMode != pObjInfo2->Attr.fMode)
1506 return false;
1507 if (pObjInfo1->Attr.enmAdditional == pObjInfo2->Attr.enmAdditional)
1508 {
1509 switch (pObjInfo1->Attr.enmAdditional)
1510 {
1511 case RTFSOBJATTRADD_UNIX:
1512 if (pObjInfo1->Attr.u.Unix.uid != pObjInfo2->Attr.u.Unix.uid)
1513 return false;
1514 if (pObjInfo1->Attr.u.Unix.gid != pObjInfo2->Attr.u.Unix.gid)
1515 return false;
1516 if (pObjInfo1->Attr.u.Unix.INodeIdDevice != pObjInfo2->Attr.u.Unix.INodeIdDevice)
1517 return false;
1518 if (pObjInfo1->Attr.u.Unix.INodeId != pObjInfo2->Attr.u.Unix.INodeId)
1519 return false;
1520 if (pObjInfo1->Attr.u.Unix.GenerationId != pObjInfo2->Attr.u.Unix.GenerationId)
1521 return false;
1522 break;
1523 default:
1524 break;
1525 }
1526 }
1527 return true;
1528}
1529
1530
1531/**
1532 * @interface_method_impl{VBOXEXTPACKHLP,pfnFindModule}
1533 */
1534/*static*/ DECLCALLBACK(int)
1535ExtPack::hlpFindModule(PCVBOXEXTPACKHLP pHlp, const char *pszName, const char *pszExt, VBOXEXTPACKMODKIND enmKind,
1536 char *pszFound, size_t cbFound, bool *pfNative)
1537{
1538 /*
1539 * Validate the input and get our bearings.
1540 */
1541 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
1542 AssertPtrNullReturn(pszExt, VERR_INVALID_POINTER);
1543 AssertPtrReturn(pszFound, VERR_INVALID_POINTER);
1544 AssertPtrNullReturn(pfNative, VERR_INVALID_POINTER);
1545 AssertReturn(enmKind > VBOXEXTPACKMODKIND_INVALID && enmKind < VBOXEXTPACKMODKIND_END, VERR_INVALID_PARAMETER);
1546
1547 AssertPtrReturn(pHlp, VERR_INVALID_POINTER);
1548 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VERR_INVALID_POINTER);
1549 ExtPack::Data *m = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1550 AssertPtrReturn(m, VERR_INVALID_POINTER);
1551 ExtPack *pThis = m->pThis;
1552 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1553
1554 /*
1555 * This is just a wrapper around findModule.
1556 */
1557 Utf8Str strFound;
1558 if (pThis->findModule(pszName, pszExt, enmKind, &strFound, pfNative, NULL))
1559 return RTStrCopy(pszFound, cbFound, strFound.c_str());
1560 return VERR_FILE_NOT_FOUND;
1561}
1562
1563/*static*/ DECLCALLBACK(int)
1564ExtPack::hlpGetFilePath(PCVBOXEXTPACKHLP pHlp, const char *pszFilename, char *pszPath, size_t cbPath)
1565{
1566 /*
1567 * Validate the input and get our bearings.
1568 */
1569 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
1570 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1571 AssertReturn(cbPath > 0, VERR_BUFFER_OVERFLOW);
1572
1573 AssertPtrReturn(pHlp, VERR_INVALID_POINTER);
1574 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VERR_INVALID_POINTER);
1575 ExtPack::Data *m = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1576 AssertPtrReturn(m, VERR_INVALID_POINTER);
1577 ExtPack *pThis = m->pThis;
1578 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1579
1580 /*
1581 * This is a simple RTPathJoin, no checking if things exists or anything.
1582 */
1583 int vrc = RTPathJoin(pszPath, cbPath, pThis->m->strExtPackPath.c_str(), pszFilename);
1584 if (RT_FAILURE(vrc))
1585 RT_BZERO(pszPath, cbPath);
1586 return vrc;
1587}
1588
1589/*static*/ DECLCALLBACK(VBOXEXTPACKCTX)
1590ExtPack::hlpGetContext(PCVBOXEXTPACKHLP pHlp)
1591{
1592 /*
1593 * Validate the input and get our bearings.
1594 */
1595 AssertPtrReturn(pHlp, VBOXEXTPACKCTX_INVALID);
1596 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VBOXEXTPACKCTX_INVALID);
1597 ExtPack::Data *m = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1598 AssertPtrReturn(m, VBOXEXTPACKCTX_INVALID);
1599 ExtPack *pThis = m->pThis;
1600 AssertPtrReturn(pThis, VBOXEXTPACKCTX_INVALID);
1601
1602 return pThis->m->enmContext;
1603}
1604
1605/*static*/ DECLCALLBACK(int)
1606ExtPack::hlpLoadHGCMService(PCVBOXEXTPACKHLP pHlp, VBOXEXTPACK_IF_CS(IConsole) *pConsole,
1607 const char *pszServiceLibrary, const char *pszServiceName)
1608{
1609#ifdef VBOX_COM_INPROC
1610 /*
1611 * Validate the input and get our bearings.
1612 */
1613 AssertPtrReturn(pszServiceLibrary, VERR_INVALID_POINTER);
1614 AssertPtrReturn(pszServiceName, VERR_INVALID_POINTER);
1615
1616 AssertPtrReturn(pHlp, VERR_INVALID_POINTER);
1617 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VERR_INVALID_POINTER);
1618 ExtPack::Data *m = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1619 AssertPtrReturn(m, VERR_INVALID_POINTER);
1620 ExtPack *pThis = m->pThis;
1621 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1622 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
1623
1624 Console *pCon = (Console *)pConsole;
1625 return pCon->hgcmLoadService(pszServiceLibrary, pszServiceName);
1626#else
1627 NOREF(pHlp); NOREF(pConsole); NOREF(pszServiceLibrary); NOREF(pszServiceName);
1628#endif
1629 return VERR_INVALID_STATE;
1630}
1631
1632/*static*/ DECLCALLBACK(int)
1633ExtPack::hlpReservedN(PCVBOXEXTPACKHLP pHlp)
1634{
1635 /*
1636 * Validate the input and get our bearings.
1637 */
1638 AssertPtrReturn(pHlp, VERR_INVALID_POINTER);
1639 AssertReturn(pHlp->u32Version == VBOXEXTPACKHLP_VERSION, VERR_INVALID_POINTER);
1640 ExtPack::Data *m = RT_FROM_CPP_MEMBER(pHlp, Data, Hlp);
1641 AssertPtrReturn(m, VERR_INVALID_POINTER);
1642 ExtPack *pThis = m->pThis;
1643 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1644
1645 return VERR_NOT_IMPLEMENTED;
1646}
1647
1648
1649
1650
1651
1652STDMETHODIMP ExtPack::COMGETTER(Name)(BSTR *a_pbstrName)
1653{
1654 CheckComArgOutPointerValid(a_pbstrName);
1655
1656 AutoCaller autoCaller(this);
1657 HRESULT hrc = autoCaller.rc();
1658 if (SUCCEEDED(hrc))
1659 {
1660 Bstr str(m->Desc.strName);
1661 str.cloneTo(a_pbstrName);
1662 }
1663 return hrc;
1664}
1665
1666STDMETHODIMP ExtPack::COMGETTER(Description)(BSTR *a_pbstrDescription)
1667{
1668 CheckComArgOutPointerValid(a_pbstrDescription);
1669
1670 AutoCaller autoCaller(this);
1671 HRESULT hrc = autoCaller.rc();
1672 if (SUCCEEDED(hrc))
1673 {
1674 Bstr str(m->Desc.strDescription);
1675 str.cloneTo(a_pbstrDescription);
1676 }
1677 return hrc;
1678}
1679
1680STDMETHODIMP ExtPack::COMGETTER(Version)(BSTR *a_pbstrVersion)
1681{
1682 CheckComArgOutPointerValid(a_pbstrVersion);
1683
1684 AutoCaller autoCaller(this);
1685 HRESULT hrc = autoCaller.rc();
1686 if (SUCCEEDED(hrc))
1687 {
1688 Bstr str(m->Desc.strVersion);
1689 str.cloneTo(a_pbstrVersion);
1690 }
1691 return hrc;
1692}
1693
1694STDMETHODIMP ExtPack::COMGETTER(Revision)(ULONG *a_puRevision)
1695{
1696 CheckComArgOutPointerValid(a_puRevision);
1697
1698 AutoCaller autoCaller(this);
1699 HRESULT hrc = autoCaller.rc();
1700 if (SUCCEEDED(hrc))
1701 *a_puRevision = m->Desc.uRevision;
1702 return hrc;
1703}
1704
1705STDMETHODIMP ExtPack::COMGETTER(Edition)(BSTR *a_pbstrEdition)
1706{
1707 CheckComArgOutPointerValid(a_pbstrEdition);
1708
1709 AutoCaller autoCaller(this);
1710 HRESULT hrc = autoCaller.rc();
1711 if (SUCCEEDED(hrc))
1712 {
1713 Bstr str(m->Desc.strEdition);
1714 str.cloneTo(a_pbstrEdition);
1715 }
1716 return hrc;
1717}
1718
1719STDMETHODIMP ExtPack::COMGETTER(VRDEModule)(BSTR *a_pbstrVrdeModule)
1720{
1721 CheckComArgOutPointerValid(a_pbstrVrdeModule);
1722
1723 AutoCaller autoCaller(this);
1724 HRESULT hrc = autoCaller.rc();
1725 if (SUCCEEDED(hrc))
1726 {
1727 Bstr str(m->Desc.strVrdeModule);
1728 str.cloneTo(a_pbstrVrdeModule);
1729 }
1730 return hrc;
1731}
1732
1733STDMETHODIMP ExtPack::COMGETTER(PlugIns)(ComSafeArrayOut(IExtPackPlugIn *, a_paPlugIns))
1734{
1735 /** @todo implement plug-ins. */
1736#ifdef VBOX_WITH_XPCOM
1737 NOREF(a_paPlugIns);
1738 NOREF(a_paPlugInsSize);
1739#endif
1740 ReturnComNotImplemented();
1741}
1742
1743STDMETHODIMP ExtPack::COMGETTER(Usable)(BOOL *a_pfUsable)
1744{
1745 CheckComArgOutPointerValid(a_pfUsable);
1746
1747 AutoCaller autoCaller(this);
1748 HRESULT hrc = autoCaller.rc();
1749 if (SUCCEEDED(hrc))
1750 *a_pfUsable = m->fUsable;
1751 return hrc;
1752}
1753
1754STDMETHODIMP ExtPack::COMGETTER(WhyUnusable)(BSTR *a_pbstrWhy)
1755{
1756 CheckComArgOutPointerValid(a_pbstrWhy);
1757
1758 AutoCaller autoCaller(this);
1759 HRESULT hrc = autoCaller.rc();
1760 if (SUCCEEDED(hrc))
1761 m->strWhyUnusable.cloneTo(a_pbstrWhy);
1762 return hrc;
1763}
1764
1765STDMETHODIMP ExtPack::COMGETTER(ShowLicense)(BOOL *a_pfShowIt)
1766{
1767 CheckComArgOutPointerValid(a_pfShowIt);
1768
1769 AutoCaller autoCaller(this);
1770 HRESULT hrc = autoCaller.rc();
1771 if (SUCCEEDED(hrc))
1772 *a_pfShowIt = m->Desc.fShowLicense;
1773 return hrc;
1774}
1775
1776STDMETHODIMP ExtPack::COMGETTER(License)(BSTR *a_pbstrHtmlLicense)
1777{
1778 Bstr bstrHtml("html");
1779 return QueryLicense(Bstr::Empty.raw(), Bstr::Empty.raw(), bstrHtml.raw(), a_pbstrHtmlLicense);
1780}
1781
1782STDMETHODIMP ExtPack::QueryLicense(IN_BSTR a_bstrPreferredLocale, IN_BSTR a_bstrPreferredLanguage, IN_BSTR a_bstrFormat,
1783 BSTR *a_pbstrLicense)
1784{
1785 /*
1786 * Validate input.
1787 */
1788 CheckComArgOutPointerValid(a_pbstrLicense);
1789 CheckComArgNotNull(a_bstrPreferredLocale);
1790 CheckComArgNotNull(a_bstrPreferredLanguage);
1791 CheckComArgNotNull(a_bstrFormat);
1792
1793 Utf8Str strPreferredLocale(a_bstrPreferredLocale);
1794 if (strPreferredLocale.length() != 2 && strPreferredLocale.length() != 0)
1795 return setError(E_FAIL, tr("The preferred locale is a two character string or empty."));
1796
1797 Utf8Str strPreferredLanguage(a_bstrPreferredLanguage);
1798 if (strPreferredLanguage.length() != 2 && strPreferredLanguage.length() != 0)
1799 return setError(E_FAIL, tr("The preferred lanuage is a two character string or empty."));
1800
1801 Utf8Str strFormat(a_bstrFormat);
1802 if ( !strFormat.equals("html")
1803 && !strFormat.equals("rtf")
1804 && !strFormat.equals("txt"))
1805 return setError(E_FAIL, tr("The license format can only have the values 'html', 'rtf' and 'txt'."));
1806
1807 /*
1808 * Combine the options to form a file name before locking down anything.
1809 */
1810 char szName[sizeof(VBOX_EXTPACK_LICENSE_NAME_PREFIX "-de_DE.html") + 2];
1811 if (strPreferredLocale.isNotEmpty() && strPreferredLanguage.isNotEmpty())
1812 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX "-%s_%s.%s",
1813 strPreferredLocale.c_str(), strPreferredLanguage.c_str(), strFormat.c_str());
1814 else if (strPreferredLocale.isNotEmpty())
1815 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX "-%s.%s", strPreferredLocale.c_str(), strFormat.c_str());
1816 else if (strPreferredLanguage.isNotEmpty())
1817 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX "-_%s.%s", strPreferredLocale.c_str(), strFormat.c_str());
1818 else
1819 RTStrPrintf(szName, sizeof(szName), VBOX_EXTPACK_LICENSE_NAME_PREFIX ".%s", strFormat.c_str());
1820
1821 /*
1822 * Effectuate the query.
1823 */
1824 AutoCaller autoCaller(this);
1825 HRESULT hrc = autoCaller.rc();
1826 if (SUCCEEDED(hrc))
1827 {
1828 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS); /* paranoia */
1829
1830 if (!m->fUsable)
1831 hrc = setError(E_FAIL, tr("%s"), m->strWhyUnusable.c_str());
1832 else
1833 {
1834 char szPath[RTPATH_MAX];
1835 int vrc = RTPathJoin(szPath, sizeof(szPath), m->strExtPackPath.c_str(), szName);
1836 if (RT_SUCCESS(vrc))
1837 {
1838 void *pvFile;
1839 size_t cbFile;
1840 vrc = RTFileReadAllEx(szPath, 0, RTFOFF_MAX, RTFILE_RDALL_O_DENY_READ, &pvFile, &cbFile);
1841 if (RT_SUCCESS(vrc))
1842 {
1843 Bstr bstrLicense((const char *)pvFile, cbFile);
1844 if (bstrLicense.isNotEmpty())
1845 {
1846 bstrLicense.detachTo(a_pbstrLicense);
1847 hrc = S_OK;
1848 }
1849 else
1850 hrc = setError(VBOX_E_IPRT_ERROR, tr("The license file '%s' is empty or contains invalid UTF-8 encoding"),
1851 szPath);
1852 RTFileReadAllFree(pvFile, cbFile);
1853 }
1854 else if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
1855 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("The license file '%s' was not found in extension pack '%s'"),
1856 szName, m->Desc.strName.c_str());
1857 else
1858 hrc = setError(VBOX_E_FILE_ERROR, tr("Failed to open the license file '%s': %Rrc"), szPath, vrc);
1859 }
1860 else
1861 hrc = setError(VBOX_E_IPRT_ERROR, tr("RTPathJoin failed: %Rrc"), vrc);
1862 }
1863 }
1864 return hrc;
1865}
1866
1867
1868STDMETHODIMP ExtPack::QueryObject(IN_BSTR a_bstrObjectId, IUnknown **a_ppUnknown)
1869{
1870 com::Guid ObjectId;
1871 CheckComArgGuid(a_bstrObjectId, ObjectId);
1872 CheckComArgOutPointerValid(a_ppUnknown);
1873
1874 AutoCaller autoCaller(this);
1875 HRESULT hrc = autoCaller.rc();
1876 if (SUCCEEDED(hrc))
1877 {
1878 if ( m->pReg
1879 && m->pReg->pfnQueryObject)
1880 {
1881 void *pvUnknown = m->pReg->pfnQueryObject(m->pReg, ObjectId.raw());
1882 if (pvUnknown)
1883 *a_ppUnknown = (IUnknown *)pvUnknown;
1884 else
1885 hrc = E_NOINTERFACE;
1886 }
1887 else
1888 hrc = E_NOINTERFACE;
1889 }
1890 return hrc;
1891}
1892
1893
1894
1895
1896DEFINE_EMPTY_CTOR_DTOR(ExtPackManager)
1897
1898/**
1899 * Called by ComObjPtr::createObject when creating the object.
1900 *
1901 * Just initialize the basic object state, do the rest in init().
1902 *
1903 * @returns S_OK.
1904 */
1905HRESULT ExtPackManager::FinalConstruct()
1906{
1907 m = NULL;
1908 return S_OK;
1909}
1910
1911/**
1912 * Initializes the extension pack manager.
1913 *
1914 * @returns COM status code.
1915 * @param a_pVirtualBox Pointer to the VirtualBox object.
1916 * @param a_enmContext The context we're in.
1917 */
1918HRESULT ExtPackManager::initExtPackManager(VirtualBox *a_pVirtualBox, VBOXEXTPACKCTX a_enmContext)
1919{
1920 AutoInitSpan autoInitSpan(this);
1921 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1922
1923 /*
1924 * Figure some stuff out before creating the instance data.
1925 */
1926 char szBaseDir[RTPATH_MAX];
1927 int rc = RTPathAppPrivateArchTop(szBaseDir, sizeof(szBaseDir));
1928 AssertLogRelRCReturn(rc, E_FAIL);
1929 rc = RTPathAppend(szBaseDir, sizeof(szBaseDir), VBOX_EXTPACK_INSTALL_DIR);
1930 AssertLogRelRCReturn(rc, E_FAIL);
1931
1932 char szCertificatDir[RTPATH_MAX];
1933 rc = RTPathAppPrivateNoArch(szCertificatDir, sizeof(szCertificatDir));
1934 AssertLogRelRCReturn(rc, E_FAIL);
1935 rc = RTPathAppend(szCertificatDir, sizeof(szCertificatDir), VBOX_EXTPACK_CERT_DIR);
1936 AssertLogRelRCReturn(rc, E_FAIL);
1937
1938 /*
1939 * Allocate and initialize the instance data.
1940 */
1941 m = new Data;
1942 m->strBaseDir = szBaseDir;
1943 m->strCertificatDirPath = szCertificatDir;
1944#if !defined(VBOX_COM_INPROC)
1945 m->pVirtualBox = a_pVirtualBox;
1946#endif
1947 m->enmContext = a_enmContext;
1948
1949 /*
1950 * Slurp in VBoxVMM which is used by VBoxPuelMain.
1951 */
1952#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_DARWIN)
1953 if (a_enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON)
1954 {
1955 int vrc = SUPR3HardenedLdrLoadAppPriv("VBoxVMM", &m->hVBoxVMM, RTLDRLOAD_FLAGS_GLOBAL, NULL);
1956 if (RT_FAILURE(vrc))
1957 m->hVBoxVMM = NIL_RTLDRMOD;
1958 /* cleanup in ::uninit()? */
1959 }
1960#endif
1961
1962 /*
1963 * Go looking for extensions. The RTDirOpen may fail if nothing has been
1964 * installed yet, or if root is paranoid and has revoked our access to them.
1965 *
1966 * We ASSUME that there are no files, directories or stuff in the directory
1967 * that exceed the max name length in RTDIRENTRYEX.
1968 */
1969 HRESULT hrc = S_OK;
1970 PRTDIR pDir;
1971 int vrc = RTDirOpen(&pDir, szBaseDir);
1972 if (RT_SUCCESS(vrc))
1973 {
1974 for (;;)
1975 {
1976 RTDIRENTRYEX Entry;
1977 vrc = RTDirReadEx(pDir, &Entry, NULL /*pcbDirEntry*/, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1978 if (RT_FAILURE(vrc))
1979 {
1980 AssertLogRelMsg(vrc == VERR_NO_MORE_FILES, ("%Rrc\n", vrc));
1981 break;
1982 }
1983 if ( RTFS_IS_DIRECTORY(Entry.Info.Attr.fMode)
1984 && strcmp(Entry.szName, ".") != 0
1985 && strcmp(Entry.szName, "..") != 0
1986 && VBoxExtPackIsValidMangledName(Entry.szName) )
1987 {
1988 /*
1989 * All directories are extensions, the shall be nothing but
1990 * extensions in this subdirectory.
1991 */
1992 char szExtPackDir[RTPATH_MAX];
1993 vrc = RTPathJoin(szExtPackDir, sizeof(szExtPackDir), m->strBaseDir.c_str(), Entry.szName);
1994 AssertLogRelRC(vrc);
1995 if (RT_SUCCESS(vrc))
1996 {
1997 RTCString *pstrName = VBoxExtPackUnmangleName(Entry.szName, RTSTR_MAX);
1998 AssertLogRel(pstrName);
1999 if (pstrName)
2000 {
2001 ComObjPtr<ExtPack> NewExtPack;
2002 HRESULT hrc2 = NewExtPack.createObject();
2003 if (SUCCEEDED(hrc2))
2004 hrc2 = NewExtPack->initWithDir(a_enmContext, pstrName->c_str(), szExtPackDir);
2005 delete pstrName;
2006 if (SUCCEEDED(hrc2))
2007 m->llInstalledExtPacks.push_back(NewExtPack);
2008 else if (SUCCEEDED(rc))
2009 hrc = hrc2;
2010 }
2011 else
2012 hrc = E_UNEXPECTED;
2013 }
2014 else
2015 hrc = E_UNEXPECTED;
2016 }
2017 }
2018 RTDirClose(pDir);
2019 }
2020 /* else: ignore, the directory probably does not exist or something. */
2021
2022 if (SUCCEEDED(hrc))
2023 autoInitSpan.setSucceeded();
2024 return hrc;
2025}
2026
2027/**
2028 * COM cruft.
2029 */
2030void ExtPackManager::FinalRelease()
2031{
2032 uninit();
2033}
2034
2035/**
2036 * Do the actual cleanup.
2037 */
2038void ExtPackManager::uninit()
2039{
2040 /* Enclose the state transition Ready->InUninit->NotReady */
2041 AutoUninitSpan autoUninitSpan(this);
2042 if (!autoUninitSpan.uninitDone() && m != NULL)
2043 {
2044 delete m;
2045 m = NULL;
2046 }
2047}
2048
2049
2050STDMETHODIMP ExtPackManager::COMGETTER(InstalledExtPacks)(ComSafeArrayOut(IExtPack *, a_paExtPacks))
2051{
2052 CheckComArgOutSafeArrayPointerValid(a_paExtPacks);
2053 Assert(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON);
2054
2055 AutoCaller autoCaller(this);
2056 HRESULT hrc = autoCaller.rc();
2057 if (SUCCEEDED(hrc))
2058 {
2059 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
2060
2061 SafeIfaceArray<IExtPack> SaExtPacks(m->llInstalledExtPacks);
2062 SaExtPacks.detachTo(ComSafeArrayOutArg(a_paExtPacks));
2063 }
2064
2065 return hrc;
2066}
2067
2068STDMETHODIMP ExtPackManager::Find(IN_BSTR a_bstrName, IExtPack **a_pExtPack)
2069{
2070 CheckComArgNotNull(a_bstrName);
2071 CheckComArgOutPointerValid(a_pExtPack);
2072 Utf8Str strName(a_bstrName);
2073 Assert(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON);
2074
2075 AutoCaller autoCaller(this);
2076 HRESULT hrc = autoCaller.rc();
2077 if (SUCCEEDED(hrc))
2078 {
2079 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
2080
2081 ComPtr<ExtPack> ptrExtPack = findExtPack(strName.c_str());
2082 if (!ptrExtPack.isNull())
2083 ptrExtPack.queryInterfaceTo(a_pExtPack);
2084 else
2085 hrc = VBOX_E_OBJECT_NOT_FOUND;
2086 }
2087
2088 return hrc;
2089}
2090
2091STDMETHODIMP ExtPackManager::OpenExtPackFile(IN_BSTR a_bstrTarballAndDigest, IExtPackFile **a_ppExtPackFile)
2092{
2093 CheckComArgNotNull(a_bstrTarballAndDigest);
2094 CheckComArgOutPointerValid(a_ppExtPackFile);
2095 AssertReturn(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON, E_UNEXPECTED);
2096
2097#if !defined(VBOX_COM_INPROC)
2098 /* The API can optionally take a ::SHA-256=<hex-digest> attribute at the
2099 end of the file name. This is just a temporary measure for
2100 backporting, in 4.2 we'll add another parameter to the method. */
2101 Utf8Str strTarball;
2102 Utf8Str strDigest;
2103 Utf8Str strTarballAndDigest(a_bstrTarballAndDigest);
2104 size_t offSha256 = strTarballAndDigest.find("::SHA-256=");
2105 if (offSha256 == Utf8Str::npos)
2106 strTarball = strTarballAndDigest;
2107 else
2108 {
2109 strTarball = strTarballAndDigest.substr(0, offSha256);
2110 strDigest = strTarballAndDigest.substr(offSha256 + sizeof("::SHA-256=") - 1);
2111 }
2112
2113 ComObjPtr<ExtPackFile> NewExtPackFile;
2114 HRESULT hrc = NewExtPackFile.createObject();
2115 if (SUCCEEDED(hrc))
2116 hrc = NewExtPackFile->initWithFile(strTarball.c_str(), strDigest.c_str(), this, m->pVirtualBox);
2117 if (SUCCEEDED(hrc))
2118 NewExtPackFile.queryInterfaceTo(a_ppExtPackFile);
2119
2120 return hrc;
2121#else
2122 return E_NOTIMPL;
2123#endif
2124}
2125
2126STDMETHODIMP ExtPackManager::Uninstall(IN_BSTR a_bstrName, BOOL a_fForcedRemoval, IN_BSTR a_bstrDisplayInfo,
2127 IProgress **a_ppProgress)
2128{
2129 CheckComArgNotNull(a_bstrName);
2130 if (a_ppProgress)
2131 *a_ppProgress = NULL;
2132 Assert(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON);
2133
2134#if !defined(VBOX_COM_INPROC)
2135 AutoCaller autoCaller(this);
2136 HRESULT hrc = autoCaller.rc();
2137 if (SUCCEEDED(hrc))
2138 {
2139 PEXTPACKUNINSTALLJOB pJob = NULL;
2140 try
2141 {
2142 pJob = new EXTPACKUNINSTALLJOB;
2143 pJob->ptrExtPackMgr = this;
2144 pJob->strName = a_bstrName;
2145 pJob->fForcedRemoval = a_fForcedRemoval != FALSE;
2146 pJob->strDisplayInfo = a_bstrDisplayInfo;
2147 hrc = pJob->ptrProgress.createObject();
2148 if (SUCCEEDED(hrc))
2149 {
2150 Bstr bstrDescription = tr("Uninstalling extension pack");
2151 hrc = pJob->ptrProgress->init(
2152#ifndef VBOX_COM_INPROC
2153 m->pVirtualBox,
2154#endif
2155 static_cast<IExtPackManager *>(this),
2156 bstrDescription.raw(),
2157 FALSE /*aCancelable*/,
2158 NULL /*aId*/);
2159 }
2160 if (SUCCEEDED(hrc))
2161 {
2162 ComPtr<Progress> ptrProgress = pJob->ptrProgress;
2163 int vrc = RTThreadCreate(NULL /*phThread*/, ExtPackManager::doUninstallThreadProc, pJob, 0,
2164 RTTHREADTYPE_DEFAULT, 0 /*fFlags*/, "ExtPackUninst");
2165 if (RT_SUCCESS(vrc))
2166 {
2167 pJob = NULL; /* the thread deletes it */
2168 ptrProgress.queryInterfaceTo(a_ppProgress);
2169 }
2170 else
2171 hrc = setError(VBOX_E_IPRT_ERROR, tr("RTThreadCreate failed with %Rrc"), vrc);
2172 }
2173 }
2174 catch (std::bad_alloc)
2175 {
2176 hrc = E_OUTOFMEMORY;
2177 }
2178 if (pJob)
2179 delete pJob;
2180 }
2181
2182 return hrc;
2183#else
2184 return E_NOTIMPL;
2185#endif
2186}
2187
2188STDMETHODIMP ExtPackManager::Cleanup(void)
2189{
2190 Assert(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON);
2191
2192 AutoCaller autoCaller(this);
2193 HRESULT hrc = autoCaller.rc();
2194 if (SUCCEEDED(hrc))
2195 {
2196 /*
2197 * Run the set-uid-to-root binary that performs the cleanup.
2198 *
2199 * Take the write lock to prevent conflicts with other calls to this
2200 * VBoxSVC instance.
2201 */
2202 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
2203 hrc = runSetUidToRootHelper(NULL,
2204 "cleanup",
2205 "--base-dir", m->strBaseDir.c_str(),
2206 (const char *)NULL);
2207 }
2208
2209 return hrc;
2210}
2211
2212STDMETHODIMP ExtPackManager::QueryAllPlugInsForFrontend(IN_BSTR a_bstrFrontend, ComSafeArrayOut(BSTR, a_pabstrPlugInModules))
2213{
2214 CheckComArgNotNull(a_bstrFrontend);
2215 Utf8Str strName(a_bstrFrontend);
2216 CheckComArgOutSafeArrayPointerValid(a_pabstrPlugInModules);
2217 Assert(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON);
2218
2219 AutoCaller autoCaller(this);
2220 HRESULT hrc = autoCaller.rc();
2221 if (SUCCEEDED(hrc))
2222 {
2223 com::SafeArray<BSTR> saPaths((size_t)0);
2224 /** @todo implement plug-ins */
2225 saPaths.detachTo(ComSafeArrayOutArg(a_pabstrPlugInModules));
2226 }
2227 return hrc;
2228}
2229
2230STDMETHODIMP ExtPackManager::IsExtPackUsable(IN_BSTR a_bstrExtPack, BOOL *aUsable)
2231{
2232 CheckComArgNotNull(a_bstrExtPack);
2233 Utf8Str strExtPack(a_bstrExtPack);
2234 *aUsable = isExtPackUsable(strExtPack.c_str());
2235 return S_OK;
2236}
2237
2238/**
2239 * Finds the success indicator string in the stderr output ofr hte helper app.
2240 *
2241 * @returns Pointer to the indicator.
2242 * @param psz The stderr output string. Can be NULL.
2243 * @param cch The size of the string.
2244 */
2245static char *findSuccessIndicator(char *psz, size_t cch)
2246{
2247 static const char s_szSuccessInd[] = "rcExit=RTEXITCODE_SUCCESS";
2248 Assert(!cch || strlen(psz) == cch);
2249 if (cch < sizeof(s_szSuccessInd) - 1)
2250 return NULL;
2251 char *pszInd = &psz[cch - sizeof(s_szSuccessInd) + 1];
2252 if (strcmp(s_szSuccessInd, pszInd))
2253 return NULL;
2254 return pszInd;
2255}
2256
2257/**
2258 * Runs the helper application that does the privileged operations.
2259 *
2260 * @returns S_OK or a failure status with error information set.
2261 * @param a_pstrDisplayInfo Platform specific display info hacks.
2262 * @param a_pszCommand The command to execute.
2263 * @param ... The argument strings that goes along with the
2264 * command. Maximum is about 16. Terminated by a
2265 * NULL.
2266 */
2267HRESULT ExtPackManager::runSetUidToRootHelper(Utf8Str const *a_pstrDisplayInfo, const char *a_pszCommand, ...)
2268{
2269 /*
2270 * Calculate the path to the helper application.
2271 */
2272 char szExecName[RTPATH_MAX];
2273 int vrc = RTPathAppPrivateArch(szExecName, sizeof(szExecName));
2274 AssertLogRelRCReturn(vrc, E_UNEXPECTED);
2275
2276 vrc = RTPathAppend(szExecName, sizeof(szExecName), VBOX_EXTPACK_HELPER_NAME);
2277 AssertLogRelRCReturn(vrc, E_UNEXPECTED);
2278
2279 /*
2280 * Convert the variable argument list to a RTProcCreate argument vector.
2281 */
2282 const char *apszArgs[20];
2283 unsigned cArgs = 0;
2284
2285 LogRel(("ExtPack: Executing '%s'", szExecName));
2286 apszArgs[cArgs++] = &szExecName[0];
2287
2288 if ( a_pstrDisplayInfo
2289 && a_pstrDisplayInfo->isNotEmpty())
2290 {
2291 LogRel((" '--display-info-hack' '%s'", a_pstrDisplayInfo->c_str()));
2292 apszArgs[cArgs++] = "--display-info-hack";
2293 apszArgs[cArgs++] = a_pstrDisplayInfo->c_str();
2294 }
2295
2296 LogRel(("'%s'", a_pszCommand));
2297 apszArgs[cArgs++] = a_pszCommand;
2298
2299 va_list va;
2300 va_start(va, a_pszCommand);
2301 const char *pszLastArg;
2302 for (;;)
2303 {
2304 AssertReturn(cArgs < RT_ELEMENTS(apszArgs) - 1, E_UNEXPECTED);
2305 pszLastArg = va_arg(va, const char *);
2306 if (!pszLastArg)
2307 break;
2308 LogRel((" '%s'", pszLastArg));
2309 apszArgs[cArgs++] = pszLastArg;
2310 };
2311 va_end(va);
2312
2313 LogRel(("\n"));
2314 apszArgs[cArgs] = NULL;
2315
2316 /*
2317 * Create a PIPE which we attach to stderr so that we can read the error
2318 * message on failure and report it back to the caller.
2319 */
2320 RTPIPE hPipeR;
2321 RTHANDLE hStdErrPipe;
2322 hStdErrPipe.enmType = RTHANDLETYPE_PIPE;
2323 vrc = RTPipeCreate(&hPipeR, &hStdErrPipe.u.hPipe, RTPIPE_C_INHERIT_WRITE);
2324 AssertLogRelRCReturn(vrc, E_UNEXPECTED);
2325
2326 /*
2327 * Spawn the process.
2328 */
2329 HRESULT hrc;
2330 RTPROCESS hProcess;
2331 vrc = RTProcCreateEx(szExecName,
2332 apszArgs,
2333 RTENV_DEFAULT,
2334 0 /*fFlags*/,
2335 NULL /*phStdIn*/,
2336 NULL /*phStdOut*/,
2337 &hStdErrPipe,
2338 NULL /*pszAsUser*/,
2339 NULL /*pszPassword*/,
2340 &hProcess);
2341 if (RT_SUCCESS(vrc))
2342 {
2343 vrc = RTPipeClose(hStdErrPipe.u.hPipe);
2344 hStdErrPipe.u.hPipe = NIL_RTPIPE;
2345
2346 /*
2347 * Read the pipe output until the process completes.
2348 */
2349 RTPROCSTATUS ProcStatus = { -42, RTPROCEXITREASON_ABEND };
2350 size_t cbStdErrBuf = 0;
2351 size_t offStdErrBuf = 0;
2352 char *pszStdErrBuf = NULL;
2353 do
2354 {
2355 /*
2356 * Service the pipe. Block waiting for output or the pipe breaking
2357 * when the process terminates.
2358 */
2359 if (hPipeR != NIL_RTPIPE)
2360 {
2361 char achBuf[1024];
2362 size_t cbRead;
2363 vrc = RTPipeReadBlocking(hPipeR, achBuf, sizeof(achBuf), &cbRead);
2364 if (RT_SUCCESS(vrc))
2365 {
2366 /* grow the buffer? */
2367 size_t cbBufReq = offStdErrBuf + cbRead + 1;
2368 if ( cbBufReq > cbStdErrBuf
2369 && cbBufReq < _256K)
2370 {
2371 size_t cbNew = RT_ALIGN_Z(cbBufReq, 16); // 1024
2372 void *pvNew = RTMemRealloc(pszStdErrBuf, cbNew);
2373 if (pvNew)
2374 {
2375 pszStdErrBuf = (char *)pvNew;
2376 cbStdErrBuf = cbNew;
2377 }
2378 }
2379
2380 /* append if we've got room. */
2381 if (cbBufReq <= cbStdErrBuf)
2382 {
2383 memcpy(&pszStdErrBuf[offStdErrBuf], achBuf, cbRead);
2384 offStdErrBuf = offStdErrBuf + cbRead;
2385 pszStdErrBuf[offStdErrBuf] = '\0';
2386 }
2387 }
2388 else
2389 {
2390 AssertLogRelMsg(vrc == VERR_BROKEN_PIPE, ("%Rrc\n", vrc));
2391 RTPipeClose(hPipeR);
2392 hPipeR = NIL_RTPIPE;
2393 }
2394 }
2395
2396 /*
2397 * Service the process. Block if we have no pipe.
2398 */
2399 if (hProcess != NIL_RTPROCESS)
2400 {
2401 vrc = RTProcWait(hProcess,
2402 hPipeR == NIL_RTPIPE ? RTPROCWAIT_FLAGS_BLOCK : RTPROCWAIT_FLAGS_NOBLOCK,
2403 &ProcStatus);
2404 if (RT_SUCCESS(vrc))
2405 hProcess = NIL_RTPROCESS;
2406 else
2407 AssertLogRelMsgStmt(vrc == VERR_PROCESS_RUNNING, ("%Rrc\n", vrc), hProcess = NIL_RTPROCESS);
2408 }
2409 } while ( hPipeR != NIL_RTPIPE
2410 || hProcess != NIL_RTPROCESS);
2411
2412 LogRel(("ExtPack: enmReason=%d iStatus=%d stderr='%s'\n",
2413 ProcStatus.enmReason, ProcStatus.iStatus, offStdErrBuf ? pszStdErrBuf : ""));
2414
2415 /*
2416 * Look for rcExit=RTEXITCODE_SUCCESS at the end of the error output,
2417 * cut it as it is only there to attest the success.
2418 */
2419 if (offStdErrBuf > 0)
2420 {
2421 RTStrStripR(pszStdErrBuf);
2422 offStdErrBuf = strlen(pszStdErrBuf);
2423 }
2424
2425 char *pszSuccessInd = findSuccessIndicator(pszStdErrBuf, offStdErrBuf);
2426 if (pszSuccessInd)
2427 {
2428 *pszSuccessInd = '\0';
2429 offStdErrBuf = pszSuccessInd - pszStdErrBuf;
2430 }
2431 else if ( ProcStatus.enmReason == RTPROCEXITREASON_NORMAL
2432 && ProcStatus.iStatus == 0)
2433 ProcStatus.iStatus = offStdErrBuf ? 667 : 666;
2434
2435 /*
2436 * Compose the status code and, on failure, error message.
2437 */
2438 if ( ProcStatus.enmReason == RTPROCEXITREASON_NORMAL
2439 && ProcStatus.iStatus == 0)
2440 hrc = S_OK;
2441 else if (ProcStatus.enmReason == RTPROCEXITREASON_NORMAL)
2442 {
2443 AssertMsg(ProcStatus.iStatus != 0, ("%s\n", pszStdErrBuf));
2444 hrc = setError(E_FAIL, tr("The installer failed with exit code %d: %s"),
2445 ProcStatus.iStatus, offStdErrBuf ? pszStdErrBuf : "");
2446 }
2447 else if (ProcStatus.enmReason == RTPROCEXITREASON_SIGNAL)
2448 hrc = setError(E_UNEXPECTED, tr("The installer was killed by signal #d (stderr: %s)"),
2449 ProcStatus.iStatus, offStdErrBuf ? pszStdErrBuf : "");
2450 else if (ProcStatus.enmReason == RTPROCEXITREASON_ABEND)
2451 hrc = setError(E_UNEXPECTED, tr("The installer aborted abnormally (stderr: %s)"),
2452 offStdErrBuf ? pszStdErrBuf : "");
2453 else
2454 hrc = setError(E_UNEXPECTED, tr("internal error: enmReason=%d iStatus=%d stderr='%s'"),
2455 ProcStatus.enmReason, ProcStatus.iStatus, offStdErrBuf ? pszStdErrBuf : "");
2456
2457 RTMemFree(pszStdErrBuf);
2458 }
2459 else
2460 hrc = setError(VBOX_E_IPRT_ERROR, tr("Failed to launch the helper application '%s' (%Rrc)"), szExecName, vrc);
2461
2462 RTPipeClose(hPipeR);
2463 RTPipeClose(hStdErrPipe.u.hPipe);
2464
2465 return hrc;
2466}
2467
2468/**
2469 * Finds an installed extension pack.
2470 *
2471 * @returns Pointer to the extension pack if found, NULL if not. (No reference
2472 * counting problem here since the caller must be holding the lock.)
2473 * @param a_pszName The name of the extension pack.
2474 */
2475ExtPack *ExtPackManager::findExtPack(const char *a_pszName)
2476{
2477 size_t cchName = strlen(a_pszName);
2478
2479 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
2480 it != m->llInstalledExtPacks.end();
2481 it++)
2482 {
2483 ExtPack::Data *pExtPackData = (*it)->m;
2484 if ( pExtPackData
2485 && pExtPackData->Desc.strName.length() == cchName
2486 && pExtPackData->Desc.strName.equalsIgnoreCase(a_pszName))
2487 return (*it);
2488 }
2489 return NULL;
2490}
2491
2492/**
2493 * Removes an installed extension pack from the internal list.
2494 *
2495 * The package is expected to exist!
2496 *
2497 * @param a_pszName The name of the extension pack.
2498 */
2499void ExtPackManager::removeExtPack(const char *a_pszName)
2500{
2501 size_t cchName = strlen(a_pszName);
2502
2503 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
2504 it != m->llInstalledExtPacks.end();
2505 it++)
2506 {
2507 ExtPack::Data *pExtPackData = (*it)->m;
2508 if ( pExtPackData
2509 && pExtPackData->Desc.strName.length() == cchName
2510 && pExtPackData->Desc.strName.equalsIgnoreCase(a_pszName))
2511 {
2512 m->llInstalledExtPacks.erase(it);
2513 return;
2514 }
2515 }
2516 AssertMsgFailed(("%s\n", a_pszName));
2517}
2518
2519#if !defined(VBOX_COM_INPROC)
2520/**
2521 * Refreshes the specified extension pack.
2522 *
2523 * This may remove the extension pack from the list, so any non-smart pointers
2524 * to the extension pack object may become invalid.
2525 *
2526 * @returns S_OK and *a_ppExtPack on success, COM status code and error
2527 * message on failure. Note that *a_ppExtPack can be NULL.
2528 *
2529 * @param a_pszName The extension to update..
2530 * @param a_fUnusableIsError If @c true, report an unusable extension pack
2531 * as an error.
2532 * @param a_ppExtPack Where to store the pointer to the extension
2533 * pack of it is still around after the refresh.
2534 * This is optional.
2535 *
2536 * @remarks Caller holds the extension manager lock.
2537 * @remarks Only called in VBoxSVC.
2538 */
2539HRESULT ExtPackManager::refreshExtPack(const char *a_pszName, bool a_fUnusableIsError, ExtPack **a_ppExtPack)
2540{
2541 Assert(m->pVirtualBox != NULL); /* Only called from VBoxSVC. */
2542
2543 HRESULT hrc;
2544 ExtPack *pExtPack = findExtPack(a_pszName);
2545 if (pExtPack)
2546 {
2547 /*
2548 * Refresh existing object.
2549 */
2550 bool fCanDelete;
2551 hrc = pExtPack->refresh(&fCanDelete);
2552 if (SUCCEEDED(hrc))
2553 {
2554 if (fCanDelete)
2555 {
2556 removeExtPack(a_pszName);
2557 pExtPack = NULL;
2558 }
2559 }
2560 }
2561 else
2562 {
2563 /*
2564 * Do this check here, otherwise VBoxExtPackCalcDir() will fail with a strange
2565 * error.
2566 */
2567 bool fValid = VBoxExtPackIsValidName(a_pszName);
2568 if (!fValid)
2569 return setError(E_FAIL, "Invalid extension pack name specified");
2570
2571 /*
2572 * Does the dir exist? Make some special effort to deal with case
2573 * sensitivie file systems (a_pszName is case insensitive and mangled).
2574 */
2575 char szDir[RTPATH_MAX];
2576 int vrc = VBoxExtPackCalcDir(szDir, sizeof(szDir), m->strBaseDir.c_str(), a_pszName);
2577 AssertLogRelRCReturn(vrc, E_FAIL);
2578
2579 RTDIRENTRYEX Entry;
2580 RTFSOBJINFO ObjInfo;
2581 vrc = RTPathQueryInfoEx(szDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2582 bool fExists = RT_SUCCESS(vrc) && RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode);
2583 if (!fExists)
2584 {
2585 PRTDIR pDir;
2586 vrc = RTDirOpen(&pDir, m->strBaseDir.c_str());
2587 if (RT_SUCCESS(vrc))
2588 {
2589 const char *pszMangledName = RTPathFilename(szDir);
2590 for (;;)
2591 {
2592 vrc = RTDirReadEx(pDir, &Entry, NULL /*pcbDirEntry*/, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2593 if (RT_FAILURE(vrc))
2594 {
2595 AssertLogRelMsg(vrc == VERR_NO_MORE_FILES, ("%Rrc\n", vrc));
2596 break;
2597 }
2598 if ( RTFS_IS_DIRECTORY(Entry.Info.Attr.fMode)
2599 && !RTStrICmp(Entry.szName, pszMangledName))
2600 {
2601 /*
2602 * The installed extension pack has a uses different case.
2603 * Update the name and directory variables.
2604 */
2605 vrc = RTPathJoin(szDir, sizeof(szDir), m->strBaseDir.c_str(), Entry.szName); /* not really necessary */
2606 AssertLogRelRCReturnStmt(vrc, RTDirClose(pDir), E_UNEXPECTED);
2607 a_pszName = Entry.szName;
2608 fExists = true;
2609 break;
2610 }
2611 }
2612 RTDirClose(pDir);
2613 }
2614 }
2615 if (fExists)
2616 {
2617 /*
2618 * We've got something, create a new extension pack object for it.
2619 */
2620 ComObjPtr<ExtPack> ptrNewExtPack;
2621 hrc = ptrNewExtPack.createObject();
2622 if (SUCCEEDED(hrc))
2623 hrc = ptrNewExtPack->initWithDir(m->enmContext, a_pszName, szDir);
2624 if (SUCCEEDED(hrc))
2625 {
2626 m->llInstalledExtPacks.push_back(ptrNewExtPack);
2627 if (ptrNewExtPack->m->fUsable)
2628 LogRel(("ExtPackManager: Found extension pack '%s'.\n", a_pszName));
2629 else
2630 LogRel(("ExtPackManager: Found bad extension pack '%s': %s\n",
2631 a_pszName, ptrNewExtPack->m->strWhyUnusable.c_str() ));
2632 pExtPack = ptrNewExtPack;
2633 }
2634 }
2635 else
2636 hrc = S_OK;
2637 }
2638
2639 /*
2640 * Report error if not usable, if that is desired.
2641 */
2642 if ( SUCCEEDED(hrc)
2643 && pExtPack
2644 && a_fUnusableIsError
2645 && !pExtPack->m->fUsable)
2646 hrc = setError(E_FAIL, "%s", pExtPack->m->strWhyUnusable.c_str());
2647
2648 if (a_ppExtPack)
2649 *a_ppExtPack = pExtPack;
2650 return hrc;
2651}
2652
2653/**
2654 * Thread wrapper around doInstall.
2655 *
2656 * @returns VINF_SUCCESS (ignored)
2657 * @param hThread The thread handle (ignored).
2658 * @param pvJob The job structure.
2659 */
2660/*static*/ DECLCALLBACK(int) ExtPackManager::doInstallThreadProc(RTTHREAD hThread, void *pvJob)
2661{
2662 PEXTPACKINSTALLJOB pJob = (PEXTPACKINSTALLJOB)pvJob;
2663 HRESULT hrc = pJob->ptrExtPackMgr->doInstall(pJob->ptrExtPackFile, pJob->fReplace, &pJob->strDisplayInfo);
2664 pJob->ptrProgress->notifyComplete(hrc);
2665 delete pJob;
2666
2667 NOREF(hThread);
2668 return VINF_SUCCESS;
2669}
2670
2671/**
2672 * Worker for IExtPackFile::Install.
2673 *
2674 * Called on a worker thread via doInstallThreadProc.
2675 *
2676 * @returns COM status code.
2677 * @param a_pExtPackFile The extension pack file, caller checks that
2678 * it's usable.
2679 * @param a_fReplace Whether to replace any existing extpack or just
2680 * fail.
2681 * @param a_pstrDisplayInfo Host specific display information hacks.
2682 * @param a_ppProgress Where to return a progress object some day. Can
2683 * be NULL.
2684 */
2685HRESULT ExtPackManager::doInstall(ExtPackFile *a_pExtPackFile, bool a_fReplace, Utf8Str const *a_pstrDisplayInfo)
2686{
2687 AssertReturn(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON, E_UNEXPECTED);
2688 RTCString const * const pStrName = &a_pExtPackFile->m->Desc.strName;
2689 RTCString const * const pStrTarball = &a_pExtPackFile->m->strExtPackFile;
2690 RTCString const * const pStrTarballDigest = &a_pExtPackFile->m->strDigest;
2691
2692 AutoCaller autoCaller(this);
2693 HRESULT hrc = autoCaller.rc();
2694 if (SUCCEEDED(hrc))
2695 {
2696 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
2697
2698 /*
2699 * Refresh the data we have on the extension pack as it
2700 * may be made stale by direct meddling or some other user.
2701 */
2702 ExtPack *pExtPack;
2703 hrc = refreshExtPack(pStrName->c_str(), false /*a_fUnusableIsError*/, &pExtPack);
2704 if (SUCCEEDED(hrc))
2705 {
2706 if (pExtPack && a_fReplace)
2707 hrc = pExtPack->callUninstallHookAndClose(m->pVirtualBox, false /*a_ForcedRemoval*/);
2708 else if (pExtPack)
2709 hrc = setError(E_FAIL,
2710 tr("Extension pack '%s' is already installed."
2711 " In case of a reinstallation, please uninstall it first"),
2712 pStrName->c_str());
2713 }
2714 if (SUCCEEDED(hrc))
2715 {
2716 /*
2717 * Run the privileged helper binary that performs the actual
2718 * installation. Then create an object for the packet (we do this
2719 * even on failure, to be on the safe side).
2720 */
2721 hrc = runSetUidToRootHelper(a_pstrDisplayInfo,
2722 "install",
2723 "--base-dir", m->strBaseDir.c_str(),
2724 "--cert-dir", m->strCertificatDirPath.c_str(),
2725 "--name", pStrName->c_str(),
2726 "--tarball", pStrTarball->c_str(),
2727 "--sha-256", pStrTarballDigest->c_str(),
2728 pExtPack ? "--replace" : (const char *)NULL,
2729 (const char *)NULL);
2730 if (SUCCEEDED(hrc))
2731 {
2732 hrc = refreshExtPack(pStrName->c_str(), true /*a_fUnusableIsError*/, &pExtPack);
2733 if (SUCCEEDED(hrc) && pExtPack)
2734 {
2735 RTERRINFOSTATIC ErrInfo;
2736 RTErrInfoInitStatic(&ErrInfo);
2737 pExtPack->callInstalledHook(m->pVirtualBox, &autoLock, &ErrInfo.Core);
2738 if (RT_SUCCESS(ErrInfo.Core.rc))
2739 LogRel(("ExtPackManager: Successfully installed extension pack '%s'.\n", pStrName->c_str()));
2740 else
2741 {
2742 LogRel(("ExtPackManager: Installed hook for '%s' failed: %Rrc - %s\n",
2743 pStrName->c_str(), ErrInfo.Core.rc, ErrInfo.Core.pszMsg));
2744
2745 /*
2746 * Uninstall the extpack if the error indicates that.
2747 */
2748 if (ErrInfo.Core.rc == VERR_EXTPACK_UNSUPPORTED_HOST_UNINSTALL)
2749 runSetUidToRootHelper(a_pstrDisplayInfo,
2750 "uninstall",
2751 "--base-dir", m->strBaseDir.c_str(),
2752 "--name", pStrName->c_str(),
2753 "--forced",
2754 (const char *)NULL);
2755 hrc = setError(E_FAIL, tr("The installation hook failed: %Rrc - %s"),
2756 ErrInfo.Core.rc, ErrInfo.Core.pszMsg);
2757 }
2758 }
2759 else if (SUCCEEDED(hrc))
2760 hrc = setError(E_FAIL, tr("Installing extension pack '%s' failed under mysterious circumstances"),
2761 pStrName->c_str());
2762 }
2763 else
2764 {
2765 ErrorInfoKeeper Eik;
2766 refreshExtPack(pStrName->c_str(), false /*a_fUnusableIsError*/, NULL);
2767 }
2768 }
2769
2770 /*
2771 * Do VirtualBoxReady callbacks now for any freshly installed
2772 * extension pack (old ones will not be called).
2773 */
2774 if (m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON)
2775 {
2776 autoLock.release();
2777 callAllVirtualBoxReadyHooks();
2778 }
2779 }
2780
2781 return hrc;
2782}
2783
2784/**
2785 * Thread wrapper around doUninstall.
2786 *
2787 * @returns VINF_SUCCESS (ignored)
2788 * @param hThread The thread handle (ignored).
2789 * @param pvJob The job structure.
2790 */
2791/*static*/ DECLCALLBACK(int) ExtPackManager::doUninstallThreadProc(RTTHREAD hThread, void *pvJob)
2792{
2793 PEXTPACKUNINSTALLJOB pJob = (PEXTPACKUNINSTALLJOB)pvJob;
2794 HRESULT hrc = pJob->ptrExtPackMgr->doUninstall(&pJob->strName, pJob->fForcedRemoval, &pJob->strDisplayInfo);
2795 pJob->ptrProgress->notifyComplete(hrc);
2796 delete pJob;
2797
2798 NOREF(hThread);
2799 return VINF_SUCCESS;
2800}
2801
2802/**
2803 * Worker for IExtPackManager::Uninstall.
2804 *
2805 * Called on a worker thread via doUninstallThreadProc.
2806 *
2807 * @returns COM status code.
2808 * @param a_pstrName The name of the extension pack to uninstall.
2809 * @param a_fForcedRemoval Whether to be skip and ignore certain bits of
2810 * the extpack feedback. To deal with misbehaving
2811 * extension pack hooks.
2812 * @param a_pstrDisplayInfo Host specific display information hacks.
2813 */
2814HRESULT ExtPackManager::doUninstall(Utf8Str const *a_pstrName, bool a_fForcedRemoval, Utf8Str const *a_pstrDisplayInfo)
2815{
2816 Assert(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON);
2817
2818 AutoCaller autoCaller(this);
2819 HRESULT hrc = autoCaller.rc();
2820 if (SUCCEEDED(hrc))
2821 {
2822 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
2823
2824 /*
2825 * Refresh the data we have on the extension pack as it may be made
2826 * stale by direct meddling or some other user.
2827 */
2828 ExtPack *pExtPack;
2829 hrc = refreshExtPack(a_pstrName->c_str(), false /*a_fUnusableIsError*/, &pExtPack);
2830 if (SUCCEEDED(hrc))
2831 {
2832 if (!pExtPack)
2833 {
2834 LogRel(("ExtPackManager: Extension pack '%s' is not installed, so nothing to uninstall.\n", a_pstrName->c_str()));
2835 hrc = S_OK; /* nothing to uninstall */
2836 }
2837 else
2838 {
2839 /*
2840 * Call the uninstall hook and unload the main dll.
2841 */
2842 hrc = pExtPack->callUninstallHookAndClose(m->pVirtualBox, a_fForcedRemoval);
2843 if (SUCCEEDED(hrc))
2844 {
2845 /*
2846 * Run the set-uid-to-root binary that performs the
2847 * uninstallation. Then refresh the object.
2848 *
2849 * This refresh is theorically subject to races, but it's of
2850 * the don't-do-that variety.
2851 */
2852 const char *pszForcedOpt = a_fForcedRemoval ? "--forced" : NULL;
2853 hrc = runSetUidToRootHelper(a_pstrDisplayInfo,
2854 "uninstall",
2855 "--base-dir", m->strBaseDir.c_str(),
2856 "--name", a_pstrName->c_str(),
2857 pszForcedOpt, /* Last as it may be NULL. */
2858 (const char *)NULL);
2859 if (SUCCEEDED(hrc))
2860 {
2861 hrc = refreshExtPack(a_pstrName->c_str(), false /*a_fUnusableIsError*/, &pExtPack);
2862 if (SUCCEEDED(hrc))
2863 {
2864 if (!pExtPack)
2865 LogRel(("ExtPackManager: Successfully uninstalled extension pack '%s'.\n", a_pstrName->c_str()));
2866 else
2867 hrc = setError(E_FAIL,
2868 tr("Uninstall extension pack '%s' failed under mysterious circumstances"),
2869 a_pstrName->c_str());
2870 }
2871 }
2872 else
2873 {
2874 ErrorInfoKeeper Eik;
2875 refreshExtPack(a_pstrName->c_str(), false /*a_fUnusableIsError*/, NULL);
2876 }
2877 }
2878 }
2879 }
2880
2881 /*
2882 * Do VirtualBoxReady callbacks now for any freshly installed
2883 * extension pack (old ones will not be called).
2884 */
2885 if (m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON)
2886 {
2887 autoLock.release();
2888 callAllVirtualBoxReadyHooks();
2889 }
2890 }
2891
2892 return hrc;
2893}
2894
2895
2896/**
2897 * Calls the pfnVirtualBoxReady hook for all working extension packs.
2898 *
2899 * @remarks The caller must not hold any locks.
2900 */
2901void ExtPackManager::callAllVirtualBoxReadyHooks(void)
2902{
2903 AutoCaller autoCaller(this);
2904 HRESULT hrc = autoCaller.rc();
2905 if (FAILED(hrc))
2906 return;
2907 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
2908 ComPtr<ExtPackManager> ptrSelfRef = this;
2909
2910 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
2911 it != m->llInstalledExtPacks.end();
2912 /* advancing below */)
2913 {
2914 if ((*it)->callVirtualBoxReadyHook(m->pVirtualBox, &autoLock))
2915 it = m->llInstalledExtPacks.begin();
2916 else
2917 it++;
2918 }
2919}
2920#endif
2921
2922/**
2923 * Calls the pfnConsoleReady hook for all working extension packs.
2924 *
2925 * @param a_pConsole The console interface.
2926 * @remarks The caller must not hold any locks.
2927 */
2928void ExtPackManager::callAllConsoleReadyHooks(IConsole *a_pConsole)
2929{
2930 AutoCaller autoCaller(this);
2931 HRESULT hrc = autoCaller.rc();
2932 if (FAILED(hrc))
2933 return;
2934 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
2935 ComPtr<ExtPackManager> ptrSelfRef = this;
2936
2937 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
2938 it != m->llInstalledExtPacks.end();
2939 /* advancing below */)
2940 {
2941 if ((*it)->callConsoleReadyHook(a_pConsole, &autoLock))
2942 it = m->llInstalledExtPacks.begin();
2943 else
2944 it++;
2945 }
2946}
2947
2948#if !defined(VBOX_COM_INPROC)
2949/**
2950 * Calls the pfnVMCreated hook for all working extension packs.
2951 *
2952 * @param a_pMachine The machine interface of the new VM.
2953 */
2954void ExtPackManager::callAllVmCreatedHooks(IMachine *a_pMachine)
2955{
2956 AutoCaller autoCaller(this);
2957 HRESULT hrc = autoCaller.rc();
2958 if (FAILED(hrc))
2959 return;
2960 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
2961 ComPtr<ExtPackManager> ptrSelfRef = this; /* paranoia */
2962 ExtPackList llExtPacks = m->llInstalledExtPacks;
2963
2964 for (ExtPackList::iterator it = llExtPacks.begin(); it != llExtPacks.end(); it++)
2965 (*it)->callVmCreatedHook(m->pVirtualBox, a_pMachine, &autoLock);
2966}
2967#endif
2968
2969/**
2970 * Calls the pfnVMConfigureVMM hook for all working extension packs.
2971 *
2972 * @returns VBox status code. Stops on the first failure, expecting the caller
2973 * to signal this to the caller of the CFGM constructor.
2974 * @param a_pConsole The console interface for the VM.
2975 * @param a_pVM The VM handle.
2976 */
2977int ExtPackManager::callAllVmConfigureVmmHooks(IConsole *a_pConsole, PVM a_pVM)
2978{
2979 AutoCaller autoCaller(this);
2980 HRESULT hrc = autoCaller.rc();
2981 if (FAILED(hrc))
2982 return Global::vboxStatusCodeFromCOM(hrc);
2983 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
2984 ComPtr<ExtPackManager> ptrSelfRef = this; /* paranoia */
2985 ExtPackList llExtPacks = m->llInstalledExtPacks;
2986
2987 for (ExtPackList::iterator it = llExtPacks.begin(); it != llExtPacks.end(); it++)
2988 {
2989 int vrc;
2990 (*it)->callVmConfigureVmmHook(a_pConsole, a_pVM, &autoLock, &vrc);
2991 if (RT_FAILURE(vrc))
2992 return vrc;
2993 }
2994
2995 return VINF_SUCCESS;
2996}
2997
2998/**
2999 * Calls the pfnVMPowerOn hook for all working extension packs.
3000 *
3001 * @returns VBox status code. Stops on the first failure, expecting the caller
3002 * to not power on the VM.
3003 * @param a_pConsole The console interface for the VM.
3004 * @param a_pVM The VM handle.
3005 */
3006int ExtPackManager::callAllVmPowerOnHooks(IConsole *a_pConsole, PVM a_pVM)
3007{
3008 AutoCaller autoCaller(this);
3009 HRESULT hrc = autoCaller.rc();
3010 if (FAILED(hrc))
3011 return Global::vboxStatusCodeFromCOM(hrc);
3012 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3013 ComPtr<ExtPackManager> ptrSelfRef = this; /* paranoia */
3014 ExtPackList llExtPacks = m->llInstalledExtPacks;
3015
3016 for (ExtPackList::iterator it = llExtPacks.begin(); it != llExtPacks.end(); it++)
3017 {
3018 int vrc;
3019 (*it)->callVmPowerOnHook(a_pConsole, a_pVM, &autoLock, &vrc);
3020 if (RT_FAILURE(vrc))
3021 return vrc;
3022 }
3023
3024 return VINF_SUCCESS;
3025}
3026
3027/**
3028 * Calls the pfnVMPowerOff hook for all working extension packs.
3029 *
3030 * @param a_pConsole The console interface for the VM.
3031 * @param a_pVM The VM handle. Can be NULL.
3032 */
3033void ExtPackManager::callAllVmPowerOffHooks(IConsole *a_pConsole, PVM a_pVM)
3034{
3035 AutoCaller autoCaller(this);
3036 HRESULT hrc = autoCaller.rc();
3037 if (FAILED(hrc))
3038 return;
3039 AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3040 ComPtr<ExtPackManager> ptrSelfRef = this; /* paranoia */
3041 ExtPackList llExtPacks = m->llInstalledExtPacks;
3042
3043 for (ExtPackList::iterator it = llExtPacks.begin(); it != llExtPacks.end(); it++)
3044 (*it)->callVmPowerOffHook(a_pConsole, a_pVM, &autoLock);
3045}
3046
3047
3048/**
3049 * Checks that the specified extension pack contains a VRDE module and that it
3050 * is shipshape.
3051 *
3052 * @returns S_OK if ok, appropriate failure status code with details.
3053 * @param a_pstrExtPack The name of the extension pack.
3054 */
3055HRESULT ExtPackManager::checkVrdeExtPack(Utf8Str const *a_pstrExtPack)
3056{
3057 AutoCaller autoCaller(this);
3058 HRESULT hrc = autoCaller.rc();
3059 if (SUCCEEDED(hrc))
3060 {
3061 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3062
3063 ExtPack *pExtPack = findExtPack(a_pstrExtPack->c_str());
3064 if (pExtPack)
3065 hrc = pExtPack->checkVrde();
3066 else
3067 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("No extension pack by the name '%s' was found"), a_pstrExtPack->c_str());
3068 }
3069
3070 return hrc;
3071}
3072
3073/**
3074 * Gets the full path to the VRDE library of the specified extension pack.
3075 *
3076 * This will do extacly the same as checkVrdeExtPack and then resolve the
3077 * library path.
3078 *
3079 * @returns S_OK if a path is returned, COM error status and message return if
3080 * not.
3081 * @param a_pstrExtPack The extension pack.
3082 * @param a_pstrVrdeLibrary Where to return the path.
3083 */
3084int ExtPackManager::getVrdeLibraryPathForExtPack(Utf8Str const *a_pstrExtPack, Utf8Str *a_pstrVrdeLibrary)
3085{
3086 AutoCaller autoCaller(this);
3087 HRESULT hrc = autoCaller.rc();
3088 if (SUCCEEDED(hrc))
3089 {
3090 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3091
3092 ExtPack *pExtPack = findExtPack(a_pstrExtPack->c_str());
3093 if (pExtPack)
3094 hrc = pExtPack->getVrdpLibraryName(a_pstrVrdeLibrary);
3095 else
3096 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("No extension pack by the name '%s' was found"), a_pstrExtPack->c_str());
3097 }
3098
3099 return hrc;
3100}
3101
3102/**
3103 * Gets the full path to the specified library of the specified extension pack.
3104 *
3105 * @returns S_OK if a path is returned, COM error status and message return if
3106 * not.
3107 * @param a_pszModuleName The library.
3108 * @param a_pstrExtPack The extension pack.
3109 * @param a_pstrVrdeLibrary Where to return the path.
3110 */
3111HRESULT ExtPackManager::getLibraryPathForExtPack(const char *a_pszModuleName, Utf8Str const *a_pstrExtPack, Utf8Str *a_pstrLibrary)
3112{
3113 AutoCaller autoCaller(this);
3114 HRESULT hrc = autoCaller.rc();
3115 if (SUCCEEDED(hrc))
3116 {
3117 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3118
3119 ExtPack *pExtPack = findExtPack(a_pstrExtPack->c_str());
3120 if (pExtPack)
3121 hrc = pExtPack->getLibraryName(a_pszModuleName, a_pstrLibrary);
3122 else
3123 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("No extension pack by the name '%s' was found"), a_pstrExtPack->c_str());
3124 }
3125
3126 return hrc;
3127}
3128
3129/**
3130 * Gets the name of the default VRDE extension pack.
3131 *
3132 * @returns S_OK or some COM error status on red tape failure.
3133 * @param a_pstrExtPack Where to return the extension pack name. Returns
3134 * empty if no extension pack wishes to be the default
3135 * VRDP provider.
3136 */
3137HRESULT ExtPackManager::getDefaultVrdeExtPack(Utf8Str *a_pstrExtPack)
3138{
3139 a_pstrExtPack->setNull();
3140
3141 AutoCaller autoCaller(this);
3142 HRESULT hrc = autoCaller.rc();
3143 if (SUCCEEDED(hrc))
3144 {
3145 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3146
3147 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
3148 it != m->llInstalledExtPacks.end();
3149 it++)
3150 {
3151 if ((*it)->wantsToBeDefaultVrde())
3152 {
3153 *a_pstrExtPack = (*it)->m->Desc.strName;
3154 break;
3155 }
3156 }
3157 }
3158 return hrc;
3159}
3160
3161/**
3162 * Checks if an extension pack is (present and) usable.
3163 *
3164 * @returns @c true if it is, otherwise @c false.
3165 * @param a_pszExtPack The name of the extension pack.
3166 */
3167bool ExtPackManager::isExtPackUsable(const char *a_pszExtPack)
3168{
3169 AutoCaller autoCaller(this);
3170 HRESULT hrc = autoCaller.rc();
3171 if (FAILED(hrc))
3172 return false;
3173 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3174
3175 ExtPack *pExtPack = findExtPack(a_pszExtPack);
3176 return pExtPack != NULL
3177 && pExtPack->m->fUsable;
3178}
3179
3180/**
3181 * Dumps all extension packs to the release log.
3182 */
3183void ExtPackManager::dumpAllToReleaseLog(void)
3184{
3185 AutoCaller autoCaller(this);
3186 HRESULT hrc = autoCaller.rc();
3187 if (FAILED(hrc))
3188 return;
3189 AutoReadLock autoLock(this COMMA_LOCKVAL_SRC_POS);
3190
3191 LogRel(("Installed Extension Packs:\n"));
3192 for (ExtPackList::iterator it = m->llInstalledExtPacks.begin();
3193 it != m->llInstalledExtPacks.end();
3194 it++)
3195 {
3196 ExtPack::Data *pExtPackData = (*it)->m;
3197 if (pExtPackData)
3198 {
3199 if (pExtPackData->fUsable)
3200 LogRel((" %s (Version: %s r%u%s%s; VRDE Module: %s)\n",
3201 pExtPackData->Desc.strName.c_str(),
3202 pExtPackData->Desc.strVersion.c_str(),
3203 pExtPackData->Desc.uRevision,
3204 pExtPackData->Desc.strEdition.isEmpty() ? "" : " ",
3205 pExtPackData->Desc.strEdition.c_str(),
3206 pExtPackData->Desc.strVrdeModule.c_str() ));
3207 else
3208 LogRel((" %s (Version: %s r%u%s%s; VRDE Module: %s unusable because of '%s')\n",
3209 pExtPackData->Desc.strName.c_str(),
3210 pExtPackData->Desc.strVersion.c_str(),
3211 pExtPackData->Desc.uRevision,
3212 pExtPackData->Desc.strEdition.isEmpty() ? "" : " ",
3213 pExtPackData->Desc.strEdition.c_str(),
3214 pExtPackData->Desc.strVrdeModule.c_str(),
3215 pExtPackData->strWhyUnusable.c_str() ));
3216 }
3217 else
3218 LogRel((" pExtPackData is NULL\n"));
3219 }
3220
3221 if (!m->llInstalledExtPacks.size())
3222 LogRel((" None installed!\n"));
3223}
3224
3225/* 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