VirtualBox

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

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

6813 src-all/ProgressImp.cpp + some formatting/line length sorting

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette