VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp@ 46649

Last change on this file since 46649 was 46649, checked in by vboxsync, 12 years ago

Forward ported r85941 and required build fixes (Main: Implemented new event queue to separate system's native event queue and our own. Also, XPCOM is not needed for handling our own events. On Windows this also fixes the system's queue quota limitation).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 183.6 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 46649 2013-06-19 11:47:32Z vboxsync $ */
2/** @file
3 * Implementation of IVirtualBox in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <iprt/asm.h>
19#include <iprt/base64.h>
20#include <iprt/buildconfig.h>
21#include <iprt/cpp/utils.h>
22#include <iprt/dir.h>
23#include <iprt/env.h>
24#include <iprt/file.h>
25#include <iprt/path.h>
26#include <iprt/process.h>
27#include <iprt/rand.h>
28#include <iprt/sha.h>
29#include <iprt/string.h>
30#include <iprt/stream.h>
31#include <iprt/thread.h>
32#include <iprt/uuid.h>
33#include <iprt/cpp/xml.h>
34
35#include <VBox/com/com.h>
36#include <VBox/com/array.h>
37#include "VBox/com/EventQueue.h"
38
39#include <VBox/err.h>
40#include <VBox/param.h>
41#include <VBox/settings.h>
42#include <VBox/version.h>
43
44#include <package-generated.h>
45
46#include <algorithm>
47#include <set>
48#include <vector>
49#include <memory> // for auto_ptr
50
51#include "VirtualBoxImpl.h"
52
53#include "Global.h"
54#include "MachineImpl.h"
55#include "MediumImpl.h"
56#include "SharedFolderImpl.h"
57#include "ProgressImpl.h"
58#include "ProgressProxyImpl.h"
59#include "HostImpl.h"
60#include "USBControllerImpl.h"
61#include "SystemPropertiesImpl.h"
62#include "GuestOSTypeImpl.h"
63#include "DHCPServerRunner.h"
64#include "DHCPServerImpl.h"
65#include "NATNetworkImpl.h"
66#ifdef VBOX_WITH_RESOURCE_USAGE_API
67# include "PerformanceImpl.h"
68#endif /* VBOX_WITH_RESOURCE_USAGE_API */
69#include "EventImpl.h"
70#include "VBoxEvents.h"
71#ifdef VBOX_WITH_EXTPACK
72# include "ExtPackManagerImpl.h"
73#endif
74#include "AutostartDb.h"
75
76#include "AutoCaller.h"
77#include "Logging.h"
78#include "objectslist.h"
79
80#ifdef RT_OS_WINDOWS
81# include "win/svchlp.h"
82# include "win/VBoxComEvents.h"
83#endif
84
85////////////////////////////////////////////////////////////////////////////////
86//
87// Definitions
88//
89////////////////////////////////////////////////////////////////////////////////
90
91#define VBOX_GLOBAL_SETTINGS_FILE "VirtualBox.xml"
92
93////////////////////////////////////////////////////////////////////////////////
94//
95// Global variables
96//
97////////////////////////////////////////////////////////////////////////////////
98
99// static
100Bstr VirtualBox::sVersion;
101
102// static
103Bstr VirtualBox::sVersionNormalized;
104
105// static
106ULONG VirtualBox::sRevision;
107
108// static
109Bstr VirtualBox::sPackageType;
110
111// static
112Bstr VirtualBox::sAPIVersion;
113
114#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
115/** Table for adaptive timeouts in the client watcher. The counter starts at
116 * the maximum value and decreases to 0. */
117static const RTMSINTERVAL s_updateAdaptTimeouts[] = { 500, 200, 100, 50, 20, 10, 5 };
118#endif
119
120////////////////////////////////////////////////////////////////////////////////
121//
122// CallbackEvent class
123//
124////////////////////////////////////////////////////////////////////////////////
125
126/**
127 * Abstract callback event class to asynchronously call VirtualBox callbacks
128 * on a dedicated event thread. Subclasses reimplement #handleCallback()
129 * to call appropriate IVirtualBoxCallback methods depending on the event
130 * to be dispatched.
131 *
132 * @note The VirtualBox instance passed to the constructor is strongly
133 * referenced, so that the VirtualBox singleton won't be released until the
134 * event gets handled by the event thread.
135 */
136class VirtualBox::CallbackEvent : public Event
137{
138public:
139
140 CallbackEvent(VirtualBox *aVirtualBox, VBoxEventType_T aWhat)
141 : mVirtualBox(aVirtualBox), mWhat(aWhat)
142 {
143 Assert(aVirtualBox);
144 }
145
146 void *handler();
147
148 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc) = 0;
149
150private:
151
152 /**
153 * Note that this is a weak ref -- the CallbackEvent handler thread
154 * is bound to the lifetime of the VirtualBox instance, so it's safe.
155 */
156 VirtualBox *mVirtualBox;
157protected:
158 VBoxEventType_T mWhat;
159};
160
161////////////////////////////////////////////////////////////////////////////////
162//
163// VirtualBox private member data definition
164//
165////////////////////////////////////////////////////////////////////////////////
166
167#if defined(RT_OS_WINDOWS)
168 #define UPDATEREQARG NULL
169 #define UPDATEREQTYPE HANDLE
170#elif defined(RT_OS_OS2)
171 #define UPDATEREQARG NIL_RTSEMEVENT
172 #define UPDATEREQTYPE RTSEMEVENT
173#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
174 #define UPDATEREQARG
175 #define UPDATEREQTYPE RTSEMEVENT
176#else
177# error "Port me!"
178#endif
179
180typedef ObjectsList<Machine> MachinesOList;
181typedef ObjectsList<Medium> MediaOList;
182typedef ObjectsList<GuestOSType> GuestOSTypesOList;
183typedef ObjectsList<SharedFolder> SharedFoldersOList;
184typedef ObjectsList<DHCPServer> DHCPServersOList;
185typedef ObjectsList<NATNetwork> NATNetworksOList;
186
187typedef std::map<Guid, ComPtr<IProgress> > ProgressMap;
188typedef std::map<Guid, ComObjPtr<Medium> > HardDiskMap;
189
190/**
191 * Main VirtualBox data structure.
192 * @note |const| members are persistent during lifetime so can be accessed
193 * without locking.
194 */
195struct VirtualBox::Data
196{
197 Data()
198 : pMainConfigFile(NULL),
199 uuidMediaRegistry("48024e5c-fdd9-470f-93af-ec29f7ea518c"),
200 uRegistryNeedsSaving(0),
201 lockMachines(LOCKCLASS_LISTOFMACHINES),
202 allMachines(lockMachines),
203 lockGuestOSTypes(LOCKCLASS_LISTOFOTHEROBJECTS),
204 allGuestOSTypes(lockGuestOSTypes),
205 lockMedia(LOCKCLASS_LISTOFMEDIA),
206 allHardDisks(lockMedia),
207 allDVDImages(lockMedia),
208 allFloppyImages(lockMedia),
209 lockSharedFolders(LOCKCLASS_LISTOFOTHEROBJECTS),
210 allSharedFolders(lockSharedFolders),
211 lockDHCPServers(LOCKCLASS_LISTOFOTHEROBJECTS),
212 allDHCPServers(lockDHCPServers),
213 lockNATNetworks(LOCKCLASS_LISTOFOTHEROBJECTS),
214 allNATNetworks(lockNATNetworks),
215 mtxProgressOperations(LOCKCLASS_PROGRESSLIST),
216 updateReq(UPDATEREQARG),
217 threadClientWatcher(NIL_RTTHREAD),
218 threadAsyncEvent(NIL_RTTHREAD),
219 pAsyncEventQ(NULL),
220 pAutostartDb(NULL),
221 fSettingsCipherKeySet(false)
222 {
223 }
224
225 ~Data()
226 {
227 if (pMainConfigFile)
228 {
229 delete pMainConfigFile;
230 pMainConfigFile = NULL;
231 }
232 };
233
234 // const data members not requiring locking
235 const Utf8Str strHomeDir;
236
237 // VirtualBox main settings file
238 const Utf8Str strSettingsFilePath;
239 settings::MainConfigFile *pMainConfigFile;
240
241 // constant pseudo-machine ID for global media registry
242 const Guid uuidMediaRegistry;
243
244 // counter if global media registry needs saving, updated using atomic
245 // operations, without requiring any locks
246 uint64_t uRegistryNeedsSaving;
247
248 // const objects not requiring locking
249 const ComObjPtr<Host> pHost;
250 const ComObjPtr<SystemProperties> pSystemProperties;
251#ifdef VBOX_WITH_RESOURCE_USAGE_API
252 const ComObjPtr<PerformanceCollector> pPerformanceCollector;
253#endif /* VBOX_WITH_RESOURCE_USAGE_API */
254
255 // Each of the following lists use a particular lock handle that protects the
256 // list as a whole. As opposed to version 3.1 and earlier, these lists no
257 // longer need the main VirtualBox object lock, but only the respective list
258 // lock. In each case, the locking order is defined that the list must be
259 // requested before object locks of members of the lists (see the order definitions
260 // in AutoLock.h; e.g. LOCKCLASS_LISTOFMACHINES before LOCKCLASS_MACHINEOBJECT).
261 RWLockHandle lockMachines;
262 MachinesOList allMachines;
263
264 RWLockHandle lockGuestOSTypes;
265 GuestOSTypesOList allGuestOSTypes;
266
267 // All the media lists are protected by the following locking handle:
268 RWLockHandle lockMedia;
269 MediaOList allHardDisks, // base images only!
270 allDVDImages,
271 allFloppyImages;
272 // the hard disks map is an additional map sorted by UUID for quick lookup
273 // and contains ALL hard disks (base and differencing); it is protected by
274 // the same lock as the other media lists above
275 HardDiskMap mapHardDisks;
276
277 // list of pending machine renames (also protected by media tree lock;
278 // see VirtualBox::rememberMachineNameChangeForMedia())
279 struct PendingMachineRename
280 {
281 Utf8Str strConfigDirOld;
282 Utf8Str strConfigDirNew;
283 };
284 typedef std::list<PendingMachineRename> PendingMachineRenamesList;
285 PendingMachineRenamesList llPendingMachineRenames;
286
287 RWLockHandle lockSharedFolders;
288 SharedFoldersOList allSharedFolders;
289
290 RWLockHandle lockDHCPServers;
291 DHCPServersOList allDHCPServers;
292
293 RWLockHandle lockNATNetworks;
294 NATNetworksOList allNATNetworks;
295
296 RWLockHandle mtxProgressOperations;
297 ProgressMap mapProgressOperations;
298
299 // the following are data for the client watcher thread
300 const UPDATEREQTYPE updateReq;
301#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
302 uint8_t updateAdaptCtr;
303#endif
304 const RTTHREAD threadClientWatcher;
305 typedef std::list<RTPROCESS> ProcessList;
306 ProcessList llProcesses;
307
308 // the following are data for the async event thread
309 const RTTHREAD threadAsyncEvent;
310 EventQueue * const pAsyncEventQ;
311 const ComObjPtr<EventSource> pEventSource;
312
313#ifdef VBOX_WITH_EXTPACK
314 /** The extension pack manager object lives here. */
315 const ComObjPtr<ExtPackManager> ptrExtPackManager;
316#endif
317
318 /** The global autostart database for the user. */
319 AutostartDb * const pAutostartDb;
320
321 /** Settings secret */
322 bool fSettingsCipherKeySet;
323 uint8_t SettingsCipherKey[RTSHA512_HASH_SIZE];
324};
325
326
327// constructor / destructor
328/////////////////////////////////////////////////////////////////////////////
329
330VirtualBox::VirtualBox()
331{}
332
333VirtualBox::~VirtualBox()
334{}
335
336HRESULT VirtualBox::FinalConstruct()
337{
338 LogFlowThisFunc(("\n"));
339
340 HRESULT rc = init();
341
342 BaseFinalConstruct();
343
344 return rc;
345}
346
347void VirtualBox::FinalRelease()
348{
349 LogFlowThisFunc(("\n"));
350
351 uninit();
352
353 BaseFinalRelease();
354}
355
356// public initializer/uninitializer for internal purposes only
357/////////////////////////////////////////////////////////////////////////////
358
359/**
360 * Initializes the VirtualBox object.
361 *
362 * @return COM result code
363 */
364HRESULT VirtualBox::init()
365{
366 /* Enclose the state transition NotReady->InInit->Ready */
367 AutoInitSpan autoInitSpan(this);
368 AssertReturn(autoInitSpan.isOk(), E_FAIL);
369
370 /* Locking this object for writing during init sounds a bit paradoxical,
371 * but in the current locking mess this avoids that some code gets a
372 * read lock and later calls code which wants the same write lock. */
373 AutoWriteLock lock(this COMMA_LOCKVAL_SRC_POS);
374
375 // allocate our instance data
376 m = new Data;
377
378 LogFlow(("===========================================================\n"));
379 LogFlowThisFuncEnter();
380
381 if (sVersion.isEmpty())
382 sVersion = RTBldCfgVersion();
383 if (sVersionNormalized.isEmpty())
384 {
385 Utf8Str tmp(RTBldCfgVersion());
386 if (tmp.endsWith(VBOX_BUILD_PUBLISHER))
387 tmp = tmp.substr(0, tmp.length() - strlen(VBOX_BUILD_PUBLISHER));
388 sVersionNormalized = tmp;
389 }
390 sRevision = RTBldCfgRevision();
391 if (sPackageType.isEmpty())
392 sPackageType = VBOX_PACKAGE_STRING;
393 if (sAPIVersion.isEmpty())
394 sAPIVersion = VBOX_API_VERSION_STRING;
395 LogFlowThisFunc(("Version: %ls, Package: %ls, API Version: %ls\n", sVersion.raw(), sPackageType.raw(), sAPIVersion.raw()));
396
397 /* Get the VirtualBox home directory. */
398 {
399 char szHomeDir[RTPATH_MAX];
400 int vrc = com::GetVBoxUserHomeDirectory(szHomeDir, sizeof(szHomeDir));
401 if (RT_FAILURE(vrc))
402 return setError(E_FAIL,
403 tr("Could not create the VirtualBox home directory '%s' (%Rrc)"),
404 szHomeDir, vrc);
405
406 unconst(m->strHomeDir) = szHomeDir;
407 }
408
409 /* compose the VirtualBox.xml file name */
410 unconst(m->strSettingsFilePath) = Utf8StrFmt("%s%c%s",
411 m->strHomeDir.c_str(),
412 RTPATH_DELIMITER,
413 VBOX_GLOBAL_SETTINGS_FILE);
414 HRESULT rc = S_OK;
415 bool fCreate = false;
416 try
417 {
418 // load and parse VirtualBox.xml; this will throw on XML or logic errors
419 try
420 {
421 m->pMainConfigFile = new settings::MainConfigFile(&m->strSettingsFilePath);
422 }
423 catch (xml::EIPRTFailure &e)
424 {
425 // this is thrown by the XML backend if the RTOpen() call fails;
426 // only if the main settings file does not exist, create it,
427 // if there's something more serious, then do fail!
428 if (e.rc() == VERR_FILE_NOT_FOUND)
429 fCreate = true;
430 else
431 throw;
432 }
433
434 if (fCreate)
435 m->pMainConfigFile = new settings::MainConfigFile(NULL);
436
437#ifdef VBOX_WITH_RESOURCE_USAGE_API
438 /* create the performance collector object BEFORE host */
439 unconst(m->pPerformanceCollector).createObject();
440 rc = m->pPerformanceCollector->init();
441 ComAssertComRCThrowRC(rc);
442#endif /* VBOX_WITH_RESOURCE_USAGE_API */
443
444 /* create the host object early, machines will need it */
445 unconst(m->pHost).createObject();
446 rc = m->pHost->init(this);
447 ComAssertComRCThrowRC(rc);
448
449 rc = m->pHost->loadSettings(m->pMainConfigFile->host);
450 if (FAILED(rc)) throw rc;
451
452 /*
453 * Create autostart database object early, because the system properties
454 * might need it.
455 */
456 unconst(m->pAutostartDb) = new AutostartDb;
457
458 /* create the system properties object, someone may need it too */
459 unconst(m->pSystemProperties).createObject();
460 rc = m->pSystemProperties->init(this);
461 ComAssertComRCThrowRC(rc);
462
463 rc = m->pSystemProperties->loadSettings(m->pMainConfigFile->systemProperties);
464 if (FAILED(rc)) throw rc;
465
466 /* guest OS type objects, needed by machines */
467 for (size_t i = 0; i < Global::cOSTypes; ++i)
468 {
469 ComObjPtr<GuestOSType> guestOSTypeObj;
470 rc = guestOSTypeObj.createObject();
471 if (SUCCEEDED(rc))
472 {
473 rc = guestOSTypeObj->init(Global::sOSTypes[i]);
474 if (SUCCEEDED(rc))
475 m->allGuestOSTypes.addChild(guestOSTypeObj);
476 }
477 ComAssertComRCThrowRC(rc);
478 }
479
480 /* all registered media, needed by machines */
481 if (FAILED(rc = initMedia(m->uuidMediaRegistry,
482 m->pMainConfigFile->mediaRegistry,
483 Utf8Str::Empty))) // const Utf8Str &machineFolder
484 throw rc;
485
486 /* machines */
487 if (FAILED(rc = initMachines()))
488 throw rc;
489
490#ifdef DEBUG
491 LogFlowThisFunc(("Dumping media backreferences\n"));
492 dumpAllBackRefs();
493#endif
494
495 /* net services - dhcp services */
496 for (settings::DHCPServersList::const_iterator it = m->pMainConfigFile->llDhcpServers.begin();
497 it != m->pMainConfigFile->llDhcpServers.end();
498 ++it)
499 {
500 const settings::DHCPServer &data = *it;
501
502 ComObjPtr<DHCPServer> pDhcpServer;
503 if (SUCCEEDED(rc = pDhcpServer.createObject()))
504 rc = pDhcpServer->init(this, data);
505 if (FAILED(rc)) throw rc;
506
507 rc = registerDHCPServer(pDhcpServer, false /* aSaveRegistry */);
508 if (FAILED(rc)) throw rc;
509 }
510
511 /* net services - nat networks */
512 for (settings::NATNetworksList::const_iterator it = m->pMainConfigFile->llNATNetworks.begin();
513 it != m->pMainConfigFile->llNATNetworks.end();
514 ++it)
515 {
516 const settings::NATNetwork &net = *it;
517
518 ComObjPtr<NATNetwork> pNATNetwork;
519 if (SUCCEEDED(rc = pNATNetwork.createObject()))
520 rc = pNATNetwork->init(this, net);
521 if (FAILED(rc)) throw rc;
522
523 rc = registerNATNetwork(pNATNetwork, false /* aSaveRegistry */);
524 if (FAILED(rc)) throw rc;
525 }
526
527 /* events */
528 if (SUCCEEDED(rc = unconst(m->pEventSource).createObject()))
529 rc = m->pEventSource->init(static_cast<IVirtualBox*>(this));
530 if (FAILED(rc)) throw rc;
531
532#ifdef VBOX_WITH_EXTPACK
533 /* extension manager */
534 rc = unconst(m->ptrExtPackManager).createObject();
535 if (SUCCEEDED(rc))
536 rc = m->ptrExtPackManager->initExtPackManager(this, VBOXEXTPACKCTX_PER_USER_DAEMON);
537 if (FAILED(rc))
538 throw rc;
539#endif
540 }
541 catch (HRESULT err)
542 {
543 /* we assume that error info is set by the thrower */
544 rc = err;
545 }
546 catch (...)
547 {
548 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
549 }
550
551 if (SUCCEEDED(rc))
552 {
553 /* start the client watcher thread */
554#if defined(RT_OS_WINDOWS)
555 unconst(m->updateReq) = ::CreateEvent(NULL, FALSE, FALSE, NULL);
556#elif defined(RT_OS_OS2)
557 RTSemEventCreate(&unconst(m->updateReq));
558#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
559 RTSemEventCreate(&unconst(m->updateReq));
560 ASMAtomicUoWriteU8(&m->updateAdaptCtr, 0);
561#else
562# error "Port me!"
563#endif
564 int vrc = RTThreadCreate(&unconst(m->threadClientWatcher),
565 ClientWatcher,
566 (void *)this,
567 0,
568 RTTHREADTYPE_MAIN_WORKER,
569 RTTHREADFLAGS_WAITABLE,
570 "Watcher");
571 ComAssertRC(vrc);
572 if (RT_FAILURE(vrc))
573 rc = E_FAIL;
574 }
575
576 if (SUCCEEDED(rc))
577 {
578 try
579 {
580 /* start the async event handler thread */
581 int vrc = RTThreadCreate(&unconst(m->threadAsyncEvent),
582 AsyncEventHandler,
583 &unconst(m->pAsyncEventQ),
584 0,
585 RTTHREADTYPE_MAIN_WORKER,
586 RTTHREADFLAGS_WAITABLE,
587 "EventHandler");
588 ComAssertRCThrow(vrc, E_FAIL);
589
590 /* wait until the thread sets m->pAsyncEventQ */
591 RTThreadUserWait(m->threadAsyncEvent, RT_INDEFINITE_WAIT);
592 ComAssertThrow(m->pAsyncEventQ, E_FAIL);
593 }
594 catch (HRESULT aRC)
595 {
596 rc = aRC;
597 }
598 }
599
600 /* Confirm a successful initialization when it's the case */
601 if (SUCCEEDED(rc))
602 autoInitSpan.setSucceeded();
603
604#ifdef VBOX_WITH_EXTPACK
605 /* Let the extension packs have a go at things. */
606 if (SUCCEEDED(rc))
607 {
608 lock.release();
609 m->ptrExtPackManager->callAllVirtualBoxReadyHooks();
610 }
611#endif
612
613 LogFlowThisFunc(("rc=%08X\n", rc));
614 LogFlowThisFuncLeave();
615 LogFlow(("===========================================================\n"));
616 return rc;
617}
618
619HRESULT VirtualBox::initMachines()
620{
621 for (settings::MachinesRegistry::const_iterator it = m->pMainConfigFile->llMachines.begin();
622 it != m->pMainConfigFile->llMachines.end();
623 ++it)
624 {
625 HRESULT rc = S_OK;
626 const settings::MachineRegistryEntry &xmlMachine = *it;
627 Guid uuid = xmlMachine.uuid;
628
629 ComObjPtr<Machine> pMachine;
630 if (SUCCEEDED(rc = pMachine.createObject()))
631 {
632 rc = pMachine->initFromSettings(this,
633 xmlMachine.strSettingsFile,
634 &uuid);
635 if (SUCCEEDED(rc))
636 rc = registerMachine(pMachine);
637 if (FAILED(rc))
638 return rc;
639 }
640 }
641
642 return S_OK;
643}
644
645/**
646 * Loads a media registry from XML and adds the media contained therein to
647 * the global lists of known media.
648 *
649 * This now (4.0) gets called from two locations:
650 *
651 * -- VirtualBox::init(), to load the global media registry from VirtualBox.xml;
652 *
653 * -- Machine::loadMachineDataFromSettings(), to load the per-machine registry
654 * from machine XML, for machines created with VirtualBox 4.0 or later.
655 *
656 * In both cases, the media found are added to the global lists so the
657 * global arrays of media (including the GUI's virtual media manager)
658 * continue to work as before.
659 *
660 * @param uuidMachineRegistry The UUID of the media registry. This is either the
661 * transient UUID created at VirtualBox startup for the global registry or
662 * a machine ID.
663 * @param mediaRegistry The XML settings structure to load, either from VirtualBox.xml
664 * or a machine XML.
665 * @return
666 */
667HRESULT VirtualBox::initMedia(const Guid &uuidRegistry,
668 const settings::MediaRegistry mediaRegistry,
669 const Utf8Str &strMachineFolder)
670{
671 LogFlow(("VirtualBox::initMedia ENTERING, uuidRegistry=%s, strMachineFolder=%s\n",
672 uuidRegistry.toString().c_str(),
673 strMachineFolder.c_str()));
674
675 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
676
677 HRESULT rc = S_OK;
678 settings::MediaList::const_iterator it;
679 for (it = mediaRegistry.llHardDisks.begin();
680 it != mediaRegistry.llHardDisks.end();
681 ++it)
682 {
683 const settings::Medium &xmlHD = *it;
684
685 ComObjPtr<Medium> pHardDisk;
686 if (SUCCEEDED(rc = pHardDisk.createObject()))
687 rc = pHardDisk->init(this,
688 NULL, // parent
689 DeviceType_HardDisk,
690 uuidRegistry,
691 xmlHD, // XML data; this recurses to processes the children
692 strMachineFolder);
693 if (FAILED(rc)) return rc;
694
695 rc = registerMedium(pHardDisk, &pHardDisk, DeviceType_HardDisk);
696 if (FAILED(rc)) return rc;
697 }
698
699 for (it = mediaRegistry.llDvdImages.begin();
700 it != mediaRegistry.llDvdImages.end();
701 ++it)
702 {
703 const settings::Medium &xmlDvd = *it;
704
705 ComObjPtr<Medium> pImage;
706 if (SUCCEEDED(pImage.createObject()))
707 rc = pImage->init(this,
708 NULL,
709 DeviceType_DVD,
710 uuidRegistry,
711 xmlDvd,
712 strMachineFolder);
713 if (FAILED(rc)) return rc;
714
715 rc = registerMedium(pImage, &pImage, DeviceType_DVD);
716 if (FAILED(rc)) return rc;
717 }
718
719 for (it = mediaRegistry.llFloppyImages.begin();
720 it != mediaRegistry.llFloppyImages.end();
721 ++it)
722 {
723 const settings::Medium &xmlFloppy = *it;
724
725 ComObjPtr<Medium> pImage;
726 if (SUCCEEDED(pImage.createObject()))
727 rc = pImage->init(this,
728 NULL,
729 DeviceType_Floppy,
730 uuidRegistry,
731 xmlFloppy,
732 strMachineFolder);
733 if (FAILED(rc)) return rc;
734
735 rc = registerMedium(pImage, &pImage, DeviceType_Floppy);
736 if (FAILED(rc)) return rc;
737 }
738
739 LogFlow(("VirtualBox::initMedia LEAVING\n"));
740
741 return S_OK;
742}
743
744void VirtualBox::uninit()
745{
746 Assert(!m->uRegistryNeedsSaving);
747 if (m->uRegistryNeedsSaving)
748 saveSettings();
749
750 /* Enclose the state transition Ready->InUninit->NotReady */
751 AutoUninitSpan autoUninitSpan(this);
752 if (autoUninitSpan.uninitDone())
753 return;
754
755 LogFlow(("===========================================================\n"));
756 LogFlowThisFuncEnter();
757 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
758
759 /* tell all our child objects we've been uninitialized */
760
761 LogFlowThisFunc(("Uninitializing machines (%d)...\n", m->allMachines.size()));
762 if (m->pHost)
763 {
764 /* It is necessary to hold the VirtualBox and Host locks here because
765 we may have to uninitialize SessionMachines. */
766 AutoMultiWriteLock2 multilock(this, m->pHost COMMA_LOCKVAL_SRC_POS);
767 m->allMachines.uninitAll();
768 }
769 else
770 m->allMachines.uninitAll();
771 m->allFloppyImages.uninitAll();
772 m->allDVDImages.uninitAll();
773 m->allHardDisks.uninitAll();
774 m->allDHCPServers.uninitAll();
775
776 m->mapProgressOperations.clear();
777
778 m->allGuestOSTypes.uninitAll();
779
780 /* Note that we release singleton children after we've all other children.
781 * In some cases this is important because these other children may use
782 * some resources of the singletons which would prevent them from
783 * uninitializing (as for example, mSystemProperties which owns
784 * MediumFormat objects which Medium objects refer to) */
785 if (m->pSystemProperties)
786 {
787 m->pSystemProperties->uninit();
788 unconst(m->pSystemProperties).setNull();
789 }
790
791 if (m->pHost)
792 {
793 m->pHost->uninit();
794 unconst(m->pHost).setNull();
795 }
796
797#ifdef VBOX_WITH_RESOURCE_USAGE_API
798 if (m->pPerformanceCollector)
799 {
800 m->pPerformanceCollector->uninit();
801 unconst(m->pPerformanceCollector).setNull();
802 }
803#endif /* VBOX_WITH_RESOURCE_USAGE_API */
804
805 LogFlowThisFunc(("Terminating the async event handler...\n"));
806 if (m->threadAsyncEvent != NIL_RTTHREAD)
807 {
808 /* signal to exit the event loop */
809 if (RT_SUCCESS(m->pAsyncEventQ->interruptEventQueueProcessing()))
810 {
811 /*
812 * Wait for thread termination (only after we've successfully
813 * interrupted the event queue processing!)
814 */
815 int vrc = RTThreadWait(m->threadAsyncEvent, 60000, NULL);
816 if (RT_FAILURE(vrc))
817 LogWarningFunc(("RTThreadWait(%RTthrd) -> %Rrc\n",
818 m->threadAsyncEvent, vrc));
819 }
820 else
821 {
822 AssertMsgFailed(("interruptEventQueueProcessing() failed\n"));
823 RTThreadWait(m->threadAsyncEvent, 0, NULL);
824 }
825
826 unconst(m->threadAsyncEvent) = NIL_RTTHREAD;
827 unconst(m->pAsyncEventQ) = NULL;
828 }
829
830 LogFlowThisFunc(("Releasing event source...\n"));
831 if (m->pEventSource)
832 {
833 // we don't perform uninit() as it's possible that some pending event refers to this source
834 unconst(m->pEventSource).setNull();
835 }
836
837 LogFlowThisFunc(("Terminating the client watcher...\n"));
838 if (m->threadClientWatcher != NIL_RTTHREAD)
839 {
840 /* signal the client watcher thread */
841 updateClientWatcher();
842 /* wait for the termination */
843 RTThreadWait(m->threadClientWatcher, RT_INDEFINITE_WAIT, NULL);
844 unconst(m->threadClientWatcher) = NIL_RTTHREAD;
845 }
846 m->llProcesses.clear();
847#if defined(RT_OS_WINDOWS)
848 if (m->updateReq != NULL)
849 {
850 ::CloseHandle(m->updateReq);
851 unconst(m->updateReq) = NULL;
852 }
853#elif defined(RT_OS_OS2)
854 if (m->updateReq != NIL_RTSEMEVENT)
855 {
856 RTSemEventDestroy(m->updateReq);
857 unconst(m->updateReq) = NIL_RTSEMEVENT;
858 }
859#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
860 if (m->updateReq != NIL_RTSEMEVENT)
861 {
862 RTSemEventDestroy(m->updateReq);
863 unconst(m->updateReq) = NIL_RTSEMEVENT;
864 }
865#else
866# error "Port me!"
867#endif
868
869 delete m->pAutostartDb;
870
871 // clean up our instance data
872 delete m;
873
874 /* Unload hard disk plugin backends. */
875 VDShutdown();
876
877 LogFlowThisFuncLeave();
878 LogFlow(("===========================================================\n"));
879}
880
881// IVirtualBox properties
882/////////////////////////////////////////////////////////////////////////////
883
884STDMETHODIMP VirtualBox::COMGETTER(Version)(BSTR *aVersion)
885{
886 CheckComArgNotNull(aVersion);
887
888 AutoCaller autoCaller(this);
889 if (FAILED(autoCaller.rc())) return autoCaller.rc();
890
891 sVersion.cloneTo(aVersion);
892 return S_OK;
893}
894
895STDMETHODIMP VirtualBox::COMGETTER(VersionNormalized)(BSTR *aVersionNormalized)
896{
897 CheckComArgNotNull(aVersionNormalized);
898
899 AutoCaller autoCaller(this);
900 if (FAILED(autoCaller.rc())) return autoCaller.rc();
901
902 sVersionNormalized.cloneTo(aVersionNormalized);
903 return S_OK;
904}
905
906STDMETHODIMP VirtualBox::COMGETTER(Revision)(ULONG *aRevision)
907{
908 CheckComArgNotNull(aRevision);
909
910 AutoCaller autoCaller(this);
911 if (FAILED(autoCaller.rc())) return autoCaller.rc();
912
913 *aRevision = sRevision;
914 return S_OK;
915}
916
917STDMETHODIMP VirtualBox::COMGETTER(PackageType)(BSTR *aPackageType)
918{
919 CheckComArgNotNull(aPackageType);
920
921 AutoCaller autoCaller(this);
922 if (FAILED(autoCaller.rc())) return autoCaller.rc();
923
924 sPackageType.cloneTo(aPackageType);
925 return S_OK;
926}
927
928STDMETHODIMP VirtualBox::COMGETTER(APIVersion)(BSTR *aAPIVersion)
929{
930 CheckComArgNotNull(aAPIVersion);
931
932 AutoCaller autoCaller(this);
933 if (FAILED(autoCaller.rc())) return autoCaller.rc();
934
935 sAPIVersion.cloneTo(aAPIVersion);
936 return S_OK;
937}
938
939STDMETHODIMP VirtualBox::COMGETTER(HomeFolder)(BSTR *aHomeFolder)
940{
941 CheckComArgNotNull(aHomeFolder);
942
943 AutoCaller autoCaller(this);
944 if (FAILED(autoCaller.rc())) return autoCaller.rc();
945
946 /* mHomeDir is const and doesn't need a lock */
947 m->strHomeDir.cloneTo(aHomeFolder);
948 return S_OK;
949}
950
951STDMETHODIMP VirtualBox::COMGETTER(SettingsFilePath)(BSTR *aSettingsFilePath)
952{
953 CheckComArgNotNull(aSettingsFilePath);
954
955 AutoCaller autoCaller(this);
956 if (FAILED(autoCaller.rc())) return autoCaller.rc();
957
958 /* mCfgFile.mName is const and doesn't need a lock */
959 m->strSettingsFilePath.cloneTo(aSettingsFilePath);
960 return S_OK;
961}
962
963STDMETHODIMP VirtualBox::COMGETTER(Host)(IHost **aHost)
964{
965 CheckComArgOutPointerValid(aHost);
966
967 AutoCaller autoCaller(this);
968 if (FAILED(autoCaller.rc())) return autoCaller.rc();
969
970 /* mHost is const, no need to lock */
971 m->pHost.queryInterfaceTo(aHost);
972 return S_OK;
973}
974
975STDMETHODIMP
976VirtualBox::COMGETTER(SystemProperties)(ISystemProperties **aSystemProperties)
977{
978 CheckComArgOutPointerValid(aSystemProperties);
979
980 AutoCaller autoCaller(this);
981 if (FAILED(autoCaller.rc())) return autoCaller.rc();
982
983 /* mSystemProperties is const, no need to lock */
984 m->pSystemProperties.queryInterfaceTo(aSystemProperties);
985 return S_OK;
986}
987
988STDMETHODIMP
989VirtualBox::COMGETTER(Machines)(ComSafeArrayOut(IMachine *, aMachines))
990{
991 CheckComArgOutSafeArrayPointerValid(aMachines);
992
993 AutoCaller autoCaller(this);
994 if (FAILED(autoCaller.rc())) return autoCaller.rc();
995
996 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
997 SafeIfaceArray<IMachine> machines(m->allMachines.getList());
998 machines.detachTo(ComSafeArrayOutArg(aMachines));
999
1000 return S_OK;
1001}
1002
1003STDMETHODIMP
1004VirtualBox::COMGETTER(MachineGroups)(ComSafeArrayOut(BSTR, aMachineGroups))
1005{
1006 CheckComArgOutSafeArrayPointerValid(aMachineGroups);
1007
1008 AutoCaller autoCaller(this);
1009 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1010
1011 std::list<Bstr> allGroups;
1012
1013 /* get copy of all machine references, to avoid holding the list lock */
1014 MachinesOList::MyList allMachines;
1015 {
1016 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1017 allMachines = m->allMachines.getList();
1018 }
1019 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
1020 it != allMachines.end();
1021 ++it)
1022 {
1023 const ComObjPtr<Machine> &pMachine = *it;
1024 AutoCaller autoMachineCaller(pMachine);
1025 if (FAILED(autoMachineCaller.rc()))
1026 continue;
1027 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
1028
1029 if (pMachine->isAccessible())
1030 {
1031 const StringsList &thisGroups = pMachine->getGroups();
1032 for (StringsList::const_iterator it2 = thisGroups.begin();
1033 it2 != thisGroups.end();
1034 ++it2)
1035 allGroups.push_back(*it2);
1036 }
1037 }
1038
1039 /* throw out any duplicates */
1040 allGroups.sort();
1041 allGroups.unique();
1042 com::SafeArray<BSTR> machineGroups(allGroups.size());
1043 size_t i = 0;
1044 for (std::list<Bstr>::const_iterator it = allGroups.begin();
1045 it != allGroups.end();
1046 ++it, i++)
1047 {
1048 const Bstr &tmp = *it;
1049 tmp.cloneTo(&machineGroups[i]);
1050 }
1051 machineGroups.detachTo(ComSafeArrayOutArg(aMachineGroups));
1052
1053 return S_OK;
1054}
1055
1056STDMETHODIMP VirtualBox::COMGETTER(HardDisks)(ComSafeArrayOut(IMedium *, aHardDisks))
1057{
1058 CheckComArgOutSafeArrayPointerValid(aHardDisks);
1059
1060 AutoCaller autoCaller(this);
1061 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1062
1063 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1064 SafeIfaceArray<IMedium> hardDisks(m->allHardDisks.getList());
1065 hardDisks.detachTo(ComSafeArrayOutArg(aHardDisks));
1066
1067 return S_OK;
1068}
1069
1070STDMETHODIMP VirtualBox::COMGETTER(DVDImages)(ComSafeArrayOut(IMedium *, aDVDImages))
1071{
1072 CheckComArgOutSafeArrayPointerValid(aDVDImages);
1073
1074 AutoCaller autoCaller(this);
1075 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1076
1077 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1078 SafeIfaceArray<IMedium> images(m->allDVDImages.getList());
1079 images.detachTo(ComSafeArrayOutArg(aDVDImages));
1080
1081 return S_OK;
1082}
1083
1084STDMETHODIMP VirtualBox::COMGETTER(FloppyImages)(ComSafeArrayOut(IMedium *, aFloppyImages))
1085{
1086 CheckComArgOutSafeArrayPointerValid(aFloppyImages);
1087
1088 AutoCaller autoCaller(this);
1089 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1090
1091 AutoReadLock al(m->allFloppyImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1092 SafeIfaceArray<IMedium> images(m->allFloppyImages.getList());
1093 images.detachTo(ComSafeArrayOutArg(aFloppyImages));
1094
1095 return S_OK;
1096}
1097
1098STDMETHODIMP VirtualBox::COMGETTER(ProgressOperations)(ComSafeArrayOut(IProgress *, aOperations))
1099{
1100 CheckComArgOutPointerValid(aOperations);
1101
1102 AutoCaller autoCaller(this);
1103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1104
1105 /* protect mProgressOperations */
1106 AutoReadLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
1107 SafeIfaceArray<IProgress> progress(m->mapProgressOperations);
1108 progress.detachTo(ComSafeArrayOutArg(aOperations));
1109
1110 return S_OK;
1111}
1112
1113STDMETHODIMP VirtualBox::COMGETTER(GuestOSTypes)(ComSafeArrayOut(IGuestOSType *, aGuestOSTypes))
1114{
1115 CheckComArgOutSafeArrayPointerValid(aGuestOSTypes);
1116
1117 AutoCaller autoCaller(this);
1118 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1119
1120 AutoReadLock al(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1121 SafeIfaceArray<IGuestOSType> ostypes(m->allGuestOSTypes.getList());
1122 ostypes.detachTo(ComSafeArrayOutArg(aGuestOSTypes));
1123
1124 return S_OK;
1125}
1126
1127STDMETHODIMP VirtualBox::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
1128{
1129#ifndef RT_OS_WINDOWS
1130 NOREF(aSharedFoldersSize);
1131#endif /* RT_OS_WINDOWS */
1132
1133 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
1134
1135 AutoCaller autoCaller(this);
1136 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1137
1138 return setError(E_NOTIMPL, "Not yet implemented");
1139}
1140
1141STDMETHODIMP
1142VirtualBox::COMGETTER(PerformanceCollector)(IPerformanceCollector **aPerformanceCollector)
1143{
1144#ifdef VBOX_WITH_RESOURCE_USAGE_API
1145 CheckComArgOutPointerValid(aPerformanceCollector);
1146
1147 AutoCaller autoCaller(this);
1148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1149
1150 /* mPerformanceCollector is const, no need to lock */
1151 m->pPerformanceCollector.queryInterfaceTo(aPerformanceCollector);
1152
1153 return S_OK;
1154#else /* !VBOX_WITH_RESOURCE_USAGE_API */
1155 ReturnComNotImplemented();
1156#endif /* !VBOX_WITH_RESOURCE_USAGE_API */
1157}
1158
1159STDMETHODIMP
1160VirtualBox::COMGETTER(DHCPServers)(ComSafeArrayOut(IDHCPServer *, aDHCPServers))
1161{
1162 CheckComArgOutSafeArrayPointerValid(aDHCPServers);
1163
1164 AutoCaller autoCaller(this);
1165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1166
1167 AutoReadLock al(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1168 SafeIfaceArray<IDHCPServer> svrs(m->allDHCPServers.getList());
1169 svrs.detachTo(ComSafeArrayOutArg(aDHCPServers));
1170
1171 return S_OK;
1172}
1173
1174
1175STDMETHODIMP
1176VirtualBox::COMGETTER(NATNetworks)(ComSafeArrayOut(INATNetwork *, aNATNetworks))
1177{
1178#ifdef VBOX_WITH_NAT_SERVICE
1179 CheckComArgOutSafeArrayPointerValid(aNATNetworks);
1180
1181 AutoCaller autoCaller(this);
1182 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1183
1184 AutoReadLock al(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1185 SafeIfaceArray<INATNetwork> nets(m->allNATNetworks.getList());
1186 nets.detachTo(ComSafeArrayOutArg(aNATNetworks));
1187
1188 return S_OK;
1189#else
1190 NOREF(aNATNetworks);
1191# ifndef RT_OS_WINDOWS
1192 NOREF(aNATNetworksSize);
1193# endif
1194 return E_NOTIMPL;
1195#endif
1196}
1197
1198
1199STDMETHODIMP
1200VirtualBox::COMGETTER(EventSource)(IEventSource ** aEventSource)
1201{
1202 CheckComArgOutPointerValid(aEventSource);
1203
1204 AutoCaller autoCaller(this);
1205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1206
1207 /* event source is const, no need to lock */
1208 m->pEventSource.queryInterfaceTo(aEventSource);
1209
1210 return S_OK;
1211}
1212
1213STDMETHODIMP
1214VirtualBox::COMGETTER(ExtensionPackManager)(IExtPackManager **aExtPackManager)
1215{
1216 CheckComArgOutPointerValid(aExtPackManager);
1217
1218 AutoCaller autoCaller(this);
1219 HRESULT hrc = autoCaller.rc();
1220 if (SUCCEEDED(hrc))
1221 {
1222#ifdef VBOX_WITH_EXTPACK
1223 /* The extension pack manager is const, no need to lock. */
1224 hrc = m->ptrExtPackManager.queryInterfaceTo(aExtPackManager);
1225#else
1226 hrc = E_NOTIMPL;
1227#endif
1228 }
1229
1230 return hrc;
1231}
1232
1233STDMETHODIMP VirtualBox::COMGETTER(InternalNetworks)(ComSafeArrayOut(BSTR, aInternalNetworks))
1234{
1235 CheckComArgOutSafeArrayPointerValid(aInternalNetworks);
1236
1237 AutoCaller autoCaller(this);
1238 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1239
1240 std::list<Bstr> allInternalNetworks;
1241
1242 /* get copy of all machine references, to avoid holding the list lock */
1243 MachinesOList::MyList allMachines;
1244 {
1245 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1246 allMachines = m->allMachines.getList();
1247 }
1248 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
1249 it != allMachines.end();
1250 ++it)
1251 {
1252 const ComObjPtr<Machine> &pMachine = *it;
1253 AutoCaller autoMachineCaller(pMachine);
1254 if (FAILED(autoMachineCaller.rc()))
1255 continue;
1256 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
1257
1258 if (pMachine->isAccessible())
1259 {
1260 uint32_t cNetworkAdapters = Global::getMaxNetworkAdapters(pMachine->getChipsetType());
1261 for (ULONG i = 0; i < cNetworkAdapters; i++)
1262 {
1263 ComPtr<INetworkAdapter> pNet;
1264 HRESULT rc = pMachine->GetNetworkAdapter(i, pNet.asOutParam());
1265 if (FAILED(rc) || pNet.isNull())
1266 continue;
1267 Bstr strInternalNetwork;
1268 rc = pNet->COMGETTER(InternalNetwork)(strInternalNetwork.asOutParam());
1269 if (FAILED(rc) || strInternalNetwork.isEmpty())
1270 continue;
1271
1272 allInternalNetworks.push_back(strInternalNetwork);
1273 }
1274 }
1275 }
1276
1277 /* throw out any duplicates */
1278 allInternalNetworks.sort();
1279 allInternalNetworks.unique();
1280 com::SafeArray<BSTR> internalNetworks(allInternalNetworks.size());
1281 size_t i = 0;
1282 for (std::list<Bstr>::const_iterator it = allInternalNetworks.begin();
1283 it != allInternalNetworks.end();
1284 ++it, i++)
1285 {
1286 const Bstr &tmp = *it;
1287 tmp.cloneTo(&internalNetworks[i]);
1288 }
1289 internalNetworks.detachTo(ComSafeArrayOutArg(aInternalNetworks));
1290
1291 return S_OK;
1292}
1293
1294STDMETHODIMP VirtualBox::COMGETTER(GenericNetworkDrivers)(ComSafeArrayOut(BSTR, aGenericNetworkDrivers))
1295{
1296 CheckComArgOutSafeArrayPointerValid(aGenericNetworkDrivers);
1297
1298 AutoCaller autoCaller(this);
1299 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1300
1301 std::list<Bstr> allGenericNetworkDrivers;
1302
1303 /* get copy of all machine references, to avoid holding the list lock */
1304 MachinesOList::MyList allMachines;
1305 {
1306 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1307 allMachines = m->allMachines.getList();
1308 }
1309 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
1310 it != allMachines.end();
1311 ++it)
1312 {
1313 const ComObjPtr<Machine> &pMachine = *it;
1314 AutoCaller autoMachineCaller(pMachine);
1315 if (FAILED(autoMachineCaller.rc()))
1316 continue;
1317 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
1318
1319 if (pMachine->isAccessible())
1320 {
1321 uint32_t cNetworkAdapters = Global::getMaxNetworkAdapters(pMachine->getChipsetType());
1322 for (ULONG i = 0; i < cNetworkAdapters; i++)
1323 {
1324 ComPtr<INetworkAdapter> pNet;
1325 HRESULT rc = pMachine->GetNetworkAdapter(i, pNet.asOutParam());
1326 if (FAILED(rc) || pNet.isNull())
1327 continue;
1328 Bstr strGenericNetworkDriver;
1329 rc = pNet->COMGETTER(GenericDriver)(strGenericNetworkDriver.asOutParam());
1330 if (FAILED(rc) || strGenericNetworkDriver.isEmpty())
1331 continue;
1332
1333 allGenericNetworkDrivers.push_back(strGenericNetworkDriver);
1334 }
1335 }
1336 }
1337
1338 /* throw out any duplicates */
1339 allGenericNetworkDrivers.sort();
1340 allGenericNetworkDrivers.unique();
1341 com::SafeArray<BSTR> genericNetworks(allGenericNetworkDrivers.size());
1342 size_t i = 0;
1343 for (std::list<Bstr>::const_iterator it = allGenericNetworkDrivers.begin();
1344 it != allGenericNetworkDrivers.end();
1345 ++it, i++)
1346 {
1347 const Bstr &tmp = *it;
1348 tmp.cloneTo(&genericNetworks[i]);
1349 }
1350 genericNetworks.detachTo(ComSafeArrayOutArg(aGenericNetworkDrivers));
1351
1352 return S_OK;
1353}
1354
1355STDMETHODIMP
1356VirtualBox::CheckFirmwarePresent(FirmwareType_T aFirmwareType,
1357 IN_BSTR aVersion,
1358 BSTR *aUrl,
1359 BSTR *aFile,
1360 BOOL *aResult)
1361{
1362 CheckComArgNotNull(aResult);
1363
1364 AutoCaller autoCaller(this);
1365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1366
1367 NOREF(aVersion);
1368
1369 static const struct
1370 {
1371 FirmwareType_T type;
1372 const char* fileName;
1373 const char* url;
1374 }
1375 firmwareDesc[] =
1376 {
1377 {
1378 /* compiled-in firmware */
1379 FirmwareType_BIOS, NULL, NULL
1380 },
1381 {
1382 FirmwareType_EFI32, "VBoxEFI32.fd", "http://virtualbox.org/firmware/VBoxEFI32.fd"
1383 },
1384 {
1385 FirmwareType_EFI64, "VBoxEFI64.fd", "http://virtualbox.org/firmware/VBoxEFI64.fd"
1386 },
1387 {
1388 FirmwareType_EFIDUAL, "VBoxEFIDual.fd", "http://virtualbox.org/firmware/VBoxEFIDual.fd"
1389 }
1390 };
1391
1392 for (size_t i = 0; i < sizeof(firmwareDesc) / sizeof(firmwareDesc[0]); i++)
1393 {
1394 if (aFirmwareType != firmwareDesc[i].type)
1395 continue;
1396
1397 /* compiled-in firmware */
1398 if (firmwareDesc[i].fileName == NULL)
1399 {
1400 *aResult = TRUE;
1401 break;
1402 }
1403
1404 Utf8Str shortName, fullName;
1405
1406 shortName = Utf8StrFmt("Firmware%c%s",
1407 RTPATH_DELIMITER,
1408 firmwareDesc[i].fileName);
1409 int rc = calculateFullPath(shortName, fullName);
1410 AssertRCReturn(rc, rc);
1411 if (RTFileExists(fullName.c_str()))
1412 {
1413 *aResult = TRUE;
1414 if (aFile)
1415 Utf8Str(fullName).cloneTo(aFile);
1416 break;
1417 }
1418
1419 char pszVBoxPath[RTPATH_MAX];
1420 rc = RTPathExecDir(pszVBoxPath, RTPATH_MAX);
1421 AssertRCReturn(rc, rc);
1422 fullName = Utf8StrFmt("%s%c%s",
1423 pszVBoxPath,
1424 RTPATH_DELIMITER,
1425 firmwareDesc[i].fileName);
1426 if (RTFileExists(fullName.c_str()))
1427 {
1428 *aResult = TRUE;
1429 if (aFile)
1430 Utf8Str(fullName).cloneTo(aFile);
1431 break;
1432 }
1433
1434 /** @todo: account for version in the URL */
1435 if (aUrl != NULL)
1436 {
1437 Utf8Str strUrl(firmwareDesc[i].url);
1438 strUrl.cloneTo(aUrl);
1439 }
1440 *aResult = FALSE;
1441
1442 /* Assume single record per firmware type */
1443 break;
1444 }
1445
1446 return S_OK;
1447}
1448// IVirtualBox methods
1449/////////////////////////////////////////////////////////////////////////////
1450
1451/* Helper for VirtualBox::ComposeMachineFilename */
1452static void sanitiseMachineFilename(Utf8Str &aName);
1453
1454STDMETHODIMP VirtualBox::ComposeMachineFilename(IN_BSTR aName,
1455 IN_BSTR aGroup,
1456 IN_BSTR aCreateFlags,
1457 IN_BSTR aBaseFolder,
1458 BSTR *aFilename)
1459{
1460 LogFlowThisFuncEnter();
1461 LogFlowThisFunc(("aName=\"%ls\",aBaseFolder=\"%ls\"\n", aName, aBaseFolder));
1462
1463 CheckComArgStrNotEmptyOrNull(aName);
1464 CheckComArgOutPointerValid(aFilename);
1465
1466 AutoCaller autoCaller(this);
1467 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1468
1469 Utf8Str strCreateFlags(aCreateFlags);
1470 Guid id;
1471 bool fDirectoryIncludesUUID = false;
1472 if (!strCreateFlags.isEmpty())
1473 {
1474 const char *pcszNext = strCreateFlags.c_str();
1475 while (*pcszNext != '\0')
1476 {
1477 Utf8Str strFlag;
1478 const char *pcszComma = RTStrStr(pcszNext, ",");
1479 if (!pcszComma)
1480 strFlag = pcszNext;
1481 else
1482 strFlag = Utf8Str(pcszNext, pcszComma - pcszNext);
1483
1484 const char *pcszEqual = RTStrStr(strFlag.c_str(), "=");
1485 /* skip over everything which doesn't contain '=' */
1486 if (pcszEqual && pcszEqual != strFlag.c_str())
1487 {
1488 Utf8Str strKey(strFlag.c_str(), pcszEqual - strFlag.c_str());
1489 Utf8Str strValue(strFlag.c_str() + (pcszEqual - strFlag.c_str() + 1));
1490
1491 if (strKey == "UUID")
1492 id = strValue.c_str();
1493 else if (strKey == "directoryIncludesUUID")
1494 fDirectoryIncludesUUID = (strValue == "1");
1495 }
1496
1497 if (!pcszComma)
1498 pcszNext += strFlag.length();
1499 else
1500 pcszNext += strFlag.length() + 1;
1501 }
1502 }
1503
1504 if (id.isZero())
1505 fDirectoryIncludesUUID = false;
1506 else if (!id.isValid())
1507 {
1508 /* do something else */
1509 return setError(E_INVALIDARG,
1510 tr("'%ls' is not a valid Guid"),
1511 id.toStringCurly().c_str());
1512 }
1513
1514 Utf8Str strGroup(aGroup);
1515 if (strGroup.isEmpty())
1516 strGroup = "/";
1517 HRESULT rc = validateMachineGroup(strGroup, true);
1518 if (FAILED(rc))
1519 return rc;
1520
1521 /* Compose the settings file name using the following scheme:
1522 *
1523 * <base_folder><group>/<machine_name>/<machine_name>.xml
1524 *
1525 * If a non-null and non-empty base folder is specified, the default
1526 * machine folder will be used as a base folder.
1527 * We sanitise the machine name to a safe white list of characters before
1528 * using it.
1529 */
1530 Utf8Str strBase = aBaseFolder;
1531 Utf8Str strName = aName;
1532 Utf8Str strDirName(strName);
1533 if (fDirectoryIncludesUUID)
1534 strDirName += Utf8StrFmt(" (%RTuuid)", id.raw());
1535 sanitiseMachineFilename(strName);
1536 sanitiseMachineFilename(strDirName);
1537
1538 if (strBase.isEmpty())
1539 /* we use the non-full folder value below to keep the path relative */
1540 getDefaultMachineFolder(strBase);
1541
1542 calculateFullPath(strBase, strBase);
1543
1544 /* eliminate toplevel group to avoid // in the result */
1545 if (strGroup == "/")
1546 strGroup.setNull();
1547 Bstr bstrSettingsFile = BstrFmt("%s%s%c%s%c%s.vbox",
1548 strBase.c_str(),
1549 strGroup.c_str(),
1550 RTPATH_DELIMITER,
1551 strDirName.c_str(),
1552 RTPATH_DELIMITER,
1553 strName.c_str());
1554
1555 bstrSettingsFile.detachTo(aFilename);
1556
1557 return S_OK;
1558}
1559
1560/**
1561 * Remove characters from a machine file name which can be problematic on
1562 * particular systems.
1563 * @param strName The file name to sanitise.
1564 */
1565void sanitiseMachineFilename(Utf8Str &strName)
1566{
1567 /** Set of characters which should be safe for use in filenames: some basic
1568 * ASCII, Unicode from Latin-1 alphabetic to the end of Hangul. We try to
1569 * skip anything that could count as a control character in Windows or
1570 * *nix, or be otherwise difficult for shells to handle (I would have
1571 * preferred to remove the space and brackets too). We also remove all
1572 * characters which need UTF-16 surrogate pairs for Windows's benefit. */
1573 RTUNICP aCpSet[] =
1574 { ' ', ' ', '(', ')', '-', '.', '0', '9', 'A', 'Z', 'a', 'z', '_', '_',
1575 0xa0, 0xd7af, '\0' };
1576 char *pszName = strName.mutableRaw();
1577 int cReplacements = RTStrPurgeComplementSet(pszName, aCpSet, '_');
1578 Assert(cReplacements >= 0);
1579 NOREF(cReplacements);
1580 /* No leading dot or dash. */
1581 if (pszName[0] == '.' || pszName[0] == '-')
1582 pszName[0] = '_';
1583 /* No trailing dot. */
1584 if (pszName[strName.length() - 1] == '.')
1585 pszName[strName.length() - 1] = '_';
1586 /* Mangle leading and trailing spaces. */
1587 for (size_t i = 0; pszName[i] == ' '; ++i)
1588 pszName[i] = '_';
1589 for (size_t i = strName.length() - 1; i && pszName[i] == ' '; --i)
1590 pszName[i] = '_';
1591}
1592
1593#ifdef DEBUG
1594/** Simple unit test/operation examples for sanitiseMachineFilename(). */
1595static unsigned testSanitiseMachineFilename(void (*pfnPrintf)(const char *, ...))
1596{
1597 unsigned cErrors = 0;
1598
1599 /** Expected results of sanitising given file names. */
1600 static struct
1601 {
1602 /** The test file name to be sanitised (Utf-8). */
1603 const char *pcszIn;
1604 /** The expected sanitised output (Utf-8). */
1605 const char *pcszOutExpected;
1606 } aTest[] =
1607 {
1608 { "OS/2 2.1", "OS_2 2.1" },
1609 { "-!My VM!-", "__My VM_-" },
1610 { "\xF0\x90\x8C\xB0", "____" },
1611 { " My VM ", "__My VM__" },
1612 { ".My VM.", "_My VM_" },
1613 { "My VM", "My VM" }
1614 };
1615 for (unsigned i = 0; i < RT_ELEMENTS(aTest); ++i)
1616 {
1617 Utf8Str str(aTest[i].pcszIn);
1618 sanitiseMachineFilename(str);
1619 if (str.compare(aTest[i].pcszOutExpected))
1620 {
1621 ++cErrors;
1622 pfnPrintf("%s: line %d, expected %s, actual %s\n",
1623 __PRETTY_FUNCTION__, i, aTest[i].pcszOutExpected,
1624 str.c_str());
1625 }
1626 }
1627 return cErrors;
1628}
1629
1630/** @todo Proper testcase. */
1631/** @todo Do we have a better method of doing init functions? */
1632namespace
1633{
1634 class TestSanitiseMachineFilename
1635 {
1636 public:
1637 TestSanitiseMachineFilename(void)
1638 {
1639 Assert(!testSanitiseMachineFilename(RTAssertMsg2));
1640 }
1641 };
1642 TestSanitiseMachineFilename s_TestSanitiseMachineFilename;
1643}
1644#endif
1645
1646/** @note Locks mSystemProperties object for reading. */
1647STDMETHODIMP VirtualBox::CreateMachine(IN_BSTR aSettingsFile,
1648 IN_BSTR aName,
1649 ComSafeArrayIn(IN_BSTR, aGroups),
1650 IN_BSTR aOsTypeId,
1651 IN_BSTR aCreateFlags,
1652 IMachine **aMachine)
1653{
1654 LogFlowThisFuncEnter();
1655 LogFlowThisFunc(("aSettingsFile=\"%ls\", aName=\"%ls\", aOsTypeId =\"%ls\", aCreateFlags=\"%ls\"\n", aSettingsFile, aName, aOsTypeId, aCreateFlags));
1656
1657 CheckComArgStrNotEmptyOrNull(aName);
1658 /** @todo tighten checks on aId? */
1659 CheckComArgOutPointerValid(aMachine);
1660
1661 AutoCaller autoCaller(this);
1662 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1663
1664 StringsList llGroups;
1665 HRESULT rc = convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1666 if (FAILED(rc))
1667 return rc;
1668
1669 Utf8Str strCreateFlags(aCreateFlags);
1670 Guid id;
1671 bool fForceOverwrite = false;
1672 bool fDirectoryIncludesUUID = false;
1673 if (!strCreateFlags.isEmpty())
1674 {
1675 const char *pcszNext = strCreateFlags.c_str();
1676 while (*pcszNext != '\0')
1677 {
1678 Utf8Str strFlag;
1679 const char *pcszComma = RTStrStr(pcszNext, ",");
1680 if (!pcszComma)
1681 strFlag = pcszNext;
1682 else
1683 strFlag = Utf8Str(pcszNext, pcszComma - pcszNext);
1684
1685 const char *pcszEqual = RTStrStr(strFlag.c_str(), "=");
1686 /* skip over everything which doesn't contain '=' */
1687 if (pcszEqual && pcszEqual != strFlag.c_str())
1688 {
1689 Utf8Str strKey(strFlag.c_str(), pcszEqual - strFlag.c_str());
1690 Utf8Str strValue(strFlag.c_str() + (pcszEqual - strFlag.c_str() + 1));
1691
1692 if (strKey == "UUID")
1693 id = strValue.c_str();
1694 else if (strKey == "forceOverwrite")
1695 fForceOverwrite = (strValue == "1");
1696 else if (strKey == "directoryIncludesUUID")
1697 fDirectoryIncludesUUID = (strValue == "1");
1698 }
1699
1700 if (!pcszComma)
1701 pcszNext += strFlag.length();
1702 else
1703 pcszNext += strFlag.length() + 1;
1704 }
1705 }
1706 /* Create UUID if none was specified. */
1707 if (id.isZero())
1708 id.create();
1709 else if (!id.isValid())
1710 {
1711 /* do something else */
1712 return setError(E_INVALIDARG,
1713 tr("'%ls' is not a valid Guid"),
1714 id.toStringCurly().c_str());
1715 }
1716
1717 /* NULL settings file means compose automatically */
1718 Bstr bstrSettingsFile(aSettingsFile);
1719 if (bstrSettingsFile.isEmpty())
1720 {
1721 Utf8Str strNewCreateFlags(Utf8StrFmt("UUID=%RTuuid", id.raw()));
1722 if (fDirectoryIncludesUUID)
1723 strNewCreateFlags += ",directoryIncludesUUID=1";
1724
1725 rc = ComposeMachineFilename(aName,
1726 Bstr(llGroups.front()).raw(),
1727 Bstr(strNewCreateFlags).raw(),
1728 NULL /* aBaseFolder */,
1729 bstrSettingsFile.asOutParam());
1730 if (FAILED(rc)) return rc;
1731 }
1732
1733 /* create a new object */
1734 ComObjPtr<Machine> machine;
1735 rc = machine.createObject();
1736 if (FAILED(rc)) return rc;
1737
1738 GuestOSType *osType = NULL;
1739 rc = findGuestOSType(Bstr(aOsTypeId), osType);
1740 if (FAILED(rc)) return rc;
1741
1742 /* initialize the machine object */
1743 rc = machine->init(this,
1744 Utf8Str(bstrSettingsFile),
1745 Utf8Str(aName),
1746 llGroups,
1747 osType,
1748 id,
1749 fForceOverwrite,
1750 fDirectoryIncludesUUID);
1751 if (SUCCEEDED(rc))
1752 {
1753 /* set the return value */
1754 rc = machine.queryInterfaceTo(aMachine);
1755 AssertComRC(rc);
1756
1757#ifdef VBOX_WITH_EXTPACK
1758 /* call the extension pack hooks */
1759 m->ptrExtPackManager->callAllVmCreatedHooks(machine);
1760#endif
1761 }
1762
1763 LogFlowThisFuncLeave();
1764
1765 return rc;
1766}
1767
1768STDMETHODIMP VirtualBox::OpenMachine(IN_BSTR aSettingsFile,
1769 IMachine **aMachine)
1770{
1771 CheckComArgStrNotEmptyOrNull(aSettingsFile);
1772 CheckComArgOutPointerValid(aMachine);
1773
1774 AutoCaller autoCaller(this);
1775 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1776
1777 HRESULT rc = E_FAIL;
1778
1779 /* create a new object */
1780 ComObjPtr<Machine> machine;
1781 rc = machine.createObject();
1782 if (SUCCEEDED(rc))
1783 {
1784 /* initialize the machine object */
1785 rc = machine->initFromSettings(this,
1786 aSettingsFile,
1787 NULL); /* const Guid *aId */
1788 if (SUCCEEDED(rc))
1789 {
1790 /* set the return value */
1791 rc = machine.queryInterfaceTo(aMachine);
1792 ComAssertComRC(rc);
1793 }
1794 }
1795
1796 return rc;
1797}
1798
1799/** @note Locks objects! */
1800STDMETHODIMP VirtualBox::RegisterMachine(IMachine *aMachine)
1801{
1802 CheckComArgNotNull(aMachine);
1803
1804 AutoCaller autoCaller(this);
1805 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1806
1807 HRESULT rc;
1808
1809 Bstr name;
1810 rc = aMachine->COMGETTER(Name)(name.asOutParam());
1811 if (FAILED(rc)) return rc;
1812
1813 /* We can safely cast child to Machine * here because only Machine
1814 * implementations of IMachine can be among our children. */
1815 Machine *pMachine = static_cast<Machine*>(aMachine);
1816
1817 AutoCaller machCaller(pMachine);
1818 ComAssertComRCRetRC(machCaller.rc());
1819
1820 rc = registerMachine(pMachine);
1821 /* fire an event */
1822 if (SUCCEEDED(rc))
1823 onMachineRegistered(pMachine->getId(), TRUE);
1824
1825 return rc;
1826}
1827
1828/** @note Locks this object for reading, then some machine objects for reading. */
1829STDMETHODIMP VirtualBox::FindMachine(IN_BSTR aNameOrId, IMachine **aMachine)
1830{
1831 LogFlowThisFuncEnter();
1832 LogFlowThisFunc(("aName=\"%ls\", aMachine={%p}\n", aNameOrId, aMachine));
1833
1834 CheckComArgStrNotEmptyOrNull(aNameOrId);
1835 CheckComArgOutPointerValid(aMachine);
1836
1837 AutoCaller autoCaller(this);
1838 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1839
1840 /* start with not found */
1841 HRESULT rc = S_OK;
1842 ComObjPtr<Machine> pMachineFound;
1843
1844 Guid id(aNameOrId);
1845 if (id.isValid() && !id.isZero())
1846
1847 rc = findMachine(id,
1848 true /* fPermitInaccessible */,
1849 true /* setError */,
1850 &pMachineFound);
1851 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error
1852 else
1853 {
1854 Utf8Str strName(aNameOrId);
1855 rc = findMachineByName(aNameOrId,
1856 true /* setError */,
1857 &pMachineFound);
1858 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error
1859 }
1860
1861 /* this will set (*machine) to NULL if machineObj is null */
1862 pMachineFound.queryInterfaceTo(aMachine);
1863
1864 LogFlowThisFunc(("aName=\"%ls\", aMachine=%p, rc=%08X\n", aNameOrId, *aMachine, rc));
1865 LogFlowThisFuncLeave();
1866
1867 return rc;
1868}
1869
1870STDMETHODIMP VirtualBox::GetMachinesByGroups(ComSafeArrayIn(IN_BSTR, aGroups), ComSafeArrayOut(IMachine *, aMachines))
1871{
1872 CheckComArgSafeArrayNotNull(aGroups);
1873 CheckComArgOutSafeArrayPointerValid(aMachines);
1874
1875 AutoCaller autoCaller(this);
1876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1877
1878 StringsList llGroups;
1879 HRESULT rc = convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1880 if (FAILED(rc))
1881 return rc;
1882 /* we want to rely on sorted groups during compare, to save time */
1883 llGroups.sort();
1884
1885 /* get copy of all machine references, to avoid holding the list lock */
1886 MachinesOList::MyList allMachines;
1887 {
1888 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1889 allMachines = m->allMachines.getList();
1890 }
1891
1892 com::SafeIfaceArray<IMachine> saMachines;
1893 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
1894 it != allMachines.end();
1895 ++it)
1896 {
1897 const ComObjPtr<Machine> &pMachine = *it;
1898 AutoCaller autoMachineCaller(pMachine);
1899 if (FAILED(autoMachineCaller.rc()))
1900 continue;
1901 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
1902
1903 if (pMachine->isAccessible())
1904 {
1905 const StringsList &thisGroups = pMachine->getGroups();
1906 for (StringsList::const_iterator it2 = thisGroups.begin();
1907 it2 != thisGroups.end();
1908 ++it2)
1909 {
1910 const Utf8Str &group = *it2;
1911 bool fAppended = false;
1912 for (StringsList::const_iterator it3 = llGroups.begin();
1913 it3 != llGroups.end();
1914 ++it3)
1915 {
1916 int order = it3->compare(group);
1917 if (order == 0)
1918 {
1919 saMachines.push_back(pMachine);
1920 fAppended = true;
1921 break;
1922 }
1923 else if (order > 0)
1924 break;
1925 else
1926 continue;
1927 }
1928 /* avoid duplicates and save time */
1929 if (fAppended)
1930 break;
1931 }
1932 }
1933 }
1934
1935 saMachines.detachTo(ComSafeArrayOutArg(aMachines));
1936
1937 return S_OK;
1938}
1939
1940STDMETHODIMP VirtualBox::GetMachineStates(ComSafeArrayIn(IMachine *, aMachines), ComSafeArrayOut(MachineState_T, aStates))
1941{
1942 CheckComArgSafeArrayNotNull(aMachines);
1943 CheckComArgOutSafeArrayPointerValid(aStates);
1944
1945 com::SafeIfaceArray<IMachine> saMachines(ComSafeArrayInArg(aMachines));
1946 com::SafeArray<MachineState_T> saStates(saMachines.size());
1947 for (size_t i = 0; i < saMachines.size(); i++)
1948 {
1949 ComPtr<IMachine> pMachine = saMachines[i];
1950 MachineState_T state = MachineState_Null;
1951 if (!pMachine.isNull())
1952 {
1953 HRESULT rc = pMachine->COMGETTER(State)(&state);
1954 if (rc == E_ACCESSDENIED)
1955 rc = S_OK;
1956 AssertComRC(rc);
1957 }
1958 saStates[i] = state;
1959 }
1960 saStates.detachTo(ComSafeArrayOutArg(aStates));
1961
1962 return S_OK;
1963}
1964
1965STDMETHODIMP VirtualBox::CreateHardDisk(IN_BSTR aFormat,
1966 IN_BSTR aLocation,
1967 IMedium **aHardDisk)
1968{
1969 CheckComArgOutPointerValid(aHardDisk);
1970
1971 AutoCaller autoCaller(this);
1972 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1973
1974 /* we don't access non-const data members so no need to lock */
1975
1976 Utf8Str format(aFormat);
1977 if (format.isEmpty())
1978 getDefaultHardDiskFormat(format);
1979
1980 ComObjPtr<Medium> hardDisk;
1981 hardDisk.createObject();
1982 HRESULT rc = hardDisk->init(this,
1983 format,
1984 aLocation,
1985 Guid::Empty /* media registry: none yet */);
1986
1987 if (SUCCEEDED(rc))
1988 hardDisk.queryInterfaceTo(aHardDisk);
1989
1990 return rc;
1991}
1992
1993STDMETHODIMP VirtualBox::OpenMedium(IN_BSTR aLocation,
1994 DeviceType_T deviceType,
1995 AccessMode_T accessMode,
1996 BOOL fForceNewUuid,
1997 IMedium **aMedium)
1998{
1999 HRESULT rc = S_OK;
2000 CheckComArgStrNotEmptyOrNull(aLocation);
2001 CheckComArgOutPointerValid(aMedium);
2002
2003 AutoCaller autoCaller(this);
2004 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2005
2006 Guid id(aLocation);
2007 ComObjPtr<Medium> pMedium;
2008
2009 // have to get write lock as the whole find/update sequence must be done
2010 // in one critical section, otherwise there are races which can lead to
2011 // multiple Medium objects with the same content
2012 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2013
2014 // check if the device type is correct, and see if a medium for the
2015 // given path has already initialized; if so, return that
2016 switch (deviceType)
2017 {
2018 case DeviceType_HardDisk:
2019 if (id.isValid() && !id.isZero())
2020 rc = findHardDiskById(id, false /* setError */, &pMedium);
2021 else
2022 rc = findHardDiskByLocation(aLocation,
2023 false, /* aSetError */
2024 &pMedium);
2025 break;
2026
2027 case DeviceType_Floppy:
2028 case DeviceType_DVD:
2029 if (id.isValid() && !id.isZero())
2030 rc = findDVDOrFloppyImage(deviceType, &id, Utf8Str::Empty,
2031 false /* setError */, &pMedium);
2032 else
2033 rc = findDVDOrFloppyImage(deviceType, NULL, aLocation,
2034 false /* setError */, &pMedium);
2035
2036 // enforce read-only for DVDs even if caller specified ReadWrite
2037 if (deviceType == DeviceType_DVD)
2038 accessMode = AccessMode_ReadOnly;
2039 break;
2040
2041 default:
2042 return setError(E_INVALIDARG, "Device type must be HardDisk, DVD or Floppy %d", deviceType);
2043 }
2044
2045 if (pMedium.isNull())
2046 {
2047 pMedium.createObject();
2048 treeLock.release();
2049 rc = pMedium->init(this,
2050 aLocation,
2051 (accessMode == AccessMode_ReadWrite) ? Medium::OpenReadWrite : Medium::OpenReadOnly,
2052 !!fForceNewUuid,
2053 deviceType);
2054 treeLock.acquire();
2055
2056 if (SUCCEEDED(rc))
2057 {
2058 rc = registerMedium(pMedium, &pMedium, deviceType);
2059
2060 treeLock.release();
2061
2062 /* Note that it's important to call uninit() on failure to register
2063 * because the differencing hard disk would have been already associated
2064 * with the parent and this association needs to be broken. */
2065
2066 if (FAILED(rc))
2067 {
2068 pMedium->uninit();
2069 rc = VBOX_E_OBJECT_NOT_FOUND;
2070 }
2071 }
2072 else
2073 rc = VBOX_E_OBJECT_NOT_FOUND;
2074 }
2075
2076 if (SUCCEEDED(rc))
2077 pMedium.queryInterfaceTo(aMedium);
2078
2079 return rc;
2080}
2081
2082
2083/** @note Locks this object for reading. */
2084STDMETHODIMP VirtualBox::GetGuestOSType(IN_BSTR aId, IGuestOSType **aType)
2085{
2086 CheckComArgNotNull(aType);
2087
2088 AutoCaller autoCaller(this);
2089 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2090
2091 *aType = NULL;
2092
2093 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2094 for (GuestOSTypesOList::iterator it = m->allGuestOSTypes.begin();
2095 it != m->allGuestOSTypes.end();
2096 ++it)
2097 {
2098 const Bstr &typeId = (*it)->id();
2099 AssertMsg(!typeId.isEmpty(), ("ID must not be NULL"));
2100 if (typeId.compare(aId, Bstr::CaseInsensitive) == 0)
2101 {
2102 (*it).queryInterfaceTo(aType);
2103 break;
2104 }
2105 }
2106
2107 return (*aType) ? S_OK :
2108 setError(E_INVALIDARG,
2109 tr("'%ls' is not a valid Guest OS type"),
2110 aId);
2111}
2112
2113STDMETHODIMP VirtualBox::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath,
2114 BOOL /* aWritable */, BOOL /* aAutoMount */)
2115{
2116 CheckComArgStrNotEmptyOrNull(aName);
2117 CheckComArgStrNotEmptyOrNull(aHostPath);
2118
2119 AutoCaller autoCaller(this);
2120 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2121
2122 return setError(E_NOTIMPL, "Not yet implemented");
2123}
2124
2125STDMETHODIMP VirtualBox::RemoveSharedFolder(IN_BSTR aName)
2126{
2127 CheckComArgStrNotEmptyOrNull(aName);
2128
2129 AutoCaller autoCaller(this);
2130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2131
2132 return setError(E_NOTIMPL, "Not yet implemented");
2133}
2134
2135/**
2136 * @note Locks this object for reading.
2137 */
2138STDMETHODIMP VirtualBox::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
2139{
2140 using namespace settings;
2141
2142 CheckComArgOutSafeArrayPointerValid(aKeys);
2143
2144 AutoCaller autoCaller(this);
2145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2146
2147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2148
2149 com::SafeArray<BSTR> saKeys(m->pMainConfigFile->mapExtraDataItems.size());
2150 int i = 0;
2151 for (StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.begin();
2152 it != m->pMainConfigFile->mapExtraDataItems.end();
2153 ++it, ++i)
2154 {
2155 const Utf8Str &strName = it->first; // the key
2156 strName.cloneTo(&saKeys[i]);
2157 }
2158 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
2159
2160 return S_OK;
2161}
2162
2163/**
2164 * @note Locks this object for reading.
2165 */
2166STDMETHODIMP VirtualBox::GetExtraData(IN_BSTR aKey,
2167 BSTR *aValue)
2168{
2169 CheckComArgStrNotEmptyOrNull(aKey);
2170 CheckComArgNotNull(aValue);
2171
2172 AutoCaller autoCaller(this);
2173 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2174
2175 /* start with nothing found */
2176 Utf8Str strKey(aKey);
2177 Bstr bstrResult;
2178
2179 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
2180 if (it != m->pMainConfigFile->mapExtraDataItems.end())
2181 // found:
2182 bstrResult = it->second; // source is a Utf8Str
2183
2184 /* return the result to caller (may be empty) */
2185 bstrResult.cloneTo(aValue);
2186
2187 return S_OK;
2188}
2189
2190/**
2191 * @note Locks this object for writing.
2192 */
2193STDMETHODIMP VirtualBox::SetExtraData(IN_BSTR aKey,
2194 IN_BSTR aValue)
2195{
2196 CheckComArgStrNotEmptyOrNull(aKey);
2197
2198 AutoCaller autoCaller(this);
2199 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2200
2201 Utf8Str strKey(aKey);
2202 Utf8Str strValue(aValue);
2203 Utf8Str strOldValue; // empty
2204
2205 // locking note: we only hold the read lock briefly to look up the old value,
2206 // then release it and call the onExtraCanChange callbacks. There is a small
2207 // chance of a race insofar as the callback might be called twice if two callers
2208 // change the same key at the same time, but that's a much better solution
2209 // than the deadlock we had here before. The actual changing of the extradata
2210 // is then performed under the write lock and race-free.
2211
2212 // look up the old value first; if nothing has changed then we need not do anything
2213 {
2214 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
2215 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
2216 if (it != m->pMainConfigFile->mapExtraDataItems.end())
2217 strOldValue = it->second;
2218 }
2219
2220 bool fChanged;
2221 if ((fChanged = (strOldValue != strValue)))
2222 {
2223 // ask for permission from all listeners outside the locks;
2224 // onExtraDataCanChange() only briefly requests the VirtualBox
2225 // lock to copy the list of callbacks to invoke
2226 Bstr error;
2227 Bstr bstrValue(aValue);
2228
2229 if (!onExtraDataCanChange(Guid::Empty, aKey, bstrValue.raw(), error))
2230 {
2231 const char *sep = error.isEmpty() ? "" : ": ";
2232 CBSTR err = error.raw();
2233 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
2234 sep, err));
2235 return setError(E_ACCESSDENIED,
2236 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
2237 aKey,
2238 bstrValue.raw(),
2239 sep,
2240 err);
2241 }
2242
2243 // data is changing and change not vetoed: then write it out under the lock
2244
2245 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2246
2247 if (strValue.isEmpty())
2248 m->pMainConfigFile->mapExtraDataItems.erase(strKey);
2249 else
2250 m->pMainConfigFile->mapExtraDataItems[strKey] = strValue;
2251 // creates a new key if needed
2252
2253 /* save settings on success */
2254 HRESULT rc = saveSettings();
2255 if (FAILED(rc)) return rc;
2256 }
2257
2258 // fire notification outside the lock
2259 if (fChanged)
2260 onExtraDataChange(Guid::Empty, aKey, aValue);
2261
2262 return S_OK;
2263}
2264
2265/**
2266 *
2267 */
2268STDMETHODIMP VirtualBox::SetSettingsSecret(IN_BSTR aValue)
2269{
2270 storeSettingsKey(aValue);
2271 decryptSettings();
2272 return S_OK;
2273}
2274
2275int VirtualBox::decryptMediumSettings(Medium *pMedium)
2276{
2277 Bstr bstrCipher;
2278 HRESULT hrc = pMedium->GetProperty(Bstr("InitiatorSecretEncrypted").raw(),
2279 bstrCipher.asOutParam());
2280 if (SUCCEEDED(hrc))
2281 {
2282 Utf8Str strPlaintext;
2283 int rc = decryptSetting(&strPlaintext, bstrCipher);
2284 if (RT_SUCCESS(rc))
2285 pMedium->setPropertyDirect("InitiatorSecret", strPlaintext);
2286 else
2287 return rc;
2288 }
2289 return VINF_SUCCESS;
2290}
2291
2292/**
2293 * Decrypt all encrypted settings.
2294 *
2295 * So far we only have encrypted iSCSI initiator secrets so we just go through
2296 * all hard disk mediums and determine the plain 'InitiatorSecret' from
2297 * 'InitiatorSecretEncrypted. The latter is stored as Base64 because medium
2298 * properties need to be null-terminated strings.
2299 */
2300int VirtualBox::decryptSettings()
2301{
2302 bool fFailure = false;
2303 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2304 for (MediaList::const_iterator mt = m->allHardDisks.begin();
2305 mt != m->allHardDisks.end();
2306 ++mt)
2307 {
2308 ComObjPtr<Medium> pMedium = *mt;
2309 AutoCaller medCaller(pMedium);
2310 if (FAILED(medCaller.rc()))
2311 continue;
2312 AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
2313 int vrc = decryptMediumSettings(pMedium);
2314 if (RT_FAILURE(vrc))
2315 fFailure = true;
2316 }
2317 return fFailure ? VERR_INVALID_PARAMETER : VINF_SUCCESS;
2318}
2319
2320/**
2321 * Encode.
2322 *
2323 * @param aPlaintext plaintext to be encrypted
2324 * @param aCiphertext resulting ciphertext (base64-encoded)
2325 */
2326int VirtualBox::encryptSetting(const Utf8Str &aPlaintext, Utf8Str *aCiphertext)
2327{
2328 uint8_t abCiphertext[32];
2329 char szCipherBase64[128];
2330 size_t cchCipherBase64;
2331 int rc = encryptSettingBytes((uint8_t*)aPlaintext.c_str(), abCiphertext,
2332 aPlaintext.length()+1, sizeof(abCiphertext));
2333 if (RT_SUCCESS(rc))
2334 {
2335 rc = RTBase64Encode(abCiphertext, sizeof(abCiphertext),
2336 szCipherBase64, sizeof(szCipherBase64),
2337 &cchCipherBase64);
2338 if (RT_SUCCESS(rc))
2339 *aCiphertext = szCipherBase64;
2340 }
2341 return rc;
2342}
2343
2344/**
2345 * Decode.
2346 *
2347 * @param aPlaintext resulting plaintext
2348 * @param aCiphertext ciphertext (base64-encoded) to decrypt
2349 */
2350int VirtualBox::decryptSetting(Utf8Str *aPlaintext, const Utf8Str &aCiphertext)
2351{
2352 uint8_t abPlaintext[64];
2353 uint8_t abCiphertext[64];
2354 size_t cbCiphertext;
2355 int rc = RTBase64Decode(aCiphertext.c_str(),
2356 abCiphertext, sizeof(abCiphertext),
2357 &cbCiphertext, NULL);
2358 if (RT_SUCCESS(rc))
2359 {
2360 rc = decryptSettingBytes(abPlaintext, abCiphertext, cbCiphertext);
2361 if (RT_SUCCESS(rc))
2362 {
2363 for (unsigned i = 0; i < cbCiphertext; i++)
2364 {
2365 /* sanity check: null-terminated string? */
2366 if (abPlaintext[i] == '\0')
2367 {
2368 /* sanity check: valid UTF8 string? */
2369 if (RTStrIsValidEncoding((const char*)abPlaintext))
2370 {
2371 *aPlaintext = Utf8Str((const char*)abPlaintext);
2372 return VINF_SUCCESS;
2373 }
2374 }
2375 }
2376 rc = VERR_INVALID_MAGIC;
2377 }
2378 }
2379 return rc;
2380}
2381
2382/**
2383 * Encrypt secret bytes. Use the m->SettingsCipherKey as key.
2384 *
2385 * @param aPlaintext clear text to be encrypted
2386 * @param aCiphertext resulting encrypted text
2387 * @param aPlaintextSize size of the plaintext
2388 * @param aCiphertextSize size of the ciphertext
2389 */
2390int VirtualBox::encryptSettingBytes(const uint8_t *aPlaintext, uint8_t *aCiphertext,
2391 size_t aPlaintextSize, size_t aCiphertextSize) const
2392{
2393 unsigned i, j;
2394 uint8_t aBytes[64];
2395
2396 if (!m->fSettingsCipherKeySet)
2397 return VERR_INVALID_STATE;
2398
2399 if (aCiphertextSize > sizeof(aBytes))
2400 return VERR_BUFFER_OVERFLOW;
2401
2402 if (aCiphertextSize < 32)
2403 return VERR_INVALID_PARAMETER;
2404
2405 AssertCompile(sizeof(m->SettingsCipherKey) >= 32);
2406
2407 /* store the first 8 bytes of the cipherkey for verification */
2408 for (i = 0, j = 0; i < 8; i++, j++)
2409 aCiphertext[i] = m->SettingsCipherKey[j];
2410
2411 for (unsigned k = 0; k < aPlaintextSize && i < aCiphertextSize; i++, k++)
2412 {
2413 aCiphertext[i] = (aPlaintext[k] ^ m->SettingsCipherKey[j]);
2414 if (++j >= sizeof(m->SettingsCipherKey))
2415 j = 0;
2416 }
2417
2418 /* fill with random data to have a minimal length (salt) */
2419 if (i < aCiphertextSize)
2420 {
2421 RTRandBytes(aBytes, aCiphertextSize - i);
2422 for (int k = 0; i < aCiphertextSize; i++, k++)
2423 {
2424 aCiphertext[i] = aBytes[k] ^ m->SettingsCipherKey[j];
2425 if (++j >= sizeof(m->SettingsCipherKey))
2426 j = 0;
2427 }
2428 }
2429
2430 return VINF_SUCCESS;
2431}
2432
2433/**
2434 * Decrypt secret bytes. Use the m->SettingsCipherKey as key.
2435 *
2436 * @param aPlaintext resulting plaintext
2437 * @param aCiphertext ciphertext to be decrypted
2438 * @param aCiphertextSize size of the ciphertext == size of the plaintext
2439 */
2440int VirtualBox::decryptSettingBytes(uint8_t *aPlaintext,
2441 const uint8_t *aCiphertext, size_t aCiphertextSize) const
2442{
2443 unsigned i, j;
2444
2445 if (!m->fSettingsCipherKeySet)
2446 return VERR_INVALID_STATE;
2447
2448 if (aCiphertextSize < 32)
2449 return VERR_INVALID_PARAMETER;
2450
2451 /* key verification */
2452 for (i = 0, j = 0; i < 8; i++, j++)
2453 if (aCiphertext[i] != m->SettingsCipherKey[j])
2454 return VERR_INVALID_MAGIC;
2455
2456 /* poison */
2457 memset(aPlaintext, 0xff, aCiphertextSize);
2458 for (int k = 0; i < aCiphertextSize; i++, k++)
2459 {
2460 aPlaintext[k] = aCiphertext[i] ^ m->SettingsCipherKey[j];
2461 if (++j >= sizeof(m->SettingsCipherKey))
2462 j = 0;
2463 }
2464
2465 return VINF_SUCCESS;
2466}
2467
2468/**
2469 * Store a settings key.
2470 *
2471 * @param aKey the key to store
2472 */
2473void VirtualBox::storeSettingsKey(const Utf8Str &aKey)
2474{
2475 RTSha512(aKey.c_str(), aKey.length(), m->SettingsCipherKey);
2476 m->fSettingsCipherKeySet = true;
2477}
2478
2479// public methods only for internal purposes
2480/////////////////////////////////////////////////////////////////////////////
2481
2482#ifdef DEBUG
2483void VirtualBox::dumpAllBackRefs()
2484{
2485 {
2486 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2487 for (MediaList::const_iterator mt = m->allHardDisks.begin();
2488 mt != m->allHardDisks.end();
2489 ++mt)
2490 {
2491 ComObjPtr<Medium> pMedium = *mt;
2492 pMedium->dumpBackRefs();
2493 }
2494 }
2495 {
2496 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2497 for (MediaList::const_iterator mt = m->allDVDImages.begin();
2498 mt != m->allDVDImages.end();
2499 ++mt)
2500 {
2501 ComObjPtr<Medium> pMedium = *mt;
2502 pMedium->dumpBackRefs();
2503 }
2504 }
2505}
2506#endif
2507
2508/**
2509 * Posts an event to the event queue that is processed asynchronously
2510 * on a dedicated thread.
2511 *
2512 * Posting events to the dedicated event queue is useful to perform secondary
2513 * actions outside any object locks -- for example, to iterate over a list
2514 * of callbacks and inform them about some change caused by some object's
2515 * method call.
2516 *
2517 * @param event event to post; must have been allocated using |new|, will
2518 * be deleted automatically by the event thread after processing
2519 *
2520 * @note Doesn't lock any object.
2521 */
2522HRESULT VirtualBox::postEvent(Event *event)
2523{
2524 AssertReturn(event, E_FAIL);
2525
2526 HRESULT rc;
2527 AutoCaller autoCaller(this);
2528 if (SUCCEEDED((rc = autoCaller.rc())))
2529 {
2530 if (autoCaller.state() != Ready)
2531 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the event is discarded!\n",
2532 autoCaller.state()));
2533 // return S_OK
2534 else if ( (m->pAsyncEventQ)
2535 && (m->pAsyncEventQ->postEvent(event))
2536 )
2537 return S_OK;
2538 else
2539 rc = E_FAIL;
2540 }
2541
2542 // in any event of failure, we must clean up here, or we'll leak;
2543 // the caller has allocated the object using new()
2544 delete event;
2545 return rc;
2546}
2547
2548/**
2549 * Adds a progress to the global collection of pending operations.
2550 * Usually gets called upon progress object initialization.
2551 *
2552 * @param aProgress Operation to add to the collection.
2553 *
2554 * @note Doesn't lock objects.
2555 */
2556HRESULT VirtualBox::addProgress(IProgress *aProgress)
2557{
2558 CheckComArgNotNull(aProgress);
2559
2560 AutoCaller autoCaller(this);
2561 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2562
2563 Bstr id;
2564 HRESULT rc = aProgress->COMGETTER(Id)(id.asOutParam());
2565 AssertComRCReturnRC(rc);
2566
2567 /* protect mProgressOperations */
2568 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
2569
2570 m->mapProgressOperations.insert(ProgressMap::value_type(Guid(id), aProgress));
2571 return S_OK;
2572}
2573
2574/**
2575 * Removes the progress from the global collection of pending operations.
2576 * Usually gets called upon progress completion.
2577 *
2578 * @param aId UUID of the progress operation to remove
2579 *
2580 * @note Doesn't lock objects.
2581 */
2582HRESULT VirtualBox::removeProgress(IN_GUID aId)
2583{
2584 AutoCaller autoCaller(this);
2585 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2586
2587 ComPtr<IProgress> progress;
2588
2589 /* protect mProgressOperations */
2590 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
2591
2592 size_t cnt = m->mapProgressOperations.erase(aId);
2593 Assert(cnt == 1);
2594 NOREF(cnt);
2595
2596 return S_OK;
2597}
2598
2599#ifdef RT_OS_WINDOWS
2600
2601struct StartSVCHelperClientData
2602{
2603 ComObjPtr<VirtualBox> that;
2604 ComObjPtr<Progress> progress;
2605 bool privileged;
2606 VirtualBox::SVCHelperClientFunc func;
2607 void *user;
2608};
2609
2610/**
2611 * Helper method that starts a worker thread that:
2612 * - creates a pipe communication channel using SVCHlpClient;
2613 * - starts an SVC Helper process that will inherit this channel;
2614 * - executes the supplied function by passing it the created SVCHlpClient
2615 * and opened instance to communicate to the Helper process and the given
2616 * Progress object.
2617 *
2618 * The user function is supposed to communicate to the helper process
2619 * using the \a aClient argument to do the requested job and optionally expose
2620 * the progress through the \a aProgress object. The user function should never
2621 * call notifyComplete() on it: this will be done automatically using the
2622 * result code returned by the function.
2623 *
2624 * Before the user function is started, the communication channel passed to
2625 * the \a aClient argument is fully set up, the function should start using
2626 * its write() and read() methods directly.
2627 *
2628 * The \a aVrc parameter of the user function may be used to return an error
2629 * code if it is related to communication errors (for example, returned by
2630 * the SVCHlpClient members when they fail). In this case, the correct error
2631 * message using this value will be reported to the caller. Note that the
2632 * value of \a aVrc is inspected only if the user function itself returns
2633 * success.
2634 *
2635 * If a failure happens anywhere before the user function would be normally
2636 * called, it will be called anyway in special "cleanup only" mode indicated
2637 * by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
2638 * all the function is supposed to do is to cleanup its aUser argument if
2639 * necessary (it's assumed that the ownership of this argument is passed to
2640 * the user function once #startSVCHelperClient() returns a success, thus
2641 * making it responsible for the cleanup).
2642 *
2643 * After the user function returns, the thread will send the SVCHlpMsg::Null
2644 * message to indicate a process termination.
2645 *
2646 * @param aPrivileged |true| to start the SVC Helper process as a privileged
2647 * user that can perform administrative tasks
2648 * @param aFunc user function to run
2649 * @param aUser argument to the user function
2650 * @param aProgress progress object that will track operation completion
2651 *
2652 * @note aPrivileged is currently ignored (due to some unsolved problems in
2653 * Vista) and the process will be started as a normal (unprivileged)
2654 * process.
2655 *
2656 * @note Doesn't lock anything.
2657 */
2658HRESULT VirtualBox::startSVCHelperClient(bool aPrivileged,
2659 SVCHelperClientFunc aFunc,
2660 void *aUser, Progress *aProgress)
2661{
2662 AssertReturn(aFunc, E_POINTER);
2663 AssertReturn(aProgress, E_POINTER);
2664
2665 AutoCaller autoCaller(this);
2666 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2667
2668 /* create the SVCHelperClientThread() argument */
2669 std::auto_ptr <StartSVCHelperClientData>
2670 d(new StartSVCHelperClientData());
2671 AssertReturn(d.get(), E_OUTOFMEMORY);
2672
2673 d->that = this;
2674 d->progress = aProgress;
2675 d->privileged = aPrivileged;
2676 d->func = aFunc;
2677 d->user = aUser;
2678
2679 RTTHREAD tid = NIL_RTTHREAD;
2680 int vrc = RTThreadCreate(&tid, SVCHelperClientThread,
2681 static_cast <void *>(d.get()),
2682 0, RTTHREADTYPE_MAIN_WORKER,
2683 RTTHREADFLAGS_WAITABLE, "SVCHelper");
2684 if (RT_FAILURE(vrc))
2685 return setError(E_FAIL, "Could not create SVCHelper thread (%Rrc)", vrc);
2686
2687 /* d is now owned by SVCHelperClientThread(), so release it */
2688 d.release();
2689
2690 return S_OK;
2691}
2692
2693/**
2694 * Worker thread for startSVCHelperClient().
2695 */
2696/* static */
2697DECLCALLBACK(int)
2698VirtualBox::SVCHelperClientThread(RTTHREAD aThread, void *aUser)
2699{
2700 LogFlowFuncEnter();
2701
2702 std::auto_ptr<StartSVCHelperClientData>
2703 d(static_cast<StartSVCHelperClientData*>(aUser));
2704
2705 HRESULT rc = S_OK;
2706 bool userFuncCalled = false;
2707
2708 do
2709 {
2710 AssertBreakStmt(d.get(), rc = E_POINTER);
2711 AssertReturn(!d->progress.isNull(), E_POINTER);
2712
2713 /* protect VirtualBox from uninitialization */
2714 AutoCaller autoCaller(d->that);
2715 if (!autoCaller.isOk())
2716 {
2717 /* it's too late */
2718 rc = autoCaller.rc();
2719 break;
2720 }
2721
2722 int vrc = VINF_SUCCESS;
2723
2724 Guid id;
2725 id.create();
2726 SVCHlpClient client;
2727 vrc = client.create(Utf8StrFmt("VirtualBox\\SVCHelper\\{%RTuuid}",
2728 id.raw()).c_str());
2729 if (RT_FAILURE(vrc))
2730 {
2731 rc = d->that->setError(E_FAIL,
2732 tr("Could not create the communication channel (%Rrc)"), vrc);
2733 break;
2734 }
2735
2736 /* get the path to the executable */
2737 char exePathBuf[RTPATH_MAX];
2738 char *exePath = RTProcGetExecutablePath(exePathBuf, RTPATH_MAX);
2739 if (!exePath)
2740 {
2741 rc = d->that->setError(E_FAIL, tr("Cannot get executable name"));
2742 break;
2743 }
2744
2745 Utf8Str argsStr = Utf8StrFmt("/Helper %s", client.name().c_str());
2746
2747 LogFlowFunc(("Starting '\"%s\" %s'...\n", exePath, argsStr.c_str()));
2748
2749 RTPROCESS pid = NIL_RTPROCESS;
2750
2751 if (d->privileged)
2752 {
2753 /* Attempt to start a privileged process using the Run As dialog */
2754
2755 Bstr file = exePath;
2756 Bstr parameters = argsStr;
2757
2758 SHELLEXECUTEINFO shExecInfo;
2759
2760 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
2761
2762 shExecInfo.fMask = NULL;
2763 shExecInfo.hwnd = NULL;
2764 shExecInfo.lpVerb = L"runas";
2765 shExecInfo.lpFile = file.raw();
2766 shExecInfo.lpParameters = parameters.raw();
2767 shExecInfo.lpDirectory = NULL;
2768 shExecInfo.nShow = SW_NORMAL;
2769 shExecInfo.hInstApp = NULL;
2770
2771 if (!ShellExecuteEx(&shExecInfo))
2772 {
2773 int vrc2 = RTErrConvertFromWin32(GetLastError());
2774 /* hide excessive details in case of a frequent error
2775 * (pressing the Cancel button to close the Run As dialog) */
2776 if (vrc2 == VERR_CANCELLED)
2777 rc = d->that->setError(E_FAIL,
2778 tr("Operation canceled by the user"));
2779 else
2780 rc = d->that->setError(E_FAIL,
2781 tr("Could not launch a privileged process '%s' (%Rrc)"),
2782 exePath, vrc2);
2783 break;
2784 }
2785 }
2786 else
2787 {
2788 const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 };
2789 vrc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &pid);
2790 if (RT_FAILURE(vrc))
2791 {
2792 rc = d->that->setError(E_FAIL,
2793 tr("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
2794 break;
2795 }
2796 }
2797
2798 /* wait for the client to connect */
2799 vrc = client.connect();
2800 if (RT_SUCCESS(vrc))
2801 {
2802 /* start the user supplied function */
2803 rc = d->func(&client, d->progress, d->user, &vrc);
2804 userFuncCalled = true;
2805 }
2806
2807 /* send the termination signal to the process anyway */
2808 {
2809 int vrc2 = client.write(SVCHlpMsg::Null);
2810 if (RT_SUCCESS(vrc))
2811 vrc = vrc2;
2812 }
2813
2814 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
2815 {
2816 rc = d->that->setError(E_FAIL,
2817 tr("Could not operate the communication channel (%Rrc)"), vrc);
2818 break;
2819 }
2820 }
2821 while (0);
2822
2823 if (FAILED(rc) && !userFuncCalled)
2824 {
2825 /* call the user function in the "cleanup only" mode
2826 * to let it free resources passed to in aUser */
2827 d->func(NULL, NULL, d->user, NULL);
2828 }
2829
2830 d->progress->notifyComplete(rc);
2831
2832 LogFlowFuncLeave();
2833 return 0;
2834}
2835
2836#endif /* RT_OS_WINDOWS */
2837
2838/**
2839 * Sends a signal to the client watcher thread to rescan the set of machines
2840 * that have open sessions.
2841 *
2842 * @note Doesn't lock anything.
2843 */
2844void VirtualBox::updateClientWatcher()
2845{
2846 AutoCaller autoCaller(this);
2847 AssertComRCReturnVoid(autoCaller.rc());
2848
2849 AssertReturnVoid(m->threadClientWatcher != NIL_RTTHREAD);
2850
2851 /* sent an update request */
2852#if defined(RT_OS_WINDOWS)
2853 ::SetEvent(m->updateReq);
2854#elif defined(RT_OS_OS2)
2855 RTSemEventSignal(m->updateReq);
2856#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
2857 ASMAtomicUoWriteU8(&m->updateAdaptCtr, RT_ELEMENTS(s_updateAdaptTimeouts) - 1);
2858 RTSemEventSignal(m->updateReq);
2859#else
2860# error "Port me!"
2861#endif
2862}
2863
2864/**
2865 * Adds the given child process ID to the list of processes to be reaped.
2866 * This call should be followed by #updateClientWatcher() to take the effect.
2867 */
2868void VirtualBox::addProcessToReap(RTPROCESS pid)
2869{
2870 AutoCaller autoCaller(this);
2871 AssertComRCReturnVoid(autoCaller.rc());
2872
2873 /// @todo (dmik) Win32?
2874#ifndef RT_OS_WINDOWS
2875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2876 m->llProcesses.push_back(pid);
2877#endif
2878}
2879
2880/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2881struct MachineEvent : public VirtualBox::CallbackEvent
2882{
2883 MachineEvent(VirtualBox *aVB, VBoxEventType_T aWhat, const Guid &aId, BOOL aBool)
2884 : CallbackEvent(aVB, aWhat), id(aId.toUtf16())
2885 , mBool(aBool)
2886 { }
2887
2888 MachineEvent(VirtualBox *aVB, VBoxEventType_T aWhat, const Guid &aId, MachineState_T aState)
2889 : CallbackEvent(aVB, aWhat), id(aId.toUtf16())
2890 , mState(aState)
2891 {}
2892
2893 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2894 {
2895 switch (mWhat)
2896 {
2897 case VBoxEventType_OnMachineDataChanged:
2898 aEvDesc.init(aSource, mWhat, id.raw(), mBool);
2899 break;
2900
2901 case VBoxEventType_OnMachineStateChanged:
2902 aEvDesc.init(aSource, mWhat, id.raw(), mState);
2903 break;
2904
2905 case VBoxEventType_OnMachineRegistered:
2906 aEvDesc.init(aSource, mWhat, id.raw(), mBool);
2907 break;
2908
2909 default:
2910 AssertFailedReturn(S_OK);
2911 }
2912 return S_OK;
2913 }
2914
2915 Bstr id;
2916 MachineState_T mState;
2917 BOOL mBool;
2918};
2919
2920/**
2921 * @note Doesn't lock any object.
2922 */
2923void VirtualBox::onMachineStateChange(const Guid &aId, MachineState_T aState)
2924{
2925 postEvent(new MachineEvent(this, VBoxEventType_OnMachineStateChanged, aId, aState));
2926}
2927
2928/**
2929 * @note Doesn't lock any object.
2930 */
2931void VirtualBox::onMachineDataChange(const Guid &aId, BOOL aTemporary)
2932{
2933 postEvent(new MachineEvent(this, VBoxEventType_OnMachineDataChanged, aId, aTemporary));
2934}
2935
2936/**
2937 * @note Locks this object for reading.
2938 */
2939BOOL VirtualBox::onExtraDataCanChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue,
2940 Bstr &aError)
2941{
2942 LogFlowThisFunc(("machine={%s} aKey={%ls} aValue={%ls}\n",
2943 aId.toString().c_str(), aKey, aValue));
2944
2945 AutoCaller autoCaller(this);
2946 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2947
2948 BOOL allowChange = TRUE;
2949 Bstr id = aId.toUtf16();
2950
2951 VBoxEventDesc evDesc;
2952 evDesc.init(m->pEventSource, VBoxEventType_OnExtraDataCanChange, id.raw(), aKey, aValue);
2953 BOOL fDelivered = evDesc.fire(3000); /* Wait up to 3 secs for delivery */
2954 //Assert(fDelivered);
2955 if (fDelivered)
2956 {
2957 ComPtr<IEvent> aEvent;
2958 evDesc.getEvent(aEvent.asOutParam());
2959 ComPtr<IExtraDataCanChangeEvent> aCanChangeEvent = aEvent;
2960 Assert(aCanChangeEvent);
2961 BOOL fVetoed = FALSE;
2962 aCanChangeEvent->IsVetoed(&fVetoed);
2963 allowChange = !fVetoed;
2964
2965 if (!allowChange)
2966 {
2967 SafeArray<BSTR> aVetos;
2968 aCanChangeEvent->GetVetos(ComSafeArrayAsOutParam(aVetos));
2969 if (aVetos.size() > 0)
2970 aError = aVetos[0];
2971 }
2972 }
2973 else
2974 allowChange = TRUE;
2975
2976 LogFlowThisFunc(("allowChange=%RTbool\n", allowChange));
2977 return allowChange;
2978}
2979
2980/** Event for onExtraDataChange() */
2981struct ExtraDataEvent : public VirtualBox::CallbackEvent
2982{
2983 ExtraDataEvent(VirtualBox *aVB, const Guid &aMachineId,
2984 IN_BSTR aKey, IN_BSTR aVal)
2985 : CallbackEvent(aVB, VBoxEventType_OnExtraDataChanged)
2986 , machineId(aMachineId.toUtf16()), key(aKey), val(aVal)
2987 {}
2988
2989 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2990 {
2991 return aEvDesc.init(aSource, VBoxEventType_OnExtraDataChanged, machineId.raw(), key.raw(), val.raw());
2992 }
2993
2994 Bstr machineId, key, val;
2995};
2996
2997/**
2998 * @note Doesn't lock any object.
2999 */
3000void VirtualBox::onExtraDataChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue)
3001{
3002 postEvent(new ExtraDataEvent(this, aId, aKey, aValue));
3003}
3004
3005/**
3006 * @note Doesn't lock any object.
3007 */
3008void VirtualBox::onMachineRegistered(const Guid &aId, BOOL aRegistered)
3009{
3010 postEvent(new MachineEvent(this, VBoxEventType_OnMachineRegistered, aId, aRegistered));
3011}
3012
3013/** Event for onSessionStateChange() */
3014struct SessionEvent : public VirtualBox::CallbackEvent
3015{
3016 SessionEvent(VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
3017 : CallbackEvent(aVB, VBoxEventType_OnSessionStateChanged)
3018 , machineId(aMachineId.toUtf16()), sessionState(aState)
3019 {}
3020
3021 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
3022 {
3023 return aEvDesc.init(aSource, VBoxEventType_OnSessionStateChanged, machineId.raw(), sessionState);
3024 }
3025 Bstr machineId;
3026 SessionState_T sessionState;
3027};
3028
3029/**
3030 * @note Doesn't lock any object.
3031 */
3032void VirtualBox::onSessionStateChange(const Guid &aId, SessionState_T aState)
3033{
3034 postEvent(new SessionEvent(this, aId, aState));
3035}
3036
3037/** Event for onSnapshotTaken(), onSnapshotDeleted() and onSnapshotChange() */
3038struct SnapshotEvent : public VirtualBox::CallbackEvent
3039{
3040 SnapshotEvent(VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
3041 VBoxEventType_T aWhat)
3042 : CallbackEvent(aVB, aWhat)
3043 , machineId(aMachineId), snapshotId(aSnapshotId)
3044 {}
3045
3046 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
3047 {
3048 return aEvDesc.init(aSource, mWhat, machineId.toUtf16().raw(),
3049 snapshotId.toUtf16().raw());
3050 }
3051
3052 Guid machineId;
3053 Guid snapshotId;
3054};
3055
3056/**
3057 * @note Doesn't lock any object.
3058 */
3059void VirtualBox::onSnapshotTaken(const Guid &aMachineId, const Guid &aSnapshotId)
3060{
3061 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
3062 VBoxEventType_OnSnapshotTaken));
3063}
3064
3065/**
3066 * @note Doesn't lock any object.
3067 */
3068void VirtualBox::onSnapshotDeleted(const Guid &aMachineId, const Guid &aSnapshotId)
3069{
3070 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
3071 VBoxEventType_OnSnapshotDeleted));
3072}
3073
3074/**
3075 * @note Doesn't lock any object.
3076 */
3077void VirtualBox::onSnapshotChange(const Guid &aMachineId, const Guid &aSnapshotId)
3078{
3079 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
3080 VBoxEventType_OnSnapshotChanged));
3081}
3082
3083/** Event for onGuestPropertyChange() */
3084struct GuestPropertyEvent : public VirtualBox::CallbackEvent
3085{
3086 GuestPropertyEvent(VirtualBox *aVBox, const Guid &aMachineId,
3087 IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
3088 : CallbackEvent(aVBox, VBoxEventType_OnGuestPropertyChanged),
3089 machineId(aMachineId),
3090 name(aName),
3091 value(aValue),
3092 flags(aFlags)
3093 {}
3094
3095 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
3096 {
3097 return aEvDesc.init(aSource, VBoxEventType_OnGuestPropertyChanged,
3098 machineId.toUtf16().raw(), name.raw(), value.raw(), flags.raw());
3099 }
3100
3101 Guid machineId;
3102 Bstr name, value, flags;
3103};
3104
3105/**
3106 * @note Doesn't lock any object.
3107 */
3108void VirtualBox::onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName,
3109 IN_BSTR aValue, IN_BSTR aFlags)
3110{
3111 postEvent(new GuestPropertyEvent(this, aMachineId, aName, aValue, aFlags));
3112}
3113
3114/** Event for onMachineUninit(), this is not a CallbackEvent */
3115class MachineUninitEvent : public Event
3116{
3117public:
3118
3119 MachineUninitEvent(VirtualBox *aVirtualBox, Machine *aMachine)
3120 : mVirtualBox(aVirtualBox), mMachine(aMachine)
3121 {
3122 Assert(aVirtualBox);
3123 Assert(aMachine);
3124 }
3125
3126 void *handler()
3127 {
3128#ifdef VBOX_WITH_RESOURCE_USAGE_API
3129 /* Handle unregistering metrics here, as it is not vital to get
3130 * it done immediately. It reduces the number of locks needed and
3131 * the lock contention in SessionMachine::uninit. */
3132 {
3133 AutoWriteLock mLock(mMachine COMMA_LOCKVAL_SRC_POS);
3134 mMachine->unregisterMetrics(mVirtualBox->performanceCollector(), mMachine);
3135 }
3136#endif /* VBOX_WITH_RESOURCE_USAGE_API */
3137
3138 return NULL;
3139 }
3140
3141private:
3142
3143 /**
3144 * Note that this is a weak ref -- the CallbackEvent handler thread
3145 * is bound to the lifetime of the VirtualBox instance, so it's safe.
3146 */
3147 VirtualBox *mVirtualBox;
3148
3149 /** Reference to the machine object. */
3150 ComObjPtr<Machine> mMachine;
3151};
3152
3153/**
3154 * Trigger internal event. This isn't meant to be signalled to clients.
3155 * @note Doesn't lock any object.
3156 */
3157void VirtualBox::onMachineUninit(Machine *aMachine)
3158{
3159 postEvent(new MachineUninitEvent(this, aMachine));
3160}
3161
3162/**
3163 * @note Doesn't lock any object.
3164 */
3165void VirtualBox::onNatRedirectChange(const Guid &aMachineId, ULONG ulSlot, bool fRemove, IN_BSTR aName,
3166 NATProtocol_T aProto, IN_BSTR aHostIp, uint16_t aHostPort,
3167 IN_BSTR aGuestIp, uint16_t aGuestPort)
3168{
3169 fireNATRedirectEvent(m->pEventSource, aMachineId.toUtf16().raw(), ulSlot, fRemove, aName, aProto, aHostIp,
3170 aHostPort, aGuestIp, aGuestPort);
3171}
3172
3173void VirtualBox::onNATNetworkChange(IN_BSTR aName)
3174{
3175 fireNATNetworkChangedEvent(m->pEventSource, aName);
3176}
3177
3178void VirtualBox::onNATNetworkStartStop(IN_BSTR aName, BOOL fStart)
3179{
3180 fireNATNetworkStartStopEvent(m->pEventSource, aName, fStart);
3181}
3182void VirtualBox::onNATNetworkSetting(IN_BSTR aNetworkName, BOOL aEnabled,
3183 IN_BSTR aNetwork, IN_BSTR aGateway,
3184 BOOL aAdvertiseDefaultIpv6RouteEnabled,
3185 BOOL fNeedDhcpServer)
3186{
3187 fireNATNetworkSettingEvent(m->pEventSource, aNetworkName, aEnabled,
3188 aNetwork, aGateway,
3189 aAdvertiseDefaultIpv6RouteEnabled, fNeedDhcpServer);
3190}
3191
3192void VirtualBox::onNATNetworkPortForward(IN_BSTR aNetworkName, BOOL create, BOOL fIpv6,
3193 IN_BSTR aRuleName, NATProtocol_T proto,
3194 IN_BSTR aHostIp, LONG aHostPort,
3195 IN_BSTR aGuestIp, LONG aGuestPort)
3196{
3197 fireNATNetworkPortForwardEvent(m->pEventSource, aNetworkName, create,
3198 fIpv6, aRuleName, proto,
3199 aHostIp, aHostPort,
3200 aGuestIp, aGuestPort);
3201}
3202
3203/**
3204 * @note Locks this object for reading.
3205 */
3206ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType()
3207{
3208 ComObjPtr<GuestOSType> type;
3209 AutoCaller autoCaller(this);
3210 AssertComRCReturn(autoCaller.rc(), type);
3211
3212 /* unknown type must always be the first */
3213 ComAssertRet(m->allGuestOSTypes.size() > 0, type);
3214
3215 return m->allGuestOSTypes.front();
3216}
3217
3218/**
3219 * Returns the list of opened machines (machines having direct sessions opened
3220 * by client processes) and optionally the list of direct session controls.
3221 *
3222 * @param aMachines Where to put opened machines (will be empty if none).
3223 * @param aControls Where to put direct session controls (optional).
3224 *
3225 * @note The returned lists contain smart pointers. So, clear it as soon as
3226 * it becomes no more necessary to release instances.
3227 *
3228 * @note It can be possible that a session machine from the list has been
3229 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
3230 * when accessing unprotected data directly.
3231 *
3232 * @note Locks objects for reading.
3233 */
3234void VirtualBox::getOpenedMachines(SessionMachinesList &aMachines,
3235 InternalControlList *aControls /*= NULL*/)
3236{
3237 AutoCaller autoCaller(this);
3238 AssertComRCReturnVoid(autoCaller.rc());
3239
3240 aMachines.clear();
3241 if (aControls)
3242 aControls->clear();
3243
3244 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3245
3246 for (MachinesOList::iterator it = m->allMachines.begin();
3247 it != m->allMachines.end();
3248 ++it)
3249 {
3250 ComObjPtr<SessionMachine> sm;
3251 ComPtr<IInternalSessionControl> ctl;
3252 if ((*it)->isSessionOpen(sm, &ctl))
3253 {
3254 aMachines.push_back(sm);
3255 if (aControls)
3256 aControls->push_back(ctl);
3257 }
3258 }
3259}
3260
3261/**
3262 * Searches for a machine object with the given ID in the collection
3263 * of registered machines.
3264 *
3265 * @param aId Machine UUID to look for.
3266 * @param aPermitInaccessible If true, inaccessible machines will be found;
3267 * if false, this will fail if the given machine is inaccessible.
3268 * @param aSetError If true, set errorinfo if the machine is not found.
3269 * @param aMachine Returned machine, if found.
3270 * @return
3271 */
3272HRESULT VirtualBox::findMachine(const Guid &aId,
3273 bool fPermitInaccessible,
3274 bool aSetError,
3275 ComObjPtr<Machine> *aMachine /* = NULL */)
3276{
3277 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
3278
3279 AutoCaller autoCaller(this);
3280 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3281
3282 {
3283 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3284
3285 for (MachinesOList::iterator it = m->allMachines.begin();
3286 it != m->allMachines.end();
3287 ++it)
3288 {
3289 ComObjPtr<Machine> pMachine = *it;
3290
3291 if (!fPermitInaccessible)
3292 {
3293 // skip inaccessible machines
3294 AutoCaller machCaller(pMachine);
3295 if (FAILED(machCaller.rc()))
3296 continue;
3297 }
3298
3299 if (pMachine->getId() == aId)
3300 {
3301 rc = S_OK;
3302 if (aMachine)
3303 *aMachine = pMachine;
3304 break;
3305 }
3306 }
3307 }
3308
3309 if (aSetError && FAILED(rc))
3310 rc = setError(rc,
3311 tr("Could not find a registered machine with UUID {%RTuuid}"),
3312 aId.raw());
3313
3314 return rc;
3315}
3316
3317/**
3318 * Searches for a machine object with the given name or location in the
3319 * collection of registered machines.
3320 *
3321 * @param aName Machine name or location to look for.
3322 * @param aSetError If true, set errorinfo if the machine is not found.
3323 * @param aMachine Returned machine, if found.
3324 * @return
3325 */
3326HRESULT VirtualBox::findMachineByName(const Utf8Str &aName, bool aSetError,
3327 ComObjPtr<Machine> *aMachine /* = NULL */)
3328{
3329 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
3330
3331 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3332 for (MachinesOList::iterator it = m->allMachines.begin();
3333 it != m->allMachines.end();
3334 ++it)
3335 {
3336 ComObjPtr<Machine> &pMachine = *it;
3337 AutoCaller machCaller(pMachine);
3338 if (machCaller.rc())
3339 continue; // we can't ask inaccessible machines for their names
3340
3341 AutoReadLock machLock(pMachine COMMA_LOCKVAL_SRC_POS);
3342 if (pMachine->getName() == aName)
3343 {
3344 rc = S_OK;
3345 if (aMachine)
3346 *aMachine = pMachine;
3347 break;
3348 }
3349 if (!RTPathCompare(pMachine->getSettingsFileFull().c_str(), aName.c_str()))
3350 {
3351 rc = S_OK;
3352 if (aMachine)
3353 *aMachine = pMachine;
3354 break;
3355 }
3356 }
3357
3358 if (aSetError && FAILED(rc))
3359 rc = setError(rc,
3360 tr("Could not find a registered machine named '%s'"), aName.c_str());
3361
3362 return rc;
3363}
3364
3365static HRESULT validateMachineGroupHelper(const Utf8Str &aGroup, bool fPrimary, VirtualBox *pVirtualBox)
3366{
3367 /* empty strings are invalid */
3368 if (aGroup.isEmpty())
3369 return E_INVALIDARG;
3370 /* the toplevel group is valid */
3371 if (aGroup == "/")
3372 return S_OK;
3373 /* any other strings of length 1 are invalid */
3374 if (aGroup.length() == 1)
3375 return E_INVALIDARG;
3376 /* must start with a slash */
3377 if (aGroup.c_str()[0] != '/')
3378 return E_INVALIDARG;
3379 /* must not end with a slash */
3380 if (aGroup.c_str()[aGroup.length() - 1] == '/')
3381 return E_INVALIDARG;
3382 /* check the group components */
3383 const char *pStr = aGroup.c_str() + 1; /* first char is /, skip it */
3384 while (pStr)
3385 {
3386 char *pSlash = RTStrStr(pStr, "/");
3387 if (pSlash)
3388 {
3389 /* no empty components (or // sequences in other words) */
3390 if (pSlash == pStr)
3391 return E_INVALIDARG;
3392 /* check if the machine name rules are violated, because that means
3393 * the group components are too close to the limits. */
3394 Utf8Str tmp((const char *)pStr, (size_t)(pSlash - pStr));
3395 Utf8Str tmp2(tmp);
3396 sanitiseMachineFilename(tmp);
3397 if (tmp != tmp2)
3398 return E_INVALIDARG;
3399 if (fPrimary)
3400 {
3401 HRESULT rc = pVirtualBox->findMachineByName(tmp,
3402 false /* aSetError */);
3403 if (SUCCEEDED(rc))
3404 return VBOX_E_VM_ERROR;
3405 }
3406 pStr = pSlash + 1;
3407 }
3408 else
3409 {
3410 /* check if the machine name rules are violated, because that means
3411 * the group components is too close to the limits. */
3412 Utf8Str tmp(pStr);
3413 Utf8Str tmp2(tmp);
3414 sanitiseMachineFilename(tmp);
3415 if (tmp != tmp2)
3416 return E_INVALIDARG;
3417 pStr = NULL;
3418 }
3419 }
3420 return S_OK;
3421}
3422
3423/**
3424 * Validates a machine group.
3425 *
3426 * @param aMachineGroup Machine group.
3427 * @param fPrimary Set if this is the primary group.
3428 *
3429 * @return S_OK or E_INVALIDARG
3430 */
3431HRESULT VirtualBox::validateMachineGroup(const Utf8Str &aGroup, bool fPrimary)
3432{
3433 HRESULT rc = validateMachineGroupHelper(aGroup, fPrimary, this);
3434 if (FAILED(rc))
3435 {
3436 if (rc == VBOX_E_VM_ERROR)
3437 rc = setError(E_INVALIDARG,
3438 tr("Machine group '%s' conflicts with a virtual machine name"),
3439 aGroup.c_str());
3440 else
3441 rc = setError(rc,
3442 tr("Invalid machine group '%s'"),
3443 aGroup.c_str());
3444 }
3445 return rc;
3446}
3447
3448/**
3449 * Takes a list of machine groups, and sanitizes/validates it.
3450 *
3451 * @param aMachineGroups Safearray with the machine groups.
3452 * @param pllMachineGroups Pointer to list of strings for the result.
3453 *
3454 * @return S_OK or E_INVALIDARG
3455 */
3456HRESULT VirtualBox::convertMachineGroups(ComSafeArrayIn(IN_BSTR, aMachineGroups), StringsList *pllMachineGroups)
3457{
3458 pllMachineGroups->clear();
3459 if (aMachineGroups)
3460 {
3461 com::SafeArray<IN_BSTR> machineGroups(ComSafeArrayInArg(aMachineGroups));
3462 for (size_t i = 0; i < machineGroups.size(); i++)
3463 {
3464 Utf8Str group(machineGroups[i]);
3465 if (group.length() == 0)
3466 group = "/";
3467
3468 HRESULT rc = validateMachineGroup(group, i == 0);
3469 if (FAILED(rc))
3470 return rc;
3471
3472 /* no duplicates please */
3473 if ( find(pllMachineGroups->begin(), pllMachineGroups->end(), group)
3474 == pllMachineGroups->end())
3475 pllMachineGroups->push_back(group);
3476 }
3477 if (pllMachineGroups->size() == 0)
3478 pllMachineGroups->push_back("/");
3479 }
3480 else
3481 pllMachineGroups->push_back("/");
3482
3483 return S_OK;
3484}
3485
3486/**
3487 * Searches for a Medium object with the given ID in the list of registered
3488 * hard disks.
3489 *
3490 * @param aId ID of the hard disk. Must not be empty.
3491 * @param aSetError If @c true , the appropriate error info is set in case
3492 * when the hard disk is not found.
3493 * @param aHardDisk Where to store the found hard disk object (can be NULL).
3494 *
3495 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
3496 *
3497 * @note Locks the media tree for reading.
3498 */
3499HRESULT VirtualBox::findHardDiskById(const Guid &id,
3500 bool aSetError,
3501 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
3502{
3503 AssertReturn(!id.isZero(), E_INVALIDARG);
3504
3505 // we use the hard disks map, but it is protected by the
3506 // hard disk _list_ lock handle
3507 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3508
3509 HardDiskMap::const_iterator it = m->mapHardDisks.find(id);
3510 if (it != m->mapHardDisks.end())
3511 {
3512 if (aHardDisk)
3513 *aHardDisk = (*it).second;
3514 return S_OK;
3515 }
3516
3517 if (aSetError)
3518 return setError(VBOX_E_OBJECT_NOT_FOUND,
3519 tr("Could not find an open hard disk with UUID {%RTuuid}"),
3520 id.raw());
3521
3522 return VBOX_E_OBJECT_NOT_FOUND;
3523}
3524
3525/**
3526 * Searches for a Medium object with the given ID or location in the list of
3527 * registered hard disks. If both ID and location are specified, the first
3528 * object that matches either of them (not necessarily both) is returned.
3529 *
3530 * @param aLocation Full location specification. Must not be empty.
3531 * @param aSetError If @c true , the appropriate error info is set in case
3532 * when the hard disk is not found.
3533 * @param aHardDisk Where to store the found hard disk object (can be NULL).
3534 *
3535 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
3536 *
3537 * @note Locks the media tree for reading.
3538 */
3539HRESULT VirtualBox::findHardDiskByLocation(const Utf8Str &strLocation,
3540 bool aSetError,
3541 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
3542{
3543 AssertReturn(!strLocation.isEmpty(), E_INVALIDARG);
3544
3545 // we use the hard disks map, but it is protected by the
3546 // hard disk _list_ lock handle
3547 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3548
3549 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
3550 it != m->mapHardDisks.end();
3551 ++it)
3552 {
3553 const ComObjPtr<Medium> &pHD = (*it).second;
3554
3555 AutoCaller autoCaller(pHD);
3556 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3557 AutoWriteLock mlock(pHD COMMA_LOCKVAL_SRC_POS);
3558
3559 Utf8Str strLocationFull = pHD->getLocationFull();
3560
3561 if (0 == RTPathCompare(strLocationFull.c_str(), strLocation.c_str()))
3562 {
3563 if (aHardDisk)
3564 *aHardDisk = pHD;
3565 return S_OK;
3566 }
3567 }
3568
3569 if (aSetError)
3570 return setError(VBOX_E_OBJECT_NOT_FOUND,
3571 tr("Could not find an open hard disk with location '%s'"),
3572 strLocation.c_str());
3573
3574 return VBOX_E_OBJECT_NOT_FOUND;
3575}
3576
3577/**
3578 * Searches for a Medium object with the given ID or location in the list of
3579 * registered DVD or floppy images, depending on the @a mediumType argument.
3580 * If both ID and file path are specified, the first object that matches either
3581 * of them (not necessarily both) is returned.
3582 *
3583 * @param mediumType Must be either DeviceType_DVD or DeviceType_Floppy.
3584 * @param aId ID of the image file (unused when NULL).
3585 * @param aLocation Full path to the image file (unused when NULL).
3586 * @param aSetError If @c true, the appropriate error info is set in case when
3587 * the image is not found.
3588 * @param aImage Where to store the found image object (can be NULL).
3589 *
3590 * @return S_OK when found or E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
3591 *
3592 * @note Locks the media tree for reading.
3593 */
3594HRESULT VirtualBox::findDVDOrFloppyImage(DeviceType_T mediumType,
3595 const Guid *aId,
3596 const Utf8Str &aLocation,
3597 bool aSetError,
3598 ComObjPtr<Medium> *aImage /* = NULL */)
3599{
3600 AssertReturn(aId || !aLocation.isEmpty(), E_INVALIDARG);
3601
3602 Utf8Str location;
3603 if (!aLocation.isEmpty())
3604 {
3605 int vrc = calculateFullPath(aLocation, location);
3606 if (RT_FAILURE(vrc))
3607 return setError(VBOX_E_FILE_ERROR,
3608 tr("Invalid image file location '%s' (%Rrc)"),
3609 aLocation.c_str(),
3610 vrc);
3611 }
3612
3613 MediaOList *pMediaList;
3614
3615 switch (mediumType)
3616 {
3617 case DeviceType_DVD:
3618 pMediaList = &m->allDVDImages;
3619 break;
3620
3621 case DeviceType_Floppy:
3622 pMediaList = &m->allFloppyImages;
3623 break;
3624
3625 default:
3626 return E_INVALIDARG;
3627 }
3628
3629 AutoReadLock alock(pMediaList->getLockHandle() COMMA_LOCKVAL_SRC_POS);
3630
3631 bool found = false;
3632
3633 for (MediaList::const_iterator it = pMediaList->begin();
3634 it != pMediaList->end();
3635 ++it)
3636 {
3637 // no AutoCaller, registered image life time is bound to this
3638 Medium *pMedium = *it;
3639 AutoReadLock imageLock(pMedium COMMA_LOCKVAL_SRC_POS);
3640 const Utf8Str &strLocationFull = pMedium->getLocationFull();
3641
3642 found = ( aId
3643 && pMedium->getId() == *aId)
3644 || ( !aLocation.isEmpty()
3645 && RTPathCompare(location.c_str(),
3646 strLocationFull.c_str()) == 0);
3647 if (found)
3648 {
3649 if (pMedium->getDeviceType() != mediumType)
3650 {
3651 if (mediumType == DeviceType_DVD)
3652 return setError(E_INVALIDARG,
3653 "Cannot mount DVD medium '%s' as floppy", strLocationFull.c_str());
3654 else
3655 return setError(E_INVALIDARG,
3656 "Cannot mount floppy medium '%s' as DVD", strLocationFull.c_str());
3657 }
3658
3659 if (aImage)
3660 *aImage = pMedium;
3661 break;
3662 }
3663 }
3664
3665 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
3666
3667 if (aSetError && !found)
3668 {
3669 if (aId)
3670 setError(rc,
3671 tr("Could not find an image file with UUID {%RTuuid} in the media registry ('%s')"),
3672 aId->raw(),
3673 m->strSettingsFilePath.c_str());
3674 else
3675 setError(rc,
3676 tr("Could not find an image file with location '%s' in the media registry ('%s')"),
3677 aLocation.c_str(),
3678 m->strSettingsFilePath.c_str());
3679 }
3680
3681 return rc;
3682}
3683
3684/**
3685 * Searches for an IMedium object that represents the given UUID.
3686 *
3687 * If the UUID is empty (indicating an empty drive), this sets pMedium
3688 * to NULL and returns S_OK.
3689 *
3690 * If the UUID refers to a host drive of the given device type, this
3691 * sets pMedium to the object from the list in IHost and returns S_OK.
3692 *
3693 * If the UUID is an image file, this sets pMedium to the object that
3694 * findDVDOrFloppyImage() returned.
3695 *
3696 * If none of the above apply, this returns VBOX_E_OBJECT_NOT_FOUND.
3697 *
3698 * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy.
3699 * @param uuid UUID to search for; must refer to a host drive or an image file or be null.
3700 * @param fRefresh Whether to refresh the list of host drives in IHost (see Host::getDrives())
3701 * @param pMedium out: IMedium object found.
3702 * @return
3703 */
3704HRESULT VirtualBox::findRemoveableMedium(DeviceType_T mediumType,
3705 const Guid &uuid,
3706 bool fRefresh,
3707 bool aSetError,
3708 ComObjPtr<Medium> &pMedium)
3709{
3710 if (uuid.isZero())
3711 {
3712 // that's easy
3713 pMedium.setNull();
3714 return S_OK;
3715 }
3716 else if (!uuid.isValid())
3717 {
3718 /* handling of case invalid GUID */
3719 return setError(VBOX_E_OBJECT_NOT_FOUND,
3720 tr("Guid '%ls' is invalid"),
3721 uuid.toString().c_str());
3722 }
3723
3724 // first search for host drive with that UUID
3725 HRESULT rc = m->pHost->findHostDriveById(mediumType,
3726 uuid,
3727 fRefresh,
3728 pMedium);
3729 if (rc == VBOX_E_OBJECT_NOT_FOUND)
3730 // then search for an image with that UUID
3731 rc = findDVDOrFloppyImage(mediumType, &uuid, Utf8Str::Empty, aSetError, &pMedium);
3732
3733 return rc;
3734}
3735
3736HRESULT VirtualBox::findGuestOSType(const Bstr &bstrOSType,
3737 GuestOSType*& pGuestOSType)
3738{
3739 /* Look for a GuestOSType object */
3740 AssertMsg(m->allGuestOSTypes.size() != 0,
3741 ("Guest OS types array must be filled"));
3742
3743 if (bstrOSType.isEmpty())
3744 {
3745 pGuestOSType = NULL;
3746 return S_OK;
3747 }
3748
3749 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3750 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
3751 it != m->allGuestOSTypes.end();
3752 ++it)
3753 {
3754 if ((*it)->id() == bstrOSType)
3755 {
3756 pGuestOSType = *it;
3757 return S_OK;
3758 }
3759 }
3760
3761 return setError(VBOX_E_OBJECT_NOT_FOUND,
3762 tr("Guest OS type '%ls' is invalid"),
3763 bstrOSType.raw());
3764}
3765
3766/**
3767 * Returns the constant pseudo-machine UUID that is used to identify the
3768 * global media registry.
3769 *
3770 * Starting with VirtualBox 4.0 each medium remembers in its instance data
3771 * in which media registry it is saved (if any): this can either be a machine
3772 * UUID, if it's in a per-machine media registry, or this global ID.
3773 *
3774 * This UUID is only used to identify the VirtualBox object while VirtualBox
3775 * is running. It is a compile-time constant and not saved anywhere.
3776 *
3777 * @return
3778 */
3779const Guid& VirtualBox::getGlobalRegistryId() const
3780{
3781 return m->uuidMediaRegistry;
3782}
3783
3784const ComObjPtr<Host>& VirtualBox::host() const
3785{
3786 return m->pHost;
3787}
3788
3789SystemProperties* VirtualBox::getSystemProperties() const
3790{
3791 return m->pSystemProperties;
3792}
3793
3794#ifdef VBOX_WITH_EXTPACK
3795/**
3796 * Getter that SystemProperties and others can use to talk to the extension
3797 * pack manager.
3798 */
3799ExtPackManager* VirtualBox::getExtPackManager() const
3800{
3801 return m->ptrExtPackManager;
3802}
3803#endif
3804
3805/**
3806 * Getter that machines can talk to the autostart database.
3807 */
3808AutostartDb* VirtualBox::getAutostartDb() const
3809{
3810 return m->pAutostartDb;
3811}
3812
3813#ifdef VBOX_WITH_RESOURCE_USAGE_API
3814const ComObjPtr<PerformanceCollector>& VirtualBox::performanceCollector() const
3815{
3816 return m->pPerformanceCollector;
3817}
3818#endif /* VBOX_WITH_RESOURCE_USAGE_API */
3819
3820/**
3821 * Returns the default machine folder from the system properties
3822 * with proper locking.
3823 * @return
3824 */
3825void VirtualBox::getDefaultMachineFolder(Utf8Str &str) const
3826{
3827 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3828 str = m->pSystemProperties->m->strDefaultMachineFolder;
3829}
3830
3831/**
3832 * Returns the default hard disk format from the system properties
3833 * with proper locking.
3834 * @return
3835 */
3836void VirtualBox::getDefaultHardDiskFormat(Utf8Str &str) const
3837{
3838 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3839 str = m->pSystemProperties->m->strDefaultHardDiskFormat;
3840}
3841
3842const Utf8Str& VirtualBox::homeDir() const
3843{
3844 return m->strHomeDir;
3845}
3846
3847/**
3848 * Calculates the absolute path of the given path taking the VirtualBox home
3849 * directory as the current directory.
3850 *
3851 * @param aPath Path to calculate the absolute path for.
3852 * @param aResult Where to put the result (used only on success, can be the
3853 * same Utf8Str instance as passed in @a aPath).
3854 * @return IPRT result.
3855 *
3856 * @note Doesn't lock any object.
3857 */
3858int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
3859{
3860 AutoCaller autoCaller(this);
3861 AssertComRCReturn(autoCaller.rc(), VERR_GENERAL_FAILURE);
3862
3863 /* no need to lock since mHomeDir is const */
3864
3865 char folder[RTPATH_MAX];
3866 int vrc = RTPathAbsEx(m->strHomeDir.c_str(),
3867 strPath.c_str(),
3868 folder,
3869 sizeof(folder));
3870 if (RT_SUCCESS(vrc))
3871 aResult = folder;
3872
3873 return vrc;
3874}
3875
3876/**
3877 * Copies strSource to strTarget, making it relative to the VirtualBox config folder
3878 * if it is a subdirectory thereof, or simply copying it otherwise.
3879 *
3880 * @param strSource Path to evalue and copy.
3881 * @param strTarget Buffer to receive target path.
3882 */
3883void VirtualBox::copyPathRelativeToConfig(const Utf8Str &strSource,
3884 Utf8Str &strTarget)
3885{
3886 AutoCaller autoCaller(this);
3887 AssertComRCReturnVoid(autoCaller.rc());
3888
3889 // no need to lock since mHomeDir is const
3890
3891 // use strTarget as a temporary buffer to hold the machine settings dir
3892 strTarget = m->strHomeDir;
3893 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
3894 // is relative: then append what's left
3895 strTarget.append(strSource.c_str() + strTarget.length()); // include '/'
3896 else
3897 // is not relative: then overwrite
3898 strTarget = strSource;
3899}
3900
3901// private methods
3902/////////////////////////////////////////////////////////////////////////////
3903
3904/**
3905 * Checks if there is a hard disk, DVD or floppy image with the given ID or
3906 * location already registered.
3907 *
3908 * On return, sets @a aConflict to the string describing the conflicting medium,
3909 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
3910 * either case. A failure is unexpected.
3911 *
3912 * @param aId UUID to check.
3913 * @param aLocation Location to check.
3914 * @param aConflict Where to return parameters of the conflicting medium.
3915 * @param ppMedium Medium reference in case this is simply a duplicate.
3916 *
3917 * @note Locks the media tree and media objects for reading.
3918 */
3919HRESULT VirtualBox::checkMediaForConflicts(const Guid &aId,
3920 const Utf8Str &aLocation,
3921 Utf8Str &aConflict,
3922 ComObjPtr<Medium> *ppMedium)
3923{
3924 AssertReturn(!aId.isZero() && !aLocation.isEmpty(), E_FAIL);
3925 AssertReturn(ppMedium, E_INVALIDARG);
3926
3927 aConflict.setNull();
3928 ppMedium->setNull();
3929
3930 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3931
3932 HRESULT rc = S_OK;
3933
3934 ComObjPtr<Medium> pMediumFound;
3935 const char *pcszType = NULL;
3936
3937 if (aId.isValid() && !aId.isZero())
3938 rc = findHardDiskById(aId, false /* aSetError */, &pMediumFound);
3939 if (FAILED(rc) && !aLocation.isEmpty())
3940 rc = findHardDiskByLocation(aLocation, false /* aSetError */, &pMediumFound);
3941 if (SUCCEEDED(rc))
3942 pcszType = tr("hard disk");
3943
3944 if (!pcszType)
3945 {
3946 rc = findDVDOrFloppyImage(DeviceType_DVD, &aId, aLocation, false /* aSetError */, &pMediumFound);
3947 if (SUCCEEDED(rc))
3948 pcszType = tr("CD/DVD image");
3949 }
3950
3951 if (!pcszType)
3952 {
3953 rc = findDVDOrFloppyImage(DeviceType_Floppy, &aId, aLocation, false /* aSetError */, &pMediumFound);
3954 if (SUCCEEDED(rc))
3955 pcszType = tr("floppy image");
3956 }
3957
3958 if (pcszType && pMediumFound)
3959 {
3960 /* Note: no AutoCaller since bound to this */
3961 AutoReadLock mlock(pMediumFound COMMA_LOCKVAL_SRC_POS);
3962
3963 Utf8Str strLocFound = pMediumFound->getLocationFull();
3964 Guid idFound = pMediumFound->getId();
3965
3966 if ( (RTPathCompare(strLocFound.c_str(), aLocation.c_str()) == 0)
3967 && (idFound == aId)
3968 )
3969 *ppMedium = pMediumFound;
3970
3971 aConflict = Utf8StrFmt(tr("%s '%s' with UUID {%RTuuid}"),
3972 pcszType,
3973 strLocFound.c_str(),
3974 idFound.raw());
3975 }
3976
3977 return S_OK;
3978}
3979
3980/**
3981 * Checks whether the given UUID is already in use by one medium for the
3982 * given device type.
3983 *
3984 * @returns true if the UUID is already in use
3985 * fale otherwise
3986 * @param aId The UUID to check.
3987 * @param deviceType The device type the UUID is going to be checked for
3988 * conflicts.
3989 */
3990bool VirtualBox::isMediaUuidInUse(const Guid &aId, DeviceType_T deviceType)
3991{
3992 /* A zero UUID is invalid here, always claim that it is already used. */
3993 AssertReturn(!aId.isZero(), true);
3994
3995 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3996
3997 HRESULT rc = S_OK;
3998 bool fInUse = false;
3999
4000 ComObjPtr<Medium> pMediumFound;
4001
4002 switch (deviceType)
4003 {
4004 case DeviceType_HardDisk:
4005 rc = findHardDiskById(aId, false /* aSetError */, &pMediumFound);
4006 break;
4007 case DeviceType_DVD:
4008 rc = findDVDOrFloppyImage(DeviceType_DVD, &aId, Utf8Str::Empty, false /* aSetError */, &pMediumFound);
4009 break;
4010 case DeviceType_Floppy:
4011 rc = findDVDOrFloppyImage(DeviceType_Floppy, &aId, Utf8Str::Empty, false /* aSetError */, &pMediumFound);
4012 break;
4013 default:
4014 AssertMsgFailed(("Invalid device type %d\n", deviceType));
4015 }
4016
4017 if (SUCCEEDED(rc) && pMediumFound)
4018 fInUse = true;
4019
4020 return fInUse;
4021}
4022
4023/**
4024 * Called from Machine::prepareSaveSettings() when it has detected
4025 * that a machine has been renamed. Such renames will require
4026 * updating the global media registry during the
4027 * VirtualBox::saveSettings() that follows later.
4028*
4029 * When a machine is renamed, there may well be media (in particular,
4030 * diff images for snapshots) in the global registry that will need
4031 * to have their paths updated. Before 3.2, Machine::saveSettings
4032 * used to call VirtualBox::saveSettings implicitly, which was both
4033 * unintuitive and caused locking order problems. Now, we remember
4034 * such pending name changes with this method so that
4035 * VirtualBox::saveSettings() can process them properly.
4036 */
4037void VirtualBox::rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
4038 const Utf8Str &strNewConfigDir)
4039{
4040 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4041
4042 Data::PendingMachineRename pmr;
4043 pmr.strConfigDirOld = strOldConfigDir;
4044 pmr.strConfigDirNew = strNewConfigDir;
4045 m->llPendingMachineRenames.push_back(pmr);
4046}
4047
4048struct SaveMediaRegistriesDesc
4049{
4050 MediaList llMedia;
4051 ComObjPtr<VirtualBox> pVirtualBox;
4052};
4053
4054static int fntSaveMediaRegistries(RTTHREAD ThreadSelf, void *pvUser)
4055{
4056 NOREF(ThreadSelf);
4057 SaveMediaRegistriesDesc *pDesc = (SaveMediaRegistriesDesc *)pvUser;
4058 if (!pDesc)
4059 {
4060 LogRelFunc(("Thread for saving media registries lacks parameters\n"));
4061 return VERR_INVALID_PARAMETER;
4062 }
4063
4064 for (MediaList::const_iterator it = pDesc->llMedia.begin();
4065 it != pDesc->llMedia.end();
4066 ++it)
4067 {
4068 Medium *pMedium = *it;
4069 pMedium->markRegistriesModified();
4070 }
4071
4072 pDesc->pVirtualBox->saveModifiedRegistries();
4073
4074 pDesc->llMedia.clear();
4075 pDesc->pVirtualBox.setNull();
4076 delete pDesc;
4077
4078 return VINF_SUCCESS;
4079}
4080
4081/**
4082 * Goes through all known media (hard disks, floppies and DVDs) and saves
4083 * those into the given settings::MediaRegistry structures whose registry
4084 * ID match the given UUID.
4085 *
4086 * Before actually writing to the structures, all media paths (not just the
4087 * ones for the given registry) are updated if machines have been renamed
4088 * since the last call.
4089 *
4090 * This gets called from two contexts:
4091 *
4092 * -- VirtualBox::saveSettings() with the UUID of the global registry
4093 * (VirtualBox::Data.uuidRegistry); this will save those media
4094 * which had been loaded from the global registry or have been
4095 * attached to a "legacy" machine which can't save its own registry;
4096 *
4097 * -- Machine::saveSettings() with the UUID of a machine, if a medium
4098 * has been attached to a machine created with VirtualBox 4.0 or later.
4099 *
4100 * Media which have only been temporarily opened without having been
4101 * attached to a machine have a NULL registry UUID and therefore don't
4102 * get saved.
4103 *
4104 * This locks the media tree. Throws HRESULT on errors!
4105 *
4106 * @param mediaRegistry Settings structure to fill.
4107 * @param uuidRegistry The UUID of the media registry; either a machine UUID (if machine registry) or the UUID of the global registry.
4108 * @param strMachineFolder The machine folder for relative paths, if machine registry, or an empty string otherwise.
4109 */
4110void VirtualBox::saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
4111 const Guid &uuidRegistry,
4112 const Utf8Str &strMachineFolder)
4113{
4114 // lock all media for the following; use a write lock because we're
4115 // modifying the PendingMachineRenamesList, which is protected by this
4116 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4117
4118 // if a machine was renamed, then we'll need to refresh media paths
4119 if (m->llPendingMachineRenames.size())
4120 {
4121 // make a single list from the three media lists so we don't need three loops
4122 MediaList llAllMedia;
4123 // with hard disks, we must use the map, not the list, because the list only has base images
4124 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
4125 llAllMedia.push_back(it->second);
4126 for (MediaList::iterator it = m->allDVDImages.begin(); it != m->allDVDImages.end(); ++it)
4127 llAllMedia.push_back(*it);
4128 for (MediaList::iterator it = m->allFloppyImages.begin(); it != m->allFloppyImages.end(); ++it)
4129 llAllMedia.push_back(*it);
4130
4131 SaveMediaRegistriesDesc *pDesc = new SaveMediaRegistriesDesc();
4132 for (MediaList::iterator it = llAllMedia.begin();
4133 it != llAllMedia.end();
4134 ++it)
4135 {
4136 Medium *pMedium = *it;
4137 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
4138 it2 != m->llPendingMachineRenames.end();
4139 ++it2)
4140 {
4141 const Data::PendingMachineRename &pmr = *it2;
4142 HRESULT rc = pMedium->updatePath(pmr.strConfigDirOld,
4143 pmr.strConfigDirNew);
4144 if (SUCCEEDED(rc))
4145 {
4146 // Remember which medium objects has been changed,
4147 // to trigger saving their registries later.
4148 pDesc->llMedia.push_back(pMedium);
4149 } else if (rc == VBOX_E_FILE_ERROR)
4150 /* nothing */;
4151 else
4152 AssertComRC(rc);
4153 }
4154 }
4155 // done, don't do it again until we have more machine renames
4156 m->llPendingMachineRenames.clear();
4157
4158 if (pDesc->llMedia.size())
4159 {
4160 // Handle the media registry saving in a separate thread, to
4161 // avoid giant locking problems and passing up the list many
4162 // levels up to whoever triggered saveSettings, as there are
4163 // lots of places which would need to handle saving more settings.
4164 pDesc->pVirtualBox = this;
4165 int vrc = RTThreadCreate(NULL,
4166 fntSaveMediaRegistries,
4167 (void *)pDesc,
4168 0, // cbStack (default)
4169 RTTHREADTYPE_MAIN_WORKER,
4170 0, // flags
4171 "SaveMediaReg");
4172 ComAssertRC(vrc);
4173 // failure means that settings aren't saved, but there isn't
4174 // much we can do besides avoiding memory leaks
4175 if (RT_FAILURE(vrc))
4176 {
4177 LogRelFunc(("Failed to create thread for saving media registries (%Rrc)\n", vrc));
4178 delete pDesc;
4179 }
4180 }
4181 else
4182 delete pDesc;
4183 }
4184
4185 struct {
4186 MediaOList &llSource;
4187 settings::MediaList &llTarget;
4188 } s[] =
4189 {
4190 // hard disks
4191 { m->allHardDisks, mediaRegistry.llHardDisks },
4192 // CD/DVD images
4193 { m->allDVDImages, mediaRegistry.llDvdImages },
4194 // floppy images
4195 { m->allFloppyImages, mediaRegistry.llFloppyImages }
4196 };
4197
4198 HRESULT rc;
4199
4200 for (size_t i = 0; i < RT_ELEMENTS(s); ++i)
4201 {
4202 MediaOList &llSource = s[i].llSource;
4203 settings::MediaList &llTarget = s[i].llTarget;
4204 llTarget.clear();
4205 for (MediaList::const_iterator it = llSource.begin();
4206 it != llSource.end();
4207 ++it)
4208 {
4209 Medium *pMedium = *it;
4210 AutoCaller autoCaller(pMedium);
4211 if (FAILED(autoCaller.rc())) throw autoCaller.rc();
4212 AutoReadLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
4213
4214 if (pMedium->isInRegistry(uuidRegistry))
4215 {
4216 settings::Medium med;
4217 rc = pMedium->saveSettings(med, strMachineFolder); // this recurses into child hard disks
4218 if (FAILED(rc)) throw rc;
4219 llTarget.push_back(med);
4220 }
4221 }
4222 }
4223}
4224
4225/**
4226 * Helper function which actually writes out VirtualBox.xml, the main configuration file.
4227 * Gets called from the public VirtualBox::SaveSettings() as well as from various other
4228 * places internally when settings need saving.
4229 *
4230 * @note Caller must have locked the VirtualBox object for writing and must not hold any
4231 * other locks since this locks all kinds of member objects and trees temporarily,
4232 * which could cause conflicts.
4233 */
4234HRESULT VirtualBox::saveSettings()
4235{
4236 AutoCaller autoCaller(this);
4237 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4238
4239 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
4240 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
4241
4242 HRESULT rc = S_OK;
4243
4244 try
4245 {
4246 // machines
4247 m->pMainConfigFile->llMachines.clear();
4248 {
4249 AutoReadLock machinesLock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4250 for (MachinesOList::iterator it = m->allMachines.begin();
4251 it != m->allMachines.end();
4252 ++it)
4253 {
4254 Machine *pMachine = *it;
4255 // save actual machine registry entry
4256 settings::MachineRegistryEntry mre;
4257 rc = pMachine->saveRegistryEntry(mre);
4258 m->pMainConfigFile->llMachines.push_back(mre);
4259 }
4260 }
4261
4262 saveMediaRegistry(m->pMainConfigFile->mediaRegistry,
4263 m->uuidMediaRegistry, // global media registry ID
4264 Utf8Str::Empty); // strMachineFolder
4265
4266 m->pMainConfigFile->llDhcpServers.clear();
4267 {
4268 AutoReadLock dhcpLock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4269 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
4270 it != m->allDHCPServers.end();
4271 ++it)
4272 {
4273 settings::DHCPServer d;
4274 rc = (*it)->saveSettings(d);
4275 if (FAILED(rc)) throw rc;
4276 m->pMainConfigFile->llDhcpServers.push_back(d);
4277 }
4278 }
4279
4280#ifdef VBOX_WITH_NAT_SERVICE
4281 /* Saving NAT Network configuration */
4282 m->pMainConfigFile->llNATNetworks.clear();
4283 {
4284 AutoReadLock natNetworkLock(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4285 for (NATNetworksOList::const_iterator it = m->allNATNetworks.begin();
4286 it != m->allNATNetworks.end();
4287 ++it)
4288 {
4289 settings::NATNetwork n;
4290 rc = (*it)->saveSettings(n);
4291 if (FAILED(rc)) throw rc;
4292 m->pMainConfigFile->llNATNetworks.push_back(n);
4293 }
4294 }
4295#endif
4296
4297 // leave extra data alone, it's still in the config file
4298
4299 // host data (USB filters)
4300 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
4301 if (FAILED(rc)) throw rc;
4302
4303 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
4304 if (FAILED(rc)) throw rc;
4305
4306 // and write out the XML, still under the lock
4307 m->pMainConfigFile->write(m->strSettingsFilePath);
4308 }
4309 catch (HRESULT err)
4310 {
4311 /* we assume that error info is set by the thrower */
4312 rc = err;
4313 }
4314 catch (...)
4315 {
4316 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
4317 }
4318
4319 return rc;
4320}
4321
4322/**
4323 * Helper to register the machine.
4324 *
4325 * When called during VirtualBox startup, adds the given machine to the
4326 * collection of registered machines. Otherwise tries to mark the machine
4327 * as registered, and, if succeeded, adds it to the collection and
4328 * saves global settings.
4329 *
4330 * @note The caller must have added itself as a caller of the @a aMachine
4331 * object if calls this method not on VirtualBox startup.
4332 *
4333 * @param aMachine machine to register
4334 *
4335 * @note Locks objects!
4336 */
4337HRESULT VirtualBox::registerMachine(Machine *aMachine)
4338{
4339 ComAssertRet(aMachine, E_INVALIDARG);
4340
4341 AutoCaller autoCaller(this);
4342 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4343
4344 HRESULT rc = S_OK;
4345
4346 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4347
4348 {
4349 ComObjPtr<Machine> pMachine;
4350 rc = findMachine(aMachine->getId(),
4351 true /* fPermitInaccessible */,
4352 false /* aDoSetError */,
4353 &pMachine);
4354 if (SUCCEEDED(rc))
4355 {
4356 /* sanity */
4357 AutoLimitedCaller machCaller(pMachine);
4358 AssertComRC(machCaller.rc());
4359
4360 return setError(E_INVALIDARG,
4361 tr("Registered machine with UUID {%RTuuid} ('%s') already exists"),
4362 aMachine->getId().raw(),
4363 pMachine->getSettingsFileFull().c_str());
4364 }
4365
4366 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
4367 rc = S_OK;
4368 }
4369
4370 if (autoCaller.state() != InInit)
4371 {
4372 rc = aMachine->prepareRegister();
4373 if (FAILED(rc)) return rc;
4374 }
4375
4376 /* add to the collection of registered machines */
4377 m->allMachines.addChild(aMachine);
4378
4379 if (autoCaller.state() != InInit)
4380 rc = saveSettings();
4381
4382 return rc;
4383}
4384
4385/**
4386 * Remembers the given medium object by storing it in either the global
4387 * medium registry or a machine one.
4388 *
4389 * @note Caller must hold the media tree lock for writing; in addition, this
4390 * locks @a pMedium for reading
4391 *
4392 * @param pMedium Medium object to remember.
4393 * @param ppMedium Actually stored medium object. Can be different if due
4394 * to an unavoidable race there was a duplicate Medium object
4395 * created.
4396 * @param argType Either DeviceType_HardDisk, DeviceType_DVD or DeviceType_Floppy.
4397 * @return
4398 */
4399HRESULT VirtualBox::registerMedium(const ComObjPtr<Medium> &pMedium,
4400 ComObjPtr<Medium> *ppMedium,
4401 DeviceType_T argType)
4402{
4403 AssertReturn(pMedium != NULL, E_INVALIDARG);
4404 AssertReturn(ppMedium != NULL, E_INVALIDARG);
4405
4406 AutoCaller autoCaller(this);
4407 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4408
4409 AutoCaller mediumCaller(pMedium);
4410 AssertComRCReturn(mediumCaller.rc(), mediumCaller.rc());
4411
4412 const char *pszDevType = NULL;
4413 ObjectsList<Medium> *pall = NULL;
4414 switch (argType)
4415 {
4416 case DeviceType_HardDisk:
4417 pall = &m->allHardDisks;
4418 pszDevType = tr("hard disk");
4419 break;
4420 case DeviceType_DVD:
4421 pszDevType = tr("DVD image");
4422 pall = &m->allDVDImages;
4423 break;
4424 case DeviceType_Floppy:
4425 pszDevType = tr("floppy image");
4426 pall = &m->allFloppyImages;
4427 break;
4428 default:
4429 AssertMsgFailedReturn(("invalid device type %d", argType), E_INVALIDARG);
4430 }
4431
4432 // caller must hold the media tree write lock
4433 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
4434
4435 Guid id;
4436 Utf8Str strLocationFull;
4437 ComObjPtr<Medium> pParent;
4438 {
4439 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4440 id = pMedium->getId();
4441 strLocationFull = pMedium->getLocationFull();
4442 pParent = pMedium->getParent();
4443 }
4444
4445 HRESULT rc;
4446
4447 Utf8Str strConflict;
4448 ComObjPtr<Medium> pDupMedium;
4449 rc = checkMediaForConflicts(id,
4450 strLocationFull,
4451 strConflict,
4452 &pDupMedium);
4453 if (FAILED(rc)) return rc;
4454
4455 if (pDupMedium.isNull())
4456 {
4457 if (strConflict.length())
4458 return setError(E_INVALIDARG,
4459 tr("Cannot register the %s '%s' {%RTuuid} because a %s already exists"),
4460 pszDevType,
4461 strLocationFull.c_str(),
4462 id.raw(),
4463 strConflict.c_str(),
4464 m->strSettingsFilePath.c_str());
4465
4466 // add to the collection if it is a base medium
4467 if (pParent.isNull())
4468 pall->getList().push_back(pMedium);
4469
4470 // store all hard disks (even differencing images) in the map
4471 if (argType == DeviceType_HardDisk)
4472 m->mapHardDisks[id] = pMedium;
4473
4474 *ppMedium = pMedium;
4475 }
4476 else
4477 {
4478 // pMedium may be the last reference to the Medium object, and the
4479 // caller may have specified the same ComObjPtr as the output parameter.
4480 // In this case the assignment will uninit the object, and we must not
4481 // have a caller pending.
4482 mediumCaller.release();
4483 *ppMedium = pDupMedium;
4484 }
4485
4486 return rc;
4487}
4488
4489/**
4490 * Removes the given medium from the respective registry.
4491 *
4492 * @param pMedium Hard disk object to remove.
4493 *
4494 * @note Caller must hold the media tree lock for writing; in addition, this locks @a pMedium for reading
4495 */
4496HRESULT VirtualBox::unregisterMedium(Medium *pMedium)
4497{
4498 AssertReturn(pMedium != NULL, E_INVALIDARG);
4499
4500 AutoCaller autoCaller(this);
4501 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4502
4503 AutoCaller mediumCaller(pMedium);
4504 AssertComRCReturn(mediumCaller.rc(), mediumCaller.rc());
4505
4506 // caller must hold the media tree write lock
4507 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
4508
4509 Guid id;
4510 ComObjPtr<Medium> pParent;
4511 DeviceType_T devType;
4512 {
4513 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4514 id = pMedium->getId();
4515 pParent = pMedium->getParent();
4516 devType = pMedium->getDeviceType();
4517 }
4518
4519 ObjectsList<Medium> *pall = NULL;
4520 switch (devType)
4521 {
4522 case DeviceType_HardDisk:
4523 pall = &m->allHardDisks;
4524 break;
4525 case DeviceType_DVD:
4526 pall = &m->allDVDImages;
4527 break;
4528 case DeviceType_Floppy:
4529 pall = &m->allFloppyImages;
4530 break;
4531 default:
4532 AssertMsgFailedReturn(("invalid device type %d", devType), E_INVALIDARG);
4533 }
4534
4535 // remove from the collection if it is a base medium
4536 if (pParent.isNull())
4537 pall->getList().remove(pMedium);
4538
4539 // remove all hard disks (even differencing images) from map
4540 if (devType == DeviceType_HardDisk)
4541 {
4542 size_t cnt = m->mapHardDisks.erase(id);
4543 Assert(cnt == 1);
4544 NOREF(cnt);
4545 }
4546
4547 return S_OK;
4548}
4549
4550/**
4551 * Little helper called from unregisterMachineMedia() to recursively add media to the given list,
4552 * with children appearing before their parents.
4553 * @param llMedia
4554 * @param pMedium
4555 */
4556void VirtualBox::pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium)
4557{
4558 // recurse first, then add ourselves; this way children end up on the
4559 // list before their parents
4560
4561 const MediaList &llChildren = pMedium->getChildren();
4562 for (MediaList::const_iterator it = llChildren.begin();
4563 it != llChildren.end();
4564 ++it)
4565 {
4566 Medium *pChild = *it;
4567 pushMediumToListWithChildren(llMedia, pChild);
4568 }
4569
4570 Log(("Pushing medium %RTuuid\n", pMedium->getId().raw()));
4571 llMedia.push_back(pMedium);
4572}
4573
4574/**
4575 * Unregisters all Medium objects which belong to the given machine registry.
4576 * Gets called from Machine::uninit() just before the machine object dies
4577 * and must only be called with a machine UUID as the registry ID.
4578 *
4579 * Locks the media tree.
4580 *
4581 * @param uuidMachine Medium registry ID (always a machine UUID)
4582 * @return
4583 */
4584HRESULT VirtualBox::unregisterMachineMedia(const Guid &uuidMachine)
4585{
4586 Assert(!uuidMachine.isZero() && uuidMachine.isValid());
4587
4588 LogFlowFuncEnter();
4589
4590 AutoCaller autoCaller(this);
4591 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4592
4593 MediaList llMedia2Close;
4594
4595 {
4596 AutoWriteLock tlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4597
4598 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
4599 it != m->allHardDisks.getList().end();
4600 ++it)
4601 {
4602 ComObjPtr<Medium> pMedium = *it;
4603 AutoCaller medCaller(pMedium);
4604 if (FAILED(medCaller.rc())) return medCaller.rc();
4605 AutoReadLock medlock(pMedium COMMA_LOCKVAL_SRC_POS);
4606
4607 if (pMedium->isInRegistry(uuidMachine))
4608 // recursively with children first
4609 pushMediumToListWithChildren(llMedia2Close, pMedium);
4610 }
4611 }
4612
4613 for (MediaList::iterator it = llMedia2Close.begin();
4614 it != llMedia2Close.end();
4615 ++it)
4616 {
4617 ComObjPtr<Medium> pMedium = *it;
4618 Log(("Closing medium %RTuuid\n", pMedium->getId().raw()));
4619 AutoCaller mac(pMedium);
4620 pMedium->close(mac);
4621 }
4622
4623 LogFlowFuncLeave();
4624
4625 return S_OK;
4626}
4627
4628/**
4629 * Removes the given machine object from the internal list of registered machines.
4630 * Called from Machine::Unregister().
4631 * @param pMachine
4632 * @param id UUID of the machine. Must be passed by caller because machine may be dead by this time.
4633 * @return
4634 */
4635HRESULT VirtualBox::unregisterMachine(Machine *pMachine,
4636 const Guid &id)
4637{
4638 // remove from the collection of registered machines
4639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4640 m->allMachines.removeChild(pMachine);
4641 // save the global registry
4642 HRESULT rc = saveSettings();
4643 alock.release();
4644
4645 /*
4646 * Now go over all known media and checks if they were registered in the
4647 * media registry of the given machine. Each such medium is then moved to
4648 * a different media registry to make sure it doesn't get lost since its
4649 * media registry is about to go away.
4650 *
4651 * This fixes the following use case: Image A.vdi of machine A is also used
4652 * by machine B, but registered in the media registry of machine A. If machine
4653 * A is deleted, A.vdi must be moved to the registry of B, or else B will
4654 * become inaccessible.
4655 */
4656 {
4657 AutoReadLock tlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4658 // iterate over the list of *base* images
4659 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
4660 it != m->allHardDisks.getList().end();
4661 ++it)
4662 {
4663 ComObjPtr<Medium> &pMedium = *it;
4664 AutoCaller medCaller(pMedium);
4665 if (FAILED(medCaller.rc())) return medCaller.rc();
4666 AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
4667
4668 if (pMedium->removeRegistry(id, true /* fRecurse */))
4669 {
4670 // machine ID was found in base medium's registry list:
4671 // move this base image and all its children to another registry then
4672 // 1) first, find a better registry to add things to
4673 const Guid *puuidBetter = pMedium->getAnyMachineBackref();
4674 if (puuidBetter)
4675 {
4676 // 2) better registry found: then use that
4677 pMedium->addRegistry(*puuidBetter, true /* fRecurse */);
4678 // 3) and make sure the registry is saved below
4679 mlock.release();
4680 tlock.release();
4681 markRegistryModified(*puuidBetter);
4682 tlock.acquire();
4683 mlock.release();
4684 }
4685 }
4686 }
4687 }
4688
4689 saveModifiedRegistries();
4690
4691 /* fire an event */
4692 onMachineRegistered(id, FALSE);
4693
4694 return rc;
4695}
4696
4697/**
4698 * Marks the registry for @a uuid as modified, so that it's saved in a later
4699 * call to saveModifiedRegistries().
4700 *
4701 * @param uuid
4702 */
4703void VirtualBox::markRegistryModified(const Guid &uuid)
4704{
4705 if (uuid == getGlobalRegistryId())
4706 ASMAtomicIncU64(&m->uRegistryNeedsSaving);
4707 else
4708 {
4709 ComObjPtr<Machine> pMachine;
4710 HRESULT rc = findMachine(uuid,
4711 false /* fPermitInaccessible */,
4712 false /* aSetError */,
4713 &pMachine);
4714 if (SUCCEEDED(rc))
4715 {
4716 AutoCaller machineCaller(pMachine);
4717 if (SUCCEEDED(machineCaller.rc()))
4718 ASMAtomicIncU64(&pMachine->uRegistryNeedsSaving);
4719 }
4720 }
4721}
4722
4723/**
4724 * Saves all settings files according to the modified flags in the Machine
4725 * objects and in the VirtualBox object.
4726 *
4727 * This locks machines and the VirtualBox object as necessary, so better not
4728 * hold any locks before calling this.
4729 *
4730 * @return
4731 */
4732void VirtualBox::saveModifiedRegistries()
4733{
4734 HRESULT rc = S_OK;
4735 bool fNeedsGlobalSettings = false;
4736 uint64_t uOld;
4737
4738 {
4739 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4740 for (MachinesOList::iterator it = m->allMachines.begin();
4741 it != m->allMachines.end();
4742 ++it)
4743 {
4744 const ComObjPtr<Machine> &pMachine = *it;
4745
4746 for (;;)
4747 {
4748 uOld = ASMAtomicReadU64(&pMachine->uRegistryNeedsSaving);
4749 if (!uOld)
4750 break;
4751 if (ASMAtomicCmpXchgU64(&pMachine->uRegistryNeedsSaving, 0, uOld))
4752 break;
4753 ASMNopPause();
4754 }
4755 if (uOld)
4756 {
4757 AutoCaller autoCaller(pMachine);
4758 if (FAILED(autoCaller.rc()))
4759 continue;
4760 /* object is already dead, no point in saving settings */
4761 if (autoCaller.state() != Ready)
4762 continue;
4763 AutoWriteLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
4764 rc = pMachine->saveSettings(&fNeedsGlobalSettings,
4765 Machine::SaveS_Force); // caller said save, so stop arguing
4766 }
4767 }
4768 }
4769
4770 for (;;)
4771 {
4772 uOld = ASMAtomicReadU64(&m->uRegistryNeedsSaving);
4773 if (!uOld)
4774 break;
4775 if (ASMAtomicCmpXchgU64(&m->uRegistryNeedsSaving, 0, uOld))
4776 break;
4777 ASMNopPause();
4778 }
4779 if (uOld || fNeedsGlobalSettings)
4780 {
4781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4782 rc = saveSettings();
4783 }
4784 NOREF(rc); /* XXX */
4785}
4786
4787
4788/* static */
4789const Bstr &VirtualBox::getVersionNormalized()
4790{
4791 return sVersionNormalized;
4792}
4793
4794/**
4795 * Checks if the path to the specified file exists, according to the path
4796 * information present in the file name. Optionally the path is created.
4797 *
4798 * Note that the given file name must contain the full path otherwise the
4799 * extracted relative path will be created based on the current working
4800 * directory which is normally unknown.
4801 *
4802 * @param aFileName Full file name which path is checked/created.
4803 * @param aCreate Flag if the path should be created if it doesn't exist.
4804 *
4805 * @return Extended error information on failure to check/create the path.
4806 */
4807/* static */
4808HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName, bool fCreate)
4809{
4810 Utf8Str strDir(strFileName);
4811 strDir.stripFilename();
4812 if (!RTDirExists(strDir.c_str()))
4813 {
4814 if (fCreate)
4815 {
4816 int vrc = RTDirCreateFullPath(strDir.c_str(), 0700);
4817 if (RT_FAILURE(vrc))
4818 return setErrorStatic(VBOX_E_IPRT_ERROR,
4819 Utf8StrFmt(tr("Could not create the directory '%s' (%Rrc)"),
4820 strDir.c_str(),
4821 vrc));
4822 }
4823 else
4824 return setErrorStatic(VBOX_E_IPRT_ERROR,
4825 Utf8StrFmt(tr("Directory '%s' does not exist"),
4826 strDir.c_str()));
4827 }
4828
4829 return S_OK;
4830}
4831
4832const Utf8Str& VirtualBox::settingsFilePath()
4833{
4834 return m->strSettingsFilePath;
4835}
4836
4837/**
4838 * Returns the lock handle which protects the media trees (hard disks,
4839 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
4840 * are no longer protected by the VirtualBox lock, but by this more
4841 * specialized lock. Mind the locking order: always request this lock
4842 * after the VirtualBox object lock but before the locks of the media
4843 * objects contained in these lists. See AutoLock.h.
4844 */
4845RWLockHandle& VirtualBox::getMediaTreeLockHandle()
4846{
4847 return m->lockMedia;
4848}
4849
4850/**
4851 * Thread function that watches the termination of all client processes
4852 * that have opened sessions using IMachine::LockMachine()
4853 */
4854// static
4855DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
4856{
4857 LogFlowFuncEnter();
4858
4859 VirtualBox *that = (VirtualBox*)pvUser;
4860 Assert(that);
4861
4862 typedef std::vector< ComObjPtr<Machine> > MachineVector;
4863 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
4864
4865 SessionMachineVector machines;
4866 MachineVector spawnedMachines;
4867
4868 size_t cnt = 0;
4869 size_t cntSpawned = 0;
4870
4871 VirtualBoxBase::initializeComForThread();
4872
4873#if defined(RT_OS_WINDOWS)
4874
4875 /// @todo (dmik) processes reaping!
4876
4877 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
4878 handles[0] = that->m->updateReq;
4879
4880 do
4881 {
4882 AutoCaller autoCaller(that);
4883 /* VirtualBox has been early uninitialized, terminate */
4884 if (!autoCaller.isOk())
4885 break;
4886
4887 do
4888 {
4889 /* release the caller to let uninit() ever proceed */
4890 autoCaller.release();
4891
4892 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
4893 handles,
4894 FALSE,
4895 INFINITE);
4896
4897 /* Restore the caller before using VirtualBox. If it fails, this
4898 * means VirtualBox is being uninitialized and we must terminate. */
4899 autoCaller.add();
4900 if (!autoCaller.isOk())
4901 break;
4902
4903 bool update = false;
4904
4905 if (rc == WAIT_OBJECT_0)
4906 {
4907 /* update event is signaled */
4908 update = true;
4909 }
4910 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
4911 {
4912 /* machine mutex is released */
4913 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
4914 update = true;
4915 }
4916 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
4917 {
4918 /* machine mutex is abandoned due to client process termination */
4919 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
4920 update = true;
4921 }
4922 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
4923 {
4924 /* spawned VM process has terminated (normally or abnormally) */
4925 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
4926 checkForSpawnFailure();
4927 update = true;
4928 }
4929
4930 if (update)
4931 {
4932 /* close old process handles */
4933 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
4934 CloseHandle(handles[i]);
4935
4936 // lock the machines list for reading
4937 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4938
4939 /* obtain a new set of opened machines */
4940 cnt = 0;
4941 machines.clear();
4942
4943 for (MachinesOList::iterator it = that->m->allMachines.begin();
4944 it != that->m->allMachines.end();
4945 ++it)
4946 {
4947 /// @todo handle situations with more than 64 objects
4948 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
4949 ("MAXIMUM_WAIT_OBJECTS reached"));
4950
4951 ComObjPtr<SessionMachine> sm;
4952 HANDLE ipcSem;
4953 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4954 {
4955 machines.push_back(sm);
4956 handles[1 + cnt] = ipcSem;
4957 ++cnt;
4958 }
4959 }
4960
4961 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4962
4963 /* obtain a new set of spawned machines */
4964 cntSpawned = 0;
4965 spawnedMachines.clear();
4966
4967 for (MachinesOList::iterator it = that->m->allMachines.begin();
4968 it != that->m->allMachines.end();
4969 ++it)
4970 {
4971 /// @todo handle situations with more than 64 objects
4972 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
4973 ("MAXIMUM_WAIT_OBJECTS reached"));
4974
4975 RTPROCESS pid;
4976 if ((*it)->isSessionSpawning(&pid))
4977 {
4978 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
4979 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
4980 pid, GetLastError()));
4981 if (rc == 0)
4982 {
4983 spawnedMachines.push_back(*it);
4984 handles[1 + cnt + cntSpawned] = ph;
4985 ++cntSpawned;
4986 }
4987 }
4988 }
4989
4990 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4991
4992 // machines lock unwinds here
4993 }
4994 }
4995 while (true);
4996 }
4997 while (0);
4998
4999 /* close old process handles */
5000 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
5001 CloseHandle(handles[i]);
5002
5003 /* release sets of machines if any */
5004 machines.clear();
5005 spawnedMachines.clear();
5006
5007 ::CoUninitialize();
5008
5009#elif defined(RT_OS_OS2)
5010
5011 /// @todo (dmik) processes reaping!
5012
5013 /* according to PMREF, 64 is the maximum for the muxwait list */
5014 SEMRECORD handles[64];
5015
5016 HMUX muxSem = NULLHANDLE;
5017
5018 do
5019 {
5020 AutoCaller autoCaller(that);
5021 /* VirtualBox has been early uninitialized, terminate */
5022 if (!autoCaller.isOk())
5023 break;
5024
5025 do
5026 {
5027 /* release the caller to let uninit() ever proceed */
5028 autoCaller.release();
5029
5030 int vrc = RTSemEventWait(that->m->updateReq, 500);
5031
5032 /* Restore the caller before using VirtualBox. If it fails, this
5033 * means VirtualBox is being uninitialized and we must terminate. */
5034 autoCaller.add();
5035 if (!autoCaller.isOk())
5036 break;
5037
5038 bool update = false;
5039 bool updateSpawned = false;
5040
5041 if (RT_SUCCESS(vrc))
5042 {
5043 /* update event is signaled */
5044 update = true;
5045 updateSpawned = true;
5046 }
5047 else
5048 {
5049 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
5050 ("RTSemEventWait returned %Rrc\n", vrc));
5051
5052 /* are there any mutexes? */
5053 if (cnt > 0)
5054 {
5055 /* figure out what's going on with machines */
5056
5057 unsigned long semId = 0;
5058 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
5059 SEM_IMMEDIATE_RETURN, &semId);
5060
5061 if (arc == NO_ERROR)
5062 {
5063 /* machine mutex is normally released */
5064 Assert(semId >= 0 && semId < cnt);
5065 if (semId >= 0 && semId < cnt)
5066 {
5067#if 0//def DEBUG
5068 {
5069 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
5070 LogFlowFunc(("released mutex: machine='%ls'\n",
5071 machines[semId]->name().raw()));
5072 }
5073#endif
5074 machines[semId]->checkForDeath();
5075 }
5076 update = true;
5077 }
5078 else if (arc == ERROR_SEM_OWNER_DIED)
5079 {
5080 /* machine mutex is abandoned due to client process
5081 * termination; find which mutex is in the Owner Died
5082 * state */
5083 for (size_t i = 0; i < cnt; ++ i)
5084 {
5085 PID pid; TID tid;
5086 unsigned long reqCnt;
5087 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
5088 if (arc == ERROR_SEM_OWNER_DIED)
5089 {
5090 /* close the dead mutex as asked by PMREF */
5091 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
5092
5093 Assert(i >= 0 && i < cnt);
5094 if (i >= 0 && i < cnt)
5095 {
5096#if 0//def DEBUG
5097 {
5098 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
5099 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
5100 machines[i]->name().raw()));
5101 }
5102#endif
5103 machines[i]->checkForDeath();
5104 }
5105 }
5106 }
5107 update = true;
5108 }
5109 else
5110 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
5111 ("DosWaitMuxWaitSem returned %d\n", arc));
5112 }
5113
5114 /* are there any spawning sessions? */
5115 if (cntSpawned > 0)
5116 {
5117 for (size_t i = 0; i < cntSpawned; ++ i)
5118 updateSpawned |= (spawnedMachines[i])->
5119 checkForSpawnFailure();
5120 }
5121 }
5122
5123 if (update || updateSpawned)
5124 {
5125 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
5126
5127 if (update)
5128 {
5129 /* close the old muxsem */
5130 if (muxSem != NULLHANDLE)
5131 ::DosCloseMuxWaitSem(muxSem);
5132
5133 /* obtain a new set of opened machines */
5134 cnt = 0;
5135 machines.clear();
5136
5137 for (MachinesOList::iterator it = that->m->allMachines.begin();
5138 it != that->m->allMachines.end(); ++ it)
5139 {
5140 /// @todo handle situations with more than 64 objects
5141 AssertMsg(cnt <= 64 /* according to PMREF */,
5142 ("maximum of 64 mutex semaphores reached (%d)",
5143 cnt));
5144
5145 ComObjPtr<SessionMachine> sm;
5146 HMTX ipcSem;
5147 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
5148 {
5149 machines.push_back(sm);
5150 handles[cnt].hsemCur = (HSEM)ipcSem;
5151 handles[cnt].ulUser = cnt;
5152 ++ cnt;
5153 }
5154 }
5155
5156 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
5157
5158 if (cnt > 0)
5159 {
5160 /* create a new muxsem */
5161 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
5162 handles,
5163 DCMW_WAIT_ANY);
5164 AssertMsg(arc == NO_ERROR,
5165 ("DosCreateMuxWaitSem returned %d\n", arc));
5166 NOREF(arc);
5167 }
5168 }
5169
5170 if (updateSpawned)
5171 {
5172 /* obtain a new set of spawned machines */
5173 spawnedMachines.clear();
5174
5175 for (MachinesOList::iterator it = that->m->allMachines.begin();
5176 it != that->m->allMachines.end(); ++ it)
5177 {
5178 if ((*it)->isSessionSpawning())
5179 spawnedMachines.push_back(*it);
5180 }
5181
5182 cntSpawned = spawnedMachines.size();
5183 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
5184 }
5185 }
5186 }
5187 while (true);
5188 }
5189 while (0);
5190
5191 /* close the muxsem */
5192 if (muxSem != NULLHANDLE)
5193 ::DosCloseMuxWaitSem(muxSem);
5194
5195 /* release sets of machines if any */
5196 machines.clear();
5197 spawnedMachines.clear();
5198
5199#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
5200
5201 bool update = false;
5202 bool updateSpawned = false;
5203
5204 do
5205 {
5206 AutoCaller autoCaller(that);
5207 if (!autoCaller.isOk())
5208 break;
5209
5210 do
5211 {
5212 /* release the caller to let uninit() ever proceed */
5213 autoCaller.release();
5214
5215 /* determine wait timeout adaptively: after updating information
5216 * relevant to the client watcher, check a few times more
5217 * frequently. This ensures good reaction time when the signalling
5218 * has to be done a bit before the actual change for technical
5219 * reasons, and saves CPU cycles when no activities are expected. */
5220 RTMSINTERVAL cMillies;
5221 {
5222 uint8_t uOld, uNew;
5223 do
5224 {
5225 uOld = ASMAtomicUoReadU8(&that->m->updateAdaptCtr);
5226 uNew = uOld ? uOld - 1 : uOld;
5227 } while (!ASMAtomicCmpXchgU8(&that->m->updateAdaptCtr, uNew, uOld));
5228 Assert(uOld <= RT_ELEMENTS(s_updateAdaptTimeouts) - 1);
5229 cMillies = s_updateAdaptTimeouts[uOld];
5230 }
5231
5232 int rc = RTSemEventWait(that->m->updateReq, cMillies);
5233
5234 /*
5235 * Restore the caller before using VirtualBox. If it fails, this
5236 * means VirtualBox is being uninitialized and we must terminate.
5237 */
5238 autoCaller.add();
5239 if (!autoCaller.isOk())
5240 break;
5241
5242 if (RT_SUCCESS(rc) || update || updateSpawned)
5243 {
5244 /* RT_SUCCESS(rc) means an update event is signaled */
5245
5246 // lock the machines list for reading
5247 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5248
5249 if (RT_SUCCESS(rc) || update)
5250 {
5251 /* obtain a new set of opened machines */
5252 machines.clear();
5253
5254 for (MachinesOList::iterator it = that->m->allMachines.begin();
5255 it != that->m->allMachines.end();
5256 ++it)
5257 {
5258 ComObjPtr<SessionMachine> sm;
5259 if ((*it)->isSessionOpenOrClosing(sm))
5260 machines.push_back(sm);
5261 }
5262
5263 cnt = machines.size();
5264 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
5265 }
5266
5267 if (RT_SUCCESS(rc) || updateSpawned)
5268 {
5269 /* obtain a new set of spawned machines */
5270 spawnedMachines.clear();
5271
5272 for (MachinesOList::iterator it = that->m->allMachines.begin();
5273 it != that->m->allMachines.end();
5274 ++it)
5275 {
5276 if ((*it)->isSessionSpawning())
5277 spawnedMachines.push_back(*it);
5278 }
5279
5280 cntSpawned = spawnedMachines.size();
5281 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
5282 }
5283
5284 // machines lock unwinds here
5285 }
5286
5287 update = false;
5288 for (size_t i = 0; i < cnt; ++ i)
5289 update |= (machines[i])->checkForDeath();
5290
5291 updateSpawned = false;
5292 for (size_t i = 0; i < cntSpawned; ++ i)
5293 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
5294
5295 /* reap child processes */
5296 {
5297 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
5298 if (that->m->llProcesses.size())
5299 {
5300 LogFlowFunc(("UPDATE: child process count = %d\n",
5301 that->m->llProcesses.size()));
5302 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
5303 while (it != that->m->llProcesses.end())
5304 {
5305 RTPROCESS pid = *it;
5306 RTPROCSTATUS status;
5307 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
5308 if (vrc == VINF_SUCCESS)
5309 {
5310 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
5311 pid, pid, status.iStatus,
5312 status.enmReason));
5313 it = that->m->llProcesses.erase(it);
5314 }
5315 else
5316 {
5317 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
5318 pid, pid, vrc));
5319 if (vrc != VERR_PROCESS_RUNNING)
5320 {
5321 /* remove the process if it is not already running */
5322 it = that->m->llProcesses.erase(it);
5323 }
5324 else
5325 ++ it;
5326 }
5327 }
5328 }
5329 }
5330 }
5331 while (true);
5332 }
5333 while (0);
5334
5335 /* release sets of machines if any */
5336 machines.clear();
5337 spawnedMachines.clear();
5338
5339#else
5340# error "Port me!"
5341#endif
5342
5343 VirtualBoxBase::uninitializeComForThread();
5344 LogFlowFuncLeave();
5345 return 0;
5346}
5347
5348/**
5349 * Thread function that handles custom events posted using #postEvent().
5350 */
5351// static
5352DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
5353{
5354 LogFlowFuncEnter();
5355
5356 AssertReturn(pvUser, VERR_INVALID_POINTER);
5357
5358 HRESULT hr = com::Initialize();
5359 if (FAILED(hr))
5360 return VERR_COM_UNEXPECTED;
5361
5362 int rc = VINF_SUCCESS;
5363
5364 try
5365 {
5366 /* Create an event queue for the current thread. */
5367 EventQueue *pEventQueue = new EventQueue();
5368 AssertPtr(pEventQueue);
5369
5370 /* Return the queue to the one who created this thread. */
5371 *(static_cast <EventQueue **>(pvUser)) = pEventQueue;
5372
5373 /* signal that we're ready. */
5374 RTThreadUserSignal(thread);
5375
5376 /*
5377 * In case of spurious wakeups causing VERR_TIMEOUTs and/or other return codes
5378 * we must not stop processing events and delete the pEventQueue object. This must
5379 * be done ONLY when we stop this loop via interruptEventQueueProcessing().
5380 * See @bugref{5724}.
5381 */
5382 while (pEventQueue->processEventQueue(RT_INDEFINITE_WAIT) != VERR_INTERRUPTED)
5383 /* nothing */ ;
5384
5385 delete pEventQueue;
5386 }
5387 catch (std::bad_alloc &ba)
5388 {
5389 rc = VERR_NO_MEMORY;
5390 NOREF(ba);
5391 }
5392
5393 com::Shutdown();
5394
5395 LogFlowFuncLeaveRC(rc);
5396 return rc;
5397}
5398
5399
5400////////////////////////////////////////////////////////////////////////////////
5401
5402/**
5403 * Takes the current list of registered callbacks of the managed VirtualBox
5404 * instance, and calls #handleCallback() for every callback item from the
5405 * list, passing the item as an argument.
5406 *
5407 * @note Locks the managed VirtualBox object for reading but leaves the lock
5408 * before iterating over callbacks and calling their methods.
5409 */
5410void *VirtualBox::CallbackEvent::handler()
5411{
5412 if (!mVirtualBox)
5413 return NULL;
5414
5415 AutoCaller autoCaller(mVirtualBox);
5416 if (!autoCaller.isOk())
5417 {
5418 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
5419 autoCaller.state()));
5420 /* We don't need mVirtualBox any more, so release it */
5421 mVirtualBox = NULL;
5422 return NULL;
5423 }
5424
5425 {
5426 VBoxEventDesc evDesc;
5427 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
5428
5429 evDesc.fire(/* don't wait for delivery */0);
5430 }
5431
5432 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
5433 return NULL;
5434}
5435
5436//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
5437//{
5438// return E_NOTIMPL;
5439//}
5440
5441STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
5442{
5443 CheckComArgStrNotEmptyOrNull(aName);
5444 CheckComArgNotNull(aServer);
5445
5446 AutoCaller autoCaller(this);
5447 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5448
5449 ComObjPtr<DHCPServer> dhcpServer;
5450 dhcpServer.createObject();
5451 HRESULT rc = dhcpServer->init(this, aName);
5452 if (FAILED(rc)) return rc;
5453
5454 rc = registerDHCPServer(dhcpServer, true);
5455 if (FAILED(rc)) return rc;
5456
5457 dhcpServer.queryInterfaceTo(aServer);
5458
5459 return rc;
5460}
5461
5462STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
5463{
5464 CheckComArgStrNotEmptyOrNull(aName);
5465 CheckComArgNotNull(aServer);
5466
5467 AutoCaller autoCaller(this);
5468 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5469
5470 HRESULT rc;
5471 Bstr bstr;
5472 ComPtr<DHCPServer> found;
5473
5474 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5475
5476 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
5477 it != m->allDHCPServers.end();
5478 ++it)
5479 {
5480 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
5481 if (FAILED(rc)) return rc;
5482
5483 if (bstr == aName)
5484 {
5485 found = *it;
5486 break;
5487 }
5488 }
5489
5490 if (!found)
5491 return E_INVALIDARG;
5492
5493 return found.queryInterfaceTo(aServer);
5494}
5495
5496STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
5497{
5498 CheckComArgNotNull(aServer);
5499
5500 AutoCaller autoCaller(this);
5501 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5502
5503 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
5504
5505 return rc;
5506}
5507
5508/**
5509 * Remembers the given DHCP server in the settings.
5510 *
5511 * @param aDHCPServer DHCP server object to remember.
5512 * @param aSaveSettings @c true to save settings to disk (default).
5513 *
5514 * When @a aSaveSettings is @c true, this operation may fail because of the
5515 * failed #saveSettings() method it calls. In this case, the dhcp server object
5516 * will not be remembered. It is therefore the responsibility of the caller to
5517 * call this method as the last step of some action that requires registration
5518 * in order to make sure that only fully functional dhcp server objects get
5519 * registered.
5520 *
5521 * @note Locks this object for writing and @a aDHCPServer for reading.
5522 */
5523HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
5524 bool aSaveSettings /*= true*/)
5525{
5526 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
5527
5528 AutoCaller autoCaller(this);
5529 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5530
5531 AutoCaller dhcpServerCaller(aDHCPServer);
5532 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
5533
5534 Bstr name;
5535 HRESULT rc;
5536 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
5537 if (FAILED(rc)) return rc;
5538
5539 ComPtr<IDHCPServer> existing;
5540 rc = FindDHCPServerByNetworkName(name.raw(), existing.asOutParam());
5541 if (SUCCEEDED(rc))
5542 return E_INVALIDARG;
5543
5544 rc = S_OK;
5545
5546 m->allDHCPServers.addChild(aDHCPServer);
5547
5548 if (aSaveSettings)
5549 {
5550 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
5551 rc = saveSettings();
5552 vboxLock.release();
5553
5554 if (FAILED(rc))
5555 unregisterDHCPServer(aDHCPServer, false /* aSaveSettings */);
5556 }
5557
5558 return rc;
5559}
5560
5561/**
5562 * Removes the given DHCP server from the settings.
5563 *
5564 * @param aDHCPServer DHCP server object to remove.
5565 * @param aSaveSettings @c true to save settings to disk (default).
5566 *
5567 * When @a aSaveSettings is @c true, this operation may fail because of the
5568 * failed #saveSettings() method it calls. In this case, the DHCP server
5569 * will NOT be removed from the settingsi when this method returns.
5570 *
5571 * @note Locks this object for writing.
5572 */
5573HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
5574 bool aSaveSettings /*= true*/)
5575{
5576 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
5577
5578 AutoCaller autoCaller(this);
5579 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5580
5581 AutoCaller dhcpServerCaller(aDHCPServer);
5582 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
5583
5584 m->allDHCPServers.removeChild(aDHCPServer);
5585
5586 HRESULT rc = S_OK;
5587
5588 if (aSaveSettings)
5589 {
5590 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
5591 rc = saveSettings();
5592 vboxLock.release();
5593
5594 if (FAILED(rc))
5595 registerDHCPServer(aDHCPServer, false /* aSaveSettings */);
5596 }
5597
5598 return rc;
5599}
5600
5601
5602/**
5603 * NAT Network
5604 */
5605
5606STDMETHODIMP VirtualBox::CreateNATNetwork(IN_BSTR aName, INATNetwork ** aNatNetwork)
5607{
5608#ifdef VBOX_WITH_NAT_SERVICE
5609 CheckComArgStrNotEmptyOrNull(aName);
5610 CheckComArgNotNull(aNatNetwork);
5611
5612 AutoCaller autoCaller(this);
5613 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5614
5615 ComObjPtr<NATNetwork> natNetwork;
5616 natNetwork.createObject();
5617 HRESULT rc = natNetwork->init(this, aName);
5618 if (FAILED(rc)) return rc;
5619
5620 rc = registerNATNetwork(natNetwork, true);
5621 if (FAILED(rc)) return rc;
5622
5623 natNetwork.queryInterfaceTo(aNatNetwork);
5624
5625 fireNATNetworkCreationDeletionEvent(m->pEventSource, aName, TRUE);
5626 return rc;
5627#else
5628 NOREF(aName);
5629 NOREF(aNatNetwork);
5630 return E_NOTIMPL;
5631#endif
5632}
5633
5634STDMETHODIMP VirtualBox::FindNATNetworkByName(IN_BSTR aName, INATNetwork ** aNetwork)
5635{
5636#ifdef VBOX_WITH_NAT_SERVICE
5637 CheckComArgStrNotEmptyOrNull(aName);
5638 CheckComArgNotNull(aNetwork);
5639
5640 AutoCaller autoCaller(this);
5641 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5642
5643 HRESULT rc;
5644 Bstr bstr;
5645 ComPtr<NATNetwork> found;
5646
5647 AutoReadLock alock(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5648
5649 for (NATNetworksOList::const_iterator it = m->allNATNetworks.begin();
5650 it != m->allNATNetworks.end();
5651 ++it)
5652 {
5653 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
5654 if (FAILED(rc)) return rc;
5655
5656 if (bstr == aName)
5657 {
5658 found = *it;
5659 break;
5660 }
5661 }
5662
5663 if (!found)
5664 return E_INVALIDARG;
5665
5666 return found.queryInterfaceTo(aNetwork);
5667#else
5668 NOREF(aName);
5669 NOREF(aNetwork);
5670 return E_NOTIMPL;
5671#endif
5672}
5673
5674STDMETHODIMP VirtualBox::RemoveNATNetwork(INATNetwork * aNetwork)
5675{
5676#ifdef VBOX_WITH_NAT_SERVICE
5677 CheckComArgNotNull(aNetwork);
5678
5679 AutoCaller autoCaller(this);
5680 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5681 Bstr name;
5682 HRESULT rc;
5683 NATNetwork *network = static_cast<NATNetwork *>(aNetwork);
5684 rc = network->COMGETTER(NetworkName)(name.asOutParam());
5685 rc = unregisterNATNetwork(network, true);
5686 fireNATNetworkCreationDeletionEvent(m->pEventSource, name.raw(), FALSE);
5687 return rc;
5688#else
5689 NOREF(aNetwork);
5690 return E_NOTIMPL;
5691#endif
5692
5693}
5694/**
5695 * Remembers the given NAT network in the settings.
5696 *
5697 * @param aNATNetwork NAT Network object to remember.
5698 * @param aSaveSettings @c true to save settings to disk (default).
5699 *
5700 *
5701 * @note Locks this object for writing and @a aNATNetwork for reading.
5702 */
5703HRESULT VirtualBox::registerNATNetwork(NATNetwork *aNATNetwork,
5704 bool aSaveSettings /*= true*/)
5705{
5706#ifdef VBOX_WITH_NAT_SERVICE
5707 AssertReturn(aNATNetwork != NULL, E_INVALIDARG);
5708
5709 AutoCaller autoCaller(this);
5710 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5711
5712 AutoCaller natNetworkCaller(aNATNetwork);
5713 AssertComRCReturn(natNetworkCaller.rc(), natNetworkCaller.rc());
5714
5715 Bstr name;
5716 HRESULT rc;
5717 rc = aNATNetwork->COMGETTER(NetworkName)(name.asOutParam());
5718 if (FAILED(rc)) return rc;
5719
5720 ComPtr<INATNetwork> existing;
5721 rc = FindNATNetworkByName(name.raw(), existing.asOutParam());
5722 if (SUCCEEDED(rc))
5723 return E_INVALIDARG;
5724
5725 rc = S_OK;
5726
5727 m->allNATNetworks.addChild(aNATNetwork);
5728
5729 if (aSaveSettings)
5730 {
5731 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
5732 rc = saveSettings();
5733 vboxLock.release();
5734
5735 if (FAILED(rc))
5736 unregisterNATNetwork(aNATNetwork, false /* aSaveSettings */);
5737 }
5738
5739 return rc;
5740#else
5741 NOREF(aNATNetwork);
5742 NOREF(aSaveSettings);
5743 /* No panic please (silently ignore) */
5744 return S_OK;
5745#endif
5746}
5747
5748/**
5749 * Removes the given NAT network from the settings.
5750 *
5751 * @param aNATNetwork NAT network object to remove.
5752 * @param aSaveSettings @c true to save settings to disk (default).
5753 *
5754 * When @a aSaveSettings is @c true, this operation may fail because of the
5755 * failed #saveSettings() method it calls. In this case, the DHCP server
5756 * will NOT be removed from the settingsi when this method returns.
5757 *
5758 * @note Locks this object for writing.
5759 */
5760HRESULT VirtualBox::unregisterNATNetwork(NATNetwork *aNATNetwork,
5761 bool aSaveSettings /*= true*/)
5762{
5763#ifdef VBOX_WITH_NAT_SERVICE
5764 AssertReturn(aNATNetwork != NULL, E_INVALIDARG);
5765
5766 AutoCaller autoCaller(this);
5767 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5768
5769 AutoCaller natNetworkCaller(aNATNetwork);
5770 AssertComRCReturn(natNetworkCaller.rc(), natNetworkCaller.rc());
5771
5772 m->allNATNetworks.removeChild(aNATNetwork);
5773
5774 HRESULT rc = S_OK;
5775
5776 if (aSaveSettings)
5777 {
5778 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
5779 rc = saveSettings();
5780 vboxLock.release();
5781
5782 if (FAILED(rc))
5783 registerNATNetwork(aNATNetwork, false /* aSaveSettings */);
5784 }
5785
5786 return rc;
5787#else
5788 NOREF(aNATNetwork);
5789 NOREF(aSaveSettings);
5790 return E_NOTIMPL;
5791#endif
5792}
5793/* 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