VirtualBox

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

Last change on this file since 39650 was 39180, checked in by vboxsync, 13 years ago

ExtPack: Adding a 'edition' attribute to the Version tag and cleaning up the version string validation confusion. Currently hacking the edition into the version property of IExtPack, adding a property later.

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