VirtualBox

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

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

remove changes that break up log and error strings, introduced in 92972

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