VirtualBox

source: vbox/trunk/src/VBox/Main/MachineImpl.cpp@ 24457

Last change on this file since 24457 was 24364, checked in by vboxsync, 15 years ago

Main: Fixed XPCOM warnings caused by Machine::GetCpuIdLeaf not setting error info. Use UINT32_MAX instead of -1, avoids gcc warnings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 313.6 KB
Line 
1/* $Id: MachineImpl.cpp 24364 2009-11-05 02:18:31Z vboxsync $ */
2
3/** @file
4 * Implementation of IMachine in VBoxSVC.
5 */
6
7/*
8 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23/* Make sure all the stdint.h macros are included - must come first! */
24#ifndef __STDC_LIMIT_MACROS
25# define __STDC_LIMIT_MACROS
26#endif
27#ifndef __STDC_CONSTANT_MACROS
28# define __STDC_CONSTANT_MACROS
29#endif
30
31#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
32# include <errno.h>
33# include <sys/types.h>
34# include <sys/stat.h>
35# include <sys/ipc.h>
36# include <sys/sem.h>
37#endif
38
39#include "VirtualBoxImpl.h"
40#include "MachineImpl.h"
41#include "ProgressImpl.h"
42#include "MediumAttachmentImpl.h"
43#include "MediumImpl.h"
44#include "USBControllerImpl.h"
45#include "HostImpl.h"
46#include "SharedFolderImpl.h"
47#include "GuestOSTypeImpl.h"
48#include "VirtualBoxErrorInfoImpl.h"
49#include "GuestImpl.h"
50#include "StorageControllerImpl.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "Logging.h"
57#include "Performance.h"
58
59#include <stdio.h>
60#include <stdlib.h>
61
62#include <iprt/path.h>
63#include <iprt/dir.h>
64#include <iprt/asm.h>
65#include <iprt/process.h>
66#include <iprt/cpputils.h>
67#include <iprt/env.h>
68#include <iprt/string.h>
69
70#include <VBox/com/array.h>
71
72#include <VBox/err.h>
73#include <VBox/param.h>
74#include <VBox/settings.h>
75
76#ifdef VBOX_WITH_GUEST_PROPS
77# include <VBox/HostServices/GuestPropertySvc.h>
78# include <VBox/com/array.h>
79#endif
80
81#include <algorithm>
82
83#include <typeinfo>
84
85#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
86#define HOSTSUFF_EXE ".exe"
87#else /* !RT_OS_WINDOWS */
88#define HOSTSUFF_EXE ""
89#endif /* !RT_OS_WINDOWS */
90
91// defines / prototypes
92/////////////////////////////////////////////////////////////////////////////
93
94/////////////////////////////////////////////////////////////////////////////
95// Machine::Data structure
96/////////////////////////////////////////////////////////////////////////////
97
98Machine::Data::Data()
99{
100 mRegistered = FALSE;
101 mAccessible = FALSE;
102 /* mUuid is initialized in Machine::init() */
103
104 mMachineState = MachineState_PoweredOff;
105 RTTimeNow(&mLastStateChange);
106
107 mMachineStateDeps = 0;
108 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
109 mMachineStateChangePending = 0;
110
111 mCurrentStateModified = TRUE;
112 mHandleCfgFile = NIL_RTFILE;
113
114 mSession.mPid = NIL_RTPROCESS;
115 mSession.mState = SessionState_Closed;
116}
117
118Machine::Data::~Data()
119{
120 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
121 {
122 RTSemEventMultiDestroy(mMachineStateDepsSem);
123 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
124 }
125}
126
127/////////////////////////////////////////////////////////////////////////////
128// Machine::UserData structure
129/////////////////////////////////////////////////////////////////////////////
130
131Machine::UserData::UserData()
132{
133 /* default values for a newly created machine */
134
135 mNameSync = TRUE;
136 mTeleporterEnabled = FALSE;
137 mTeleporterPort = 0;
138
139 /* mName, mOSTypeId, mSnapshotFolder, mSnapshotFolderFull are initialized in
140 * Machine::init() */
141}
142
143Machine::UserData::~UserData()
144{
145}
146
147/////////////////////////////////////////////////////////////////////////////
148// Machine::HWData structure
149/////////////////////////////////////////////////////////////////////////////
150
151Machine::HWData::HWData()
152{
153 /* default values for a newly created machine */
154 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
155 mMemorySize = 128;
156 mCPUCount = 1;
157 mMemoryBalloonSize = 0;
158 mStatisticsUpdateInterval = 0;
159 mVRAMSize = 8;
160 mAccelerate3DEnabled = false;
161 mAccelerate2DVideoEnabled = false;
162 mMonitorCount = 1;
163 mHWVirtExEnabled = true;
164 mHWVirtExNestedPagingEnabled = false;
165 mHWVirtExVPIDEnabled = false;
166 mHWVirtExExclusive = true;
167 mPAEEnabled = false;
168 mSyntheticCpu = false;
169 mPropertyServiceActive = false;
170
171 /* default boot order: floppy - DVD - HDD */
172 mBootOrder [0] = DeviceType_Floppy;
173 mBootOrder [1] = DeviceType_DVD;
174 mBootOrder [2] = DeviceType_HardDisk;
175 for (size_t i = 3; i < RT_ELEMENTS (mBootOrder); ++i)
176 mBootOrder [i] = DeviceType_Null;
177
178 mClipboardMode = ClipboardMode_Bidirectional;
179 mGuestPropertyNotificationPatterns = "";
180
181 mFirmwareType = FirmwareType_BIOS;
182}
183
184Machine::HWData::~HWData()
185{
186}
187
188bool Machine::HWData::operator==(const HWData &that) const
189{
190 if (this == &that)
191 return true;
192
193 if (mHWVersion != that.mHWVersion ||
194 mHardwareUUID != that.mHardwareUUID ||
195 mMemorySize != that.mMemorySize ||
196 mMemoryBalloonSize != that.mMemoryBalloonSize ||
197 mStatisticsUpdateInterval != that.mStatisticsUpdateInterval ||
198 mVRAMSize != that.mVRAMSize ||
199 mFirmwareType != that.mFirmwareType ||
200 mAccelerate3DEnabled != that.mAccelerate3DEnabled ||
201 mAccelerate2DVideoEnabled != that.mAccelerate2DVideoEnabled ||
202 mMonitorCount != that.mMonitorCount ||
203 mHWVirtExEnabled != that.mHWVirtExEnabled ||
204 mHWVirtExNestedPagingEnabled != that.mHWVirtExNestedPagingEnabled ||
205 mHWVirtExVPIDEnabled != that.mHWVirtExVPIDEnabled ||
206 mHWVirtExExclusive != that.mHWVirtExExclusive ||
207 mPAEEnabled != that.mPAEEnabled ||
208 mSyntheticCpu != that.mSyntheticCpu ||
209 mCPUCount != that.mCPUCount ||
210 mClipboardMode != that.mClipboardMode)
211 return false;
212
213 for (size_t i = 0; i < RT_ELEMENTS (mBootOrder); ++i)
214 if (mBootOrder [i] != that.mBootOrder [i])
215 return false;
216
217 if (mSharedFolders.size() != that.mSharedFolders.size())
218 return false;
219
220 if (mSharedFolders.size() == 0)
221 return true;
222
223 /* Make copies to speed up comparison */
224 SharedFolderList folders = mSharedFolders;
225 SharedFolderList thatFolders = that.mSharedFolders;
226
227 SharedFolderList::iterator it = folders.begin();
228 while (it != folders.end())
229 {
230 bool found = false;
231 SharedFolderList::iterator thatIt = thatFolders.begin();
232 while (thatIt != thatFolders.end())
233 {
234 if ( (*it)->name() == (*thatIt)->name()
235 && RTPathCompare(Utf8Str((*it)->hostPath()).c_str(),
236 Utf8Str((*thatIt)->hostPath()).c_str()
237 ) == 0)
238 {
239 thatFolders.erase (thatIt);
240 found = true;
241 break;
242 }
243 else
244 ++thatIt;
245 }
246 if (found)
247 it = folders.erase (it);
248 else
249 return false;
250 }
251
252 Assert (folders.size() == 0 && thatFolders.size() == 0);
253
254 return true;
255}
256
257/////////////////////////////////////////////////////////////////////////////
258// Machine::HDData structure
259/////////////////////////////////////////////////////////////////////////////
260
261Machine::MediaData::MediaData()
262{
263}
264
265Machine::MediaData::~MediaData()
266{
267}
268
269bool Machine::MediaData::operator== (const MediaData &that) const
270{
271 if (this == &that)
272 return true;
273
274 if (mAttachments.size() != that.mAttachments.size())
275 return false;
276
277 if (mAttachments.size() == 0)
278 return true;
279
280 /* Make copies to speed up comparison */
281 AttachmentList atts = mAttachments;
282 AttachmentList thatAtts = that.mAttachments;
283
284 AttachmentList::iterator it = atts.begin();
285 while (it != atts.end())
286 {
287 bool found = false;
288 AttachmentList::iterator thatIt = thatAtts.begin();
289 while (thatIt != thatAtts.end())
290 {
291 if ((*it)->matches((*thatIt)->controllerName(),
292 (*thatIt)->port(),
293 (*thatIt)->device()) &&
294 (*it)->passthrough() == (*thatIt)->passthrough() &&
295 (*it)->medium().equalsTo ((*thatIt)->medium()))
296 {
297 thatAtts.erase (thatIt);
298 found = true;
299 break;
300 }
301 else
302 ++thatIt;
303 }
304 if (found)
305 it = atts.erase (it);
306 else
307 return false;
308 }
309
310 Assert (atts.size() == 0 && thatAtts.size() == 0);
311
312 return true;
313}
314
315/////////////////////////////////////////////////////////////////////////////
316// Machine class
317/////////////////////////////////////////////////////////////////////////////
318
319// constructor / destructor
320/////////////////////////////////////////////////////////////////////////////
321
322Machine::Machine() : mType (IsMachine) {}
323
324Machine::~Machine() {}
325
326HRESULT Machine::FinalConstruct()
327{
328 LogFlowThisFunc(("\n"));
329 return S_OK;
330}
331
332void Machine::FinalRelease()
333{
334 LogFlowThisFunc(("\n"));
335 uninit();
336}
337
338/**
339 * Initializes the instance.
340 *
341 * @param aParent Associated parent object
342 * @param aConfigFile Local file system path to the VM settings file (can
343 * be relative to the VirtualBox config directory).
344 * @param aMode Init_New, Init_Existing or Init_Registered
345 * @param aName name for the machine when aMode is Init_New
346 * (ignored otherwise)
347 * @param aOsType OS Type of this machine
348 * @param aNameSync |TRUE| to automatically sync settings dir and file
349 * name with the machine name. |FALSE| is used for legacy
350 * machines where the file name is specified by the
351 * user and should never change. Used only in Init_New
352 * mode (ignored otherwise).
353 * @param aId UUID of the machine. Required for aMode==Init_Registered
354 * and optional for aMode==Init_New. Used for consistency
355 * check when aMode is Init_Registered; must match UUID
356 * stored in the settings file. Used for predefining the
357 * UUID of a VM when aMode is Init_New.
358 *
359 * @return Success indicator. if not S_OK, the machine object is invalid
360 */
361HRESULT Machine::init(VirtualBox *aParent,
362 const Utf8Str &strConfigFile,
363 InitMode aMode,
364 CBSTR aName /* = NULL */,
365 GuestOSType *aOsType /* = NULL */,
366 BOOL aNameSync /* = TRUE */,
367 const Guid *aId /* = NULL */)
368{
369 LogFlowThisFuncEnter();
370 LogFlowThisFunc (("aConfigFile='%s', aMode=%d\n", strConfigFile.raw(), aMode));
371
372 AssertReturn (aParent, E_INVALIDARG);
373 AssertReturn (!strConfigFile.isEmpty(), E_INVALIDARG);
374 AssertReturn(aMode != Init_New || (aName != NULL && *aName != '\0'),
375 E_INVALIDARG);
376 AssertReturn(aMode != Init_Registered || aId != NULL, E_FAIL);
377
378 /* Enclose the state transition NotReady->InInit->Ready */
379 AutoInitSpan autoInitSpan(this);
380 AssertReturn(autoInitSpan.isOk(), E_FAIL);
381
382 HRESULT rc = S_OK;
383
384 /* share the parent weakly */
385 unconst(mParent) = aParent;
386
387 /* register with parent early, since uninit() will unconditionally
388 * unregister on failure */
389 mParent->addDependentChild (this);
390
391 /* allocate the essential machine data structure (the rest will be
392 * allocated later by initDataAndChildObjects() */
393 mData.allocate();
394
395 mData->m_pMachineConfigFile = NULL;
396
397 /* memorize the config file name (as provided) */
398 mData->m_strConfigFile = strConfigFile;
399
400 /* get the full file name */
401 int vrc = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
402 if (RT_FAILURE(vrc))
403 return setError(VBOX_E_FILE_ERROR,
404 tr("Invalid machine settings file name '%s' (%Rrc)"),
405 strConfigFile.raw(),
406 vrc);
407
408 if (aMode == Init_Registered)
409 {
410 mData->mRegistered = TRUE;
411
412 /* store the supplied UUID (will be used to check for UUID consistency
413 * in loadSettings() */
414 unconst(mData->mUuid) = *aId;
415
416 // now load the settings from XML:
417 rc = registeredInit();
418 }
419 else
420 {
421 if (aMode == Init_Import)
422 {
423 // we're reading the settings file below
424 }
425 else if (aMode == Init_New)
426 {
427 /* check for the file existence */
428 RTFILE f = NIL_RTFILE;
429 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
430 if ( RT_SUCCESS(vrc)
431 || vrc == VERR_SHARING_VIOLATION
432 )
433 {
434 rc = setError(VBOX_E_FILE_ERROR,
435 tr("Machine settings file '%s' already exists"),
436 mData->m_strConfigFileFull.raw());
437 if (RT_SUCCESS(vrc))
438 RTFileClose(f);
439 }
440 else
441 {
442 if ( vrc != VERR_FILE_NOT_FOUND
443 && vrc != VERR_PATH_NOT_FOUND
444 )
445 rc = setError(VBOX_E_FILE_ERROR,
446 tr("Invalid machine settings file name '%s' (%Rrc)"),
447 mData->m_strConfigFileFull.raw(),
448 vrc);
449 }
450
451 // create an empty machine config
452 mData->m_pMachineConfigFile = new settings::MachineConfigFile(NULL);
453 }
454 else
455 AssertFailed();
456
457 if (SUCCEEDED(rc))
458 rc = initDataAndChildObjects();
459
460 if (SUCCEEDED(rc))
461 {
462 /* set to true now to cause uninit() to call
463 * uninitDataAndChildObjects() on failure */
464 mData->mAccessible = TRUE;
465
466 if (aMode != Init_New)
467 {
468 rc = loadSettings(false /* aRegistered */);
469 }
470 else
471 {
472 /* create the machine UUID */
473 if (aId)
474 unconst(mData->mUuid) = *aId;
475 else
476 unconst(mData->mUuid).create();
477
478 /* memorize the provided new machine's name */
479 mUserData->mName = aName;
480 mUserData->mNameSync = aNameSync;
481
482 /* initialize the default snapshots folder
483 * (note: depends on the name value set above!) */
484 rc = COMSETTER(SnapshotFolder)(NULL);
485 AssertComRC(rc);
486
487 if (aOsType)
488 {
489 /* Store OS type */
490 mUserData->mOSTypeId = aOsType->id();
491
492 /* Apply BIOS defaults */
493 mBIOSSettings->applyDefaults (aOsType);
494
495 /* Apply network adapters defaults */
496 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); ++slot)
497 mNetworkAdapters [slot]->applyDefaults (aOsType);
498
499 /* Apply serial port defaults */
500 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); ++slot)
501 mSerialPorts [slot]->applyDefaults (aOsType);
502 }
503 }
504
505 /* commit all changes made during the initialization */
506 if (SUCCEEDED(rc))
507 commit();
508 }
509 }
510
511 /* Confirm a successful initialization when it's the case */
512 if (SUCCEEDED(rc))
513 {
514 if (mData->mAccessible)
515 autoInitSpan.setSucceeded();
516 else
517 autoInitSpan.setLimited();
518 }
519
520 LogFlowThisFunc(("mName='%ls', mRegistered=%RTbool, mAccessible=%RTbool "
521 "rc=%08X\n",
522 !!mUserData ? mUserData->mName.raw() : NULL,
523 mData->mRegistered, mData->mAccessible, rc));
524
525 LogFlowThisFuncLeave();
526
527 return rc;
528}
529
530/**
531 * Initializes the registered machine by loading the settings file.
532 * This method is separated from #init() in order to make it possible to
533 * retry the operation after VirtualBox startup instead of refusing to
534 * startup the whole VirtualBox server in case if the settings file of some
535 * registered VM is invalid or inaccessible.
536 *
537 * @note Must be always called from this object's write lock
538 * (unless called from #init() that doesn't need any locking).
539 * @note Locks the mUSBController method for writing.
540 * @note Subclasses must not call this method.
541 */
542HRESULT Machine::registeredInit()
543{
544 AssertReturn(mType == IsMachine, E_FAIL);
545 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
546 AssertReturn(!mData->mAccessible, E_FAIL);
547
548 HRESULT rc = initDataAndChildObjects();
549
550 if (SUCCEEDED(rc))
551 {
552 /* Temporarily reset the registered flag in order to let setters
553 * potentially called from loadSettings() succeed (isMutable() used in
554 * all setters will return FALSE for a Machine instance if mRegistered
555 * is TRUE). */
556 mData->mRegistered = FALSE;
557
558 rc = loadSettings(true /* aRegistered */);
559
560 /* Restore the registered flag (even on failure) */
561 mData->mRegistered = TRUE;
562 }
563
564 if (SUCCEEDED(rc))
565 {
566 /* Set mAccessible to TRUE only if we successfully locked and loaded
567 * the settings file */
568 mData->mAccessible = TRUE;
569
570 /* commit all changes made during loading the settings file */
571 commit();
572 }
573 else
574 {
575 /* If the machine is registered, then, instead of returning a
576 * failure, we mark it as inaccessible and set the result to
577 * success to give it a try later */
578
579 /* fetch the current error info */
580 mData->mAccessError = com::ErrorInfo();
581 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
582 mData->mUuid.raw(),
583 mData->mAccessError.getText().raw()));
584
585 /* rollback all changes */
586 rollback (false /* aNotify */);
587
588 /* uninitialize the common part to make sure all data is reset to
589 * default (null) values */
590 uninitDataAndChildObjects();
591
592 rc = S_OK;
593 }
594
595 return rc;
596}
597
598/**
599 * Uninitializes the instance.
600 * Called either from FinalRelease() or by the parent when it gets destroyed.
601 *
602 * @note The caller of this method must make sure that this object
603 * a) doesn't have active callers on the current thread and b) is not locked
604 * by the current thread; otherwise uninit() will hang either a) due to
605 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
606 * a dead-lock caused by this thread waiting for all callers on the other
607 * threads are done but preventing them from doing so by holding a lock.
608 */
609void Machine::uninit()
610{
611 LogFlowThisFuncEnter();
612
613 Assert (!isWriteLockOnCurrentThread());
614
615 /* Enclose the state transition Ready->InUninit->NotReady */
616 AutoUninitSpan autoUninitSpan(this);
617 if (autoUninitSpan.uninitDone())
618 return;
619
620 Assert (mType == IsMachine);
621 Assert (!!mData);
622
623 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
624 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
625
626 /* Enter this object lock because there may be a SessionMachine instance
627 * somewhere around, that shares our data and lock but doesn't use our
628 * addCaller()/removeCaller(), and it may be also accessing the same data
629 * members. mParent lock is necessary as well because of
630 * SessionMachine::uninit(), etc.
631 */
632 AutoMultiWriteLock2 alock (mParent, this);
633
634 if (!mData->mSession.mMachine.isNull())
635 {
636 /* Theoretically, this can only happen if the VirtualBox server has been
637 * terminated while there were clients running that owned open direct
638 * sessions. Since in this case we are definitely called by
639 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
640 * won't happen on the client watcher thread (because it does
641 * VirtualBox::addCaller() for the duration of the
642 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
643 * cannot happen until the VirtualBox caller is released). This is
644 * important, because SessionMachine::uninit() cannot correctly operate
645 * after we return from this method (it expects the Machine instance is
646 * still valid). We'll call it ourselves below.
647 */
648 LogWarningThisFunc(("Session machine is not NULL (%p), "
649 "the direct session is still open!\n",
650 (SessionMachine *) mData->mSession.mMachine));
651
652 if (Global::IsOnlineOrTransient (mData->mMachineState))
653 {
654 LogWarningThisFunc(("Setting state to Aborted!\n"));
655 /* set machine state using SessionMachine reimplementation */
656 static_cast <Machine *> (mData->mSession.mMachine)
657 ->setMachineState (MachineState_Aborted);
658 }
659
660 /*
661 * Uninitialize SessionMachine using public uninit() to indicate
662 * an unexpected uninitialization.
663 */
664 mData->mSession.mMachine->uninit();
665 /* SessionMachine::uninit() must set mSession.mMachine to null */
666 Assert (mData->mSession.mMachine.isNull());
667 }
668
669 /* the lock is no more necessary (SessionMachine is uninitialized) */
670 alock.leave();
671
672 if (isModified())
673 {
674 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
675 rollback (false /* aNotify */);
676 }
677
678 if (mData->mAccessible)
679 uninitDataAndChildObjects();
680
681 /* free the essential data structure last */
682 mData.free();
683
684 mParent->removeDependentChild (this);
685
686 LogFlowThisFuncLeave();
687}
688
689// IMachine properties
690/////////////////////////////////////////////////////////////////////////////
691
692STDMETHODIMP Machine::COMGETTER(Parent) (IVirtualBox **aParent)
693{
694 CheckComArgOutPointerValid(aParent);
695
696 AutoLimitedCaller autoCaller(this);
697 CheckComRCReturnRC(autoCaller.rc());
698
699 /* mParent is constant during life time, no need to lock */
700 mParent.queryInterfaceTo(aParent);
701
702 return S_OK;
703}
704
705STDMETHODIMP Machine::COMGETTER(Accessible) (BOOL *aAccessible)
706{
707 CheckComArgOutPointerValid(aAccessible);
708
709 AutoLimitedCaller autoCaller(this);
710 CheckComRCReturnRC(autoCaller.rc());
711
712 LogFlowThisFunc(("ENTER\n"));
713
714 AutoWriteLock alock(this);
715
716 HRESULT rc = S_OK;
717
718 if (!mData->mAccessible)
719 {
720 /* try to initialize the VM once more if not accessible */
721
722 AutoReinitSpan autoReinitSpan(this);
723 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
724
725#ifdef DEBUG
726 LogFlowThisFunc(("Dumping media backreferences\n"));
727 mParent->dumpAllBackRefs();
728#endif
729
730 if (mData->m_pMachineConfigFile)
731 {
732 // @todo why are we parsing this several times?
733 // this is hugely inefficient
734 delete mData->m_pMachineConfigFile;
735 mData->m_pMachineConfigFile = NULL;
736 }
737
738 rc = registeredInit();
739
740 if (SUCCEEDED(rc) && mData->mAccessible)
741 {
742 autoReinitSpan.setSucceeded();
743
744 /* make sure interesting parties will notice the accessibility
745 * state change */
746 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
747 mParent->onMachineDataChange(mData->mUuid);
748 }
749 }
750
751 if (SUCCEEDED(rc))
752 *aAccessible = mData->mAccessible;
753
754 LogFlowThisFuncLeave();
755
756 return rc;
757}
758
759STDMETHODIMP Machine::COMGETTER(AccessError) (IVirtualBoxErrorInfo **aAccessError)
760{
761 CheckComArgOutPointerValid(aAccessError);
762
763 AutoLimitedCaller autoCaller(this);
764 CheckComRCReturnRC(autoCaller.rc());
765
766 AutoReadLock alock(this);
767
768 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
769 {
770 /* return shortly */
771 aAccessError = NULL;
772 return S_OK;
773 }
774
775 HRESULT rc = S_OK;
776
777 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
778 rc = errorInfo.createObject();
779 if (SUCCEEDED(rc))
780 {
781 errorInfo->init (mData->mAccessError.getResultCode(),
782 mData->mAccessError.getInterfaceID(),
783 mData->mAccessError.getComponent(),
784 mData->mAccessError.getText());
785 rc = errorInfo.queryInterfaceTo(aAccessError);
786 }
787
788 return rc;
789}
790
791STDMETHODIMP Machine::COMGETTER(Name) (BSTR *aName)
792{
793 CheckComArgOutPointerValid(aName);
794
795 AutoCaller autoCaller(this);
796 CheckComRCReturnRC(autoCaller.rc());
797
798 AutoReadLock alock(this);
799
800 mUserData->mName.cloneTo(aName);
801
802 return S_OK;
803}
804
805STDMETHODIMP Machine::COMSETTER(Name) (IN_BSTR aName)
806{
807 CheckComArgNotNull (aName);
808
809 if (!*aName)
810 return setError(E_INVALIDARG,
811 tr("Machine name cannot be empty"));
812
813 AutoCaller autoCaller(this);
814 CheckComRCReturnRC(autoCaller.rc());
815
816 AutoWriteLock alock(this);
817
818 HRESULT rc = checkStateDependency(MutableStateDep);
819 CheckComRCReturnRC(rc);
820
821 mUserData.backup();
822 mUserData->mName = aName;
823
824 return S_OK;
825}
826
827STDMETHODIMP Machine::COMGETTER(Description) (BSTR *aDescription)
828{
829 CheckComArgOutPointerValid(aDescription);
830
831 AutoCaller autoCaller(this);
832 CheckComRCReturnRC(autoCaller.rc());
833
834 AutoReadLock alock(this);
835
836 mUserData->mDescription.cloneTo(aDescription);
837
838 return S_OK;
839}
840
841STDMETHODIMP Machine::COMSETTER(Description) (IN_BSTR aDescription)
842{
843 AutoCaller autoCaller(this);
844 CheckComRCReturnRC(autoCaller.rc());
845
846 AutoWriteLock alock(this);
847
848 HRESULT rc = checkStateDependency(MutableStateDep);
849 CheckComRCReturnRC(rc);
850
851 mUserData.backup();
852 mUserData->mDescription = aDescription;
853
854 return S_OK;
855}
856
857STDMETHODIMP Machine::COMGETTER(Id) (BSTR *aId)
858{
859 CheckComArgOutPointerValid(aId);
860
861 AutoLimitedCaller autoCaller(this);
862 CheckComRCReturnRC(autoCaller.rc());
863
864 AutoReadLock alock(this);
865
866 mData->mUuid.toUtf16().cloneTo(aId);
867
868 return S_OK;
869}
870
871STDMETHODIMP Machine::COMGETTER(OSTypeId) (BSTR *aOSTypeId)
872{
873 CheckComArgOutPointerValid(aOSTypeId);
874
875 AutoCaller autoCaller(this);
876 CheckComRCReturnRC(autoCaller.rc());
877
878 AutoReadLock alock(this);
879
880 mUserData->mOSTypeId.cloneTo(aOSTypeId);
881
882 return S_OK;
883}
884
885STDMETHODIMP Machine::COMSETTER(OSTypeId) (IN_BSTR aOSTypeId)
886{
887 CheckComArgNotNull (aOSTypeId);
888
889 AutoCaller autoCaller(this);
890 CheckComRCReturnRC(autoCaller.rc());
891
892 /* look up the object by Id to check it is valid */
893 ComPtr<IGuestOSType> guestOSType;
894 HRESULT rc = mParent->GetGuestOSType (aOSTypeId,
895 guestOSType.asOutParam());
896 CheckComRCReturnRC(rc);
897
898 /* when setting, always use the "etalon" value for consistency -- lookup
899 * by ID is case-insensitive and the input value may have different case */
900 Bstr osTypeId;
901 rc = guestOSType->COMGETTER(Id) (osTypeId.asOutParam());
902 CheckComRCReturnRC(rc);
903
904 AutoWriteLock alock(this);
905
906 rc = checkStateDependency(MutableStateDep);
907 CheckComRCReturnRC(rc);
908
909 mUserData.backup();
910 mUserData->mOSTypeId = osTypeId;
911
912 return S_OK;
913}
914
915
916STDMETHODIMP Machine::COMGETTER(FirmwareType) (FirmwareType_T *aFirmwareType)
917{
918 CheckComArgOutPointerValid(aFirmwareType);
919
920 AutoCaller autoCaller(this);
921 CheckComRCReturnRC(autoCaller.rc());
922
923 AutoReadLock alock(this);
924
925 *aFirmwareType = mHWData->mFirmwareType;
926
927 return S_OK;
928}
929
930STDMETHODIMP Machine::COMSETTER(FirmwareType) (FirmwareType_T aFirmwareType)
931{
932 AutoCaller autoCaller(this);
933 CheckComRCReturnRC(autoCaller.rc());
934 AutoWriteLock alock(this);
935
936 int rc = checkStateDependency(MutableStateDep);
937 CheckComRCReturnRC(rc);
938
939 mHWData.backup();
940 mHWData->mFirmwareType = aFirmwareType;
941
942 return S_OK;
943}
944
945STDMETHODIMP Machine::COMGETTER(HardwareVersion) (BSTR *aHWVersion)
946{
947 if (!aHWVersion)
948 return E_POINTER;
949
950 AutoCaller autoCaller(this);
951 CheckComRCReturnRC(autoCaller.rc());
952
953 AutoReadLock alock(this);
954
955 mHWData->mHWVersion.cloneTo(aHWVersion);
956
957 return S_OK;
958}
959
960STDMETHODIMP Machine::COMSETTER(HardwareVersion) (IN_BSTR aHWVersion)
961{
962 /* check known version */
963 Utf8Str hwVersion = aHWVersion;
964 if ( hwVersion.compare ("1") != 0
965 && hwVersion.compare ("2") != 0)
966 return setError(E_INVALIDARG,
967 tr("Invalid hardware version: %ls\n"), aHWVersion);
968
969 AutoCaller autoCaller(this);
970 CheckComRCReturnRC(autoCaller.rc());
971
972 AutoWriteLock alock(this);
973
974 HRESULT rc = checkStateDependency(MutableStateDep);
975 CheckComRCReturnRC(rc);
976
977 mHWData.backup();
978 mHWData->mHWVersion = hwVersion;
979
980 return S_OK;
981}
982
983STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
984{
985 CheckComArgOutPointerValid(aUUID);
986
987 AutoCaller autoCaller(this);
988 CheckComRCReturnRC(autoCaller.rc());
989
990 AutoReadLock alock(this);
991
992 if (!mHWData->mHardwareUUID.isEmpty())
993 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
994 else
995 mData->mUuid.toUtf16().cloneTo(aUUID);
996
997 return S_OK;
998}
999
1000STDMETHODIMP Machine::COMSETTER(HardwareUUID) (IN_BSTR aUUID)
1001{
1002 Guid hardwareUUID(aUUID);
1003 if (hardwareUUID.isEmpty())
1004 return E_INVALIDARG;
1005
1006 AutoCaller autoCaller(this);
1007 CheckComRCReturnRC(autoCaller.rc());
1008
1009 AutoWriteLock alock(this);
1010
1011 HRESULT rc = checkStateDependency(MutableStateDep);
1012 CheckComRCReturnRC(rc);
1013
1014 mHWData.backup();
1015 if (hardwareUUID == mData->mUuid)
1016 mHWData->mHardwareUUID.clear();
1017 else
1018 mHWData->mHardwareUUID = hardwareUUID;
1019
1020 return S_OK;
1021}
1022
1023STDMETHODIMP Machine::COMGETTER(MemorySize) (ULONG *memorySize)
1024{
1025 if (!memorySize)
1026 return E_POINTER;
1027
1028 AutoCaller autoCaller(this);
1029 CheckComRCReturnRC(autoCaller.rc());
1030
1031 AutoReadLock alock(this);
1032
1033 *memorySize = mHWData->mMemorySize;
1034
1035 return S_OK;
1036}
1037
1038STDMETHODIMP Machine::COMSETTER(MemorySize) (ULONG memorySize)
1039{
1040 /* check RAM limits */
1041 if ( memorySize < MM_RAM_MIN_IN_MB
1042 || memorySize > MM_RAM_MAX_IN_MB
1043 )
1044 return setError(E_INVALIDARG,
1045 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1046 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1047
1048 AutoCaller autoCaller(this);
1049 CheckComRCReturnRC(autoCaller.rc());
1050
1051 AutoWriteLock alock(this);
1052
1053 HRESULT rc = checkStateDependency(MutableStateDep);
1054 CheckComRCReturnRC(rc);
1055
1056 mHWData.backup();
1057 mHWData->mMemorySize = memorySize;
1058
1059 return S_OK;
1060}
1061
1062STDMETHODIMP Machine::COMGETTER(CPUCount) (ULONG *CPUCount)
1063{
1064 if (!CPUCount)
1065 return E_POINTER;
1066
1067 AutoCaller autoCaller(this);
1068 CheckComRCReturnRC(autoCaller.rc());
1069
1070 AutoReadLock alock(this);
1071
1072 *CPUCount = mHWData->mCPUCount;
1073
1074 return S_OK;
1075}
1076
1077STDMETHODIMP Machine::COMSETTER(CPUCount) (ULONG CPUCount)
1078{
1079 /* check RAM limits */
1080 if ( CPUCount < SchemaDefs::MinCPUCount
1081 || CPUCount > SchemaDefs::MaxCPUCount
1082 )
1083 return setError(E_INVALIDARG,
1084 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1085 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1086
1087 AutoCaller autoCaller(this);
1088 CheckComRCReturnRC(autoCaller.rc());
1089
1090 AutoWriteLock alock(this);
1091
1092 HRESULT rc = checkStateDependency(MutableStateDep);
1093 CheckComRCReturnRC(rc);
1094
1095 mHWData.backup();
1096 mHWData->mCPUCount = CPUCount;
1097
1098 return S_OK;
1099}
1100
1101STDMETHODIMP Machine::COMGETTER(VRAMSize) (ULONG *memorySize)
1102{
1103 if (!memorySize)
1104 return E_POINTER;
1105
1106 AutoCaller autoCaller(this);
1107 CheckComRCReturnRC(autoCaller.rc());
1108
1109 AutoReadLock alock(this);
1110
1111 *memorySize = mHWData->mVRAMSize;
1112
1113 return S_OK;
1114}
1115
1116STDMETHODIMP Machine::COMSETTER(VRAMSize) (ULONG memorySize)
1117{
1118 /* check VRAM limits */
1119 if (memorySize < SchemaDefs::MinGuestVRAM ||
1120 memorySize > SchemaDefs::MaxGuestVRAM)
1121 return setError(E_INVALIDARG,
1122 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1123 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1124
1125 AutoCaller autoCaller(this);
1126 CheckComRCReturnRC(autoCaller.rc());
1127
1128 AutoWriteLock alock(this);
1129
1130 HRESULT rc = checkStateDependency(MutableStateDep);
1131 CheckComRCReturnRC(rc);
1132
1133 mHWData.backup();
1134 mHWData->mVRAMSize = memorySize;
1135
1136 return S_OK;
1137}
1138
1139/** @todo this method should not be public */
1140STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize) (ULONG *memoryBalloonSize)
1141{
1142 if (!memoryBalloonSize)
1143 return E_POINTER;
1144
1145 AutoCaller autoCaller(this);
1146 CheckComRCReturnRC(autoCaller.rc());
1147
1148 AutoReadLock alock(this);
1149
1150 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1151
1152 return S_OK;
1153}
1154
1155/** @todo this method should not be public */
1156STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize) (ULONG memoryBalloonSize)
1157{
1158 /* check limits */
1159 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON (mHWData->mMemorySize))
1160 return setError(E_INVALIDARG,
1161 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1162 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON (mHWData->mMemorySize));
1163
1164 AutoCaller autoCaller(this);
1165 CheckComRCReturnRC(autoCaller.rc());
1166
1167 AutoWriteLock alock(this);
1168
1169 HRESULT rc = checkStateDependency(MutableStateDep);
1170 CheckComRCReturnRC(rc);
1171
1172 mHWData.backup();
1173 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1174
1175 return S_OK;
1176}
1177
1178/** @todo this method should not be public */
1179STDMETHODIMP Machine::COMGETTER(StatisticsUpdateInterval) (ULONG *statisticsUpdateInterval)
1180{
1181 if (!statisticsUpdateInterval)
1182 return E_POINTER;
1183
1184 AutoCaller autoCaller(this);
1185 CheckComRCReturnRC(autoCaller.rc());
1186
1187 AutoReadLock alock(this);
1188
1189 *statisticsUpdateInterval = mHWData->mStatisticsUpdateInterval;
1190
1191 return S_OK;
1192}
1193
1194/** @todo this method should not be public */
1195STDMETHODIMP Machine::COMSETTER(StatisticsUpdateInterval) (ULONG statisticsUpdateInterval)
1196{
1197 AutoCaller autoCaller(this);
1198 CheckComRCReturnRC(autoCaller.rc());
1199
1200 AutoWriteLock alock(this);
1201
1202 HRESULT rc = checkStateDependency(MutableStateDep);
1203 CheckComRCReturnRC(rc);
1204
1205 mHWData.backup();
1206 mHWData->mStatisticsUpdateInterval = statisticsUpdateInterval;
1207
1208 return S_OK;
1209}
1210
1211
1212STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1213{
1214 if (!enabled)
1215 return E_POINTER;
1216
1217 AutoCaller autoCaller(this);
1218 CheckComRCReturnRC(autoCaller.rc());
1219
1220 AutoReadLock alock(this);
1221
1222 *enabled = mHWData->mAccelerate3DEnabled;
1223
1224 return S_OK;
1225}
1226
1227STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1228{
1229 AutoCaller autoCaller(this);
1230 CheckComRCReturnRC(autoCaller.rc());
1231
1232 AutoWriteLock alock(this);
1233
1234 HRESULT rc = checkStateDependency(MutableStateDep);
1235 CheckComRCReturnRC(rc);
1236
1237 /** @todo check validity! */
1238
1239 mHWData.backup();
1240 mHWData->mAccelerate3DEnabled = enable;
1241
1242 return S_OK;
1243}
1244
1245
1246STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1247{
1248 if (!enabled)
1249 return E_POINTER;
1250
1251 AutoCaller autoCaller(this);
1252 CheckComRCReturnRC(autoCaller.rc());
1253
1254 AutoReadLock alock(this);
1255
1256 *enabled = mHWData->mAccelerate2DVideoEnabled;
1257
1258 return S_OK;
1259}
1260
1261STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1262{
1263 AutoCaller autoCaller(this);
1264 CheckComRCReturnRC(autoCaller.rc());
1265
1266 AutoWriteLock alock(this);
1267
1268 HRESULT rc = checkStateDependency(MutableStateDep);
1269 CheckComRCReturnRC(rc);
1270
1271 /** @todo check validity! */
1272
1273 mHWData.backup();
1274 mHWData->mAccelerate2DVideoEnabled = enable;
1275
1276 return S_OK;
1277}
1278
1279STDMETHODIMP Machine::COMGETTER(MonitorCount) (ULONG *monitorCount)
1280{
1281 if (!monitorCount)
1282 return E_POINTER;
1283
1284 AutoCaller autoCaller(this);
1285 CheckComRCReturnRC(autoCaller.rc());
1286
1287 AutoReadLock alock(this);
1288
1289 *monitorCount = mHWData->mMonitorCount;
1290
1291 return S_OK;
1292}
1293
1294STDMETHODIMP Machine::COMSETTER(MonitorCount) (ULONG monitorCount)
1295{
1296 /* make sure monitor count is a sensible number */
1297 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1298 return setError(E_INVALIDARG,
1299 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1300 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1301
1302 AutoCaller autoCaller(this);
1303 CheckComRCReturnRC(autoCaller.rc());
1304
1305 AutoWriteLock alock(this);
1306
1307 HRESULT rc = checkStateDependency(MutableStateDep);
1308 CheckComRCReturnRC(rc);
1309
1310 mHWData.backup();
1311 mHWData->mMonitorCount = monitorCount;
1312
1313 return S_OK;
1314}
1315
1316STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1317{
1318 if (!biosSettings)
1319 return E_POINTER;
1320
1321 AutoCaller autoCaller(this);
1322 CheckComRCReturnRC(autoCaller.rc());
1323
1324 /* mBIOSSettings is constant during life time, no need to lock */
1325 mBIOSSettings.queryInterfaceTo(biosSettings);
1326
1327 return S_OK;
1328}
1329
1330STDMETHODIMP Machine::GetCpuProperty(CpuPropertyType_T property, BOOL *aVal)
1331{
1332 if (!aVal)
1333 return E_POINTER;
1334
1335 AutoCaller autoCaller(this);
1336 CheckComRCReturnRC(autoCaller.rc());
1337
1338 AutoReadLock alock(this);
1339
1340 switch(property)
1341 {
1342 case CpuPropertyType_PAE:
1343 *aVal = mHWData->mPAEEnabled;
1344 break;
1345
1346 case CpuPropertyType_Synthetic:
1347 *aVal = mHWData->mSyntheticCpu;
1348 break;
1349
1350 default:
1351 return E_INVALIDARG;
1352 }
1353 return S_OK;
1354}
1355
1356STDMETHODIMP Machine::SetCpuProperty(CpuPropertyType_T property, BOOL aVal)
1357{
1358 AutoCaller autoCaller(this);
1359 CheckComRCReturnRC(autoCaller.rc());
1360
1361 AutoWriteLock alock(this);
1362
1363 HRESULT rc = checkStateDependency(MutableStateDep);
1364 CheckComRCReturnRC(rc);
1365
1366 switch(property)
1367 {
1368 case CpuPropertyType_PAE:
1369 mHWData->mPAEEnabled = !!aVal;
1370 break;
1371
1372 case CpuPropertyType_Synthetic:
1373 mHWData->mSyntheticCpu = !!aVal;
1374 break;
1375
1376 default:
1377 return E_INVALIDARG;
1378 }
1379 return S_OK;
1380}
1381
1382STDMETHODIMP Machine::GetCpuIdLeaf(ULONG id, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
1383{
1384 CheckComArgOutPointerValid(aValEax);
1385 CheckComArgOutPointerValid(aValEbx);
1386 CheckComArgOutPointerValid(aValEcx);
1387 CheckComArgOutPointerValid(aValEdx);
1388
1389 AutoCaller autoCaller(this);
1390 CheckComRCReturnRC(autoCaller.rc());
1391
1392 AutoReadLock alock(this);
1393
1394 switch(id)
1395 {
1396 case 0x0:
1397 case 0x1:
1398 case 0x2:
1399 case 0x3:
1400 case 0x4:
1401 case 0x5:
1402 case 0x6:
1403 case 0x7:
1404 case 0x8:
1405 case 0x9:
1406 case 0xA:
1407 if (mHWData->mCpuIdStdLeafs[id].ulId != id)
1408 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), id);
1409
1410 *aValEax = mHWData->mCpuIdStdLeafs[id].ulEax;
1411 *aValEbx = mHWData->mCpuIdStdLeafs[id].ulEbx;
1412 *aValEcx = mHWData->mCpuIdStdLeafs[id].ulEcx;
1413 *aValEdx = mHWData->mCpuIdStdLeafs[id].ulEdx;
1414 break;
1415
1416 case 0x80000000:
1417 case 0x80000001:
1418 case 0x80000002:
1419 case 0x80000003:
1420 case 0x80000004:
1421 case 0x80000005:
1422 case 0x80000006:
1423 case 0x80000007:
1424 case 0x80000008:
1425 case 0x80000009:
1426 case 0x8000000A:
1427 if (mHWData->mCpuIdExtLeafs[id - 0x80000000].ulId != id)
1428 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), id);
1429
1430 *aValEax = mHWData->mCpuIdExtLeafs[id - 0x80000000].ulEax;
1431 *aValEbx = mHWData->mCpuIdExtLeafs[id - 0x80000000].ulEbx;
1432 *aValEcx = mHWData->mCpuIdExtLeafs[id - 0x80000000].ulEcx;
1433 *aValEdx = mHWData->mCpuIdExtLeafs[id - 0x80000000].ulEdx;
1434 break;
1435
1436 default:
1437 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), id);
1438 }
1439 return S_OK;
1440}
1441
1442STDMETHODIMP Machine::SetCpuIdLeaf(ULONG id, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
1443{
1444 AutoCaller autoCaller(this);
1445 CheckComRCReturnRC(autoCaller.rc());
1446
1447 AutoWriteLock alock(this);
1448
1449 HRESULT rc = checkStateDependency(MutableStateDep);
1450 CheckComRCReturnRC(rc);
1451
1452 switch(id)
1453 {
1454 case 0x0:
1455 case 0x1:
1456 case 0x2:
1457 case 0x3:
1458 case 0x4:
1459 case 0x5:
1460 case 0x6:
1461 case 0x7:
1462 case 0x8:
1463 case 0x9:
1464 case 0xA:
1465 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA);
1466 AssertRelease(id < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
1467 mHWData->mCpuIdStdLeafs[id].ulId = id;
1468 mHWData->mCpuIdStdLeafs[id].ulEax = aValEax;
1469 mHWData->mCpuIdStdLeafs[id].ulEbx = aValEbx;
1470 mHWData->mCpuIdStdLeafs[id].ulEcx = aValEcx;
1471 mHWData->mCpuIdStdLeafs[id].ulEdx = aValEdx;
1472 break;
1473
1474 case 0x80000000:
1475 case 0x80000001:
1476 case 0x80000002:
1477 case 0x80000003:
1478 case 0x80000004:
1479 case 0x80000005:
1480 case 0x80000006:
1481 case 0x80000007:
1482 case 0x80000008:
1483 case 0x80000009:
1484 case 0x8000000A:
1485 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA);
1486 AssertRelease(id - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
1487 mHWData->mCpuIdExtLeafs[id - 0x80000000].ulId = id;
1488 mHWData->mCpuIdExtLeafs[id - 0x80000000].ulEax = aValEax;
1489 mHWData->mCpuIdExtLeafs[id - 0x80000000].ulEbx = aValEbx;
1490 mHWData->mCpuIdExtLeafs[id - 0x80000000].ulEcx = aValEcx;
1491 mHWData->mCpuIdExtLeafs[id - 0x80000000].ulEdx = aValEdx;
1492 break;
1493
1494 default:
1495 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), id);
1496 }
1497 return S_OK;
1498}
1499
1500STDMETHODIMP Machine::RemoveCpuIdLeaf(ULONG id)
1501{
1502 AutoCaller autoCaller(this);
1503 CheckComRCReturnRC(autoCaller.rc());
1504
1505 AutoWriteLock alock(this);
1506
1507 HRESULT rc = checkStateDependency(MutableStateDep);
1508 CheckComRCReturnRC(rc);
1509
1510 switch(id)
1511 {
1512 case 0x0:
1513 case 0x1:
1514 case 0x2:
1515 case 0x3:
1516 case 0x4:
1517 case 0x5:
1518 case 0x6:
1519 case 0x7:
1520 case 0x8:
1521 case 0x9:
1522 case 0xA:
1523 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA);
1524 AssertRelease(id < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
1525 /* Invalidate leaf. */
1526 mHWData->mCpuIdStdLeafs[id].ulId = UINT32_MAX;
1527 break;
1528
1529 case 0x80000000:
1530 case 0x80000001:
1531 case 0x80000002:
1532 case 0x80000003:
1533 case 0x80000004:
1534 case 0x80000005:
1535 case 0x80000006:
1536 case 0x80000007:
1537 case 0x80000008:
1538 case 0x80000009:
1539 case 0x8000000A:
1540 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA);
1541 AssertRelease(id - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
1542 /* Invalidate leaf. */
1543 mHWData->mCpuIdExtLeafs[id - 0x80000000].ulId = UINT32_MAX;
1544 break;
1545
1546 default:
1547 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), id);
1548 }
1549 return S_OK;
1550}
1551
1552STDMETHODIMP Machine::RemoveAllCpuIdLeafs()
1553{
1554 AutoCaller autoCaller(this);
1555 CheckComRCReturnRC(autoCaller.rc());
1556
1557 AutoWriteLock alock(this);
1558
1559 HRESULT rc = checkStateDependency(MutableStateDep);
1560 CheckComRCReturnRC(rc);
1561
1562 /* Invalidate all standard leafs. */
1563 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
1564 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
1565
1566 /* Invalidate all extended leafs. */
1567 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
1568 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
1569
1570 return S_OK;
1571}
1572
1573STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
1574{
1575 if (!aVal)
1576 return E_POINTER;
1577
1578 AutoCaller autoCaller(this);
1579 CheckComRCReturnRC(autoCaller.rc());
1580
1581 AutoReadLock alock(this);
1582
1583 switch(property)
1584 {
1585 case HWVirtExPropertyType_Enabled:
1586 *aVal = mHWData->mHWVirtExEnabled;
1587 break;
1588
1589 case HWVirtExPropertyType_Exclusive:
1590 *aVal = mHWData->mHWVirtExExclusive;
1591 break;
1592
1593 case HWVirtExPropertyType_VPID:
1594 *aVal = mHWData->mHWVirtExVPIDEnabled;
1595 break;
1596
1597 case HWVirtExPropertyType_NestedPaging:
1598 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
1599 break;
1600
1601 default:
1602 return E_INVALIDARG;
1603 }
1604 return S_OK;
1605}
1606
1607STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
1608{
1609 AutoCaller autoCaller(this);
1610 CheckComRCReturnRC(autoCaller.rc());
1611
1612 AutoWriteLock alock(this);
1613
1614 HRESULT rc = checkStateDependency(MutableStateDep);
1615 CheckComRCReturnRC(rc);
1616
1617 switch(property)
1618 {
1619 case HWVirtExPropertyType_Enabled:
1620 mHWData.backup();
1621 mHWData->mHWVirtExEnabled = !!aVal;
1622 break;
1623
1624 case HWVirtExPropertyType_Exclusive:
1625 mHWData.backup();
1626 mHWData->mHWVirtExExclusive = !!aVal;
1627 break;
1628
1629 case HWVirtExPropertyType_VPID:
1630 mHWData.backup();
1631 mHWData->mHWVirtExVPIDEnabled = !!aVal;
1632 break;
1633
1634 case HWVirtExPropertyType_NestedPaging:
1635 mHWData.backup();
1636 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
1637 break;
1638
1639 default:
1640 return E_INVALIDARG;
1641 }
1642 return S_OK;
1643}
1644
1645STDMETHODIMP Machine::COMGETTER(SnapshotFolder) (BSTR *aSnapshotFolder)
1646{
1647 CheckComArgOutPointerValid(aSnapshotFolder);
1648
1649 AutoCaller autoCaller(this);
1650 CheckComRCReturnRC(autoCaller.rc());
1651
1652 AutoReadLock alock(this);
1653
1654 mUserData->mSnapshotFolderFull.cloneTo(aSnapshotFolder);
1655
1656 return S_OK;
1657}
1658
1659STDMETHODIMP Machine::COMSETTER(SnapshotFolder) (IN_BSTR aSnapshotFolder)
1660{
1661 /* @todo (r=dmik):
1662 * 1. Allow to change the name of the snapshot folder containing snapshots
1663 * 2. Rename the folder on disk instead of just changing the property
1664 * value (to be smart and not to leave garbage). Note that it cannot be
1665 * done here because the change may be rolled back. Thus, the right
1666 * place is #saveSettings().
1667 */
1668
1669 AutoCaller autoCaller(this);
1670 CheckComRCReturnRC(autoCaller.rc());
1671
1672 AutoWriteLock alock(this);
1673
1674 HRESULT rc = checkStateDependency(MutableStateDep);
1675 CheckComRCReturnRC(rc);
1676
1677 if (!mData->mCurrentSnapshot.isNull())
1678 return setError(E_FAIL,
1679 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
1680
1681 Utf8Str snapshotFolder = aSnapshotFolder;
1682
1683 if (snapshotFolder.isEmpty())
1684 {
1685 if (isInOwnDir())
1686 {
1687 /* the default snapshots folder is 'Snapshots' in the machine dir */
1688 snapshotFolder = Utf8Str ("Snapshots");
1689 }
1690 else
1691 {
1692 /* the default snapshots folder is {UUID}, for backwards
1693 * compatibility and to resolve conflicts */
1694 snapshotFolder = Utf8StrFmt ("{%RTuuid}", mData->mUuid.raw());
1695 }
1696 }
1697
1698 int vrc = calculateFullPath(snapshotFolder, snapshotFolder);
1699 if (RT_FAILURE(vrc))
1700 return setError(E_FAIL,
1701 tr("Invalid snapshot folder '%ls' (%Rrc)"),
1702 aSnapshotFolder, vrc);
1703
1704 mUserData.backup();
1705 mUserData->mSnapshotFolder = aSnapshotFolder;
1706 mUserData->mSnapshotFolderFull = snapshotFolder;
1707
1708 return S_OK;
1709}
1710
1711STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
1712{
1713 if (ComSafeArrayOutIsNull(aAttachments))
1714 return E_POINTER;
1715
1716 AutoCaller autoCaller(this);
1717 CheckComRCReturnRC(autoCaller.rc());
1718
1719 AutoReadLock alock(this);
1720
1721 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
1722 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
1723
1724 return S_OK;
1725}
1726
1727STDMETHODIMP Machine::COMGETTER(VRDPServer)(IVRDPServer **vrdpServer)
1728{
1729#ifdef VBOX_WITH_VRDP
1730 if (!vrdpServer)
1731 return E_POINTER;
1732
1733 AutoCaller autoCaller(this);
1734 CheckComRCReturnRC(autoCaller.rc());
1735
1736 AutoReadLock alock(this);
1737
1738 Assert (!!mVRDPServer);
1739 mVRDPServer.queryInterfaceTo(vrdpServer);
1740
1741 return S_OK;
1742#else
1743 NOREF(vrdpServer);
1744 ReturnComNotImplemented();
1745#endif
1746}
1747
1748STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
1749{
1750 if (!audioAdapter)
1751 return E_POINTER;
1752
1753 AutoCaller autoCaller(this);
1754 CheckComRCReturnRC(autoCaller.rc());
1755
1756 AutoReadLock alock(this);
1757
1758 mAudioAdapter.queryInterfaceTo(audioAdapter);
1759 return S_OK;
1760}
1761
1762STDMETHODIMP Machine::COMGETTER(USBController) (IUSBController **aUSBController)
1763{
1764#ifdef VBOX_WITH_USB
1765 CheckComArgOutPointerValid(aUSBController);
1766
1767 AutoCaller autoCaller(this);
1768 CheckComRCReturnRC(autoCaller.rc());
1769
1770 MultiResult rc = mParent->host()->checkUSBProxyService();
1771 CheckComRCReturnRC(rc);
1772
1773 AutoReadLock alock(this);
1774
1775 return rc = mUSBController.queryInterfaceTo(aUSBController);
1776#else
1777 /* Note: The GUI depends on this method returning E_NOTIMPL with no
1778 * extended error info to indicate that USB is simply not available
1779 * (w/o treting it as a failure), for example, as in OSE */
1780 NOREF(aUSBController);
1781 ReturnComNotImplemented();
1782#endif
1783}
1784
1785STDMETHODIMP Machine::COMGETTER(SettingsFilePath) (BSTR *aFilePath)
1786{
1787 CheckComArgOutPointerValid(aFilePath);
1788
1789 AutoLimitedCaller autoCaller(this);
1790 CheckComRCReturnRC(autoCaller.rc());
1791
1792 AutoReadLock alock(this);
1793
1794 mData->m_strConfigFileFull.cloneTo(aFilePath);
1795 return S_OK;
1796}
1797
1798STDMETHODIMP Machine::COMGETTER(SettingsModified) (BOOL *aModified)
1799{
1800 CheckComArgOutPointerValid(aModified);
1801
1802 AutoCaller autoCaller(this);
1803 CheckComRCReturnRC(autoCaller.rc());
1804
1805 AutoWriteLock alock(this);
1806
1807 HRESULT rc = checkStateDependency(MutableStateDep);
1808 CheckComRCReturnRC(rc);
1809
1810 if (mData->mInitMode == Init_New)
1811 /*
1812 * if this is a new machine then no config file exists yet, so always return TRUE
1813 */
1814 *aModified = TRUE;
1815 else
1816 *aModified = isModified();
1817
1818 return S_OK;
1819}
1820
1821STDMETHODIMP Machine::COMGETTER(SessionState) (SessionState_T *aSessionState)
1822{
1823 CheckComArgOutPointerValid(aSessionState);
1824
1825 AutoCaller autoCaller(this);
1826 CheckComRCReturnRC(autoCaller.rc());
1827
1828 AutoReadLock alock(this);
1829
1830 *aSessionState = mData->mSession.mState;
1831
1832 return S_OK;
1833}
1834
1835STDMETHODIMP Machine::COMGETTER(SessionType) (BSTR *aSessionType)
1836{
1837 CheckComArgOutPointerValid(aSessionType);
1838
1839 AutoCaller autoCaller(this);
1840 CheckComRCReturnRC(autoCaller.rc());
1841
1842 AutoReadLock alock(this);
1843
1844 if (mData->mSession.mType.isNull())
1845 Bstr("").cloneTo(aSessionType);
1846 else
1847 mData->mSession.mType.cloneTo(aSessionType);
1848
1849 return S_OK;
1850}
1851
1852STDMETHODIMP Machine::COMGETTER(SessionPid) (ULONG *aSessionPid)
1853{
1854 CheckComArgOutPointerValid(aSessionPid);
1855
1856 AutoCaller autoCaller(this);
1857 CheckComRCReturnRC(autoCaller.rc());
1858
1859 AutoReadLock alock(this);
1860
1861 *aSessionPid = mData->mSession.mPid;
1862
1863 return S_OK;
1864}
1865
1866STDMETHODIMP Machine::COMGETTER(State) (MachineState_T *machineState)
1867{
1868 if (!machineState)
1869 return E_POINTER;
1870
1871 AutoCaller autoCaller(this);
1872 CheckComRCReturnRC(autoCaller.rc());
1873
1874 AutoReadLock alock(this);
1875
1876 *machineState = mData->mMachineState;
1877
1878 return S_OK;
1879}
1880
1881STDMETHODIMP Machine::COMGETTER(LastStateChange) (LONG64 *aLastStateChange)
1882{
1883 CheckComArgOutPointerValid(aLastStateChange);
1884
1885 AutoCaller autoCaller(this);
1886 CheckComRCReturnRC(autoCaller.rc());
1887
1888 AutoReadLock alock(this);
1889
1890 *aLastStateChange = RTTimeSpecGetMilli (&mData->mLastStateChange);
1891
1892 return S_OK;
1893}
1894
1895STDMETHODIMP Machine::COMGETTER(StateFilePath) (BSTR *aStateFilePath)
1896{
1897 CheckComArgOutPointerValid(aStateFilePath);
1898
1899 AutoCaller autoCaller(this);
1900 CheckComRCReturnRC(autoCaller.rc());
1901
1902 AutoReadLock alock(this);
1903
1904 if (mSSData->mStateFilePath.isEmpty())
1905 Bstr("").cloneTo(aStateFilePath);
1906 else
1907 mSSData->mStateFilePath.cloneTo(aStateFilePath);
1908
1909 return S_OK;
1910}
1911
1912STDMETHODIMP Machine::COMGETTER(LogFolder) (BSTR *aLogFolder)
1913{
1914 CheckComArgOutPointerValid(aLogFolder);
1915
1916 AutoCaller autoCaller(this);
1917 AssertComRCReturnRC(autoCaller.rc());
1918
1919 AutoReadLock alock(this);
1920
1921 Utf8Str logFolder;
1922 getLogFolder (logFolder);
1923
1924 Bstr (logFolder).cloneTo(aLogFolder);
1925
1926 return S_OK;
1927}
1928
1929STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
1930{
1931 CheckComArgOutPointerValid(aCurrentSnapshot);
1932
1933 AutoCaller autoCaller(this);
1934 CheckComRCReturnRC(autoCaller.rc());
1935
1936 AutoReadLock alock(this);
1937
1938 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
1939
1940 return S_OK;
1941}
1942
1943STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
1944{
1945 CheckComArgOutPointerValid(aSnapshotCount);
1946
1947 AutoCaller autoCaller(this);
1948 CheckComRCReturnRC(autoCaller.rc());
1949
1950 AutoReadLock alock(this);
1951
1952 *aSnapshotCount = mData->mFirstSnapshot.isNull()
1953 ? 0
1954 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
1955
1956 return S_OK;
1957}
1958
1959STDMETHODIMP Machine::COMGETTER(CurrentStateModified) (BOOL *aCurrentStateModified)
1960{
1961 CheckComArgOutPointerValid(aCurrentStateModified);
1962
1963 AutoCaller autoCaller(this);
1964 CheckComRCReturnRC(autoCaller.rc());
1965
1966 AutoReadLock alock(this);
1967
1968 /* Note: for machines with no snapshots, we always return FALSE
1969 * (mData->mCurrentStateModified will be TRUE in this case, for historical
1970 * reasons :) */
1971
1972 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
1973 ? FALSE
1974 : mData->mCurrentStateModified;
1975
1976 return S_OK;
1977}
1978
1979STDMETHODIMP Machine::COMGETTER(SharedFolders) (ComSafeArrayOut(ISharedFolder *, aSharedFolders))
1980{
1981 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
1982
1983 AutoCaller autoCaller(this);
1984 CheckComRCReturnRC(autoCaller.rc());
1985
1986 AutoReadLock alock(this);
1987
1988 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
1989 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
1990
1991 return S_OK;
1992}
1993
1994STDMETHODIMP Machine::COMGETTER(ClipboardMode) (ClipboardMode_T *aClipboardMode)
1995{
1996 CheckComArgOutPointerValid(aClipboardMode);
1997
1998 AutoCaller autoCaller(this);
1999 CheckComRCReturnRC(autoCaller.rc());
2000
2001 AutoReadLock alock(this);
2002
2003 *aClipboardMode = mHWData->mClipboardMode;
2004
2005 return S_OK;
2006}
2007
2008STDMETHODIMP
2009Machine::COMSETTER(ClipboardMode) (ClipboardMode_T aClipboardMode)
2010{
2011 AutoCaller autoCaller(this);
2012 CheckComRCReturnRC(autoCaller.rc());
2013
2014 AutoWriteLock alock(this);
2015
2016 HRESULT rc = checkStateDependency(MutableStateDep);
2017 CheckComRCReturnRC(rc);
2018
2019 mHWData.backup();
2020 mHWData->mClipboardMode = aClipboardMode;
2021
2022 return S_OK;
2023}
2024
2025STDMETHODIMP
2026Machine::COMGETTER(GuestPropertyNotificationPatterns) (BSTR *aPatterns)
2027{
2028 CheckComArgOutPointerValid(aPatterns);
2029
2030 AutoCaller autoCaller(this);
2031 CheckComRCReturnRC(autoCaller.rc());
2032
2033 AutoReadLock alock(this);
2034
2035 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2036
2037 return RT_LIKELY (aPatterns != NULL) ? S_OK : E_OUTOFMEMORY; /** @todo r=bird: this is wrong... :-) */
2038}
2039
2040STDMETHODIMP
2041Machine::COMSETTER(GuestPropertyNotificationPatterns) (IN_BSTR aPatterns)
2042{
2043 AssertLogRelReturn (VALID_PTR (aPatterns), E_POINTER);
2044 AutoCaller autoCaller(this);
2045 CheckComRCReturnRC(autoCaller.rc());
2046
2047 AutoWriteLock alock(this);
2048
2049 HRESULT rc = checkStateDependency(MutableStateDep);
2050 CheckComRCReturnRC(rc);
2051
2052 mHWData.backup();
2053 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2054
2055 return RT_LIKELY (!mHWData->mGuestPropertyNotificationPatterns.isNull())
2056 ? S_OK : E_OUTOFMEMORY;
2057}
2058
2059STDMETHODIMP
2060Machine::COMGETTER(StorageControllers) (ComSafeArrayOut(IStorageController *, aStorageControllers))
2061{
2062 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2063
2064 AutoCaller autoCaller(this);
2065 CheckComRCReturnRC(autoCaller.rc());
2066
2067 AutoReadLock alock(this);
2068
2069 SafeIfaceArray<IStorageController> ctrls (*mStorageControllers.data());
2070 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2071
2072 return S_OK;
2073}
2074
2075STDMETHODIMP
2076Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2077{
2078 CheckComArgOutPointerValid(aEnabled);
2079
2080 AutoCaller autoCaller(this);
2081 CheckComRCReturnRC(autoCaller.rc());
2082
2083 AutoReadLock alock(this);
2084
2085 *aEnabled = mUserData->mTeleporterEnabled;
2086
2087 return S_OK;
2088}
2089
2090STDMETHODIMP
2091Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2092{
2093 AutoCaller autoCaller(this);
2094 CheckComRCReturnRC(autoCaller.rc());
2095
2096 AutoWriteLock alock(this);
2097
2098 /* Only allow it to be set to true when PoweredOff or Aborted.
2099 (Clearing it is always permitted.) */
2100 if ( aEnabled
2101 && mData->mRegistered
2102 && ( mType != IsSessionMachine
2103 || ( mData->mMachineState != MachineState_PoweredOff
2104 && mData->mMachineState != MachineState_Teleported
2105 && mData->mMachineState != MachineState_Aborted
2106 )
2107 )
2108 )
2109 return setError(VBOX_E_INVALID_VM_STATE,
2110 tr("The machine is not powered off (state is %s)"),
2111 Global::stringifyMachineState(mData->mMachineState));
2112
2113 mUserData.backup();
2114 mUserData->mTeleporterEnabled = aEnabled;
2115
2116 return S_OK;
2117}
2118
2119STDMETHODIMP
2120Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2121{
2122 CheckComArgOutPointerValid(aPort);
2123
2124 AutoCaller autoCaller(this);
2125 CheckComRCReturnRC(autoCaller.rc());
2126
2127 AutoReadLock alock(this);
2128
2129 *aPort = mUserData->mTeleporterPort;
2130
2131 return S_OK;
2132}
2133
2134STDMETHODIMP
2135Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2136{
2137 if (aPort >= _64K)
2138 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2139
2140 AutoCaller autoCaller(this);
2141 CheckComRCReturnRC(autoCaller.rc());
2142
2143 AutoWriteLock alock(this);
2144
2145 HRESULT rc = checkStateDependency(MutableStateDep);
2146 CheckComRCReturnRC(rc);
2147
2148 mUserData.backup();
2149 mUserData->mTeleporterPort = aPort;
2150
2151 return S_OK;
2152}
2153
2154STDMETHODIMP
2155Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2156{
2157 CheckComArgOutPointerValid(aAddress);
2158
2159 AutoCaller autoCaller(this);
2160 CheckComRCReturnRC(autoCaller.rc());
2161
2162 AutoReadLock alock(this);
2163
2164 mUserData->mTeleporterAddress.cloneTo(aAddress);
2165
2166 return S_OK;
2167}
2168
2169STDMETHODIMP
2170Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2171{
2172 AutoCaller autoCaller(this);
2173 CheckComRCReturnRC(autoCaller.rc());
2174
2175 AutoWriteLock alock(this);
2176
2177 HRESULT rc = checkStateDependency(MutableStateDep);
2178 CheckComRCReturnRC(rc);
2179
2180 mUserData.backup();
2181 mUserData->mTeleporterAddress = aAddress;
2182
2183 return S_OK;
2184}
2185
2186STDMETHODIMP
2187Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2188{
2189 CheckComArgOutPointerValid(aPassword);
2190
2191 AutoCaller autoCaller(this);
2192 CheckComRCReturnRC(autoCaller.rc());
2193
2194 AutoReadLock alock(this);
2195
2196 mUserData->mTeleporterPassword.cloneTo(aPassword);
2197
2198 return S_OK;
2199}
2200
2201STDMETHODIMP
2202Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2203{
2204 AutoCaller autoCaller(this);
2205 CheckComRCReturnRC(autoCaller.rc());
2206
2207 AutoWriteLock alock(this);
2208
2209 HRESULT rc = checkStateDependency(MutableStateDep);
2210 CheckComRCReturnRC(rc);
2211
2212 mUserData.backup();
2213 mUserData->mTeleporterPassword = aPassword;
2214
2215 return S_OK;
2216}
2217
2218
2219// IMachine methods
2220/////////////////////////////////////////////////////////////////////////////
2221
2222STDMETHODIMP Machine::SetBootOrder (ULONG aPosition, DeviceType_T aDevice)
2223{
2224 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
2225 return setError(E_INVALIDARG,
2226 tr ("Invalid boot position: %lu (must be in range [1, %lu])"),
2227 aPosition, SchemaDefs::MaxBootPosition);
2228
2229 if (aDevice == DeviceType_USB)
2230 return setError(E_NOTIMPL,
2231 tr("Booting from USB device is currently not supported"));
2232
2233 AutoCaller autoCaller(this);
2234 CheckComRCReturnRC(autoCaller.rc());
2235
2236 AutoWriteLock alock(this);
2237
2238 HRESULT rc = checkStateDependency(MutableStateDep);
2239 CheckComRCReturnRC(rc);
2240
2241 mHWData.backup();
2242 mHWData->mBootOrder [aPosition - 1] = aDevice;
2243
2244 return S_OK;
2245}
2246
2247STDMETHODIMP Machine::GetBootOrder (ULONG aPosition, DeviceType_T *aDevice)
2248{
2249 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
2250 return setError(E_INVALIDARG,
2251 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
2252 aPosition, SchemaDefs::MaxBootPosition);
2253
2254 AutoCaller autoCaller(this);
2255 CheckComRCReturnRC(autoCaller.rc());
2256
2257 AutoReadLock alock(this);
2258
2259 *aDevice = mHWData->mBootOrder [aPosition - 1];
2260
2261 return S_OK;
2262}
2263
2264STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
2265 LONG aControllerPort,
2266 LONG aDevice,
2267 DeviceType_T aType,
2268 IN_BSTR aId)
2269{
2270 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aId=\"%ls\"\n",
2271 aControllerName, aControllerPort, aDevice, aType, aId));
2272
2273 CheckComArgNotNull(aControllerName);
2274 CheckComArgNotNull(aId);
2275
2276 AutoCaller autoCaller(this);
2277 CheckComRCReturnRC(autoCaller.rc());
2278
2279 /* VirtualBox::findHardDisk() and the corresponding other methods for
2280 * DVD and floppy media need *write* lock (for getting rid of unneeded
2281 * host drives which got enumerated); also we want to make sure the
2282 * media object we pick up doesn't get unregistered before we finish. */
2283 AutoMultiWriteLock2 alock(mParent, this);
2284
2285 HRESULT rc = checkStateDependency(MutableStateDep);
2286 CheckComRCReturnRC(rc);
2287
2288 /// @todo NEWMEDIA implicit machine registration
2289 if (!mData->mRegistered)
2290 return setError(VBOX_E_INVALID_OBJECT_STATE,
2291 tr("Cannot attach storage devices to an unregistered machine"));
2292
2293 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
2294
2295 if (Global::IsOnlineOrTransient(mData->mMachineState))
2296 return setError(VBOX_E_INVALID_VM_STATE,
2297 tr("Invalid machine state: %s"),
2298 Global::stringifyMachineState(mData->mMachineState));
2299
2300 /* Check for an existing controller. */
2301 ComObjPtr<StorageController> ctl;
2302 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
2303 CheckComRCReturnRC(rc);
2304
2305 /* check that the port and device are not out of range. */
2306 ULONG portCount;
2307 ULONG devicesPerPort;
2308 rc = ctl->COMGETTER(PortCount)(&portCount);
2309 CheckComRCReturnRC(rc);
2310 rc = ctl->COMGETTER(MaxDevicesPerPortCount)(&devicesPerPort);
2311 CheckComRCReturnRC(rc);
2312
2313 if ( (aControllerPort < 0)
2314 || (aControllerPort >= (LONG)portCount)
2315 || (aDevice < 0)
2316 || (aDevice >= (LONG)devicesPerPort)
2317 )
2318 return setError(E_INVALIDARG,
2319 tr("The port and/or count parameter are out of range [%lu:%lu]"),
2320 portCount,
2321 devicesPerPort);
2322
2323 /* check if the device slot is already busy */
2324 MediumAttachment *pAttachTemp;
2325 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
2326 aControllerName,
2327 aControllerPort,
2328 aDevice)))
2329 {
2330 Medium *pMedium = pAttachTemp->medium();
2331 if (pMedium)
2332 {
2333 AutoReadLock mediumLock(pMedium);
2334 return setError(VBOX_E_OBJECT_IN_USE,
2335 tr("Medium '%s' is already attached to device slot %d on port %d of controller '%ls' of this virtual machine"),
2336 pMedium->locationFull().raw(), aDevice, aControllerPort, aControllerName);
2337 }
2338 else
2339 return setError(VBOX_E_OBJECT_IN_USE,
2340 tr("Device is already attached to slot %d on port %d of controller '%ls' of this virtual machine"),
2341 aDevice, aControllerPort, aControllerName);
2342 }
2343
2344 Guid id(aId);
2345
2346 ComObjPtr<Medium> medium;
2347 switch (aType)
2348 {
2349 case DeviceType_HardDisk:
2350 /* find a hard disk by UUID */
2351 rc = mParent->findHardDisk(&id, NULL, true /* aSetError */, &medium);
2352 CheckComRCReturnRC(rc);
2353 break;
2354
2355 case DeviceType_DVD:
2356 if (!id.isEmpty())
2357 {
2358 /* first search for host drive */
2359 SafeIfaceArray<IMedium> drivevec;
2360 rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
2361 if (SUCCEEDED(rc))
2362 {
2363 for (size_t i = 0; i < drivevec.size(); ++i)
2364 {
2365 /// @todo eliminate this conversion
2366 ComObjPtr<Medium> med = (Medium *)drivevec[i];
2367 if (med->id() == id)
2368 {
2369 medium = med;
2370 break;
2371 }
2372 }
2373 }
2374
2375 if (medium.isNull())
2376 {
2377 /* find a DVD image by UUID */
2378 rc = mParent->findDVDImage(&id, NULL, true /* aSetError */, &medium);
2379 CheckComRCReturnRC(rc);
2380 }
2381 }
2382 else
2383 {
2384 /* null UUID means null medium, which needs no code */
2385 }
2386 break;
2387
2388 case DeviceType_Floppy:
2389 if (!id.isEmpty())
2390 {
2391 /* first search for host drive */
2392 SafeIfaceArray<IMedium> drivevec;
2393 rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
2394 if (SUCCEEDED(rc))
2395 {
2396 for (size_t i = 0; i < drivevec.size(); ++i)
2397 {
2398 /// @todo eliminate this conversion
2399 ComObjPtr<Medium> med = (Medium *)drivevec[i];
2400 if (med->id() == id)
2401 {
2402 medium = med;
2403 break;
2404 }
2405 }
2406 }
2407
2408 if (medium.isNull())
2409 {
2410 /* find a floppy image by UUID */
2411 rc = mParent->findFloppyImage(&id, NULL, true /* aSetError */, &medium);
2412 CheckComRCReturnRC(rc);
2413 }
2414 }
2415 else
2416 {
2417 /* null UUID means null medium, which needs no code */
2418 }
2419 break;
2420
2421 default:
2422 return setError(E_INVALIDARG,
2423 tr("The device type %d is not recognized"),
2424 (int)aType);
2425 }
2426
2427 AutoCaller mediumCaller(medium);
2428 CheckComRCReturnRC(mediumCaller.rc());
2429
2430 AutoWriteLock mediumLock(medium);
2431
2432 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
2433 && !medium.isNull())
2434 {
2435 return setError(VBOX_E_OBJECT_IN_USE,
2436 tr("Medium '%s' is already attached to this virtual machine"),
2437 medium->locationFull().raw());
2438 }
2439
2440 bool indirect = false;
2441 if (!medium.isNull())
2442 indirect = medium->isReadOnly();
2443 bool associate = true;
2444
2445 do
2446 {
2447 if (mMediaData.isBackedUp())
2448 {
2449 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
2450
2451 /* check if the medium was attached to the VM before we started
2452 * changing attachments in which case the attachment just needs to
2453 * be restored */
2454 if ((pAttachTemp = findAttachment(oldAtts, medium)))
2455 {
2456 AssertReturn(!indirect, E_FAIL);
2457
2458 /* see if it's the same bus/channel/device */
2459 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
2460 {
2461 /* the simplest case: restore the whole attachment
2462 * and return, nothing else to do */
2463 mMediaData->mAttachments.push_back(pAttachTemp);
2464 return S_OK;
2465 }
2466
2467 /* bus/channel/device differ; we need a new attachment object,
2468 * but don't try to associate it again */
2469 associate = false;
2470 break;
2471 }
2472 }
2473
2474 /* go further only if the attachment is to be indirect */
2475 if (!indirect)
2476 break;
2477
2478 /* perform the so called smart attachment logic for indirect
2479 * attachments. Note that smart attachment is only applicable to base
2480 * hard disks. */
2481
2482 if (medium->parent().isNull())
2483 {
2484 /* first, investigate the backup copy of the current hard disk
2485 * attachments to make it possible to re-attach existing diffs to
2486 * another device slot w/o losing their contents */
2487 if (mMediaData.isBackedUp())
2488 {
2489 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
2490
2491 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
2492 uint32_t foundLevel = 0;
2493
2494 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
2495 it != oldAtts.end();
2496 ++it)
2497 {
2498 uint32_t level = 0;
2499 MediumAttachment *pAttach = *it;
2500 ComObjPtr<Medium> pMedium = pAttach->medium();
2501 Assert(!pMedium.isNull() || pAttach->type() != DeviceType_HardDisk);
2502 if (pMedium.isNull())
2503 continue;
2504
2505 if (pMedium->base(&level).equalsTo(medium))
2506 {
2507 /* skip the hard disk if its currently attached (we
2508 * cannot attach the same hard disk twice) */
2509 if (findAttachment(mMediaData->mAttachments,
2510 pMedium))
2511 continue;
2512
2513 /* matched device, channel and bus (i.e. attached to the
2514 * same place) will win and immediately stop the search;
2515 * otherwise the attachment that has the youngest
2516 * descendant of medium will be used
2517 */
2518 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
2519 {
2520 /* the simplest case: restore the whole attachment
2521 * and return, nothing else to do */
2522 mMediaData->mAttachments.push_back(*it);
2523 return S_OK;
2524 }
2525 else if ( foundIt == oldAtts.end()
2526 || level > foundLevel /* prefer younger */
2527 )
2528 {
2529 foundIt = it;
2530 foundLevel = level;
2531 }
2532 }
2533 }
2534
2535 if (foundIt != oldAtts.end())
2536 {
2537 /* use the previously attached hard disk */
2538 medium = (*foundIt)->medium();
2539 mediumCaller.attach(medium);
2540 CheckComRCReturnRC(mediumCaller.rc());
2541 mediumLock.attach(medium);
2542 /* not implicit, doesn't require association with this VM */
2543 indirect = false;
2544 associate = false;
2545 /* go right to the MediumAttachment creation */
2546 break;
2547 }
2548 }
2549
2550 /* then, search through snapshots for the best diff in the given
2551 * hard disk's chain to base the new diff on */
2552
2553 ComObjPtr<Medium> base;
2554 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
2555 while (snap)
2556 {
2557 AutoReadLock snapLock(snap);
2558
2559 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
2560
2561 MediaData::AttachmentList::const_iterator foundIt = snapAtts.end();
2562 uint32_t foundLevel = 0;
2563
2564 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
2565 it != snapAtts.end();
2566 ++it)
2567 {
2568 MediumAttachment *pAttach = *it;
2569 ComObjPtr<Medium> pMedium = pAttach->medium();
2570 Assert(!pMedium.isNull() || pAttach->type() != DeviceType_HardDisk);
2571 if (pMedium.isNull())
2572 continue;
2573
2574 uint32_t level = 0;
2575 if (pMedium->base(&level).equalsTo(medium))
2576 {
2577 /* matched device, channel and bus (i.e. attached to the
2578 * same place) will win and immediately stop the search;
2579 * otherwise the attachment that has the youngest
2580 * descendant of medium will be used
2581 */
2582 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
2583 {
2584 foundIt = it;
2585 break;
2586 }
2587 else if ( foundIt == snapAtts.end()
2588 || level > foundLevel /* prefer younger */
2589 )
2590 {
2591 foundIt = it;
2592 foundLevel = level;
2593 }
2594 }
2595 }
2596
2597 if (foundIt != snapAtts.end())
2598 {
2599 base = (*foundIt)->medium();
2600 break;
2601 }
2602
2603 snap = snap->parent();
2604 }
2605
2606 /* found a suitable diff, use it as a base */
2607 if (!base.isNull())
2608 {
2609 medium = base;
2610 mediumCaller.attach(medium);
2611 CheckComRCReturnRC(mediumCaller.rc());
2612 mediumLock.attach(medium);
2613 }
2614 }
2615
2616 ComObjPtr<Medium> diff;
2617 diff.createObject();
2618 rc = diff->init(mParent,
2619 medium->preferredDiffFormat().raw(),
2620 BstrFmt("%ls"RTPATH_SLASH_STR,
2621 mUserData->mSnapshotFolderFull.raw()).raw());
2622 CheckComRCReturnRC(rc);
2623
2624 /* make sure the hard disk is not modified before createDiffStorage() */
2625 rc = medium->LockRead(NULL);
2626 CheckComRCReturnRC(rc);
2627
2628 /* will leave the lock before the potentially lengthy operation, so
2629 * protect with the special state */
2630 MachineState_T oldState = mData->mMachineState;
2631 setMachineState(MachineState_SettingUp);
2632
2633 mediumLock.leave();
2634 alock.leave();
2635
2636 rc = medium->createDiffStorageAndWait(diff, MediumVariant_Standard);
2637
2638 alock.enter();
2639 mediumLock.enter();
2640
2641 setMachineState(oldState);
2642
2643 medium->UnlockRead(NULL);
2644
2645 CheckComRCReturnRC(rc);
2646
2647 /* use the created diff for the actual attachment */
2648 medium = diff;
2649 mediumCaller.attach(medium);
2650 CheckComRCReturnRC(mediumCaller.rc());
2651 mediumLock.attach(medium);
2652 }
2653 while (0);
2654
2655 ComObjPtr<MediumAttachment> attachment;
2656 attachment.createObject();
2657 rc = attachment->init(this, medium, aControllerName, aControllerPort, aDevice, aType, indirect);
2658 CheckComRCReturnRC(rc);
2659
2660 if (associate && !medium.isNull())
2661 {
2662 /* as the last step, associate the medium to the VM */
2663 rc = medium->attachTo(mData->mUuid);
2664 /* here we can fail because of Deleting, or being in process of
2665 * creating a Diff */
2666 CheckComRCReturnRC(rc);
2667 }
2668
2669 /* success: finally remember the attachment */
2670 mMediaData.backup();
2671 mMediaData->mAttachments.push_back(attachment);
2672
2673 return rc;
2674}
2675
2676STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
2677 LONG aDevice)
2678{
2679 CheckComArgNotNull(aControllerName);
2680
2681 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
2682 aControllerName, aControllerPort, aDevice));
2683
2684 AutoCaller autoCaller(this);
2685 CheckComRCReturnRC(autoCaller.rc());
2686
2687 AutoWriteLock alock(this);
2688
2689 HRESULT rc = checkStateDependency(MutableStateDep);
2690 CheckComRCReturnRC(rc);
2691
2692 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
2693
2694 if (Global::IsOnlineOrTransient(mData->mMachineState))
2695 return setError(VBOX_E_INVALID_VM_STATE,
2696 tr("Invalid machine state: %s"),
2697 Global::stringifyMachineState(mData->mMachineState));
2698
2699 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
2700 aControllerName,
2701 aControllerPort,
2702 aDevice);
2703 if (!pAttach)
2704 return setError(VBOX_E_OBJECT_NOT_FOUND,
2705 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
2706 aDevice, aControllerPort, aControllerName);
2707
2708
2709 if (pAttach->isImplicit())
2710 {
2711 /* attempt to implicitly delete the implicitly created diff */
2712
2713 /// @todo move the implicit flag from MediumAttachment to Medium
2714 /// and forbid any hard disk operation when it is implicit. Or maybe
2715 /// a special media state for it to make it even more simple.
2716
2717 Assert(mMediaData.isBackedUp());
2718
2719 /* will leave the lock before the potentially lengthy operation, so
2720 * protect with the special state */
2721 MachineState_T oldState = mData->mMachineState;
2722 setMachineState(MachineState_SettingUp);
2723
2724 alock.leave();
2725
2726 ComObjPtr<Medium> hd = pAttach->medium();
2727 rc = hd->deleteStorageAndWait();
2728
2729 alock.enter();
2730
2731 setMachineState(oldState);
2732
2733 CheckComRCReturnRC(rc);
2734 }
2735
2736 mMediaData.backup();
2737
2738 /* we cannot use erase (it) below because backup() above will create
2739 * a copy of the list and make this copy active, but the iterator
2740 * still refers to the original and is not valid for the copy */
2741 mMediaData->mAttachments.remove(pAttach);
2742
2743 return S_OK;
2744}
2745
2746STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
2747 LONG aDevice, BOOL aPassthrough)
2748{
2749 CheckComArgNotNull(aControllerName);
2750
2751 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aPassthrough=%d\n",
2752 aControllerName, aControllerPort, aDevice, aPassthrough));
2753
2754 AutoCaller autoCaller(this);
2755 CheckComRCReturnRC(autoCaller.rc());
2756
2757 AutoWriteLock alock(this);
2758
2759 HRESULT rc = checkStateDependency(MutableStateDep);
2760 CheckComRCReturnRC(rc);
2761
2762 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
2763
2764 if (Global::IsOnlineOrTransient(mData->mMachineState))
2765 return setError(VBOX_E_INVALID_VM_STATE,
2766 tr("Invalid machine state: %s"),
2767 Global::stringifyMachineState(mData->mMachineState));
2768
2769 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
2770 aControllerName,
2771 aControllerPort,
2772 aDevice);
2773 if (!pAttach)
2774 return setError(VBOX_E_OBJECT_NOT_FOUND,
2775 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
2776 aDevice, aControllerPort, aControllerName);
2777
2778
2779 mMediaData.backup();
2780
2781 AutoWriteLock attLock(pAttach);
2782
2783 if (pAttach->type() != DeviceType_DVD)
2784 return setError(E_INVALIDARG,
2785 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
2786 aDevice, aControllerPort, aControllerName);
2787 pAttach->updatePassthrough(!!aPassthrough);
2788
2789 return S_OK;
2790}
2791
2792STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
2793 LONG aControllerPort,
2794 LONG aDevice,
2795 IN_BSTR aId)
2796{
2797 int rc = S_OK;
2798 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
2799 aControllerName, aControllerPort, aDevice));
2800
2801 CheckComArgNotNull(aControllerName);
2802 CheckComArgNotNull(aId);
2803
2804 AutoCaller autoCaller(this);
2805 CheckComRCReturnRC(autoCaller.rc());
2806
2807 AutoWriteLock alock(this);
2808
2809 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
2810 aControllerName,
2811 aControllerPort,
2812 aDevice);
2813 if (pAttach.isNull())
2814 return setError(VBOX_E_OBJECT_NOT_FOUND,
2815 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
2816 aDevice, aControllerPort, aControllerName);
2817
2818 Guid id(aId);
2819 ComObjPtr<Medium> medium;
2820 switch (pAttach->type())
2821 {
2822 case DeviceType_DVD:
2823 if (!id.isEmpty())
2824 {
2825 /* find a DVD by host device UUID */
2826 SafeIfaceArray<IMedium> drivevec;
2827 rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
2828 if (SUCCEEDED(rc))
2829 {
2830 for (size_t i = 0; i < drivevec.size(); ++i)
2831 {
2832 /// @todo eliminate this conversion
2833 ComObjPtr<Medium> med = (Medium *)drivevec[i];
2834 if (id == med->id())
2835 {
2836 medium = med;
2837 break;
2838 }
2839 }
2840 }
2841 /* find a DVD by UUID */
2842 if (medium.isNull())
2843 rc = mParent->findDVDImage(&id, NULL, true /* aDoSetError */, &medium);
2844 }
2845 CheckComRCReturnRC(rc);
2846 break;
2847 case DeviceType_Floppy:
2848 if (!id.isEmpty())
2849 {
2850 /* find a Floppy by host device UUID */
2851 SafeIfaceArray<IMedium> drivevec;
2852 rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
2853 if (SUCCEEDED(rc))
2854 {
2855 for (size_t i = 0; i < drivevec.size(); ++i)
2856 {
2857 /// @todo eliminate this conversion
2858 ComObjPtr<Medium> med = (Medium *)drivevec[i];
2859 if (id == med->id())
2860 {
2861 medium = med;
2862 break;
2863 }
2864 }
2865 }
2866 /* find a Floppy by UUID */
2867 if (medium.isNull())
2868 rc = mParent->findFloppyImage(&id, NULL, true /* aDoSetError */, &medium);
2869 }
2870 CheckComRCReturnRC(rc);
2871 break;
2872 default:
2873 return setError(VBOX_E_INVALID_OBJECT_STATE,
2874 tr("Cannot change medium attached to device slot %d on port %d of controller '%ls'"),
2875 aDevice, aControllerPort, aControllerName);
2876 }
2877
2878 if (SUCCEEDED(rc))
2879 {
2880
2881 mMediaData.backup();
2882 /* The backup operation makes the pAttach reference point to the
2883 * old settings. Re-get the correct reference. */
2884 pAttach = findAttachment(mMediaData->mAttachments,
2885 aControllerName,
2886 aControllerPort,
2887 aDevice);
2888 AutoWriteLock attLock(pAttach);
2889 if (!medium.isNull())
2890 medium->attachTo(mData->mUuid);
2891 pAttach->updateMedium(medium, false /* aImplicit */);
2892 }
2893
2894 alock.unlock();
2895 onMediumChange(pAttach);
2896
2897 return rc;
2898}
2899
2900STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
2901 LONG aControllerPort,
2902 LONG aDevice,
2903 IMedium **aMedium)
2904{
2905 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
2906 aControllerName, aControllerPort, aDevice));
2907
2908 CheckComArgNotNull(aControllerName);
2909 CheckComArgOutPointerValid(aMedium);
2910
2911 AutoCaller autoCaller(this);
2912 CheckComRCReturnRC(autoCaller.rc());
2913
2914 AutoReadLock alock(this);
2915
2916 *aMedium = NULL;
2917
2918 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
2919 aControllerName,
2920 aControllerPort,
2921 aDevice);
2922 if (pAttach.isNull())
2923 return setError(VBOX_E_OBJECT_NOT_FOUND,
2924 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
2925 aDevice, aControllerPort, aControllerName);
2926
2927 pAttach->medium().queryInterfaceTo(aMedium);
2928
2929 return S_OK;
2930}
2931
2932STDMETHODIMP Machine::GetSerialPort (ULONG slot, ISerialPort **port)
2933{
2934 CheckComArgOutPointerValid(port);
2935 CheckComArgExpr (slot, slot < RT_ELEMENTS (mSerialPorts));
2936
2937 AutoCaller autoCaller(this);
2938 CheckComRCReturnRC(autoCaller.rc());
2939
2940 AutoReadLock alock(this);
2941
2942 mSerialPorts [slot].queryInterfaceTo(port);
2943
2944 return S_OK;
2945}
2946
2947STDMETHODIMP Machine::GetParallelPort (ULONG slot, IParallelPort **port)
2948{
2949 CheckComArgOutPointerValid(port);
2950 CheckComArgExpr (slot, slot < RT_ELEMENTS (mParallelPorts));
2951
2952 AutoCaller autoCaller(this);
2953 CheckComRCReturnRC(autoCaller.rc());
2954
2955 AutoReadLock alock(this);
2956
2957 mParallelPorts [slot].queryInterfaceTo(port);
2958
2959 return S_OK;
2960}
2961
2962STDMETHODIMP Machine::GetNetworkAdapter (ULONG slot, INetworkAdapter **adapter)
2963{
2964 CheckComArgOutPointerValid(adapter);
2965 CheckComArgExpr (slot, slot < RT_ELEMENTS (mNetworkAdapters));
2966
2967 AutoCaller autoCaller(this);
2968 CheckComRCReturnRC(autoCaller.rc());
2969
2970 AutoReadLock alock(this);
2971
2972 mNetworkAdapters[slot].queryInterfaceTo(adapter);
2973
2974 return S_OK;
2975}
2976
2977STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
2978{
2979 if (ComSafeArrayOutIsNull(aKeys))
2980 return E_POINTER;
2981
2982 AutoCaller autoCaller (this);
2983 CheckComRCReturnRC (autoCaller.rc());
2984
2985 AutoReadLock alock (this);
2986
2987 com::SafeArray<BSTR> saKeys(mData->m_pMachineConfigFile->mapExtraDataItems.size());
2988 int i = 0;
2989 for (settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.begin();
2990 it != mData->m_pMachineConfigFile->mapExtraDataItems.end();
2991 ++it, ++i)
2992 {
2993 const Utf8Str &strKey = it->first;
2994 strKey.cloneTo(&saKeys[i]);
2995 }
2996 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
2997
2998 return S_OK;
2999 }
3000
3001 /**
3002 * @note Locks this object for reading.
3003 */
3004STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
3005 BSTR *aValue)
3006{
3007 CheckComArgNotNull(aKey);
3008 CheckComArgOutPointerValid(aValue);
3009
3010 AutoCaller autoCaller (this);
3011 CheckComRCReturnRC (autoCaller.rc());
3012
3013 /* start with nothing found */
3014 Bstr bstrResult("");
3015
3016 AutoReadLock alock (this);
3017
3018 settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
3019 if (it != mData->m_pMachineConfigFile->mapExtraDataItems.end())
3020 // found:
3021 bstrResult = it->second; // source is a Utf8Str
3022
3023 /* return the result to caller (may be empty) */
3024 bstrResult.cloneTo(aValue);
3025
3026 return S_OK;
3027}
3028
3029 /**
3030 * @note Locks mParent for writing + this object for writing.
3031 */
3032STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
3033{
3034 CheckComArgNotNull(aKey);
3035
3036 AutoCaller autoCaller(this);
3037 CheckComRCReturnRC (autoCaller.rc());
3038
3039 Utf8Str strKey(aKey);
3040 Utf8Str strValue(aValue);
3041 Utf8Str strOldValue; // empty
3042
3043 // locking note: we only hold the read lock briefly to look up the old value,
3044 // then release it and call the onExtraCanChange callbacks. There is a small
3045 // chance of a race insofar as the callback might be called twice if two callers
3046 // change the same key at the same time, but that's a much better solution
3047 // than the deadlock we had here before. The actual changing of the extradata
3048 // is then performed under the write lock and race-free.
3049
3050 // look up the old value first; if nothing's changed then we need not do anything
3051 {
3052 AutoReadLock alock(this); // hold read lock only while looking up
3053 settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.find(strKey);
3054 if (it != mData->m_pMachineConfigFile->mapExtraDataItems.end())
3055 strOldValue = it->second;
3056 }
3057
3058 bool fChanged;
3059 if ((fChanged = (strOldValue != strValue)))
3060 {
3061 // ask for permission from all listeners outside the locks;
3062 // onExtraDataCanChange() only briefly requests the VirtualBox
3063 // lock to copy the list of callbacks to invoke
3064 Bstr error;
3065 Bstr bstrValue;
3066 if (aValue)
3067 bstrValue = aValue;
3068 else
3069 bstrValue = (const char *)"";
3070
3071 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue, error))
3072 {
3073 const char *sep = error.isEmpty() ? "" : ": ";
3074 CBSTR err = error.isNull() ? (CBSTR) L"" : error.raw();
3075 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
3076 sep, err));
3077 return setError(E_ACCESSDENIED,
3078 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
3079 aKey,
3080 bstrValue.raw(),
3081 sep,
3082 err);
3083 }
3084
3085 // data is changing and change not vetoed: then write it out under the locks
3086
3087 // saveSettings() needs VirtualBox write lock
3088 AutoMultiWriteLock2 alock(mParent, this);
3089
3090 if (mType == IsSnapshotMachine)
3091 {
3092 HRESULT rc = checkStateDependency(MutableStateDep);
3093 CheckComRCReturnRC (rc);
3094 }
3095
3096 if (strValue.isEmpty())
3097 mData->m_pMachineConfigFile->mapExtraDataItems.erase(strKey);
3098 else
3099 mData->m_pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
3100 // creates a new key if needed
3101
3102 /* save settings on success */
3103 HRESULT rc = saveSettings();
3104 CheckComRCReturnRC (rc);
3105 }
3106
3107 // fire notification outside the lock
3108 if (fChanged)
3109 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
3110
3111 return S_OK;
3112}
3113
3114STDMETHODIMP Machine::SaveSettings()
3115{
3116 AutoCaller autoCaller(this);
3117 CheckComRCReturnRC(autoCaller.rc());
3118
3119 /* saveSettings() needs mParent lock */
3120 AutoMultiWriteLock2 alock(mParent, this);
3121
3122 /* when there was auto-conversion, we want to save the file even if
3123 * the VM is saved */
3124 HRESULT rc = checkStateDependency(MutableStateDep);
3125 CheckComRCReturnRC(rc);
3126
3127 /* the settings file path may never be null */
3128 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
3129
3130 /* save all VM data excluding snapshots */
3131 return saveSettings();
3132}
3133
3134STDMETHODIMP Machine::DiscardSettings()
3135{
3136 AutoCaller autoCaller(this);
3137 CheckComRCReturnRC(autoCaller.rc());
3138
3139 AutoWriteLock alock(this);
3140
3141 HRESULT rc = checkStateDependency(MutableStateDep);
3142 CheckComRCReturnRC(rc);
3143
3144 /*
3145 * during this rollback, the session will be notified if data has
3146 * been actually changed
3147 */
3148 rollback (true /* aNotify */);
3149
3150 return S_OK;
3151}
3152
3153STDMETHODIMP Machine::DeleteSettings()
3154{
3155 AutoCaller autoCaller(this);
3156 CheckComRCReturnRC(autoCaller.rc());
3157
3158 AutoWriteLock alock(this);
3159
3160 HRESULT rc = checkStateDependency(MutableStateDep);
3161 CheckComRCReturnRC(rc);
3162
3163 if (mData->mRegistered)
3164 return setError(VBOX_E_INVALID_VM_STATE,
3165 tr("Cannot delete settings of a registered machine"));
3166
3167 /* delete the settings only when the file actually exists */
3168 if (mData->m_pMachineConfigFile->fileExists())
3169 {
3170 int vrc = RTFileDelete(mData->m_strConfigFileFull.c_str());
3171 if (RT_FAILURE(vrc))
3172 return setError(VBOX_E_IPRT_ERROR,
3173 tr("Could not delete the settings file '%s' (%Rrc)"),
3174 mData->m_strConfigFileFull.raw(),
3175 vrc);
3176
3177 /* delete the Logs folder, nothing important should be left
3178 * there (we don't check for errors because the user might have
3179 * some private files there that we don't want to delete) */
3180 Utf8Str logFolder;
3181 getLogFolder(logFolder);
3182 Assert(logFolder.length());
3183 if (RTDirExists(logFolder.c_str()))
3184 {
3185 /* Delete all VBox.log[.N] files from the Logs folder
3186 * (this must be in sync with the rotation logic in
3187 * Console::powerUpThread()). Also, delete the VBox.png[.N]
3188 * files that may have been created by the GUI. */
3189 Utf8Str log = Utf8StrFmt("%s/VBox.log", logFolder.raw());
3190 RTFileDelete(log.c_str());
3191 log = Utf8StrFmt("%s/VBox.png", logFolder.raw());
3192 RTFileDelete(log.c_str());
3193 for (int i = 3; i >= 0; i--)
3194 {
3195 log = Utf8StrFmt("%s/VBox.log.%d", logFolder.raw(), i);
3196 RTFileDelete(log.c_str());
3197 log = Utf8StrFmt("%s/VBox.png.%d", logFolder.raw(), i);
3198 RTFileDelete(log.c_str());
3199 }
3200
3201 RTDirRemove(logFolder.c_str());
3202 }
3203
3204 /* delete the Snapshots folder, nothing important should be left
3205 * there (we don't check for errors because the user might have
3206 * some private files there that we don't want to delete) */
3207 Utf8Str snapshotFolder(mUserData->mSnapshotFolderFull);
3208 Assert(snapshotFolder.length());
3209 if (RTDirExists(snapshotFolder.c_str()))
3210 RTDirRemove(snapshotFolder.c_str());
3211
3212 /* delete the directory that contains the settings file, but only
3213 * if it matches the VM name (i.e. a structure created by default in
3214 * prepareSaveSettings()) */
3215 {
3216 Utf8Str settingsDir;
3217 if (isInOwnDir(&settingsDir))
3218 RTDirRemove(settingsDir.c_str());
3219 }
3220 }
3221
3222 return S_OK;
3223}
3224
3225STDMETHODIMP Machine::GetSnapshot (IN_BSTR aId, ISnapshot **aSnapshot)
3226{
3227 CheckComArgOutPointerValid(aSnapshot);
3228
3229 AutoCaller autoCaller(this);
3230 CheckComRCReturnRC(autoCaller.rc());
3231
3232 AutoReadLock alock(this);
3233
3234 Guid id(aId);
3235 ComObjPtr<Snapshot> snapshot;
3236
3237 HRESULT rc = findSnapshot (id, snapshot, true /* aSetError */);
3238 snapshot.queryInterfaceTo(aSnapshot);
3239
3240 return rc;
3241}
3242
3243STDMETHODIMP Machine::FindSnapshot (IN_BSTR aName, ISnapshot **aSnapshot)
3244{
3245 CheckComArgNotNull (aName);
3246 CheckComArgOutPointerValid(aSnapshot);
3247
3248 AutoCaller autoCaller(this);
3249 CheckComRCReturnRC(autoCaller.rc());
3250
3251 AutoReadLock alock(this);
3252
3253 ComObjPtr<Snapshot> snapshot;
3254
3255 HRESULT rc = findSnapshot(aName, snapshot, true /* aSetError */);
3256 snapshot.queryInterfaceTo(aSnapshot);
3257
3258 return rc;
3259}
3260
3261STDMETHODIMP Machine::SetCurrentSnapshot (IN_BSTR /* aId */)
3262{
3263 /// @todo (dmik) don't forget to set
3264 // mData->mCurrentStateModified to FALSE
3265
3266 return setError (E_NOTIMPL, "Not implemented");
3267}
3268
3269STDMETHODIMP Machine::CreateSharedFolder (IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable)
3270{
3271 CheckComArgNotNull(aName);
3272 CheckComArgNotNull(aHostPath);
3273
3274 AutoCaller autoCaller(this);
3275 CheckComRCReturnRC(autoCaller.rc());
3276
3277 AutoWriteLock alock(this);
3278
3279 HRESULT rc = checkStateDependency(MutableStateDep);
3280 CheckComRCReturnRC(rc);
3281
3282 ComObjPtr<SharedFolder> sharedFolder;
3283 rc = findSharedFolder (aName, sharedFolder, false /* aSetError */);
3284 if (SUCCEEDED(rc))
3285 return setError(VBOX_E_OBJECT_IN_USE,
3286 tr("Shared folder named '%ls' already exists"),
3287 aName);
3288
3289 sharedFolder.createObject();
3290 rc = sharedFolder->init (machine(), aName, aHostPath, aWritable);
3291 CheckComRCReturnRC(rc);
3292
3293 mHWData.backup();
3294 mHWData->mSharedFolders.push_back (sharedFolder);
3295
3296 /* inform the direct session if any */
3297 alock.leave();
3298 onSharedFolderChange();
3299
3300 return S_OK;
3301}
3302
3303STDMETHODIMP Machine::RemoveSharedFolder (IN_BSTR aName)
3304{
3305 CheckComArgNotNull (aName);
3306
3307 AutoCaller autoCaller(this);
3308 CheckComRCReturnRC(autoCaller.rc());
3309
3310 AutoWriteLock alock(this);
3311
3312 HRESULT rc = checkStateDependency(MutableStateDep);
3313 CheckComRCReturnRC(rc);
3314
3315 ComObjPtr<SharedFolder> sharedFolder;
3316 rc = findSharedFolder (aName, sharedFolder, true /* aSetError */);
3317 CheckComRCReturnRC(rc);
3318
3319 mHWData.backup();
3320 mHWData->mSharedFolders.remove (sharedFolder);
3321
3322 /* inform the direct session if any */
3323 alock.leave();
3324 onSharedFolderChange();
3325
3326 return S_OK;
3327}
3328
3329STDMETHODIMP Machine::CanShowConsoleWindow (BOOL *aCanShow)
3330{
3331 CheckComArgOutPointerValid(aCanShow);
3332
3333 /* start with No */
3334 *aCanShow = FALSE;
3335
3336 AutoCaller autoCaller(this);
3337 AssertComRCReturnRC(autoCaller.rc());
3338
3339 ComPtr<IInternalSessionControl> directControl;
3340 {
3341 AutoReadLock alock(this);
3342
3343 if (mData->mSession.mState != SessionState_Open)
3344 return setError(VBOX_E_INVALID_VM_STATE,
3345 tr("Machine session is not open (session state: %s)"),
3346 Global::stringifySessionState(mData->mSession.mState));
3347
3348 directControl = mData->mSession.mDirectControl;
3349 }
3350
3351 /* ignore calls made after #OnSessionEnd() is called */
3352 if (!directControl)
3353 return S_OK;
3354
3355 ULONG64 dummy;
3356 return directControl->OnShowWindow (TRUE /* aCheck */, aCanShow, &dummy);
3357}
3358
3359STDMETHODIMP Machine::ShowConsoleWindow (ULONG64 *aWinId)
3360{
3361 CheckComArgOutPointerValid(aWinId);
3362
3363 AutoCaller autoCaller(this);
3364 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3365
3366 ComPtr<IInternalSessionControl> directControl;
3367 {
3368 AutoReadLock alock(this);
3369
3370 if (mData->mSession.mState != SessionState_Open)
3371 return setError(E_FAIL,
3372 tr("Machine session is not open (session state: %s)"),
3373 Global::stringifySessionState(mData->mSession.mState));
3374
3375 directControl = mData->mSession.mDirectControl;
3376 }
3377
3378 /* ignore calls made after #OnSessionEnd() is called */
3379 if (!directControl)
3380 return S_OK;
3381
3382 BOOL dummy;
3383 return directControl->OnShowWindow (FALSE /* aCheck */, &dummy, aWinId);
3384}
3385
3386STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
3387 BSTR *aValue,
3388 ULONG64 *aTimestamp,
3389 BSTR *aFlags)
3390{
3391#if !defined (VBOX_WITH_GUEST_PROPS)
3392 ReturnComNotImplemented();
3393#else
3394 CheckComArgNotNull(aName);
3395 CheckComArgOutPointerValid(aValue);
3396 CheckComArgOutPointerValid(aTimestamp);
3397 CheckComArgOutPointerValid(aFlags);
3398
3399 AutoCaller autoCaller(this);
3400 CheckComRCReturnRC(autoCaller.rc());
3401
3402 AutoReadLock alock(this);
3403
3404 using namespace guestProp;
3405 HRESULT rc = E_FAIL;
3406
3407 Utf8Str strName(aName);
3408
3409 if (!mHWData->mPropertyServiceActive)
3410 {
3411 bool found = false;
3412 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
3413 (it != mHWData->mGuestProperties.end()) && !found;
3414 ++it)
3415 {
3416 if (it->strName == strName)
3417 {
3418 char szFlags[MAX_FLAGS_LEN + 1];
3419 it->strValue.cloneTo(aValue);
3420 *aTimestamp = it->mTimestamp;
3421 writeFlags(it->mFlags, szFlags);
3422 Bstr(szFlags).cloneTo(aFlags);
3423 found = true;
3424 }
3425 }
3426 rc = S_OK;
3427 }
3428 else
3429 {
3430 ComPtr<IInternalSessionControl> directControl =
3431 mData->mSession.mDirectControl;
3432
3433 /* just be on the safe side when calling another process */
3434 alock.unlock();
3435
3436 /* fail if we were called after #OnSessionEnd() is called. This is a
3437 * silly race condition. */
3438
3439 if (!directControl)
3440 rc = E_FAIL;
3441 else
3442 rc = directControl->AccessGuestProperty (aName, NULL, NULL,
3443 false /* isSetter */,
3444 aValue, aTimestamp, aFlags);
3445 }
3446 return rc;
3447#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
3448}
3449
3450STDMETHODIMP Machine::GetGuestPropertyValue (IN_BSTR aName, BSTR *aValue)
3451{
3452 ULONG64 dummyTimestamp;
3453 BSTR dummyFlags;
3454 return GetGuestProperty (aName, aValue, &dummyTimestamp, &dummyFlags);
3455}
3456
3457STDMETHODIMP Machine::GetGuestPropertyTimestamp (IN_BSTR aName, ULONG64 *aTimestamp)
3458{
3459 BSTR dummyValue;
3460 BSTR dummyFlags;
3461 return GetGuestProperty (aName, &dummyValue, aTimestamp, &dummyFlags);
3462}
3463
3464STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName,
3465 IN_BSTR aValue,
3466 IN_BSTR aFlags)
3467{
3468#if !defined (VBOX_WITH_GUEST_PROPS)
3469 ReturnComNotImplemented();
3470#else
3471 using namespace guestProp;
3472
3473 CheckComArgNotNull(aName);
3474 CheckComArgNotNull(aValue);
3475 if ((aFlags != NULL) && !VALID_PTR (aFlags))
3476 return E_INVALIDARG;
3477
3478 HRESULT rc = S_OK;
3479
3480 try
3481 {
3482 Utf8Str utf8Name(aName);
3483 Utf8Str utf8Flags(aFlags);
3484 Utf8Str utf8Patterns(mHWData->mGuestPropertyNotificationPatterns);
3485
3486 bool matchAll = false;
3487 if (utf8Patterns.isEmpty())
3488 matchAll = true;
3489
3490 uint32_t fFlags = NILFLAG;
3491 if ( (aFlags != NULL)
3492 && RT_FAILURE(validateFlags (utf8Flags.raw(), &fFlags))
3493 )
3494 return setError(E_INVALIDARG,
3495 tr("Invalid flag values: '%ls'"),
3496 aFlags);
3497
3498 AutoCaller autoCaller(this);
3499 CheckComRCReturnRC(autoCaller.rc());
3500
3501 AutoWriteLock alock(this);
3502
3503 rc = checkStateDependency(MutableStateDep);
3504 CheckComRCReturnRC(rc);
3505
3506 rc = S_OK;
3507
3508 if (!mHWData->mPropertyServiceActive)
3509 {
3510 bool found = false;
3511 HWData::GuestProperty property;
3512 property.mFlags = NILFLAG;
3513
3514 if (SUCCEEDED(rc))
3515 {
3516 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
3517 it != mHWData->mGuestProperties.end();
3518 ++it)
3519 if (it->strName == utf8Name)
3520 {
3521 property = *it;
3522 if (it->mFlags & (RDONLYHOST))
3523 rc = setError(E_ACCESSDENIED,
3524 tr("The property '%ls' cannot be changed by the host"),
3525 aName);
3526 else
3527 {
3528 mHWData.backup();
3529 /* The backup() operation invalidates our iterator, so
3530 * get a new one. */
3531 for (it = mHWData->mGuestProperties.begin();
3532 it->strName != utf8Name;
3533 ++it)
3534 ;
3535 mHWData->mGuestProperties.erase (it);
3536 }
3537 found = true;
3538 break;
3539 }
3540 }
3541 if (found && SUCCEEDED(rc))
3542 {
3543 if (*aValue)
3544 {
3545 RTTIMESPEC time;
3546 property.strValue = aValue;
3547 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
3548 if (aFlags != NULL)
3549 property.mFlags = fFlags;
3550 mHWData->mGuestProperties.push_back (property);
3551 }
3552 }
3553 else if (SUCCEEDED(rc) && *aValue)
3554 {
3555 RTTIMESPEC time;
3556 mHWData.backup();
3557 property.strName = aName;
3558 property.strValue = aValue;
3559 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
3560 property.mFlags = fFlags;
3561 mHWData->mGuestProperties.push_back (property);
3562 }
3563 if ( SUCCEEDED(rc)
3564 && ( matchAll
3565 || RTStrSimplePatternMultiMatch (utf8Patterns.raw(), RTSTR_MAX,
3566 utf8Name.raw(), RTSTR_MAX, NULL)
3567 )
3568 )
3569 mParent->onGuestPropertyChange (mData->mUuid, aName, aValue, aFlags);
3570 }
3571 else
3572 {
3573 ComPtr<IInternalSessionControl> directControl =
3574 mData->mSession.mDirectControl;
3575
3576 /* just be on the safe side when calling another process */
3577 alock.leave();
3578
3579 BSTR dummy = NULL;
3580 ULONG64 dummy64;
3581 if (!directControl)
3582 rc = E_FAIL;
3583 else
3584 rc = directControl->AccessGuestProperty
3585 (aName, *aValue ? aValue : NULL, aFlags,
3586 true /* isSetter */, &dummy, &dummy64, &dummy);
3587 }
3588 }
3589 catch (std::bad_alloc &)
3590 {
3591 rc = E_OUTOFMEMORY;
3592 }
3593
3594 return rc;
3595#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
3596}
3597
3598STDMETHODIMP Machine::SetGuestPropertyValue (IN_BSTR aName, IN_BSTR aValue)
3599{
3600 return SetGuestProperty (aName, aValue, NULL);
3601}
3602
3603STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
3604 ComSafeArrayOut(BSTR, aNames),
3605 ComSafeArrayOut(BSTR, aValues),
3606 ComSafeArrayOut(ULONG64, aTimestamps),
3607 ComSafeArrayOut(BSTR, aFlags))
3608{
3609#if !defined (VBOX_WITH_GUEST_PROPS)
3610 ReturnComNotImplemented();
3611#else
3612 if (!VALID_PTR (aPatterns) && (aPatterns != NULL))
3613 return E_POINTER;
3614
3615 CheckComArgOutSafeArrayPointerValid(aNames);
3616 CheckComArgOutSafeArrayPointerValid(aValues);
3617 CheckComArgOutSafeArrayPointerValid(aTimestamps);
3618 CheckComArgOutSafeArrayPointerValid(aFlags);
3619
3620 AutoCaller autoCaller(this);
3621 CheckComRCReturnRC(autoCaller.rc());
3622
3623 AutoReadLock alock(this);
3624
3625 using namespace guestProp;
3626 HRESULT rc = E_FAIL;
3627
3628 Utf8Str strPatterns(aPatterns);
3629
3630 bool matchAll = false;
3631 if ((NULL == aPatterns) || (0 == aPatterns[0]))
3632 matchAll = true;
3633 if (!mHWData->mPropertyServiceActive)
3634 {
3635
3636 /*
3637 * Look for matching patterns and build up a list.
3638 */
3639 HWData::GuestPropertyList propList;
3640 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
3641 it != mHWData->mGuestProperties.end();
3642 ++it)
3643 if ( matchAll
3644 || RTStrSimplePatternMultiMatch(strPatterns.raw(),
3645 RTSTR_MAX,
3646 it->strName.raw(),
3647 RTSTR_MAX, NULL)
3648 )
3649 propList.push_back(*it);
3650
3651 /*
3652 * And build up the arrays for returning the property information.
3653 */
3654 size_t cEntries = propList.size();
3655 SafeArray<BSTR> names (cEntries);
3656 SafeArray<BSTR> values (cEntries);
3657 SafeArray<ULONG64> timestamps (cEntries);
3658 SafeArray<BSTR> flags (cEntries);
3659 size_t iProp = 0;
3660 for (HWData::GuestPropertyList::iterator it = propList.begin();
3661 it != propList.end();
3662 ++it)
3663 {
3664 char szFlags[MAX_FLAGS_LEN + 1];
3665 it->strName.cloneTo(&names[iProp]);
3666 it->strValue.cloneTo(&values[iProp]);
3667 timestamps[iProp] = it->mTimestamp;
3668 writeFlags(it->mFlags, szFlags);
3669 Bstr(szFlags).cloneTo(&flags[iProp]);
3670 ++iProp;
3671 }
3672 names.detachTo(ComSafeArrayOutArg(aNames));
3673 values.detachTo(ComSafeArrayOutArg(aValues));
3674 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
3675 flags.detachTo(ComSafeArrayOutArg(aFlags));
3676 rc = S_OK;
3677 }
3678 else
3679 {
3680 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
3681
3682 /* just be on the safe side when calling another process */
3683 alock.unlock();
3684
3685 if (!directControl)
3686 rc = E_FAIL;
3687 else
3688 rc = directControl->EnumerateGuestProperties(aPatterns,
3689 ComSafeArrayOutArg(aNames),
3690 ComSafeArrayOutArg(aValues),
3691 ComSafeArrayOutArg(aTimestamps),
3692 ComSafeArrayOutArg(aFlags));
3693 }
3694 return rc;
3695#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
3696}
3697
3698STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
3699 ComSafeArrayOut(IMediumAttachment*, aAttachments))
3700{
3701 MediaData::AttachmentList atts;
3702
3703 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
3704 CheckComRCReturnRC(rc);
3705
3706 SafeIfaceArray<IMediumAttachment> attachments(atts);
3707 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
3708
3709 return S_OK;
3710}
3711
3712STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
3713 LONG aControllerPort,
3714 LONG aDevice,
3715 IMediumAttachment **aAttachment)
3716{
3717 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
3718 aControllerName, aControllerPort, aDevice));
3719
3720 CheckComArgNotNull(aControllerName);
3721 CheckComArgOutPointerValid(aAttachment);
3722
3723 AutoCaller autoCaller(this);
3724 CheckComRCReturnRC(autoCaller.rc());
3725
3726 AutoReadLock alock(this);
3727
3728 *aAttachment = NULL;
3729
3730 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
3731 aControllerName,
3732 aControllerPort,
3733 aDevice);
3734 if (pAttach.isNull())
3735 return setError(VBOX_E_OBJECT_NOT_FOUND,
3736 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3737 aDevice, aControllerPort, aControllerName);
3738
3739 pAttach.queryInterfaceTo(aAttachment);
3740
3741 return S_OK;
3742}
3743
3744STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
3745 StorageBus_T aConnectionType,
3746 IStorageController **controller)
3747{
3748 CheckComArgStrNotEmptyOrNull(aName);
3749
3750 if ( (aConnectionType <= StorageBus_Null)
3751 || (aConnectionType > StorageBus_Floppy))
3752 return setError (E_INVALIDARG,
3753 tr ("Invalid connection type: %d"),
3754 aConnectionType);
3755
3756 AutoCaller autoCaller(this);
3757 CheckComRCReturnRC(autoCaller.rc());
3758
3759 AutoWriteLock alock(this);
3760
3761 HRESULT rc = checkStateDependency(MutableStateDep);
3762 CheckComRCReturnRC(rc);
3763
3764 /* try to find one with the name first. */
3765 ComObjPtr<StorageController> ctrl;
3766
3767 rc = getStorageControllerByName (aName, ctrl, false /* aSetError */);
3768 if (SUCCEEDED(rc))
3769 return setError (VBOX_E_OBJECT_IN_USE,
3770 tr ("Storage controller named '%ls' already exists"), aName);
3771
3772 ctrl.createObject();
3773
3774 /* get a new instance number for the storage controller */
3775 ULONG ulInstance = 0;
3776 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
3777 it != mStorageControllers->end();
3778 ++it)
3779 {
3780 if ((*it)->storageBus() == aConnectionType)
3781 {
3782 ULONG ulCurInst = (*it)->instance();
3783
3784 if (ulCurInst >= ulInstance)
3785 ulInstance = ulCurInst + 1;
3786 }
3787 }
3788
3789 rc = ctrl->init(this, aName, aConnectionType, ulInstance);
3790 CheckComRCReturnRC(rc);
3791
3792 mStorageControllers.backup();
3793 mStorageControllers->push_back (ctrl);
3794
3795 ctrl.queryInterfaceTo(controller);
3796
3797 /* inform the direct session if any */
3798 alock.leave();
3799 onStorageControllerChange();
3800
3801 return S_OK;
3802}
3803
3804STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
3805 IStorageController **aStorageController)
3806{
3807 CheckComArgStrNotEmptyOrNull(aName);
3808
3809 AutoCaller autoCaller(this);
3810 CheckComRCReturnRC(autoCaller.rc());
3811
3812 AutoReadLock alock(this);
3813
3814 ComObjPtr<StorageController> ctrl;
3815
3816 HRESULT rc = getStorageControllerByName (aName, ctrl, true /* aSetError */);
3817 if (SUCCEEDED(rc))
3818 ctrl.queryInterfaceTo(aStorageController);
3819
3820 return rc;
3821}
3822
3823STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
3824 IStorageController **aStorageController)
3825{
3826 AutoCaller autoCaller(this);
3827 CheckComRCReturnRC(autoCaller.rc());
3828
3829 AutoReadLock alock(this);
3830
3831 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
3832 it != mStorageControllers->end();
3833 ++it)
3834 {
3835 if ((*it)->instance() == aInstance)
3836 {
3837 (*it).queryInterfaceTo(aStorageController);
3838 return S_OK;
3839 }
3840 }
3841
3842 return setError(VBOX_E_OBJECT_NOT_FOUND,
3843 tr("Could not find a storage controller with instance number '%lu'"),
3844 aInstance);
3845}
3846
3847STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
3848{
3849 CheckComArgStrNotEmptyOrNull(aName);
3850
3851 AutoCaller autoCaller(this);
3852 CheckComRCReturnRC(autoCaller.rc());
3853
3854 AutoWriteLock alock(this);
3855
3856 HRESULT rc = checkStateDependency(MutableStateDep);
3857 CheckComRCReturnRC(rc);
3858
3859 ComObjPtr<StorageController> ctrl;
3860 rc = getStorageControllerByName (aName, ctrl, true /* aSetError */);
3861 CheckComRCReturnRC(rc);
3862
3863 /* We can remove the controller only if there is no device attached. */
3864 /* check if the device slot is already busy */
3865 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
3866 it != mMediaData->mAttachments.end();
3867 ++it)
3868 {
3869 if (Bstr((*it)->controllerName()) == aName)
3870 return setError(VBOX_E_OBJECT_IN_USE,
3871 tr("Storage controller named '%ls' has still devices attached"),
3872 aName);
3873 }
3874
3875 /* We can remove it now. */
3876 mStorageControllers.backup();
3877
3878 ctrl->unshare();
3879
3880 mStorageControllers->remove (ctrl);
3881
3882 /* inform the direct session if any */
3883 alock.leave();
3884 onStorageControllerChange();
3885
3886 return S_OK;
3887}
3888
3889// public methods for internal purposes
3890/////////////////////////////////////////////////////////////////////////////
3891
3892/**
3893 * Saves the registry entry of this machine to the given configuration node.
3894 *
3895 * @param aEntryNode Node to save the registry entry to.
3896 *
3897 * @note locks this object for reading.
3898 */
3899HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
3900{
3901 AutoLimitedCaller autoCaller(this);
3902 AssertComRCReturnRC(autoCaller.rc());
3903
3904 AutoReadLock alock(this);
3905
3906 data.uuid = mData->mUuid;
3907 data.strSettingsFile = mData->m_strConfigFile;
3908
3909 return S_OK;
3910}
3911
3912/**
3913 * Calculates the absolute path of the given path taking the directory of the
3914 * machine settings file as the current directory.
3915 *
3916 * @param aPath Path to calculate the absolute path for.
3917 * @param aResult Where to put the result (used only on success, can be the
3918 * same Utf8Str instance as passed in @a aPath).
3919 * @return IPRT result.
3920 *
3921 * @note Locks this object for reading.
3922 */
3923int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
3924{
3925 AutoCaller autoCaller(this);
3926 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3927
3928 AutoReadLock alock(this);
3929
3930 AssertReturn (!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
3931
3932 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
3933
3934 strSettingsDir.stripFilename();
3935 char folder[RTPATH_MAX];
3936 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
3937 if (RT_SUCCESS(vrc))
3938 aResult = folder;
3939
3940 return vrc;
3941}
3942
3943/**
3944 * Tries to calculate the relative path of the given absolute path using the
3945 * directory of the machine settings file as the base directory.
3946 *
3947 * @param aPath Absolute path to calculate the relative path for.
3948 * @param aResult Where to put the result (used only when it's possible to
3949 * make a relative path from the given absolute path; otherwise
3950 * left untouched).
3951 *
3952 * @note Locks this object for reading.
3953 */
3954void Machine::calculateRelativePath(const Utf8Str &strPath, Utf8Str &aResult)
3955{
3956 AutoCaller autoCaller(this);
3957 AssertComRCReturn (autoCaller.rc(), (void) 0);
3958
3959 AutoReadLock alock(this);
3960
3961 AssertReturnVoid (!mData->m_strConfigFileFull.isEmpty());
3962
3963 Utf8Str settingsDir = mData->m_strConfigFileFull;
3964
3965 settingsDir.stripFilename();
3966 if (RTPathStartsWith(strPath.c_str(), settingsDir.c_str()))
3967 {
3968 /* when assigning, we create a separate Utf8Str instance because both
3969 * aPath and aResult can point to the same memory location when this
3970 * func is called (if we just do aResult = aPath, aResult will be freed
3971 * first, and since its the same as aPath, an attempt to copy garbage
3972 * will be made. */
3973 aResult = Utf8Str(strPath.c_str() + settingsDir.length() + 1);
3974 }
3975}
3976
3977/**
3978 * Returns the full path to the machine's log folder in the
3979 * \a aLogFolder argument.
3980 */
3981void Machine::getLogFolder (Utf8Str &aLogFolder)
3982{
3983 AutoCaller autoCaller(this);
3984 AssertComRCReturnVoid (autoCaller.rc());
3985
3986 AutoReadLock alock(this);
3987
3988 Utf8Str settingsDir;
3989 if (isInOwnDir (&settingsDir))
3990 {
3991 /* Log folder is <Machines>/<VM_Name>/Logs */
3992 aLogFolder = Utf8StrFmt ("%s%cLogs", settingsDir.raw(), RTPATH_DELIMITER);
3993 }
3994 else
3995 {
3996 /* Log folder is <Machines>/<VM_SnapshotFolder>/Logs */
3997 Assert (!mUserData->mSnapshotFolderFull.isEmpty());
3998 aLogFolder = Utf8StrFmt ("%ls%cLogs", mUserData->mSnapshotFolderFull.raw(),
3999 RTPATH_DELIMITER);
4000 }
4001}
4002
4003/**
4004 * @note Locks this object for writing, calls the client process (outside the
4005 * lock).
4006 */
4007HRESULT Machine::openSession(IInternalSessionControl *aControl)
4008{
4009 LogFlowThisFuncEnter();
4010
4011 AssertReturn(aControl, E_FAIL);
4012
4013 AutoCaller autoCaller(this);
4014 CheckComRCReturnRC(autoCaller.rc());
4015
4016 AutoWriteLock alock(this);
4017
4018 if (!mData->mRegistered)
4019 return setError(E_UNEXPECTED,
4020 tr("The machine '%ls' is not registered"),
4021 mUserData->mName.raw());
4022
4023 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4024
4025 /* Hack: in case the session is closing and there is a progress object
4026 * which allows waiting for the session to be closed, take the opportunity
4027 * and do a limited wait (max. 1 second). This helps a lot when the system
4028 * is busy and thus session closing can take a little while. */
4029 if ( mData->mSession.mState == SessionState_Closing
4030 && mData->mSession.mProgress)
4031 {
4032 alock.leave();
4033 mData->mSession.mProgress->WaitForCompletion(1000);
4034 alock.enter();
4035 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4036 }
4037
4038 if (mData->mSession.mState == SessionState_Open ||
4039 mData->mSession.mState == SessionState_Closing)
4040 return setError(VBOX_E_INVALID_OBJECT_STATE,
4041 tr("A session for the machine '%ls' is currently open (or being closed)"),
4042 mUserData->mName.raw());
4043
4044 /* may not be busy */
4045 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
4046
4047 /* get the session PID */
4048 RTPROCESS pid = NIL_RTPROCESS;
4049 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
4050 aControl->GetPID((ULONG *) &pid);
4051 Assert(pid != NIL_RTPROCESS);
4052
4053 if (mData->mSession.mState == SessionState_Spawning)
4054 {
4055 /* This machine is awaiting for a spawning session to be opened, so
4056 * reject any other open attempts from processes other than one
4057 * started by #openRemoteSession(). */
4058
4059 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n",
4060 mData->mSession.mPid, mData->mSession.mPid));
4061 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
4062
4063 if (mData->mSession.mPid != pid)
4064 return setError(E_ACCESSDENIED,
4065 tr("An unexpected process (PID=0x%08X) has tried to open a direct "
4066 "session with the machine named '%ls', while only a process "
4067 "started by OpenRemoteSession (PID=0x%08X) is allowed"),
4068 pid, mUserData->mName.raw(), mData->mSession.mPid);
4069 }
4070
4071 /* create a SessionMachine object */
4072 ComObjPtr<SessionMachine> sessionMachine;
4073 sessionMachine.createObject();
4074 HRESULT rc = sessionMachine->init(this);
4075 AssertComRC(rc);
4076
4077 /* NOTE: doing return from this function after this point but
4078 * before the end is forbidden since it may call SessionMachine::uninit()
4079 * (through the ComObjPtr's destructor) which requests the VirtualBox write
4080 * lock while still holding the Machine lock in alock so that a deadlock
4081 * is possible due to the wrong lock order. */
4082
4083 if (SUCCEEDED(rc))
4084 {
4085#ifdef VBOX_WITH_RESOURCE_USAGE_API
4086 registerMetrics(mParent->performanceCollector(), this, pid);
4087#endif /* VBOX_WITH_RESOURCE_USAGE_API */
4088
4089 /*
4090 * Set the session state to Spawning to protect against subsequent
4091 * attempts to open a session and to unregister the machine after
4092 * we leave the lock.
4093 */
4094 SessionState_T origState = mData->mSession.mState;
4095 mData->mSession.mState = SessionState_Spawning;
4096
4097 /*
4098 * Leave the lock before calling the client process -- it will call
4099 * Machine/SessionMachine methods. Leaving the lock here is quite safe
4100 * because the state is Spawning, so that openRemotesession() and
4101 * openExistingSession() calls will fail. This method, called before we
4102 * enter the lock again, will fail because of the wrong PID.
4103 *
4104 * Note that mData->mSession.mRemoteControls accessed outside
4105 * the lock may not be modified when state is Spawning, so it's safe.
4106 */
4107 alock.leave();
4108
4109 LogFlowThisFunc(("Calling AssignMachine()...\n"));
4110 rc = aControl->AssignMachine(sessionMachine);
4111 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
4112
4113 /* The failure may occur w/o any error info (from RPC), so provide one */
4114 if (FAILED(rc))
4115 setError(VBOX_E_VM_ERROR,
4116 tr("Failed to assign the machine to the session (%Rrc)"), rc);
4117
4118 if (SUCCEEDED(rc) && origState == SessionState_Spawning)
4119 {
4120 /* complete the remote session initialization */
4121
4122 /* get the console from the direct session */
4123 ComPtr<IConsole> console;
4124 rc = aControl->GetRemoteConsole(console.asOutParam());
4125 ComAssertComRC(rc);
4126
4127 if (SUCCEEDED(rc) && !console)
4128 {
4129 ComAssert(!!console);
4130 rc = E_FAIL;
4131 }
4132
4133 /* assign machine & console to the remote session */
4134 if (SUCCEEDED(rc))
4135 {
4136 /*
4137 * after openRemoteSession(), the first and the only
4138 * entry in remoteControls is that remote session
4139 */
4140 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
4141 rc = mData->mSession.mRemoteControls.front()->
4142 AssignRemoteMachine(sessionMachine, console);
4143 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
4144
4145 /* The failure may occur w/o any error info (from RPC), so provide one */
4146 if (FAILED(rc))
4147 setError(VBOX_E_VM_ERROR,
4148 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
4149 }
4150
4151 if (FAILED(rc))
4152 aControl->Uninitialize();
4153 }
4154
4155 /* enter the lock again */
4156 alock.enter();
4157
4158 /* Restore the session state */
4159 mData->mSession.mState = origState;
4160 }
4161
4162 /* finalize spawning anyway (this is why we don't return on errors above) */
4163 if (mData->mSession.mState == SessionState_Spawning)
4164 {
4165 /* Note that the progress object is finalized later */
4166
4167 /* We don't reset mSession.mPid here because it is necessary for
4168 * SessionMachine::uninit() to reap the child process later. */
4169
4170 if (FAILED(rc))
4171 {
4172 /* Close the remote session, remove the remote control from the list
4173 * and reset session state to Closed (@note keep the code in sync
4174 * with the relevant part in openSession()). */
4175
4176 Assert (mData->mSession.mRemoteControls.size() == 1);
4177 if (mData->mSession.mRemoteControls.size() == 1)
4178 {
4179 ErrorInfoKeeper eik;
4180 mData->mSession.mRemoteControls.front()->Uninitialize();
4181 }
4182
4183 mData->mSession.mRemoteControls.clear();
4184 mData->mSession.mState = SessionState_Closed;
4185 }
4186 }
4187 else
4188 {
4189 /* memorize PID of the directly opened session */
4190 if (SUCCEEDED(rc))
4191 mData->mSession.mPid = pid;
4192 }
4193
4194 if (SUCCEEDED(rc))
4195 {
4196 /* memorize the direct session control and cache IUnknown for it */
4197 mData->mSession.mDirectControl = aControl;
4198 mData->mSession.mState = SessionState_Open;
4199 /* associate the SessionMachine with this Machine */
4200 mData->mSession.mMachine = sessionMachine;
4201
4202 /* request an IUnknown pointer early from the remote party for later
4203 * identity checks (it will be internally cached within mDirectControl
4204 * at least on XPCOM) */
4205 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
4206 NOREF(unk);
4207 }
4208
4209 if (mData->mSession.mProgress)
4210 {
4211 /* finalize the progress after setting the state, for consistency */
4212 mData->mSession.mProgress->notifyComplete(rc);
4213 mData->mSession.mProgress.setNull();
4214 }
4215
4216 /* Leave the lock since SessionMachine::uninit() locks VirtualBox which
4217 * would break the lock order */
4218 alock.leave();
4219
4220 /* uninitialize the created session machine on failure */
4221 if (FAILED(rc))
4222 sessionMachine->uninit();
4223
4224 LogFlowThisFunc(("rc=%08X\n", rc));
4225 LogFlowThisFuncLeave();
4226 return rc;
4227}
4228
4229/**
4230 * @note Locks this object for writing, calls the client process
4231 * (inside the lock).
4232 */
4233HRESULT Machine::openRemoteSession(IInternalSessionControl *aControl,
4234 IN_BSTR aType,
4235 IN_BSTR aEnvironment,
4236 Progress *aProgress)
4237{
4238 LogFlowThisFuncEnter();
4239
4240 AssertReturn(aControl, E_FAIL);
4241 AssertReturn(aProgress, E_FAIL);
4242
4243 AutoCaller autoCaller(this);
4244 CheckComRCReturnRC(autoCaller.rc());
4245
4246 AutoWriteLock alock(this);
4247
4248 if (!mData->mRegistered)
4249 return setError(E_UNEXPECTED,
4250 tr("The machine '%ls' is not registered"),
4251 mUserData->mName.raw());
4252
4253 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4254
4255 if (mData->mSession.mState == SessionState_Open ||
4256 mData->mSession.mState == SessionState_Spawning ||
4257 mData->mSession.mState == SessionState_Closing)
4258 return setError(VBOX_E_INVALID_OBJECT_STATE,
4259 tr("A session for the machine '%ls' is currently open (or being opened or closed)"),
4260 mUserData->mName.raw());
4261
4262 /* may not be busy */
4263 AssertReturn(!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
4264
4265 /* get the path to the executable */
4266 char szPath[RTPATH_MAX];
4267 RTPathAppPrivateArch(szPath, RTPATH_MAX);
4268 size_t sz = strlen(szPath);
4269 szPath[sz++] = RTPATH_DELIMITER;
4270 szPath[sz] = 0;
4271 char *cmd = szPath + sz;
4272 sz = RTPATH_MAX - sz;
4273
4274 int vrc = VINF_SUCCESS;
4275 RTPROCESS pid = NIL_RTPROCESS;
4276
4277 RTENV env = RTENV_DEFAULT;
4278
4279 if (aEnvironment != NULL && *aEnvironment)
4280 {
4281 char *newEnvStr = NULL;
4282
4283 do
4284 {
4285 /* clone the current environment */
4286 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
4287 AssertRCBreakStmt(vrc2, vrc = vrc2);
4288
4289 newEnvStr = RTStrDup(Utf8Str(aEnvironment).c_str());
4290 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
4291
4292 /* put new variables to the environment
4293 * (ignore empty variable names here since RTEnv API
4294 * intentionally doesn't do that) */
4295 char *var = newEnvStr;
4296 for (char *p = newEnvStr; *p; ++p)
4297 {
4298 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
4299 {
4300 *p = '\0';
4301 if (*var)
4302 {
4303 char *val = strchr (var, '=');
4304 if (val)
4305 {
4306 *val++ = '\0';
4307 vrc2 = RTEnvSetEx (env, var, val);
4308 }
4309 else
4310 vrc2 = RTEnvUnsetEx (env, var);
4311 if (RT_FAILURE(vrc2))
4312 break;
4313 }
4314 var = p + 1;
4315 }
4316 }
4317 if (RT_SUCCESS(vrc2) && *var)
4318 vrc2 = RTEnvPutEx (env, var);
4319
4320 AssertRCBreakStmt (vrc2, vrc = vrc2);
4321 }
4322 while (0);
4323
4324 if (newEnvStr != NULL)
4325 RTStrFree(newEnvStr);
4326 }
4327
4328 Bstr type (aType);
4329
4330 /* Qt is default */
4331#ifdef VBOX_WITH_QTGUI
4332 if (type == "gui" || type == "GUI/Qt")
4333 {
4334# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
4335 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
4336# else
4337 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
4338# endif
4339 Assert (sz >= sizeof (VirtualBox_exe));
4340 strcpy (cmd, VirtualBox_exe);
4341
4342 Utf8Str idStr = mData->mUuid.toString();
4343# ifdef RT_OS_WINDOWS /** @todo drop this once the RTProcCreate bug has been fixed */
4344 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0 };
4345# else
4346 Utf8Str name = mUserData->mName;
4347 const char * args[] = {szPath, "--comment", name.c_str(), "--startvm", idStr.c_str(), 0 };
4348# endif
4349 vrc = RTProcCreate(szPath, args, env, 0, &pid);
4350 }
4351#else /* !VBOX_WITH_QTGUI */
4352 if (0)
4353 ;
4354#endif /* VBOX_WITH_QTGUI */
4355
4356 else
4357
4358#ifdef VBOX_WITH_VBOXSDL
4359 if (type == "sdl" || type == "GUI/SDL")
4360 {
4361 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
4362 Assert (sz >= sizeof (VBoxSDL_exe));
4363 strcpy (cmd, VBoxSDL_exe);
4364
4365 Utf8Str idStr = mData->mUuid.toString();
4366# ifdef RT_OS_WINDOWS
4367 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0 };
4368# else
4369 Utf8Str name = mUserData->mName;
4370 const char * args[] = {szPath, "--comment", name.c_str(), "--startvm", idStr.c_str(), 0 };
4371# endif
4372 vrc = RTProcCreate(szPath, args, env, 0, &pid);
4373 }
4374#else /* !VBOX_WITH_VBOXSDL */
4375 if (0)
4376 ;
4377#endif /* !VBOX_WITH_VBOXSDL */
4378
4379 else
4380
4381#ifdef VBOX_WITH_HEADLESS
4382 if ( type == "headless"
4383 || type == "capture"
4384#ifdef VBOX_WITH_VRDP
4385 || type == "vrdp"
4386#endif
4387 )
4388 {
4389 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
4390 Assert (sz >= sizeof (VBoxHeadless_exe));
4391 strcpy (cmd, VBoxHeadless_exe);
4392
4393 Utf8Str idStr = mData->mUuid.toString();
4394 /* Leave space for 2 args, as "headless" needs --vrdp off on non-OSE. */
4395# ifdef RT_OS_WINDOWS
4396 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0, 0, 0 };
4397# else
4398 Utf8Str name = mUserData->mName;
4399 const char * args[] ={szPath, "--comment", name.c_str(), "--startvm", idStr.c_str(), 0, 0, 0 };
4400# endif
4401#ifdef VBOX_WITH_VRDP
4402 if (type == "headless")
4403 {
4404 unsigned pos = RT_ELEMENTS(args) - 3;
4405 args[pos++] = "--vrdp";
4406 args[pos] = "off";
4407 }
4408#endif
4409 if (type == "capture")
4410 {
4411 unsigned pos = RT_ELEMENTS(args) - 3;
4412 args[pos] = "--capture";
4413 }
4414 vrc = RTProcCreate(szPath, args, env, 0, &pid);
4415 }
4416#else /* !VBOX_WITH_HEADLESS */
4417 if (0)
4418 ;
4419#endif /* !VBOX_WITH_HEADLESS */
4420 else
4421 {
4422 RTEnvDestroy (env);
4423 return setError (E_INVALIDARG,
4424 tr ("Invalid session type: '%ls'"), aType);
4425 }
4426
4427 RTEnvDestroy (env);
4428
4429 if (RT_FAILURE(vrc))
4430 return setError (VBOX_E_IPRT_ERROR,
4431 tr ("Could not launch a process for the machine '%ls' (%Rrc)"),
4432 mUserData->mName.raw(), vrc);
4433
4434 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
4435
4436 /*
4437 * Note that we don't leave the lock here before calling the client,
4438 * because it doesn't need to call us back if called with a NULL argument.
4439 * Leaving the lock herer is dangerous because we didn't prepare the
4440 * launch data yet, but the client we've just started may happen to be
4441 * too fast and call openSession() that will fail (because of PID, etc.),
4442 * so that the Machine will never get out of the Spawning session state.
4443 */
4444
4445 /* inform the session that it will be a remote one */
4446 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
4447 HRESULT rc = aControl->AssignMachine (NULL);
4448 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
4449
4450 if (FAILED(rc))
4451 {
4452 /* restore the session state */
4453 mData->mSession.mState = SessionState_Closed;
4454 /* The failure may occur w/o any error info (from RPC), so provide one */
4455 return setError(VBOX_E_VM_ERROR,
4456 tr("Failed to assign the machine to the session (%Rrc)"), rc);
4457 }
4458
4459 /* attach launch data to the machine */
4460 Assert (mData->mSession.mPid == NIL_RTPROCESS);
4461 mData->mSession.mRemoteControls.push_back (aControl);
4462 mData->mSession.mProgress = aProgress;
4463 mData->mSession.mPid = pid;
4464 mData->mSession.mState = SessionState_Spawning;
4465 mData->mSession.mType = type;
4466
4467 LogFlowThisFuncLeave();
4468 return S_OK;
4469}
4470
4471/**
4472 * @note Locks this object for writing, calls the client process
4473 * (outside the lock).
4474 */
4475HRESULT Machine::openExistingSession (IInternalSessionControl *aControl)
4476{
4477 LogFlowThisFuncEnter();
4478
4479 AssertReturn(aControl, E_FAIL);
4480
4481 AutoCaller autoCaller(this);
4482 CheckComRCReturnRC(autoCaller.rc());
4483
4484 AutoWriteLock alock(this);
4485
4486 if (!mData->mRegistered)
4487 return setError (E_UNEXPECTED,
4488 tr ("The machine '%ls' is not registered"), mUserData->mName.raw());
4489
4490 LogFlowThisFunc(("mSession.state=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4491
4492 if (mData->mSession.mState != SessionState_Open)
4493 return setError (VBOX_E_INVALID_SESSION_STATE,
4494 tr ("The machine '%ls' does not have an open session"),
4495 mUserData->mName.raw());
4496
4497 ComAssertRet (!mData->mSession.mDirectControl.isNull(), E_FAIL);
4498
4499 /*
4500 * Get the console from the direct session (note that we don't leave the
4501 * lock here because GetRemoteConsole must not call us back).
4502 */
4503 ComPtr<IConsole> console;
4504 HRESULT rc = mData->mSession.mDirectControl->
4505 GetRemoteConsole (console.asOutParam());
4506 if (FAILED (rc))
4507 {
4508 /* The failure may occur w/o any error info (from RPC), so provide one */
4509 return setError (VBOX_E_VM_ERROR,
4510 tr ("Failed to get a console object from the direct session (%Rrc)"), rc);
4511 }
4512
4513 ComAssertRet (!console.isNull(), E_FAIL);
4514
4515 ComObjPtr<SessionMachine> sessionMachine = mData->mSession.mMachine;
4516 AssertReturn(!sessionMachine.isNull(), E_FAIL);
4517
4518 /*
4519 * Leave the lock before calling the client process. It's safe here
4520 * since the only thing to do after we get the lock again is to add
4521 * the remote control to the list (which doesn't directly influence
4522 * anything).
4523 */
4524 alock.leave();
4525
4526 /* attach the remote session to the machine */
4527 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
4528 rc = aControl->AssignRemoteMachine (sessionMachine, console);
4529 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
4530
4531 /* The failure may occur w/o any error info (from RPC), so provide one */
4532 if (FAILED(rc))
4533 return setError(VBOX_E_VM_ERROR,
4534 tr("Failed to assign the machine to the session (%Rrc)"),
4535 rc);
4536
4537 alock.enter();
4538
4539 /* need to revalidate the state after entering the lock again */
4540 if (mData->mSession.mState != SessionState_Open)
4541 {
4542 aControl->Uninitialize();
4543
4544 return setError(VBOX_E_INVALID_SESSION_STATE,
4545 tr("The machine '%ls' does not have an open session"),
4546 mUserData->mName.raw());
4547 }
4548
4549 /* store the control in the list */
4550 mData->mSession.mRemoteControls.push_back (aControl);
4551
4552 LogFlowThisFuncLeave();
4553 return S_OK;
4554}
4555
4556/**
4557 * Returns @c true if the given machine has an open direct session and returns
4558 * the session machine instance and additional session data (on some platforms)
4559 * if so.
4560 *
4561 * Note that when the method returns @c false, the arguments remain unchanged.
4562 *
4563 * @param aMachine Session machine object.
4564 * @param aControl Direct session control object (optional).
4565 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
4566 *
4567 * @note locks this object for reading.
4568 */
4569#if defined (RT_OS_WINDOWS)
4570bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
4571 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
4572 HANDLE *aIPCSem /*= NULL*/,
4573 bool aAllowClosing /*= false*/)
4574#elif defined (RT_OS_OS2)
4575bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
4576 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
4577 HMTX *aIPCSem /*= NULL*/,
4578 bool aAllowClosing /*= false*/)
4579#else
4580bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
4581 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
4582 bool aAllowClosing /*= false*/)
4583#endif
4584{
4585 AutoLimitedCaller autoCaller(this);
4586 AssertComRCReturn (autoCaller.rc(), false);
4587
4588 /* just return false for inaccessible machines */
4589 if (autoCaller.state() != Ready)
4590 return false;
4591
4592 AutoReadLock alock(this);
4593
4594 if (mData->mSession.mState == SessionState_Open ||
4595 (aAllowClosing && mData->mSession.mState == SessionState_Closing))
4596 {
4597 AssertReturn(!mData->mSession.mMachine.isNull(), false);
4598
4599 aMachine = mData->mSession.mMachine;
4600
4601 if (aControl != NULL)
4602 *aControl = mData->mSession.mDirectControl;
4603
4604#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4605 /* Additional session data */
4606 if (aIPCSem != NULL)
4607 *aIPCSem = aMachine->mIPCSem;
4608#endif
4609 return true;
4610 }
4611
4612 return false;
4613}
4614
4615/**
4616 * Returns @c true if the given machine has an spawning direct session and
4617 * returns and additional session data (on some platforms) if so.
4618 *
4619 * Note that when the method returns @c false, the arguments remain unchanged.
4620 *
4621 * @param aPID PID of the spawned direct session process.
4622 *
4623 * @note locks this object for reading.
4624 */
4625#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4626bool Machine::isSessionSpawning (RTPROCESS *aPID /*= NULL*/)
4627#else
4628bool Machine::isSessionSpawning()
4629#endif
4630{
4631 AutoLimitedCaller autoCaller(this);
4632 AssertComRCReturn (autoCaller.rc(), false);
4633
4634 /* just return false for inaccessible machines */
4635 if (autoCaller.state() != Ready)
4636 return false;
4637
4638 AutoReadLock alock(this);
4639
4640 if (mData->mSession.mState == SessionState_Spawning)
4641 {
4642#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4643 /* Additional session data */
4644 if (aPID != NULL)
4645 {
4646 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
4647 *aPID = mData->mSession.mPid;
4648 }
4649#endif
4650 return true;
4651 }
4652
4653 return false;
4654}
4655
4656/**
4657 * Called from the client watcher thread to check for unexpected client process
4658 * death during Session_Spawning state (e.g. before it successfully opened a
4659 * direct session).
4660 *
4661 * On Win32 and on OS/2, this method is called only when we've got the
4662 * direct client's process termination notification, so it always returns @c
4663 * true.
4664 *
4665 * On other platforms, this method returns @c true if the client process is
4666 * terminated and @c false if it's still alive.
4667 *
4668 * @note Locks this object for writing.
4669 */
4670bool Machine::checkForSpawnFailure()
4671{
4672 AutoCaller autoCaller(this);
4673 if (!autoCaller.isOk())
4674 {
4675 /* nothing to do */
4676 LogFlowThisFunc(("Already uninitialized!\n"));
4677 return true;
4678 }
4679
4680 /* VirtualBox::addProcessToReap() needs a write lock */
4681 AutoMultiWriteLock2 alock(mParent, this);
4682
4683 if (mData->mSession.mState != SessionState_Spawning)
4684 {
4685 /* nothing to do */
4686 LogFlowThisFunc(("Not spawning any more!\n"));
4687 return true;
4688 }
4689
4690 HRESULT rc = S_OK;
4691
4692#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
4693
4694 /* the process was already unexpectedly terminated, we just need to set an
4695 * error and finalize session spawning */
4696 rc = setError(E_FAIL,
4697 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
4698 name().raw());
4699#else
4700
4701 /* PID not yet initialized, skip check. */
4702 if (mData->mSession.mPid == NIL_RTPROCESS)
4703 return false;
4704
4705 RTPROCSTATUS status;
4706 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
4707 &status);
4708
4709 if (vrc != VERR_PROCESS_RUNNING)
4710 rc = setError(E_FAIL,
4711 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
4712 name().raw());
4713#endif
4714
4715 if (FAILED(rc))
4716 {
4717 /* Close the remote session, remove the remote control from the list
4718 * and reset session state to Closed (@note keep the code in sync with
4719 * the relevant part in checkForSpawnFailure()). */
4720
4721 Assert(mData->mSession.mRemoteControls.size() == 1);
4722 if (mData->mSession.mRemoteControls.size() == 1)
4723 {
4724 ErrorInfoKeeper eik;
4725 mData->mSession.mRemoteControls.front()->Uninitialize();
4726 }
4727
4728 mData->mSession.mRemoteControls.clear();
4729 mData->mSession.mState = SessionState_Closed;
4730
4731 /* finalize the progress after setting the state, for consistency */
4732 if (!mData->mSession.mProgress.isNull())
4733 {
4734 mData->mSession.mProgress->notifyComplete(rc);
4735 mData->mSession.mProgress.setNull();
4736 }
4737
4738 mParent->addProcessToReap(mData->mSession.mPid);
4739 mData->mSession.mPid = NIL_RTPROCESS;
4740
4741 mParent->onSessionStateChange(mData->mUuid, SessionState_Closed);
4742 return true;
4743 }
4744
4745 return false;
4746}
4747
4748/**
4749 * Checks that the registered flag of the machine can be set according to
4750 * the argument and sets it. On success, commits and saves all settings.
4751 *
4752 * @note When this machine is inaccessible, the only valid value for \a
4753 * aRegistered is FALSE (i.e. unregister the machine) because unregistered
4754 * inaccessible machines are not currently supported. Note that unregistering
4755 * an inaccessible machine will \b uninitialize this machine object. Therefore,
4756 * the caller must make sure there are no active Machine::addCaller() calls
4757 * on the current thread because this will block Machine::uninit().
4758 *
4759 * @note Must be called from mParent's write lock. Locks this object and
4760 * children for writing.
4761 */
4762HRESULT Machine::trySetRegistered (BOOL aRegistered)
4763{
4764 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
4765
4766 AutoLimitedCaller autoCaller(this);
4767 AssertComRCReturnRC(autoCaller.rc());
4768
4769 AutoWriteLock alock(this);
4770
4771 /* wait for state dependants to drop to zero */
4772 ensureNoStateDependencies();
4773
4774 ComAssertRet (mData->mRegistered != aRegistered, E_FAIL);
4775
4776 if (!mData->mAccessible)
4777 {
4778 /* A special case: the machine is not accessible. */
4779
4780 /* inaccessible machines can only be unregistered */
4781 AssertReturn(!aRegistered, E_FAIL);
4782
4783 /* Uninitialize ourselves here because currently there may be no
4784 * unregistered that are inaccessible (this state combination is not
4785 * supported). Note releasing the caller and leaving the lock before
4786 * calling uninit() */
4787
4788 alock.leave();
4789 autoCaller.release();
4790
4791 uninit();
4792
4793 return S_OK;
4794 }
4795
4796 AssertReturn(autoCaller.state() == Ready, E_FAIL);
4797
4798 if (aRegistered)
4799 {
4800 if (mData->mRegistered)
4801 return setError(VBOX_E_INVALID_OBJECT_STATE,
4802 tr("The machine '%ls' with UUID {%s} is already registered"),
4803 mUserData->mName.raw(),
4804 mData->mUuid.toString().raw());
4805 }
4806 else
4807 {
4808 if (mData->mMachineState == MachineState_Saved)
4809 return setError(VBOX_E_INVALID_VM_STATE,
4810 tr("Cannot unregister the machine '%ls' because it is in the Saved state"),
4811 mUserData->mName.raw());
4812
4813 size_t snapshotCount = 0;
4814 if (mData->mFirstSnapshot)
4815 snapshotCount = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4816 if (snapshotCount)
4817 return setError(VBOX_E_INVALID_OBJECT_STATE,
4818 tr("Cannot unregister the machine '%ls' because it has %d snapshots"),
4819 mUserData->mName.raw(), snapshotCount);
4820
4821 if (mData->mSession.mState != SessionState_Closed)
4822 return setError(VBOX_E_INVALID_OBJECT_STATE,
4823 tr("Cannot unregister the machine '%ls' because it has an open session"),
4824 mUserData->mName.raw());
4825
4826 if (mMediaData->mAttachments.size() != 0)
4827 return setError(VBOX_E_INVALID_OBJECT_STATE,
4828 tr("Cannot unregister the machine '%ls' because it has %d medium attachments"),
4829 mUserData->mName.raw(),
4830 mMediaData->mAttachments.size());
4831
4832 /* Note that we do not prevent unregistration of a DVD or Floppy image
4833 * is attached: as opposed to hard disks detaching such an image
4834 * implicitly in this method (which we will do below) won't have any
4835 * side effects (like detached orphan base and diff hard disks etc).*/
4836 }
4837
4838 HRESULT rc = S_OK;
4839
4840 /* Ensure the settings are saved. If we are going to be registered and
4841 * isConfigLocked() is FALSE then it means that no config file exists yet,
4842 * so create it by calling saveSettings() too. */
4843 if ( isModified()
4844 || (aRegistered && !mData->m_pMachineConfigFile->fileExists())
4845 )
4846 {
4847 rc = saveSettings();
4848 CheckComRCReturnRC(rc);
4849 }
4850
4851 /* more config checking goes here */
4852
4853 if (SUCCEEDED(rc))
4854 {
4855 /* we may have had implicit modifications we want to fix on success */
4856 commit();
4857
4858 mData->mRegistered = aRegistered;
4859 }
4860 else
4861 {
4862 /* we may have had implicit modifications we want to cancel on failure*/
4863 rollback (false /* aNotify */);
4864 }
4865
4866 return rc;
4867}
4868
4869/**
4870 * Increases the number of objects dependent on the machine state or on the
4871 * registered state. Guarantees that these two states will not change at least
4872 * until #releaseStateDependency() is called.
4873 *
4874 * Depending on the @a aDepType value, additional state checks may be made.
4875 * These checks will set extended error info on failure. See
4876 * #checkStateDependency() for more info.
4877 *
4878 * If this method returns a failure, the dependency is not added and the caller
4879 * is not allowed to rely on any particular machine state or registration state
4880 * value and may return the failed result code to the upper level.
4881 *
4882 * @param aDepType Dependency type to add.
4883 * @param aState Current machine state (NULL if not interested).
4884 * @param aRegistered Current registered state (NULL if not interested).
4885 *
4886 * @note Locks this object for writing.
4887 */
4888HRESULT Machine::addStateDependency (StateDependency aDepType /* = AnyStateDep */,
4889 MachineState_T *aState /* = NULL */,
4890 BOOL *aRegistered /* = NULL */)
4891{
4892 AutoCaller autoCaller(this);
4893 AssertComRCReturnRC(autoCaller.rc());
4894
4895 AutoWriteLock alock(this);
4896
4897 HRESULT rc = checkStateDependency(aDepType);
4898 CheckComRCReturnRC(rc);
4899
4900 {
4901 if (mData->mMachineStateChangePending != 0)
4902 {
4903 /* ensureNoStateDependencies() is waiting for state dependencies to
4904 * drop to zero so don't add more. It may make sense to wait a bit
4905 * and retry before reporting an error (since the pending state
4906 * transition should be really quick) but let's just assert for
4907 * now to see if it ever happens on practice. */
4908
4909 AssertFailed();
4910
4911 return setError(E_ACCESSDENIED,
4912 tr("Machine state change is in progress. Please retry the operation later."));
4913 }
4914
4915 ++mData->mMachineStateDeps;
4916 Assert (mData->mMachineStateDeps != 0 /* overflow */);
4917 }
4918
4919 if (aState)
4920 *aState = mData->mMachineState;
4921 if (aRegistered)
4922 *aRegistered = mData->mRegistered;
4923
4924 return S_OK;
4925}
4926
4927/**
4928 * Decreases the number of objects dependent on the machine state.
4929 * Must always complete the #addStateDependency() call after the state
4930 * dependency is no more necessary.
4931 */
4932void Machine::releaseStateDependency()
4933{
4934 AutoCaller autoCaller(this);
4935 AssertComRCReturnVoid (autoCaller.rc());
4936
4937 AutoWriteLock alock(this);
4938
4939 AssertReturnVoid (mData->mMachineStateDeps != 0
4940 /* releaseStateDependency() w/o addStateDependency()? */);
4941 -- mData->mMachineStateDeps;
4942
4943 if (mData->mMachineStateDeps == 0)
4944 {
4945 /* inform ensureNoStateDependencies() that there are no more deps */
4946 if (mData->mMachineStateChangePending != 0)
4947 {
4948 Assert (mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
4949 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
4950 }
4951 }
4952}
4953
4954// protected methods
4955/////////////////////////////////////////////////////////////////////////////
4956
4957/**
4958 * Performs machine state checks based on the @a aDepType value. If a check
4959 * fails, this method will set extended error info, otherwise it will return
4960 * S_OK. It is supposed, that on failure, the caller will immedieately return
4961 * the return value of this method to the upper level.
4962 *
4963 * When @a aDepType is AnyStateDep, this method always returns S_OK.
4964 *
4965 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
4966 * current state of this machine object allows to change settings of the
4967 * machine (i.e. the machine is not registered, or registered but not running
4968 * and not saved). It is useful to call this method from Machine setters
4969 * before performing any change.
4970 *
4971 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
4972 * as for MutableStateDep except that if the machine is saved, S_OK is also
4973 * returned. This is useful in setters which allow changing machine
4974 * properties when it is in the saved state.
4975 *
4976 * @param aDepType Dependency type to check.
4977 *
4978 * @note Non Machine based classes should use #addStateDependency() and
4979 * #releaseStateDependency() methods or the smart AutoStateDependency
4980 * template.
4981 *
4982 * @note This method must be called from under this object's read or write
4983 * lock.
4984 */
4985HRESULT Machine::checkStateDependency(StateDependency aDepType)
4986{
4987 switch (aDepType)
4988 {
4989 case AnyStateDep:
4990 {
4991 break;
4992 }
4993 case MutableStateDep:
4994 {
4995 if ( mData->mRegistered
4996 && ( mType != IsSessionMachine /** @todo This was just convered raw; Check if Running and Paused should actually be included here... (Live Migration) */
4997 || ( mData->mMachineState != MachineState_Paused
4998 && mData->mMachineState != MachineState_Running
4999 && mData->mMachineState != MachineState_Aborted
5000 && mData->mMachineState != MachineState_Teleported
5001 && mData->mMachineState != MachineState_PoweredOff
5002 )
5003 )
5004 )
5005 return setError(VBOX_E_INVALID_VM_STATE,
5006 tr("The machine is not mutable (state is %s)"),
5007 Global::stringifyMachineState(mData->mMachineState));
5008 break;
5009 }
5010 case MutableOrSavedStateDep:
5011 {
5012 if ( mData->mRegistered
5013 && ( mType != IsSessionMachine /** @todo This was just convered raw; Check if Running and Paused should actually be included here... (Live Migration) */
5014 || ( mData->mMachineState != MachineState_Paused
5015 && mData->mMachineState != MachineState_Running
5016 && mData->mMachineState != MachineState_Aborted
5017 && mData->mMachineState != MachineState_Teleported
5018 && mData->mMachineState != MachineState_Saved
5019 && mData->mMachineState != MachineState_PoweredOff
5020 )
5021 )
5022 )
5023 return setError(VBOX_E_INVALID_VM_STATE,
5024 tr("The machine is not mutable (state is %s)"),
5025 Global::stringifyMachineState(mData->mMachineState));
5026 break;
5027 }
5028 }
5029
5030 return S_OK;
5031}
5032
5033/**
5034 * Helper to initialize all associated child objects and allocate data
5035 * structures.
5036 *
5037 * This method must be called as a part of the object's initialization procedure
5038 * (usually done in the #init() method).
5039 *
5040 * @note Must be called only from #init() or from #registeredInit().
5041 */
5042HRESULT Machine::initDataAndChildObjects()
5043{
5044 AutoCaller autoCaller(this);
5045 AssertComRCReturnRC(autoCaller.rc());
5046 AssertComRCReturn (autoCaller.state() == InInit ||
5047 autoCaller.state() == Limited, E_FAIL);
5048
5049 AssertReturn(!mData->mAccessible, E_FAIL);
5050
5051 /* allocate data structures */
5052 mSSData.allocate();
5053 mUserData.allocate();
5054 mHWData.allocate();
5055 mMediaData.allocate();
5056 mStorageControllers.allocate();
5057
5058 /* initialize mOSTypeId */
5059 mUserData->mOSTypeId = mParent->getUnknownOSType()->id();
5060
5061 /* create associated BIOS settings object */
5062 unconst(mBIOSSettings).createObject();
5063 mBIOSSettings->init (this);
5064
5065#ifdef VBOX_WITH_VRDP
5066 /* create an associated VRDPServer object (default is disabled) */
5067 unconst(mVRDPServer).createObject();
5068 mVRDPServer->init (this);
5069#endif
5070
5071 /* create associated serial port objects */
5072 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
5073 {
5074 unconst(mSerialPorts [slot]).createObject();
5075 mSerialPorts [slot]->init (this, slot);
5076 }
5077
5078 /* create associated parallel port objects */
5079 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
5080 {
5081 unconst(mParallelPorts [slot]).createObject();
5082 mParallelPorts [slot]->init (this, slot);
5083 }
5084
5085 /* create the audio adapter object (always present, default is disabled) */
5086 unconst(mAudioAdapter).createObject();
5087 mAudioAdapter->init (this);
5088
5089 /* create the USB controller object (always present, default is disabled) */
5090 unconst(mUSBController).createObject();
5091 mUSBController->init (this);
5092
5093 /* create associated network adapter objects */
5094 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
5095 {
5096 unconst(mNetworkAdapters [slot]).createObject();
5097 mNetworkAdapters [slot]->init (this, slot);
5098 }
5099
5100 return S_OK;
5101}
5102
5103/**
5104 * Helper to uninitialize all associated child objects and to free all data
5105 * structures.
5106 *
5107 * This method must be called as a part of the object's uninitialization
5108 * procedure (usually done in the #uninit() method).
5109 *
5110 * @note Must be called only from #uninit() or from #registeredInit().
5111 */
5112void Machine::uninitDataAndChildObjects()
5113{
5114 AutoCaller autoCaller(this);
5115 AssertComRCReturnVoid (autoCaller.rc());
5116 AssertComRCReturnVoid (autoCaller.state() == InUninit ||
5117 autoCaller.state() == Limited);
5118
5119 /* uninit all children using addDependentChild()/removeDependentChild()
5120 * in their init()/uninit() methods */
5121 uninitDependentChildren();
5122
5123 /* tell all our other child objects we've been uninitialized */
5124
5125 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
5126 {
5127 if (mNetworkAdapters [slot])
5128 {
5129 mNetworkAdapters [slot]->uninit();
5130 unconst(mNetworkAdapters [slot]).setNull();
5131 }
5132 }
5133
5134 if (mUSBController)
5135 {
5136 mUSBController->uninit();
5137 unconst(mUSBController).setNull();
5138 }
5139
5140 if (mAudioAdapter)
5141 {
5142 mAudioAdapter->uninit();
5143 unconst(mAudioAdapter).setNull();
5144 }
5145
5146 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
5147 {
5148 if (mParallelPorts [slot])
5149 {
5150 mParallelPorts [slot]->uninit();
5151 unconst(mParallelPorts [slot]).setNull();
5152 }
5153 }
5154
5155 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
5156 {
5157 if (mSerialPorts [slot])
5158 {
5159 mSerialPorts [slot]->uninit();
5160 unconst(mSerialPorts [slot]).setNull();
5161 }
5162 }
5163
5164#ifdef VBOX_WITH_VRDP
5165 if (mVRDPServer)
5166 {
5167 mVRDPServer->uninit();
5168 unconst(mVRDPServer).setNull();
5169 }
5170#endif
5171
5172 if (mBIOSSettings)
5173 {
5174 mBIOSSettings->uninit();
5175 unconst(mBIOSSettings).setNull();
5176 }
5177
5178 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
5179 * instance is uninitialized; SessionMachine instances refer to real
5180 * Machine hard disks). This is necessary for a clean re-initialization of
5181 * the VM after successfully re-checking the accessibility state. Note
5182 * that in case of normal Machine or SnapshotMachine uninitialization (as
5183 * a result of unregistering or discarding the snapshot), outdated hard
5184 * disk attachments will already be uninitialized and deleted, so this
5185 * code will not affect them. */
5186 if (!!mMediaData && (mType == IsMachine || mType == IsSnapshotMachine))
5187 {
5188 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
5189 it != mMediaData->mAttachments.end();
5190 ++it)
5191 {
5192 ComObjPtr<Medium> hd = (*it)->medium();
5193 if (hd.isNull())
5194 continue;
5195 HRESULT rc = hd->detachFrom(mData->mUuid, snapshotId());
5196 AssertComRC (rc);
5197 }
5198 }
5199
5200 if (mType == IsMachine)
5201 {
5202 /* reset some important fields of mData */
5203 mData->mCurrentSnapshot.setNull();
5204 mData->mFirstSnapshot.setNull();
5205 }
5206
5207 /* free data structures (the essential mData structure is not freed here
5208 * since it may be still in use) */
5209 mMediaData.free();
5210 mStorageControllers.free();
5211 mHWData.free();
5212 mUserData.free();
5213 mSSData.free();
5214}
5215
5216/**
5217 * Makes sure that there are no machine state dependants. If necessary, waits
5218 * for the number of dependants to drop to zero.
5219 *
5220 * Make sure this method is called from under this object's write lock to
5221 * guarantee that no new dependants may be added when this method returns
5222 * control to the caller.
5223 *
5224 * @note Locks this object for writing. The lock will be released while waiting
5225 * (if necessary).
5226 *
5227 * @warning To be used only in methods that change the machine state!
5228 */
5229void Machine::ensureNoStateDependencies()
5230{
5231 AssertReturnVoid (isWriteLockOnCurrentThread());
5232
5233 AutoWriteLock alock(this);
5234
5235 /* Wait for all state dependants if necessary */
5236 if (mData->mMachineStateDeps != 0)
5237 {
5238 /* lazy semaphore creation */
5239 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
5240 RTSemEventMultiCreate (&mData->mMachineStateDepsSem);
5241
5242 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
5243 mData->mMachineStateDeps));
5244
5245 ++mData->mMachineStateChangePending;
5246
5247 /* reset the semaphore before waiting, the last dependant will signal
5248 * it */
5249 RTSemEventMultiReset (mData->mMachineStateDepsSem);
5250
5251 alock.leave();
5252
5253 RTSemEventMultiWait (mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
5254
5255 alock.enter();
5256
5257 -- mData->mMachineStateChangePending;
5258 }
5259}
5260
5261/**
5262 * Changes the machine state and informs callbacks.
5263 *
5264 * This method is not intended to fail so it either returns S_OK or asserts (and
5265 * returns a failure).
5266 *
5267 * @note Locks this object for writing.
5268 */
5269HRESULT Machine::setMachineState (MachineState_T aMachineState)
5270{
5271 LogFlowThisFuncEnter();
5272 LogFlowThisFunc(("aMachineState=%d\n", aMachineState));
5273
5274 AutoCaller autoCaller(this);
5275 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
5276
5277 AutoWriteLock alock(this);
5278
5279 /* wait for state dependants to drop to zero */
5280 ensureNoStateDependencies();
5281
5282 if (mData->mMachineState != aMachineState)
5283 {
5284 mData->mMachineState = aMachineState;
5285
5286 RTTimeNow (&mData->mLastStateChange);
5287
5288 mParent->onMachineStateChange(mData->mUuid, aMachineState);
5289 }
5290
5291 LogFlowThisFuncLeave();
5292 return S_OK;
5293}
5294
5295/**
5296 * Searches for a shared folder with the given logical name
5297 * in the collection of shared folders.
5298 *
5299 * @param aName logical name of the shared folder
5300 * @param aSharedFolder where to return the found object
5301 * @param aSetError whether to set the error info if the folder is
5302 * not found
5303 * @return
5304 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
5305 *
5306 * @note
5307 * must be called from under the object's lock!
5308 */
5309HRESULT Machine::findSharedFolder (CBSTR aName,
5310 ComObjPtr<SharedFolder> &aSharedFolder,
5311 bool aSetError /* = false */)
5312{
5313 bool found = false;
5314 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
5315 !found && it != mHWData->mSharedFolders.end();
5316 ++it)
5317 {
5318 AutoWriteLock alock(*it);
5319 found = (*it)->name() == aName;
5320 if (found)
5321 aSharedFolder = *it;
5322 }
5323
5324 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
5325
5326 if (aSetError && !found)
5327 setError(rc, tr("Could not find a shared folder named '%ls'"), aName);
5328
5329 return rc;
5330}
5331
5332/**
5333 * Loads all the VM settings by walking down the <Machine> node.
5334 *
5335 * @param aRegistered true when the machine is being loaded on VirtualBox
5336 * startup
5337 *
5338 * @note This method is intended to be called only from init(), so it assumes
5339 * all machine data fields have appropriate default values when it is called.
5340 *
5341 * @note Doesn't lock any objects.
5342 */
5343HRESULT Machine::loadSettings(bool aRegistered)
5344{
5345 LogFlowThisFuncEnter();
5346 AssertReturn(mType == IsMachine, E_FAIL);
5347
5348 AutoCaller autoCaller(this);
5349 AssertReturn(autoCaller.state() == InInit, E_FAIL);
5350
5351 HRESULT rc = S_OK;
5352
5353 try
5354 {
5355 Assert(mData->m_pMachineConfigFile == NULL);
5356
5357 // load and parse machine XML; this will throw on XML or logic errors
5358 mData->m_pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
5359
5360 /* If the stored UUID is not empty, it means the registered machine
5361 * is being loaded. Compare the loaded UUID with the stored one taken
5362 * from the global registry. */
5363 if (!mData->mUuid.isEmpty())
5364 {
5365 if (mData->mUuid != mData->m_pMachineConfigFile->uuid)
5366 {
5367 throw setError(E_FAIL,
5368 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
5369 mData->m_pMachineConfigFile->uuid.raw(),
5370 mData->m_strConfigFileFull.raw(),
5371 mData->mUuid.toString().raw(),
5372 mParent->settingsFilePath().raw());
5373 }
5374 }
5375 else
5376 unconst (mData->mUuid) = mData->m_pMachineConfigFile->uuid;
5377
5378 /* name (required) */
5379 mUserData->mName = mData->m_pMachineConfigFile->strName;
5380
5381 /* nameSync (optional, default is true) */
5382 mUserData->mNameSync = mData->m_pMachineConfigFile->fNameSync;
5383
5384 mUserData->mDescription = mData->m_pMachineConfigFile->strDescription;
5385
5386 // guest OS type
5387 mUserData->mOSTypeId = mData->m_pMachineConfigFile->strOsType;
5388 /* look up the object by Id to check it is valid */
5389 ComPtr<IGuestOSType> guestOSType;
5390 rc = mParent->GetGuestOSType(mUserData->mOSTypeId,
5391 guestOSType.asOutParam());
5392 CheckComRCThrowRC(rc);
5393
5394 // stateFile (optional)
5395 if (mData->m_pMachineConfigFile->strStateFile.isEmpty())
5396 mSSData->mStateFilePath.setNull();
5397 else
5398 {
5399 Utf8Str stateFilePathFull(mData->m_pMachineConfigFile->strStateFile);
5400 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
5401 if (RT_FAILURE(vrc))
5402 throw setError(E_FAIL,
5403 tr("Invalid saved state file path '%s' (%Rrc)"),
5404 mData->m_pMachineConfigFile->strStateFile.raw(),
5405 vrc);
5406 mSSData->mStateFilePath = stateFilePathFull;
5407 }
5408
5409 /* snapshotFolder (optional) */
5410 rc = COMSETTER(SnapshotFolder)(Bstr(mData->m_pMachineConfigFile->strSnapshotFolder));
5411 CheckComRCThrowRC(rc);
5412
5413 /* currentStateModified (optional, default is true) */
5414 mData->mCurrentStateModified = mData->m_pMachineConfigFile->fCurrentStateModified;
5415
5416 mData->mLastStateChange = mData->m_pMachineConfigFile->timeLastStateChange;
5417
5418 /* teleportation */
5419 mUserData->mTeleporterEnabled = mData->m_pMachineConfigFile->fTeleporterEnabled;
5420 mUserData->mTeleporterPort = mData->m_pMachineConfigFile->uTeleporterPort;
5421 mUserData->mTeleporterAddress = mData->m_pMachineConfigFile->strTeleporterAddress;
5422 mUserData->mTeleporterPassword = mData->m_pMachineConfigFile->strTeleporterPassword;
5423
5424 /*
5425 * note: all mUserData members must be assigned prior this point because
5426 * we need to commit changes in order to let mUserData be shared by all
5427 * snapshot machine instances.
5428 */
5429 mUserData.commitCopy();
5430
5431 /* Snapshot node (optional) */
5432 if (mData->m_pMachineConfigFile->llFirstSnapshot.size())
5433 {
5434 // there can only be one root snapshot
5435 Assert(mData->m_pMachineConfigFile->llFirstSnapshot.size() == 1);
5436
5437 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
5438
5439 rc = loadSnapshot(snap,
5440 mData->m_pMachineConfigFile->uuidCurrentSnapshot,
5441 NULL); // no parent == first snapshot
5442 CheckComRCThrowRC(rc);
5443 }
5444
5445 /* Hardware node (required) */
5446 rc = loadHardware(mData->m_pMachineConfigFile->hardwareMachine);
5447 CheckComRCThrowRC(rc);
5448
5449 /* Load storage controllers */
5450 rc = loadStorageControllers(mData->m_pMachineConfigFile->storageMachine, aRegistered);
5451 CheckComRCThrowRC(rc);
5452
5453 /*
5454 * NOTE: the assignment below must be the last thing to do,
5455 * otherwise it will be not possible to change the settings
5456 * somewehere in the code above because all setters will be
5457 * blocked by checkStateDependency(MutableStateDep).
5458 */
5459
5460 /* set the machine state to Aborted or Saved when appropriate */
5461 if (mData->m_pMachineConfigFile->fAborted)
5462 {
5463 Assert(!mSSData->mStateFilePath.isEmpty());
5464 mSSData->mStateFilePath.setNull();
5465
5466 /* no need to use setMachineState() during init() */
5467 mData->mMachineState = MachineState_Aborted;
5468 }
5469 else if (!mSSData->mStateFilePath.isEmpty())
5470 {
5471 /* no need to use setMachineState() during init() */
5472 mData->mMachineState = MachineState_Saved;
5473 }
5474 }
5475 catch (HRESULT err)
5476 {
5477 /* we assume that error info is set by the thrower */
5478 rc = err;
5479 }
5480 catch (...)
5481 {
5482 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
5483 }
5484
5485 LogFlowThisFuncLeave();
5486 return rc;
5487}
5488
5489/**
5490 * Recursively loads all snapshots starting from the given.
5491 *
5492 * @param aNode <Snapshot> node.
5493 * @param aCurSnapshotId Current snapshot ID from the settings file.
5494 * @param aParentSnapshot Parent snapshot.
5495 */
5496HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
5497 const Guid &aCurSnapshotId,
5498 Snapshot *aParentSnapshot)
5499{
5500 AssertReturn (mType == IsMachine, E_FAIL);
5501
5502 HRESULT rc = S_OK;
5503
5504 Utf8Str strStateFile;
5505 if (!data.strStateFile.isEmpty())
5506 {
5507 /* optional */
5508 strStateFile = data.strStateFile;
5509 int vrc = calculateFullPath(strStateFile, strStateFile);
5510 if (RT_FAILURE(vrc))
5511 return setError(E_FAIL,
5512 tr("Invalid saved state file path '%s' (%Rrc)"),
5513 strStateFile.raw(),
5514 vrc);
5515 }
5516
5517 /* create a snapshot machine object */
5518 ComObjPtr<SnapshotMachine> pSnapshotMachine;
5519 pSnapshotMachine.createObject();
5520 rc = pSnapshotMachine->init(this,
5521 data.hardware,
5522 data.storage,
5523 data.uuid,
5524 strStateFile);
5525 CheckComRCReturnRC(rc);
5526
5527 /* create a snapshot object */
5528 ComObjPtr<Snapshot> pSnapshot;
5529 pSnapshot.createObject();
5530 /* initialize the snapshot */
5531 rc = pSnapshot->init(mParent, // VirtualBox object
5532 data.uuid,
5533 data.strName,
5534 data.strDescription,
5535 data.timestamp,
5536 pSnapshotMachine,
5537 aParentSnapshot);
5538 CheckComRCReturnRC (rc);
5539
5540 /* memorize the first snapshot if necessary */
5541 if (!mData->mFirstSnapshot)
5542 mData->mFirstSnapshot = pSnapshot;
5543
5544 /* memorize the current snapshot when appropriate */
5545 if ( !mData->mCurrentSnapshot
5546 && pSnapshot->getId() == aCurSnapshotId
5547 )
5548 mData->mCurrentSnapshot = pSnapshot;
5549
5550 // now create the children
5551 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
5552 it != data.llChildSnapshots.end();
5553 ++it)
5554 {
5555 const settings::Snapshot &childData = *it;
5556 // recurse
5557 rc = loadSnapshot(childData,
5558 aCurSnapshotId,
5559 pSnapshot); // parent = the one we created above
5560 CheckComRCReturnRC(rc);
5561 }
5562
5563 return rc;
5564}
5565
5566/**
5567 * @param aNode <Hardware> node.
5568 */
5569HRESULT Machine::loadHardware(const settings::Hardware &data)
5570{
5571 AssertReturn(mType == IsMachine || mType == IsSnapshotMachine, E_FAIL);
5572
5573 HRESULT rc = S_OK;
5574
5575 try
5576 {
5577 /* The hardware version attribute (optional). */
5578 mHWData->mHWVersion = data.strVersion;
5579 mHWData->mHardwareUUID = data.uuid;
5580
5581 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
5582 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
5583 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
5584 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
5585 mHWData->mPAEEnabled = data.fPAE;
5586 mHWData->mSyntheticCpu = data.fSyntheticCpu;
5587
5588 mHWData->mCPUCount = data.cCPUs;
5589
5590 // cpuid leafs
5591 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
5592 it != data.llCpuIdLeafs.end();
5593 ++it)
5594 {
5595 const settings::CpuIdLeaf &leaf = *it;
5596
5597 switch (leaf.ulId)
5598 {
5599 case 0x0:
5600 case 0x1:
5601 case 0x2:
5602 case 0x3:
5603 case 0x4:
5604 case 0x5:
5605 case 0x6:
5606 case 0x7:
5607 case 0x8:
5608 case 0x9:
5609 case 0xA:
5610 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
5611 break;
5612
5613 case 0x80000000:
5614 case 0x80000001:
5615 case 0x80000002:
5616 case 0x80000003:
5617 case 0x80000004:
5618 case 0x80000005:
5619 case 0x80000006:
5620 case 0x80000007:
5621 case 0x80000008:
5622 case 0x80000009:
5623 case 0x8000000A:
5624 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
5625 break;
5626
5627 default:
5628 /* just ignore */
5629 break;
5630 }
5631 }
5632
5633 mHWData->mMemorySize = data.ulMemorySizeMB;
5634
5635 // boot order
5636 for (size_t i = 0;
5637 i < RT_ELEMENTS(mHWData->mBootOrder);
5638 i++)
5639 {
5640 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
5641 if (it == data.mapBootOrder.end())
5642 mHWData->mBootOrder[i] = DeviceType_Null;
5643 else
5644 mHWData->mBootOrder[i] = it->second;
5645 }
5646
5647 mHWData->mVRAMSize = data.ulVRAMSizeMB;
5648 mHWData->mMonitorCount = data.cMonitors;
5649 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
5650 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
5651 mHWData->mFirmwareType = data.firmwareType;
5652
5653#ifdef VBOX_WITH_VRDP
5654 /* RemoteDisplay */
5655 rc = mVRDPServer->loadSettings(data.vrdpSettings);
5656 CheckComRCReturnRC (rc);
5657#endif
5658
5659 /* BIOS */
5660 rc = mBIOSSettings->loadSettings(data.biosSettings);
5661 CheckComRCReturnRC (rc);
5662
5663 /* USB Controller */
5664 rc = mUSBController->loadSettings(data.usbController);
5665 CheckComRCReturnRC (rc);
5666
5667 // network adapters
5668 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
5669 it != data.llNetworkAdapters.end();
5670 ++it)
5671 {
5672 const settings::NetworkAdapter &nic = *it;
5673
5674 /* slot unicity is guaranteed by XML Schema */
5675 AssertBreak(nic.ulSlot < RT_ELEMENTS(mNetworkAdapters));
5676 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(nic);
5677 CheckComRCReturnRC (rc);
5678 }
5679
5680 // serial ports
5681 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
5682 it != data.llSerialPorts.end();
5683 ++it)
5684 {
5685 const settings::SerialPort &s = *it;
5686
5687 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
5688 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
5689 CheckComRCReturnRC (rc);
5690 }
5691
5692 // parallel ports (optional)
5693 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
5694 it != data.llParallelPorts.end();
5695 ++it)
5696 {
5697 const settings::ParallelPort &p = *it;
5698
5699 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
5700 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
5701 CheckComRCReturnRC (rc);
5702 }
5703
5704 /* AudioAdapter */
5705 rc = mAudioAdapter->loadSettings(data.audioAdapter);
5706 CheckComRCReturnRC (rc);
5707
5708 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
5709 it != data.llSharedFolders.end();
5710 ++it)
5711 {
5712 const settings::SharedFolder &sf = *it;
5713 rc = CreateSharedFolder(Bstr(sf.strName), Bstr(sf.strHostPath), sf.fWritable);
5714 CheckComRCReturnRC (rc);
5715 }
5716
5717 // Clipboard
5718 mHWData->mClipboardMode = data.clipboardMode;
5719
5720 // guest settings
5721 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
5722 mHWData->mStatisticsUpdateInterval = data.ulStatisticsUpdateInterval;
5723
5724#ifdef VBOX_WITH_GUEST_PROPS
5725 /* Guest properties (optional) */
5726 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
5727 it != data.llGuestProperties.end();
5728 ++it)
5729 {
5730 const settings::GuestProperty &prop = *it;
5731 uint32_t fFlags = guestProp::NILFLAG;
5732 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
5733 HWData::GuestProperty property = { prop.strName, prop.strValue, prop.timestamp, fFlags };
5734 mHWData->mGuestProperties.push_back(property);
5735 }
5736
5737 mHWData->mPropertyServiceActive = false;
5738 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
5739#endif /* VBOX_WITH_GUEST_PROPS defined */
5740 }
5741 catch(std::bad_alloc &)
5742 {
5743 return E_OUTOFMEMORY;
5744 }
5745
5746 AssertComRC(rc);
5747 return rc;
5748}
5749
5750 /**
5751 * @param aNode <StorageControllers> node.
5752 */
5753HRESULT Machine::loadStorageControllers(const settings::Storage &data,
5754 bool aRegistered,
5755 const Guid *aSnapshotId /* = NULL */)
5756{
5757 AssertReturn (mType == IsMachine || mType == IsSnapshotMachine, E_FAIL);
5758
5759 HRESULT rc = S_OK;
5760
5761 /* Make sure the attached hard disks don't get unregistered until we
5762 * associate them with tis machine (important for VMs loaded (opened) after
5763 * VirtualBox startup) */
5764 AutoReadLock vboxLock(mParent);
5765
5766 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
5767 it != data.llStorageControllers.end();
5768 ++it)
5769 {
5770 const settings::StorageController &ctlData = *it;
5771
5772 ComObjPtr<StorageController> pCtl;
5773 /* Try to find one with the name first. */
5774 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
5775 if (SUCCEEDED(rc))
5776 return setError(VBOX_E_OBJECT_IN_USE,
5777 tr("Storage controller named '%s' already exists"),
5778 ctlData.strName.raw());
5779
5780 pCtl.createObject();
5781 rc = pCtl->init(this,
5782 ctlData.strName,
5783 ctlData.storageBus,
5784 ctlData.ulInstance);
5785 CheckComRCReturnRC (rc);
5786
5787 mStorageControllers->push_back(pCtl);
5788
5789 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
5790 CheckComRCReturnRC (rc);
5791
5792 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
5793 CheckComRCReturnRC (rc);
5794
5795 /* Set IDE emulation settings (only for AHCI controller). */
5796 if (ctlData.controllerType == StorageControllerType_IntelAhci)
5797 {
5798 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
5799 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
5800 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
5801 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
5802 )
5803 return rc;
5804 }
5805
5806 /* Load the attached devices now. */
5807 rc = loadStorageDevices(pCtl,
5808 ctlData,
5809 aRegistered,
5810 aSnapshotId);
5811 CheckComRCReturnRC (rc);
5812 }
5813
5814 return S_OK;
5815}
5816
5817/**
5818 * @param aNode <HardDiskAttachments> node.
5819 * @param aRegistered true when the machine is being loaded on VirtualBox
5820 * startup, or when a snapshot is being loaded (wchich
5821 * currently can happen on startup only)
5822 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
5823 *
5824 * @note Lock mParent for reading and hard disks for writing before calling.
5825 */
5826HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
5827 const settings::StorageController &data,
5828 bool aRegistered,
5829 const Guid *aSnapshotId /*= NULL*/)
5830{
5831 AssertReturn ((mType == IsMachine && aSnapshotId == NULL) ||
5832 (mType == IsSnapshotMachine && aSnapshotId != NULL), E_FAIL);
5833
5834 HRESULT rc = S_OK;
5835
5836 if (!aRegistered && data.llAttachedDevices.size() > 0)
5837 /* when the machine is being loaded (opened) from a file, it cannot
5838 * have hard disks attached (this should not happen normally,
5839 * because we don't allow to attach hard disks to an unregistered
5840 * VM at all */
5841 return setError(E_FAIL,
5842 tr("Unregistered machine '%ls' cannot have storage devices attached (found %d attachments)"),
5843 mUserData->mName.raw(),
5844 data.llAttachedDevices.size());
5845
5846 /* paranoia: detect duplicate attachments */
5847 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
5848 it != data.llAttachedDevices.end();
5849 ++it)
5850 {
5851 for (settings::AttachedDevicesList::const_iterator it2 = it;
5852 it2 != data.llAttachedDevices.end();
5853 ++it2)
5854 {
5855 if (it == it2)
5856 continue;
5857
5858 if ( (*it).lPort == (*it2).lPort
5859 && (*it).lDevice == (*it2).lDevice)
5860 {
5861 return setError(E_FAIL,
5862 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%ls'"),
5863 aStorageController->name().raw(), (*it).lPort, (*it).lDevice, mUserData->mName.raw());
5864 }
5865 }
5866 }
5867
5868 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
5869 it != data.llAttachedDevices.end();
5870 ++it)
5871 {
5872 const settings::AttachedDevice &dev = *it;
5873 ComObjPtr<Medium> medium;
5874
5875 switch (dev.deviceType)
5876 {
5877 case DeviceType_Floppy:
5878 /* find a floppy by UUID */
5879 if (!dev.uuid.isEmpty())
5880 rc = mParent->findFloppyImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
5881 /* find a floppy by host device name */
5882 else if (!dev.strHostDriveSrc.isEmpty())
5883 {
5884 SafeIfaceArray<IMedium> drivevec;
5885 rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
5886 if (SUCCEEDED(rc))
5887 {
5888 for (size_t i = 0; i < drivevec.size(); ++i)
5889 {
5890 /// @todo eliminate this conversion
5891 ComObjPtr<Medium> med = (Medium *)drivevec[i];
5892 if ( dev.strHostDriveSrc == med->name()
5893 || dev.strHostDriveSrc == med->location())
5894 {
5895 medium = med;
5896 break;
5897 }
5898 }
5899 }
5900 }
5901 break;
5902
5903 case DeviceType_DVD:
5904 /* find a DVD by UUID */
5905 if (!dev.uuid.isEmpty())
5906 rc = mParent->findDVDImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
5907 /* find a DVD by host device name */
5908 else if (!dev.strHostDriveSrc.isEmpty())
5909 {
5910 SafeIfaceArray<IMedium> drivevec;
5911 rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
5912 if (SUCCEEDED(rc))
5913 {
5914 for (size_t i = 0; i < drivevec.size(); ++i)
5915 {
5916 Bstr hostDriveSrc(dev.strHostDriveSrc);
5917 /// @todo eliminate this conversion
5918 ComObjPtr<Medium> med = (Medium *)drivevec[i];
5919 if ( hostDriveSrc == med->name()
5920 || hostDriveSrc == med->location())
5921 {
5922 medium = med;
5923 break;
5924 }
5925 }
5926 }
5927 }
5928 break;
5929
5930 case DeviceType_HardDisk:
5931 {
5932 /* find a hard disk by UUID */
5933 rc = mParent->findHardDisk(&dev.uuid, NULL, true /* aDoSetError */, &medium);
5934 if (FAILED(rc))
5935 {
5936 if (mType == IsSnapshotMachine)
5937 {
5938 // wrap another error message around the "cannot find hard disk" set by findHardDisk
5939 // so the user knows that the bad disk is in a snapshot somewhere
5940 com::ErrorInfo info;
5941 return setError(E_FAIL,
5942 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
5943 aSnapshotId->raw(),
5944 info.getText().raw());
5945 }
5946 else
5947 return rc;
5948 }
5949
5950 AutoWriteLock hdLock(medium);
5951
5952 if (medium->type() == MediumType_Immutable)
5953 {
5954 if (mType == IsSnapshotMachine)
5955 return setError(E_FAIL,
5956 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
5957 "of the virtual machine '%ls' ('%s')"),
5958 medium->locationFull().raw(),
5959 dev.uuid.raw(),
5960 aSnapshotId->raw(),
5961 mUserData->mName.raw(),
5962 mData->m_strConfigFileFull.raw());
5963
5964 return setError(E_FAIL,
5965 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s')"),
5966 medium->locationFull().raw(),
5967 dev.uuid.raw(),
5968 mUserData->mName.raw(),
5969 mData->m_strConfigFileFull.raw());
5970 }
5971
5972 if ( mType != IsSnapshotMachine
5973 && medium->children().size() != 0
5974 )
5975 return setError(E_FAIL,
5976 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s') "
5977 "because it has %d differencing child hard disks"),
5978 medium->locationFull().raw(),
5979 dev.uuid.raw(),
5980 mUserData->mName.raw(),
5981 mData->m_strConfigFileFull.raw(),
5982 medium->children().size());
5983
5984 if (findAttachment(mMediaData->mAttachments,
5985 medium))
5986 return setError(E_FAIL,
5987 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%ls' ('%s')"),
5988 medium->locationFull().raw(),
5989 dev.uuid.raw(),
5990 mUserData->mName.raw(),
5991 mData->m_strConfigFileFull.raw());
5992
5993 break;
5994 }
5995
5996 default:
5997 return setError(E_FAIL,
5998 tr("Device with unknown type is attached to the virtual machine '%s' ('%s')"),
5999 medium->locationFull().raw(),
6000 mUserData->mName.raw(),
6001 mData->m_strConfigFileFull.raw());
6002 }
6003
6004 if (FAILED(rc))
6005 break;
6006
6007 ComObjPtr<MediumAttachment> pAttachment;
6008 pAttachment.createObject();
6009 rc = pAttachment->init(this,
6010 medium,
6011 aStorageController->name(),
6012 dev.lPort,
6013 dev.lDevice,
6014 dev.deviceType);
6015 CheckComRCBreakRC(rc);
6016
6017 /* associate the medium with this machine and snapshot */
6018 if (!medium.isNull())
6019 {
6020 if (mType == IsSnapshotMachine)
6021 rc = medium->attachTo(mData->mUuid, *aSnapshotId);
6022 else
6023 rc = medium->attachTo(mData->mUuid);
6024 }
6025 if (FAILED(rc))
6026 break;
6027
6028 /* backup mMediaData to let registeredInit() properly rollback on failure
6029 * (= limited accessibility) */
6030
6031 mMediaData.backup();
6032 mMediaData->mAttachments.push_back(pAttachment);
6033 }
6034
6035 return rc;
6036}
6037
6038/**
6039 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
6040 *
6041 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
6042 * @param aSnapshot where to return the found snapshot
6043 * @param aSetError true to set extended error info on failure
6044 */
6045HRESULT Machine::findSnapshot(const Guid &aId,
6046 ComObjPtr<Snapshot> &aSnapshot,
6047 bool aSetError /* = false */)
6048{
6049 AutoReadLock chlock(snapshotsTreeLockHandle());
6050
6051 if (!mData->mFirstSnapshot)
6052 {
6053 if (aSetError)
6054 return setError(E_FAIL,
6055 tr("This machine does not have any snapshots"));
6056 return E_FAIL;
6057 }
6058
6059 if (aId.isEmpty())
6060 aSnapshot = mData->mFirstSnapshot;
6061 else
6062 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId);
6063
6064 if (!aSnapshot)
6065 {
6066 if (aSetError)
6067 return setError(E_FAIL,
6068 tr("Could not find a snapshot with UUID {%s}"),
6069 aId.toString().raw());
6070 return E_FAIL;
6071 }
6072
6073 return S_OK;
6074}
6075
6076/**
6077 * Returns the snapshot with the given name or fails of no such snapshot.
6078 *
6079 * @param aName snapshot name to find
6080 * @param aSnapshot where to return the found snapshot
6081 * @param aSetError true to set extended error info on failure
6082 */
6083HRESULT Machine::findSnapshot(IN_BSTR aName,
6084 ComObjPtr<Snapshot> &aSnapshot,
6085 bool aSetError /* = false */)
6086{
6087 AssertReturn(aName, E_INVALIDARG);
6088
6089 AutoReadLock chlock(snapshotsTreeLockHandle());
6090
6091 if (!mData->mFirstSnapshot)
6092 {
6093 if (aSetError)
6094 return setError(VBOX_E_OBJECT_NOT_FOUND,
6095 tr("This machine does not have any snapshots"));
6096 return VBOX_E_OBJECT_NOT_FOUND;
6097 }
6098
6099 aSnapshot = mData->mFirstSnapshot->findChildOrSelf (aName);
6100
6101 if (!aSnapshot)
6102 {
6103 if (aSetError)
6104 return setError(VBOX_E_OBJECT_NOT_FOUND,
6105 tr("Could not find a snapshot named '%ls'"), aName);
6106 return VBOX_E_OBJECT_NOT_FOUND;
6107 }
6108
6109 return S_OK;
6110}
6111
6112/**
6113 * Returns a storage controller object with the given name.
6114 *
6115 * @param aName storage controller name to find
6116 * @param aStorageController where to return the found storage controller
6117 * @param aSetError true to set extended error info on failure
6118 */
6119HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
6120 ComObjPtr<StorageController> &aStorageController,
6121 bool aSetError /* = false */)
6122{
6123 AssertReturn (!aName.isEmpty(), E_INVALIDARG);
6124
6125 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6126 it != mStorageControllers->end();
6127 ++it)
6128 {
6129 if ((*it)->name() == aName)
6130 {
6131 aStorageController = (*it);
6132 return S_OK;
6133 }
6134 }
6135
6136 if (aSetError)
6137 return setError(VBOX_E_OBJECT_NOT_FOUND,
6138 tr("Could not find a storage controller named '%s'"),
6139 aName.raw());
6140 return VBOX_E_OBJECT_NOT_FOUND;
6141}
6142
6143HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
6144 MediaData::AttachmentList &atts)
6145{
6146 AutoCaller autoCaller(this);
6147 CheckComRCReturnRC(autoCaller.rc());
6148
6149 AutoReadLock alock(this);
6150
6151 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
6152 it != mMediaData->mAttachments.end();
6153 ++it)
6154 {
6155 if (Bstr((*it)->controllerName()) == aName)
6156 atts.push_back(*it);
6157 }
6158
6159 return S_OK;
6160}
6161
6162/**
6163 * Helper for #saveSettings. Cares about renaming the settings directory and
6164 * file if the machine name was changed and about creating a new settings file
6165 * if this is a new machine.
6166 *
6167 * @note Must be never called directly but only from #saveSettings().
6168 *
6169 * @param aRenamed receives |true| if the name was changed and the settings
6170 * file was renamed as a result, or |false| otherwise. The
6171 * value makes sense only on success.
6172 * @param aNew receives |true| if a virgin settings file was created.
6173 */
6174HRESULT Machine::prepareSaveSettings(bool &aRenamed,
6175 bool &aNew)
6176{
6177 /* Note: tecnhically, mParent needs to be locked only when the machine is
6178 * registered (see prepareSaveSettings() for details) but we don't
6179 * currently differentiate it in callers of saveSettings() so we don't
6180 * make difference here too. */
6181 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
6182 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6183
6184 HRESULT rc = S_OK;
6185
6186 aRenamed = false;
6187
6188 /* if we're ready and isConfigLocked() is FALSE then it means
6189 * that no config file exists yet (we will create a virgin one) */
6190 aNew = !mData->m_pMachineConfigFile->fileExists();
6191
6192 /* attempt to rename the settings file if machine name is changed */
6193 if ( mUserData->mNameSync
6194 && mUserData.isBackedUp()
6195 && mUserData.backedUpData()->mName != mUserData->mName
6196 )
6197 {
6198 aRenamed = true;
6199
6200 bool dirRenamed = false;
6201 bool fileRenamed = false;
6202
6203 Utf8Str configFile, newConfigFile;
6204 Utf8Str configDir, newConfigDir;
6205
6206 do
6207 {
6208 int vrc = VINF_SUCCESS;
6209
6210 Utf8Str name = mUserData.backedUpData()->mName;
6211 Utf8Str newName = mUserData->mName;
6212
6213 configFile = mData->m_strConfigFileFull;
6214
6215 /* first, rename the directory if it matches the machine name */
6216 configDir = configFile;
6217 configDir.stripFilename();
6218 newConfigDir = configDir;
6219 if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str()))
6220 {
6221 newConfigDir.stripFilename();
6222 newConfigDir = Utf8StrFmt ("%s%c%s",
6223 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
6224 /* new dir and old dir cannot be equal here because of 'if'
6225 * above and because name != newName */
6226 Assert (configDir != newConfigDir);
6227 if (!aNew)
6228 {
6229 /* perform real rename only if the machine is not new */
6230 vrc = RTPathRename (configDir.raw(), newConfigDir.raw(), 0);
6231 if (RT_FAILURE(vrc))
6232 {
6233 rc = setError(E_FAIL,
6234 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
6235 configDir.raw(),
6236 newConfigDir.raw(),
6237 vrc);
6238 break;
6239 }
6240 dirRenamed = true;
6241 }
6242 }
6243
6244 newConfigFile = Utf8StrFmt ("%s%c%s.xml",
6245 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
6246
6247 /* then try to rename the settings file itself */
6248 if (newConfigFile != configFile)
6249 {
6250 /* get the path to old settings file in renamed directory */
6251 configFile = Utf8StrFmt("%s%c%s",
6252 newConfigDir.raw(),
6253 RTPATH_DELIMITER,
6254 RTPathFilename(configFile.c_str()));
6255 if (!aNew)
6256 {
6257 /* perform real rename only if the machine is not new */
6258 vrc = RTFileRename (configFile.raw(), newConfigFile.raw(), 0);
6259 if (RT_FAILURE(vrc))
6260 {
6261 rc = setError(E_FAIL,
6262 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
6263 configFile.raw(),
6264 newConfigFile.raw(),
6265 vrc);
6266 break;
6267 }
6268 fileRenamed = true;
6269 }
6270 }
6271
6272 /* update m_strConfigFileFull amd mConfigFile */
6273 Utf8Str oldConfigFileFull = mData->m_strConfigFileFull;
6274 Utf8Str oldConfigFile = mData->m_strConfigFile;
6275 mData->m_strConfigFileFull = newConfigFile;
6276 /* try to get the relative path for mConfigFile */
6277 Utf8Str path = newConfigFile;
6278 mParent->calculateRelativePath (path, path);
6279 mData->m_strConfigFile = path;
6280
6281 /* last, try to update the global settings with the new path */
6282 if (mData->mRegistered)
6283 {
6284 rc = mParent->updateSettings(configDir.c_str(), newConfigDir.c_str());
6285 if (FAILED(rc))
6286 {
6287 /* revert to old values */
6288 mData->m_strConfigFileFull = oldConfigFileFull;
6289 mData->m_strConfigFile = oldConfigFile;
6290 break;
6291 }
6292 }
6293
6294 /* update the snapshot folder */
6295 path = mUserData->mSnapshotFolderFull;
6296 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
6297 {
6298 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
6299 path.raw() + configDir.length());
6300 mUserData->mSnapshotFolderFull = path;
6301 calculateRelativePath (path, path);
6302 mUserData->mSnapshotFolder = path;
6303 }
6304
6305 /* update the saved state file path */
6306 path = mSSData->mStateFilePath;
6307 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
6308 {
6309 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
6310 path.raw() + configDir.length());
6311 mSSData->mStateFilePath = path;
6312 }
6313
6314 /* Update saved state file paths of all online snapshots.
6315 * Note that saveSettings() will recognize name change
6316 * and will save all snapshots in this case. */
6317 if (mData->mFirstSnapshot)
6318 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
6319 newConfigDir.c_str());
6320 }
6321 while (0);
6322
6323 if (FAILED(rc))
6324 {
6325 /* silently try to rename everything back */
6326 if (fileRenamed)
6327 RTFileRename(newConfigFile.raw(), configFile.raw(), 0);
6328 if (dirRenamed)
6329 RTPathRename(newConfigDir.raw(), configDir.raw(), 0);
6330 }
6331
6332 CheckComRCReturnRC(rc);
6333 }
6334
6335 if (aNew)
6336 {
6337 /* create a virgin config file */
6338 int vrc = VINF_SUCCESS;
6339
6340 /* ensure the settings directory exists */
6341 Utf8Str path(mData->m_strConfigFileFull);
6342 path.stripFilename();
6343 if (!RTDirExists(path.c_str()))
6344 {
6345 vrc = RTDirCreateFullPath(path.c_str(), 0777);
6346 if (RT_FAILURE(vrc))
6347 {
6348 return setError(E_FAIL,
6349 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
6350 path.raw(),
6351 vrc);
6352 }
6353 }
6354
6355 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
6356 path = Utf8Str(mData->m_strConfigFileFull);
6357 vrc = RTFileOpen(&mData->mHandleCfgFile, path.c_str(),
6358 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
6359 if (RT_FAILURE(vrc))
6360 {
6361 mData->mHandleCfgFile = NIL_RTFILE;
6362 return setError(E_FAIL,
6363 tr("Could not create the settings file '%s' (%Rrc)"),
6364 path.raw(),
6365 vrc);
6366 }
6367 RTFileClose(mData->mHandleCfgFile);
6368 }
6369
6370 return rc;
6371}
6372
6373/**
6374 * Saves and commits machine data, user data and hardware data.
6375 *
6376 * Note that on failure, the data remains uncommitted.
6377 *
6378 * @a aFlags may combine the following flags:
6379 *
6380 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
6381 * Used when saving settings after an operation that makes them 100%
6382 * correspond to the settings from the current snapshot.
6383 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
6384 * #isReallyModified() returns false. This is necessary for cases when we
6385 * change machine data diectly, not through the backup()/commit() mechanism.
6386 *
6387 * @note Must be called from under mParent write lock (sometimes needed by
6388 * #prepareSaveSettings()) and this object's write lock. Locks children for
6389 * writing. There is one exception when mParent is unused and therefore may be
6390 * left unlocked: if this machine is an unregistered one.
6391 */
6392HRESULT Machine::saveSettings(int aFlags /*= 0*/)
6393{
6394 LogFlowThisFuncEnter();
6395
6396 /* Note: tecnhically, mParent needs to be locked only when the machine is
6397 * registered (see prepareSaveSettings() for details) but we don't
6398 * currently differentiate it in callers of saveSettings() so we don't
6399 * make difference here too. */
6400 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
6401 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6402
6403 /* make sure child objects are unable to modify the settings while we are
6404 * saving them */
6405 ensureNoStateDependencies();
6406
6407 AssertReturn(mType == IsMachine || mType == IsSessionMachine, E_FAIL);
6408
6409 BOOL currentStateModified = mData->mCurrentStateModified;
6410 bool settingsModified;
6411
6412 if (!(aFlags & SaveS_ResetCurStateModified) && !currentStateModified)
6413 {
6414 /* We ignore changes to user data when setting mCurrentStateModified
6415 * because the current state will not differ from the current snapshot
6416 * if only user data has been changed (user data is shared by all
6417 * snapshots). */
6418 currentStateModified = isReallyModified (true /* aIgnoreUserData */);
6419 settingsModified = mUserData.hasActualChanges() || currentStateModified;
6420 }
6421 else
6422 {
6423 if (aFlags & SaveS_ResetCurStateModified)
6424 currentStateModified = FALSE;
6425 settingsModified = isReallyModified();
6426 }
6427
6428 HRESULT rc = S_OK;
6429
6430 /* First, prepare to save settings. It will care about renaming the
6431 * settings directory and file if the machine name was changed and about
6432 * creating a new settings file if this is a new machine. */
6433 bool isRenamed = false;
6434 bool isNew = false;
6435 rc = prepareSaveSettings(isRenamed, isNew);
6436 CheckComRCReturnRC(rc);
6437
6438 try
6439 {
6440 mData->m_pMachineConfigFile->uuid = mData->mUuid;
6441 mData->m_pMachineConfigFile->strName = mUserData->mName;
6442 mData->m_pMachineConfigFile->fNameSync = !!mUserData->mNameSync;
6443 mData->m_pMachineConfigFile->strDescription = mUserData->mDescription;
6444 mData->m_pMachineConfigFile->strOsType = mUserData->mOSTypeId;
6445
6446 if ( mData->mMachineState == MachineState_Saved
6447 || mData->mMachineState == MachineState_Restoring
6448 // when deleting a snapshot we may or may not have a saved state in the current state,
6449 // so let's not assert here please
6450 || ( (mData->mMachineState == MachineState_DeletingSnapshot)
6451 && (!mSSData->mStateFilePath.isEmpty())
6452 )
6453 )
6454 {
6455 Assert(!mSSData->mStateFilePath.isEmpty());
6456 /* try to make the file name relative to the settings file dir */
6457 calculateRelativePath(mSSData->mStateFilePath, mData->m_pMachineConfigFile->strStateFile);
6458 }
6459 else
6460 {
6461 Assert(mSSData->mStateFilePath.isEmpty());
6462 mData->m_pMachineConfigFile->strStateFile.setNull();
6463 }
6464
6465 if (mData->mCurrentSnapshot)
6466 mData->m_pMachineConfigFile->uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
6467 else
6468 mData->m_pMachineConfigFile->uuidCurrentSnapshot.clear();
6469
6470 mData->m_pMachineConfigFile->strSnapshotFolder = mUserData->mSnapshotFolder;
6471 mData->m_pMachineConfigFile->fCurrentStateModified = !!currentStateModified;
6472 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
6473 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
6474/// @todo Live Migration: mData->m_pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
6475
6476 mData->m_pMachineConfigFile->fTeleporterEnabled = !!mUserData->mTeleporterEnabled;
6477 mData->m_pMachineConfigFile->uTeleporterPort = mUserData->mTeleporterPort;
6478 mData->m_pMachineConfigFile->strTeleporterAddress = mUserData->mTeleporterAddress;
6479 mData->m_pMachineConfigFile->strTeleporterPassword = mUserData->mTeleporterPassword;
6480
6481 rc = saveHardware(mData->m_pMachineConfigFile->hardwareMachine);
6482 CheckComRCThrowRC(rc);
6483
6484 rc = saveStorageControllers(mData->m_pMachineConfigFile->storageMachine);
6485 CheckComRCThrowRC(rc);
6486
6487 // save snapshots
6488 rc = saveAllSnapshots();
6489 CheckComRCThrowRC(rc);
6490
6491 // now spit it all out
6492 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
6493 }
6494 catch (HRESULT err)
6495 {
6496 /* we assume that error info is set by the thrower */
6497 rc = err;
6498 }
6499 catch (...)
6500 {
6501 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
6502 }
6503
6504 if (SUCCEEDED(rc))
6505 {
6506 commit();
6507
6508 /* memorize the new modified state */
6509 mData->mCurrentStateModified = currentStateModified;
6510 }
6511
6512 if (settingsModified || (aFlags & SaveS_InformCallbacksAnyway))
6513 {
6514 /* Fire the data change event, even on failure (since we've already
6515 * committed all data). This is done only for SessionMachines because
6516 * mutable Machine instances are always not registered (i.e. private
6517 * to the client process that creates them) and thus don't need to
6518 * inform callbacks. */
6519 if (mType == IsSessionMachine)
6520 mParent->onMachineDataChange(mData->mUuid);
6521 }
6522
6523 LogFlowThisFunc(("rc=%08X\n", rc));
6524 LogFlowThisFuncLeave();
6525 return rc;
6526}
6527
6528HRESULT Machine::saveAllSnapshots()
6529{
6530 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
6531
6532 HRESULT rc = S_OK;
6533
6534 try
6535 {
6536 mData->m_pMachineConfigFile->llFirstSnapshot.clear();
6537
6538 if (mData->mFirstSnapshot)
6539 {
6540 settings::Snapshot snapNew;
6541 mData->m_pMachineConfigFile->llFirstSnapshot.push_back(snapNew);
6542
6543 // get reference to the fresh copy of the snapshot on the list and
6544 // work on that copy directly to avoid excessive copying later
6545 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
6546
6547 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
6548 CheckComRCThrowRC(rc);
6549 }
6550
6551// if (mType == IsSessionMachine)
6552// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
6553
6554 }
6555 catch (HRESULT err)
6556 {
6557 /* we assume that error info is set by the thrower */
6558 rc = err;
6559 }
6560 catch (...)
6561 {
6562 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
6563 }
6564
6565 return rc;
6566}
6567
6568/**
6569 * Saves the VM hardware configuration. It is assumed that the
6570 * given node is empty.
6571 *
6572 * @param aNode <Hardware> node to save the VM hardware confguration to.
6573 */
6574HRESULT Machine::saveHardware(settings::Hardware &data)
6575{
6576 HRESULT rc = S_OK;
6577
6578 try
6579 {
6580 /* The hardware version attribute (optional).
6581 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
6582 if ( mHWData->mHWVersion == "1"
6583 && mSSData->mStateFilePath.isEmpty()
6584 )
6585 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some other point needs to be found where this can be done. */
6586
6587 data.strVersion = mHWData->mHWVersion;
6588 data.uuid = mHWData->mHardwareUUID;
6589
6590 // CPU
6591 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
6592 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
6593 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
6594 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
6595 data.fPAE = !!mHWData->mPAEEnabled;
6596 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
6597
6598 /* Standard and Extended CPUID leafs. */
6599 data.llCpuIdLeafs.clear();
6600 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
6601 {
6602 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
6603 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
6604 }
6605 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
6606 {
6607 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
6608 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
6609 }
6610
6611 data.cCPUs = mHWData->mCPUCount;
6612
6613 // memory
6614 data.ulMemorySizeMB = mHWData->mMemorySize;
6615
6616 // firmware
6617 data.firmwareType = mHWData->mFirmwareType;
6618
6619 // boot order
6620 data.mapBootOrder.clear();
6621 for (size_t i = 0;
6622 i < RT_ELEMENTS(mHWData->mBootOrder);
6623 ++i)
6624 data.mapBootOrder[i] = mHWData->mBootOrder[i];
6625
6626 // display
6627 data.ulVRAMSizeMB = mHWData->mVRAMSize;
6628 data.cMonitors = mHWData->mMonitorCount;
6629 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
6630 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
6631
6632#ifdef VBOX_WITH_VRDP
6633 /* VRDP settings (optional) */
6634 rc = mVRDPServer->saveSettings(data.vrdpSettings);
6635 CheckComRCThrowRC(rc);
6636#endif
6637
6638 /* BIOS (required) */
6639 rc = mBIOSSettings->saveSettings(data.biosSettings);
6640 CheckComRCThrowRC(rc);
6641
6642 /* USB Controller (required) */
6643 rc = mUSBController->saveSettings(data.usbController);
6644 CheckComRCThrowRC(rc);
6645
6646 /* Network adapters (required) */
6647 data.llNetworkAdapters.clear();
6648 for (ULONG slot = 0;
6649 slot < RT_ELEMENTS(mNetworkAdapters);
6650 ++slot)
6651 {
6652 settings::NetworkAdapter nic;
6653 nic.ulSlot = slot;
6654 rc = mNetworkAdapters[slot]->saveSettings(nic);
6655 CheckComRCThrowRC(rc);
6656
6657 data.llNetworkAdapters.push_back(nic);
6658 }
6659
6660 /* Serial ports */
6661 data.llSerialPorts.clear();
6662 for (ULONG slot = 0;
6663 slot < RT_ELEMENTS(mSerialPorts);
6664 ++slot)
6665 {
6666 settings::SerialPort s;
6667 s.ulSlot = slot;
6668 rc = mSerialPorts[slot]->saveSettings(s);
6669 CheckComRCReturnRC (rc);
6670
6671 data.llSerialPorts.push_back(s);
6672 }
6673
6674 /* Parallel ports */
6675 data.llParallelPorts.clear();
6676 for (ULONG slot = 0;
6677 slot < RT_ELEMENTS(mParallelPorts);
6678 ++slot)
6679 {
6680 settings::ParallelPort p;
6681 p.ulSlot = slot;
6682 rc = mParallelPorts[slot]->saveSettings(p);
6683 CheckComRCReturnRC (rc);
6684
6685 data.llParallelPorts.push_back(p);
6686 }
6687
6688 /* Audio adapter */
6689 rc = mAudioAdapter->saveSettings(data.audioAdapter);
6690 CheckComRCReturnRC (rc);
6691
6692 /* Shared folders */
6693 data.llSharedFolders.clear();
6694 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
6695 it != mHWData->mSharedFolders.end();
6696 ++it)
6697 {
6698 ComObjPtr<SharedFolder> pFolder = *it;
6699 settings::SharedFolder sf;
6700 sf.strName = pFolder->name();
6701 sf.strHostPath = pFolder->hostPath();
6702 sf.fWritable = !!pFolder->writable();
6703
6704 data.llSharedFolders.push_back(sf);
6705 }
6706
6707 // clipboard
6708 data.clipboardMode = mHWData->mClipboardMode;
6709
6710 /* Guest */
6711 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
6712 data.ulStatisticsUpdateInterval = mHWData->mStatisticsUpdateInterval;
6713
6714 // guest properties
6715 data.llGuestProperties.clear();
6716#ifdef VBOX_WITH_GUEST_PROPS
6717 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
6718 it != mHWData->mGuestProperties.end();
6719 ++it)
6720 {
6721 HWData::GuestProperty property = *it;
6722
6723 settings::GuestProperty prop;
6724 prop.strName = property.strName;
6725 prop.strValue = property.strValue;
6726 prop.timestamp = property.mTimestamp;
6727 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
6728 guestProp::writeFlags(property.mFlags, szFlags);
6729 prop.strFlags = szFlags;
6730
6731 data.llGuestProperties.push_back(prop);
6732 }
6733
6734 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
6735#endif /* VBOX_WITH_GUEST_PROPS defined */
6736 }
6737 catch(std::bad_alloc &)
6738 {
6739 return E_OUTOFMEMORY;
6740 }
6741
6742 AssertComRC(rc);
6743 return rc;
6744}
6745
6746/**
6747 * Saves the storage controller configuration.
6748 *
6749 * @param aNode <StorageControllers> node to save the VM hardware confguration to.
6750 */
6751HRESULT Machine::saveStorageControllers(settings::Storage &data)
6752{
6753 data.llStorageControllers.clear();
6754
6755 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6756 it != mStorageControllers->end();
6757 ++it)
6758 {
6759 HRESULT rc;
6760 ComObjPtr<StorageController> pCtl = *it;
6761
6762 settings::StorageController ctl;
6763 ctl.strName = pCtl->name();
6764 ctl.controllerType = pCtl->controllerType();
6765 ctl.storageBus = pCtl->storageBus();
6766 ctl.ulInstance = pCtl->instance();
6767
6768 /* Save the port count. */
6769 ULONG portCount;
6770 rc = pCtl->COMGETTER(PortCount)(&portCount);
6771 ComAssertComRCRet(rc, rc);
6772 ctl.ulPortCount = portCount;
6773
6774 /* Save IDE emulation settings. */
6775 if (ctl.controllerType == StorageControllerType_IntelAhci)
6776 {
6777 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
6778 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
6779 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
6780 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
6781 )
6782 ComAssertComRCRet(rc, rc);
6783 }
6784
6785 /* save the devices now. */
6786 rc = saveStorageDevices(pCtl, ctl);
6787 ComAssertComRCRet(rc, rc);
6788
6789 data.llStorageControllers.push_back(ctl);
6790 }
6791
6792 return S_OK;
6793}
6794
6795/**
6796 * Saves the hard disk confguration.
6797 */
6798HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
6799 settings::StorageController &data)
6800{
6801 MediaData::AttachmentList atts;
6802
6803 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->name()), atts);
6804 CheckComRCReturnRC (rc);
6805
6806 data.llAttachedDevices.clear();
6807 for (MediaData::AttachmentList::const_iterator it = atts.begin();
6808 it != atts.end();
6809 ++it)
6810 {
6811 settings::AttachedDevice dev;
6812
6813 MediumAttachment *pAttach = *it;
6814 Medium *pMedium = pAttach->medium();
6815
6816 dev.deviceType = pAttach->type();
6817 dev.lPort = pAttach->port();
6818 dev.lDevice = pAttach->device();
6819 if (pMedium)
6820 {
6821 BOOL fHostDrive = false;
6822 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
6823 if (FAILED(rc))
6824 return rc;
6825 if (fHostDrive)
6826 dev.strHostDriveSrc = pMedium->location();
6827 else
6828 dev.uuid = pMedium->id();
6829 dev.fPassThrough = pAttach->passthrough();
6830 }
6831
6832 data.llAttachedDevices.push_back(dev);
6833 }
6834
6835 return S_OK;
6836}
6837
6838/**
6839 * Saves machine state settings as defined by aFlags
6840 * (SaveSTS_* values).
6841 *
6842 * @param aFlags Combination of SaveSTS_* flags.
6843 *
6844 * @note Locks objects for writing.
6845 */
6846HRESULT Machine::saveStateSettings(int aFlags)
6847{
6848 if (aFlags == 0)
6849 return S_OK;
6850
6851 AutoCaller autoCaller (this);
6852 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6853
6854 /* This object's write lock is also necessary to serialize file access
6855 * (prevent concurrent reads and writes) */
6856 AutoWriteLock alock(this);
6857
6858 HRESULT rc = S_OK;
6859
6860 Assert(mData->m_pMachineConfigFile);
6861
6862 try
6863 {
6864 if (aFlags & SaveSTS_CurStateModified)
6865 mData->m_pMachineConfigFile->fCurrentStateModified = true;
6866
6867 if (aFlags & SaveSTS_StateFilePath)
6868 {
6869 if (!mSSData->mStateFilePath.isEmpty())
6870 /* try to make the file name relative to the settings file dir */
6871 calculateRelativePath(mSSData->mStateFilePath, mData->m_pMachineConfigFile->strStateFile);
6872 else
6873 mData->m_pMachineConfigFile->strStateFile.setNull();
6874 }
6875
6876 if (aFlags & SaveSTS_StateTimeStamp)
6877 {
6878 Assert( mData->mMachineState != MachineState_Aborted
6879 || mSSData->mStateFilePath.isEmpty());
6880
6881 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
6882
6883 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
6884//@todo live migration mData->m_pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
6885 }
6886
6887 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
6888 }
6889 catch (...)
6890 {
6891 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
6892 }
6893
6894 return rc;
6895}
6896
6897/**
6898 * Creates differencing hard disks for all normal hard disks attached to this
6899 * machine and a new set of attachments to refer to created disks.
6900 *
6901 * Used when taking a snapshot or when discarding the current state.
6902 *
6903 * This method assumes that mMediaData contains the original hard disk attachments
6904 * it needs to create diffs for. On success, these attachments will be replaced
6905 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
6906 * called to delete created diffs which will also rollback mMediaData and restore
6907 * whatever was backed up before calling this method.
6908 *
6909 * Attachments with non-normal hard disks are left as is.
6910 *
6911 * If @a aOnline is @c false then the original hard disks that require implicit
6912 * diffs will be locked for reading. Otherwise it is assumed that they are
6913 * already locked for writing (when the VM was started). Note that in the latter
6914 * case it is responsibility of the caller to lock the newly created diffs for
6915 * writing if this method succeeds.
6916 *
6917 * @param aFolder Folder where to create diff hard disks.
6918 * @param aProgress Progress object to run (must contain at least as
6919 * many operations left as the number of hard disks
6920 * attached).
6921 * @param aOnline Whether the VM was online prior to this operation.
6922 *
6923 * @note The progress object is not marked as completed, neither on success nor
6924 * on failure. This is a responsibility of the caller.
6925 *
6926 * @note Locks this object for writing.
6927 */
6928HRESULT Machine::createImplicitDiffs(const Bstr &aFolder,
6929 IProgress *aProgress,
6930 ULONG aWeight,
6931 bool aOnline)
6932{
6933 AssertReturn(!aFolder.isEmpty(), E_FAIL);
6934
6935 LogFlowThisFunc(("aFolder='%ls', aOnline=%d\n", aFolder.raw(), aOnline));
6936
6937 AutoCaller autoCaller(this);
6938 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
6939
6940 AutoWriteLock alock(this);
6941
6942 /* must be in a protective state because we leave the lock below */
6943 AssertReturn( mData->mMachineState == MachineState_Saving
6944 || mData->mMachineState == MachineState_LiveSnapshotting
6945 || mData->mMachineState == MachineState_RestoringSnapshot
6946 || mData->mMachineState == MachineState_DeletingSnapshot
6947 , E_FAIL);
6948
6949 HRESULT rc = S_OK;
6950
6951 typedef std::list< ComObjPtr<Medium> > LockedMedia;
6952 LockedMedia lockedMedia;
6953
6954 try
6955 {
6956 if (!aOnline)
6957 {
6958 /* lock all attached hard disks early to detect "in use"
6959 * situations before creating actual diffs */
6960 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
6961 it != mMediaData->mAttachments.end();
6962 ++it)
6963 {
6964 MediumAttachment* pAtt = *it;
6965 if (pAtt->type() == DeviceType_HardDisk)
6966 {
6967 Medium* pHD = pAtt->medium();
6968 Assert(pHD);
6969 rc = pHD->LockRead (NULL);
6970 CheckComRCThrowRC(rc);
6971 lockedMedia.push_back(pHD);
6972 }
6973 }
6974 }
6975
6976 /* remember the current list (note that we don't use backup() since
6977 * mMediaData may be already backed up) */
6978 MediaData::AttachmentList atts = mMediaData->mAttachments;
6979
6980 /* start from scratch */
6981 mMediaData->mAttachments.clear();
6982
6983 /* go through remembered attachments and create diffs for normal hard
6984 * disks and attach them */
6985 for (MediaData::AttachmentList::const_iterator it = atts.begin();
6986 it != atts.end();
6987 ++it)
6988 {
6989 MediumAttachment* pAtt = *it;
6990
6991 DeviceType_T devType = pAtt->type();
6992 Medium* medium = pAtt->medium();
6993
6994 if ( devType != DeviceType_HardDisk
6995 || medium == NULL
6996 || medium->type() != MediumType_Normal)
6997 {
6998 /* copy the attachment as is */
6999
7000 /** @todo the progress object created in Console::TakeSnaphot
7001 * only expects operations for hard disks. Later other
7002 * device types need to show up in the progress as well. */
7003 if (devType == DeviceType_HardDisk)
7004 {
7005 if (medium == NULL)
7006 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")),
7007 aWeight); // weight
7008 else
7009 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
7010 medium->base()->name().raw()),
7011 aWeight); // weight
7012 }
7013
7014 mMediaData->mAttachments.push_back(pAtt);
7015 continue;
7016 }
7017
7018 /* need a diff */
7019 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
7020 medium->base()->name().raw()),
7021 aWeight); // weight
7022
7023 ComObjPtr<Medium> diff;
7024 diff.createObject();
7025 rc = diff->init(mParent,
7026 medium->preferredDiffFormat().raw(),
7027 BstrFmt("%ls"RTPATH_SLASH_STR,
7028 mUserData->mSnapshotFolderFull.raw()).raw());
7029 CheckComRCThrowRC(rc);
7030
7031 /* leave the lock before the potentially lengthy operation */
7032 alock.leave();
7033
7034 rc = medium->createDiffStorageAndWait(diff,
7035 MediumVariant_Standard,
7036 NULL);
7037
7038 // at this point, the old image is still locked for writing, but instead
7039 // we need the new diff image locked for writing and lock the previously
7040 // current one for reading only
7041 if (aOnline)
7042 {
7043 diff->LockWrite(NULL);
7044 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(diff), true));
7045 medium->UnlockWrite(NULL);
7046 medium->LockRead(NULL);
7047 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(medium), false));
7048 }
7049
7050 alock.enter();
7051
7052 CheckComRCThrowRC(rc);
7053
7054 rc = diff->attachTo(mData->mUuid);
7055 AssertComRCThrowRC(rc);
7056
7057 /* add a new attachment */
7058 ComObjPtr<MediumAttachment> attachment;
7059 attachment.createObject();
7060 rc = attachment->init(this,
7061 diff,
7062 pAtt->controllerName(),
7063 pAtt->port(),
7064 pAtt->device(),
7065 DeviceType_HardDisk,
7066 true /* aImplicit */);
7067 CheckComRCThrowRC(rc);
7068
7069 mMediaData->mAttachments.push_back(attachment);
7070 }
7071 }
7072 catch (HRESULT aRC) { rc = aRC; }
7073
7074 /* unlock all hard disks we locked */
7075 if (!aOnline)
7076 {
7077 ErrorInfoKeeper eik;
7078
7079 for (LockedMedia::const_iterator it = lockedMedia.begin();
7080 it != lockedMedia.end();
7081 ++it)
7082 {
7083 HRESULT rc2 = (*it)->UnlockRead(NULL);
7084 AssertComRC(rc2);
7085 }
7086 }
7087
7088 if (FAILED(rc))
7089 {
7090 MultiResultRef mrc (rc);
7091
7092 mrc = deleteImplicitDiffs();
7093 }
7094
7095 return rc;
7096}
7097
7098/**
7099 * Deletes implicit differencing hard disks created either by
7100 * #createImplicitDiffs() or by #AttachMedium() and rolls back mMediaData.
7101 *
7102 * Note that to delete hard disks created by #AttachMedium() this method is
7103 * called from #fixupMedia() when the changes are rolled back.
7104 *
7105 * @note Locks this object for writing.
7106 */
7107HRESULT Machine::deleteImplicitDiffs()
7108{
7109 AutoCaller autoCaller(this);
7110 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7111
7112 AutoWriteLock alock(this);
7113
7114 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
7115
7116 HRESULT rc = S_OK;
7117
7118 MediaData::AttachmentList implicitAtts;
7119
7120 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
7121
7122 /* enumerate new attachments */
7123 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7124 it != mMediaData->mAttachments.end();
7125 ++it)
7126 {
7127 ComObjPtr<Medium> hd = (*it)->medium();
7128 if (hd.isNull())
7129 continue;
7130
7131 if ((*it)->isImplicit())
7132 {
7133 /* deassociate and mark for deletion */
7134 rc = hd->detachFrom(mData->mUuid);
7135 AssertComRC(rc);
7136 implicitAtts.push_back (*it);
7137 continue;
7138 }
7139
7140 /* was this hard disk attached before? */
7141 if (!findAttachment(oldAtts, hd))
7142 {
7143 /* no: de-associate */
7144 rc = hd->detachFrom(mData->mUuid);
7145 AssertComRC(rc);
7146 continue;
7147 }
7148 }
7149
7150 /* rollback hard disk changes */
7151 mMediaData.rollback();
7152
7153 MultiResult mrc (S_OK);
7154
7155 /* delete unused implicit diffs */
7156 if (implicitAtts.size() != 0)
7157 {
7158 /* will leave the lock before the potentially lengthy
7159 * operation, so protect with the special state (unless already
7160 * protected) */
7161 MachineState_T oldState = mData->mMachineState;
7162 if ( oldState != MachineState_Saving
7163 && oldState != MachineState_LiveSnapshotting
7164 && oldState != MachineState_RestoringSnapshot
7165 && oldState != MachineState_DeletingSnapshot
7166 )
7167 setMachineState (MachineState_SettingUp);
7168
7169 alock.leave();
7170
7171 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
7172 it != implicitAtts.end();
7173 ++it)
7174 {
7175 ComObjPtr<Medium> hd = (*it)->medium();
7176 mrc = hd->deleteStorageAndWait();
7177 }
7178
7179 alock.enter();
7180
7181 if (mData->mMachineState == MachineState_SettingUp)
7182 {
7183 setMachineState (oldState);
7184 }
7185 }
7186
7187 return mrc;
7188}
7189
7190/**
7191 * Looks through the given list of media attachments for one with the given parameters
7192 * and returns it, or NULL if not found. The list is a parameter so that backup lists
7193 * can be searched as well if needed.
7194 *
7195 * @param list
7196 * @param aControllerName
7197 * @param aControllerPort
7198 * @param aDevice
7199 * @return
7200 */
7201MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
7202 IN_BSTR aControllerName,
7203 LONG aControllerPort,
7204 LONG aDevice)
7205{
7206 for (MediaData::AttachmentList::const_iterator it = ll.begin();
7207 it != ll.end();
7208 ++it)
7209 {
7210 MediumAttachment *pAttach = *it;
7211 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
7212 return pAttach;
7213 }
7214
7215 return NULL;
7216}
7217
7218/**
7219 * Looks through the given list of media attachments for one with the given parameters
7220 * and returns it, or NULL if not found. The list is a parameter so that backup lists
7221 * can be searched as well if needed.
7222 *
7223 * @param list
7224 * @param aControllerName
7225 * @param aControllerPort
7226 * @param aDevice
7227 * @return
7228 */
7229MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
7230 ComObjPtr<Medium> pMedium)
7231{
7232 for (MediaData::AttachmentList::const_iterator it = ll.begin();
7233 it != ll.end();
7234 ++it)
7235 {
7236 MediumAttachment *pAttach = *it;
7237 ComObjPtr<Medium> pMediumThis = pAttach->medium();
7238 if (pMediumThis.equalsTo(pMedium))
7239 return pAttach;
7240 }
7241
7242 return NULL;
7243}
7244
7245/**
7246 * Looks through the given list of media attachments for one with the given parameters
7247 * and returns it, or NULL if not found. The list is a parameter so that backup lists
7248 * can be searched as well if needed.
7249 *
7250 * @param list
7251 * @param aControllerName
7252 * @param aControllerPort
7253 * @param aDevice
7254 * @return
7255 */
7256MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
7257 Guid &id)
7258{
7259 for (MediaData::AttachmentList::const_iterator it = ll.begin();
7260 it != ll.end();
7261 ++it)
7262 {
7263 MediumAttachment *pAttach = *it;
7264 ComObjPtr<Medium> pMediumThis = pAttach->medium();
7265 if (pMediumThis->id() == id)
7266 return pAttach;
7267 }
7268
7269 return NULL;
7270}
7271
7272/**
7273 * Perform deferred hard disk detachments on success and deletion of implicitly
7274 * created diffs on failure.
7275 *
7276 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
7277 * backed up).
7278 *
7279 * When the data is backed up, this method will commit mMediaData if @a aCommit is
7280 * @c true and rollback it otherwise before returning.
7281 *
7282 * If @a aOnline is @c true then this method called with @a aCommit = @c true
7283 * will also unlock the old hard disks for which the new implicit diffs were
7284 * created and will lock these new diffs for writing. When @a aCommit is @c
7285 * false, this argument is ignored.
7286 *
7287 * @param aCommit @c true if called on success.
7288 * @param aOnline Whether the VM was online prior to this operation.
7289 *
7290 * @note Locks this object for writing!
7291 */
7292void Machine::fixupMedia(bool aCommit, bool aOnline /*= false*/)
7293{
7294 AutoCaller autoCaller(this);
7295 AssertComRCReturnVoid (autoCaller.rc());
7296
7297 AutoWriteLock alock(this);
7298
7299 LogFlowThisFunc(("Entering, aCommit=%d, aOnline=%d\n", aCommit, aOnline));
7300
7301 HRESULT rc = S_OK;
7302
7303 /* no attach/detach operations -- nothing to do */
7304 if (!mMediaData.isBackedUp())
7305 return;
7306
7307 if (aCommit)
7308 {
7309 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
7310
7311 /* enumerate new attachments */
7312 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7313 it != mMediaData->mAttachments.end();
7314 ++it)
7315 {
7316 MediumAttachment *pAttach = *it;
7317
7318 pAttach->commit();
7319
7320 Medium* pMedium = pAttach->medium();
7321 bool fImplicit = pAttach->isImplicit();
7322
7323 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
7324 (pMedium) ? pMedium->name().raw() : "NULL",
7325 fImplicit));
7326
7327 /** @todo convert all this Machine-based voodoo to MediumAttachment
7328 * based commit logic. */
7329 if (fImplicit)
7330 {
7331 /* convert implicit attachment to normal */
7332 pAttach->setImplicit(false);
7333
7334 if ( aOnline
7335 && pMedium
7336 && pAttach->type() == DeviceType_HardDisk
7337 )
7338 {
7339 rc = pMedium->LockWrite(NULL);
7340 AssertComRC(rc);
7341
7342 mData->mSession.mLockedMedia.push_back(
7343 Data::Session::LockedMedia::value_type(
7344 ComPtr<IMedium>(pMedium), true));
7345
7346 /* also, relock the old hard disk which is a base for the
7347 * new diff for reading if the VM is online */
7348
7349 ComObjPtr<Medium> parent = pMedium->parent();
7350 /* make the relock atomic */
7351 AutoWriteLock parentLock (parent);
7352 rc = parent->UnlockWrite(NULL);
7353 AssertComRC(rc);
7354 rc = parent->LockRead(NULL);
7355 AssertComRC(rc);
7356
7357 /* XXX actually we should replace the old entry in that
7358 * vector (write lock => read lock) but this would take
7359 * some effort. So lets just ignore the error code in
7360 * SessionMachine::unlockMedia(). */
7361 mData->mSession.mLockedMedia.push_back(
7362 Data::Session::LockedMedia::value_type (
7363 ComPtr<IMedium>(parent), false));
7364 }
7365
7366 continue;
7367 }
7368
7369 if (pMedium)
7370 {
7371 /* was this medium attached before? */
7372 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
7373 oldIt != oldAtts.end();
7374 ++oldIt)
7375 {
7376 MediumAttachment *pOldAttach = *it;
7377 if (pOldAttach->medium().equalsTo(pMedium))
7378 {
7379 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->name().raw()));
7380
7381 /* yes: remove from old to avoid de-association */
7382 oldAtts.erase(oldIt);
7383 break;
7384 }
7385 }
7386 }
7387 }
7388
7389 /* enumerate remaining old attachments and de-associate from the
7390 * current machine state */
7391 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
7392 it != oldAtts.end();
7393 ++it)
7394 {
7395 MediumAttachment *pAttach = *it;
7396
7397 Medium* pMedium = pAttach->medium();
7398
7399 if (pMedium)
7400 {
7401 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->name().raw()));
7402
7403 /* now de-associate from the current machine state */
7404 rc = pMedium->detachFrom(mData->mUuid);
7405 AssertComRC(rc);
7406
7407 if ( aOnline
7408 && pAttach->type() == DeviceType_HardDisk)
7409 {
7410 /* unlock since not used anymore */
7411 MediumState_T state;
7412 rc = pMedium->UnlockWrite(&state);
7413 /* the disk may be alredy relocked for reading above */
7414 Assert (SUCCEEDED(rc) || state == MediumState_LockedRead);
7415 }
7416 }
7417 }
7418
7419 /* commit the hard disk changes */
7420 mMediaData.commit();
7421
7422 if (mType == IsSessionMachine)
7423 {
7424 /* attach new data to the primary machine and reshare it */
7425 mPeer->mMediaData.attach(mMediaData);
7426 }
7427 }
7428 else
7429 {
7430 /* enumerate new attachments */
7431 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7432 it != mMediaData->mAttachments.end();
7433 ++it)
7434 {
7435 (*it)->rollback();
7436 }
7437
7438 /** @todo convert all this Machine-based voodoo to MediumAttachment
7439 * based rollback logic. */
7440 // @todo r=dj the below totally fails if this gets called from Machine::rollback(),
7441 // which gets called if Machine::registeredInit() fails...
7442 deleteImplicitDiffs();
7443 }
7444
7445 return;
7446}
7447
7448/**
7449 * Returns true if the settings file is located in the directory named exactly
7450 * as the machine. This will be true if the machine settings structure was
7451 * created by default in #openConfigLoader().
7452 *
7453 * @param aSettingsDir if not NULL, the full machine settings file directory
7454 * name will be assigned there.
7455 *
7456 * @note Doesn't lock anything.
7457 * @note Not thread safe (must be called from this object's lock).
7458 */
7459bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */)
7460{
7461 Utf8Str settingsDir = mData->m_strConfigFileFull;
7462 settingsDir.stripFilename();
7463 char *dirName = RTPathFilename(settingsDir.c_str());
7464
7465 AssertReturn(dirName, false);
7466
7467 /* if we don't rename anything on name change, return false shorlty */
7468 if (!mUserData->mNameSync)
7469 return false;
7470
7471 if (aSettingsDir)
7472 *aSettingsDir = settingsDir;
7473
7474 return Bstr (dirName) == mUserData->mName;
7475}
7476
7477/**
7478 * @note Locks objects for reading!
7479 */
7480bool Machine::isModified()
7481{
7482 AutoCaller autoCaller(this);
7483 AssertComRCReturn (autoCaller.rc(), false);
7484
7485 AutoReadLock alock(this);
7486
7487 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7488 if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isModified())
7489 return true;
7490
7491 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7492 if (mSerialPorts [slot] && mSerialPorts [slot]->isModified())
7493 return true;
7494
7495 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7496 if (mParallelPorts [slot] && mParallelPorts [slot]->isModified())
7497 return true;
7498
7499 if (!mStorageControllers.isNull())
7500 {
7501 for (StorageControllerList::const_iterator it =
7502 mStorageControllers->begin();
7503 it != mStorageControllers->end();
7504 ++it)
7505 {
7506 if ((*it)->isModified())
7507 return true;
7508 }
7509 }
7510
7511 return
7512 mUserData.isBackedUp() ||
7513 mHWData.isBackedUp() ||
7514 mMediaData.isBackedUp() ||
7515 mStorageControllers.isBackedUp() ||
7516#ifdef VBOX_WITH_VRDP
7517 (mVRDPServer && mVRDPServer->isModified()) ||
7518#endif
7519 (mAudioAdapter && mAudioAdapter->isModified()) ||
7520 (mUSBController && mUSBController->isModified()) ||
7521 (mBIOSSettings && mBIOSSettings->isModified());
7522}
7523
7524/**
7525 * Returns the logical OR of data.hasActualChanges() of this and all child
7526 * objects.
7527 *
7528 * @param aIgnoreUserData @c true to ignore changes to mUserData
7529 *
7530 * @note Locks objects for reading!
7531 */
7532bool Machine::isReallyModified (bool aIgnoreUserData /* = false */)
7533{
7534 AutoCaller autoCaller(this);
7535 AssertComRCReturn (autoCaller.rc(), false);
7536
7537 AutoReadLock alock(this);
7538
7539 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7540 if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isReallyModified())
7541 return true;
7542
7543 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7544 if (mSerialPorts [slot] && mSerialPorts [slot]->isReallyModified())
7545 return true;
7546
7547 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7548 if (mParallelPorts [slot] && mParallelPorts [slot]->isReallyModified())
7549 return true;
7550
7551 if (!mStorageControllers.isBackedUp())
7552 {
7553 /* see whether any of the devices has changed its data */
7554 for (StorageControllerList::const_iterator
7555 it = mStorageControllers->begin();
7556 it != mStorageControllers->end();
7557 ++it)
7558 {
7559 if ((*it)->isReallyModified())
7560 return true;
7561 }
7562 }
7563 else
7564 {
7565 if (mStorageControllers->size() != mStorageControllers.backedUpData()->size())
7566 return true;
7567 }
7568
7569 return
7570 (!aIgnoreUserData && mUserData.hasActualChanges()) ||
7571 mHWData.hasActualChanges() ||
7572 mMediaData.hasActualChanges() ||
7573 mStorageControllers.hasActualChanges() ||
7574#ifdef VBOX_WITH_VRDP
7575 (mVRDPServer && mVRDPServer->isReallyModified()) ||
7576#endif
7577 (mAudioAdapter && mAudioAdapter->isReallyModified()) ||
7578 (mUSBController && mUSBController->isReallyModified()) ||
7579 (mBIOSSettings && mBIOSSettings->isReallyModified());
7580}
7581
7582/**
7583 * Discards all changes to machine settings.
7584 *
7585 * @param aNotify Whether to notify the direct session about changes or not.
7586 *
7587 * @note Locks objects for writing!
7588 */
7589void Machine::rollback (bool aNotify)
7590{
7591 AutoCaller autoCaller(this);
7592 AssertComRCReturn (autoCaller.rc(), (void) 0);
7593
7594 AutoWriteLock alock(this);
7595
7596 /* check for changes in own data */
7597
7598 bool sharedFoldersChanged = false, storageChanged = false;
7599
7600 if (aNotify && mHWData.isBackedUp())
7601 {
7602 if (mHWData->mSharedFolders.size() !=
7603 mHWData.backedUpData()->mSharedFolders.size())
7604 sharedFoldersChanged = true;
7605 else
7606 {
7607 for (HWData::SharedFolderList::iterator rit =
7608 mHWData->mSharedFolders.begin();
7609 rit != mHWData->mSharedFolders.end() && !sharedFoldersChanged;
7610 ++rit)
7611 {
7612 for (HWData::SharedFolderList::iterator cit =
7613 mHWData.backedUpData()->mSharedFolders.begin();
7614 cit != mHWData.backedUpData()->mSharedFolders.end();
7615 ++cit)
7616 {
7617 if ((*cit)->name() != (*rit)->name() ||
7618 (*cit)->hostPath() != (*rit)->hostPath())
7619 {
7620 sharedFoldersChanged = true;
7621 break;
7622 }
7623 }
7624 }
7625 }
7626 }
7627
7628 if (!mStorageControllers.isNull())
7629 {
7630 if (mStorageControllers.isBackedUp())
7631 {
7632 /* unitialize all new devices (absent in the backed up list). */
7633 StorageControllerList::const_iterator it = mStorageControllers->begin();
7634 StorageControllerList *backedList = mStorageControllers.backedUpData();
7635 while (it != mStorageControllers->end())
7636 {
7637 if (std::find (backedList->begin(), backedList->end(), *it ) ==
7638 backedList->end())
7639 {
7640 (*it)->uninit();
7641 }
7642 ++it;
7643 }
7644
7645 /* restore the list */
7646 mStorageControllers.rollback();
7647 }
7648
7649 /* rollback any changes to devices after restoring the list */
7650 StorageControllerList::const_iterator it = mStorageControllers->begin();
7651 while (it != mStorageControllers->end())
7652 {
7653 if ((*it)->isModified())
7654 (*it)->rollback();
7655
7656 ++it;
7657 }
7658 }
7659
7660 mUserData.rollback();
7661
7662 mHWData.rollback();
7663
7664 if (mMediaData.isBackedUp())
7665 fixupMedia(false /* aCommit */);
7666
7667 /* check for changes in child objects */
7668
7669 bool vrdpChanged = false, usbChanged = false;
7670
7671 ComPtr<INetworkAdapter> networkAdapters [RT_ELEMENTS (mNetworkAdapters)];
7672 ComPtr<ISerialPort> serialPorts [RT_ELEMENTS (mSerialPorts)];
7673 ComPtr<IParallelPort> parallelPorts [RT_ELEMENTS (mParallelPorts)];
7674
7675 if (mBIOSSettings)
7676 mBIOSSettings->rollback();
7677
7678#ifdef VBOX_WITH_VRDP
7679 if (mVRDPServer)
7680 vrdpChanged = mVRDPServer->rollback();
7681#endif
7682
7683 if (mAudioAdapter)
7684 mAudioAdapter->rollback();
7685
7686 if (mUSBController)
7687 usbChanged = mUSBController->rollback();
7688
7689 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7690 if (mNetworkAdapters [slot])
7691 if (mNetworkAdapters [slot]->rollback())
7692 networkAdapters [slot] = mNetworkAdapters [slot];
7693
7694 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7695 if (mSerialPorts [slot])
7696 if (mSerialPorts [slot]->rollback())
7697 serialPorts [slot] = mSerialPorts [slot];
7698
7699 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7700 if (mParallelPorts [slot])
7701 if (mParallelPorts [slot]->rollback())
7702 parallelPorts [slot] = mParallelPorts [slot];
7703
7704 if (aNotify)
7705 {
7706 /* inform the direct session about changes */
7707
7708 ComObjPtr<Machine> that = this;
7709 alock.leave();
7710
7711 if (sharedFoldersChanged)
7712 that->onSharedFolderChange();
7713
7714 if (vrdpChanged)
7715 that->onVRDPServerChange();
7716 if (usbChanged)
7717 that->onUSBControllerChange();
7718
7719 for (ULONG slot = 0; slot < RT_ELEMENTS (networkAdapters); slot ++)
7720 if (networkAdapters [slot])
7721 that->onNetworkAdapterChange (networkAdapters [slot], FALSE);
7722 for (ULONG slot = 0; slot < RT_ELEMENTS (serialPorts); slot ++)
7723 if (serialPorts [slot])
7724 that->onSerialPortChange (serialPorts [slot]);
7725 for (ULONG slot = 0; slot < RT_ELEMENTS (parallelPorts); slot ++)
7726 if (parallelPorts [slot])
7727 that->onParallelPortChange (parallelPorts [slot]);
7728
7729 if (storageChanged)
7730 that->onStorageControllerChange();
7731 }
7732}
7733
7734/**
7735 * Commits all the changes to machine settings.
7736 *
7737 * Note that this operation is supposed to never fail.
7738 *
7739 * @note Locks this object and children for writing.
7740 */
7741void Machine::commit()
7742{
7743 AutoCaller autoCaller(this);
7744 AssertComRCReturnVoid (autoCaller.rc());
7745
7746 AutoCaller peerCaller (mPeer);
7747 AssertComRCReturnVoid (peerCaller.rc());
7748
7749 AutoMultiWriteLock2 alock (mPeer, this);
7750
7751 /*
7752 * use safe commit to ensure Snapshot machines (that share mUserData)
7753 * will still refer to a valid memory location
7754 */
7755 mUserData.commitCopy();
7756
7757 mHWData.commit();
7758
7759 if (mMediaData.isBackedUp())
7760 fixupMedia(true /* aCommit */);
7761
7762 mBIOSSettings->commit();
7763#ifdef VBOX_WITH_VRDP
7764 mVRDPServer->commit();
7765#endif
7766 mAudioAdapter->commit();
7767 mUSBController->commit();
7768
7769 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7770 mNetworkAdapters [slot]->commit();
7771 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7772 mSerialPorts [slot]->commit();
7773 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7774 mParallelPorts [slot]->commit();
7775
7776 bool commitStorageControllers = false;
7777
7778 if (mStorageControllers.isBackedUp())
7779 {
7780 mStorageControllers.commit();
7781
7782 if (mPeer)
7783 {
7784 AutoWriteLock peerlock (mPeer);
7785
7786 /* Commit all changes to new controllers (this will reshare data with
7787 * peers for thos who have peers) */
7788 StorageControllerList *newList = new StorageControllerList();
7789 StorageControllerList::const_iterator it = mStorageControllers->begin();
7790 while (it != mStorageControllers->end())
7791 {
7792 (*it)->commit();
7793
7794 /* look if this controller has a peer device */
7795 ComObjPtr<StorageController> peer = (*it)->peer();
7796 if (!peer)
7797 {
7798 /* no peer means the device is a newly created one;
7799 * create a peer owning data this device share it with */
7800 peer.createObject();
7801 peer->init (mPeer, *it, true /* aReshare */);
7802 }
7803 else
7804 {
7805 /* remove peer from the old list */
7806 mPeer->mStorageControllers->remove (peer);
7807 }
7808 /* and add it to the new list */
7809 newList->push_back(peer);
7810
7811 ++it;
7812 }
7813
7814 /* uninit old peer's controllers that are left */
7815 it = mPeer->mStorageControllers->begin();
7816 while (it != mPeer->mStorageControllers->end())
7817 {
7818 (*it)->uninit();
7819 ++it;
7820 }
7821
7822 /* attach new list of controllers to our peer */
7823 mPeer->mStorageControllers.attach (newList);
7824 }
7825 else
7826 {
7827 /* we have no peer (our parent is the newly created machine);
7828 * just commit changes to devices */
7829 commitStorageControllers = true;
7830 }
7831 }
7832 else
7833 {
7834 /* the list of controllers itself is not changed,
7835 * just commit changes to controllers themselves */
7836 commitStorageControllers = true;
7837 }
7838
7839 if (commitStorageControllers)
7840 {
7841 StorageControllerList::const_iterator it = mStorageControllers->begin();
7842 while (it != mStorageControllers->end())
7843 {
7844 (*it)->commit();
7845 ++it;
7846 }
7847 }
7848
7849 if (mType == IsSessionMachine)
7850 {
7851 /* attach new data to the primary machine and reshare it */
7852 mPeer->mUserData.attach (mUserData);
7853 mPeer->mHWData.attach (mHWData);
7854 /* mMediaData is reshared by fixupMedia */
7855 // mPeer->mMediaData.attach(mMediaData);
7856 Assert(mPeer->mMediaData.data() == mMediaData.data());
7857 }
7858}
7859
7860/**
7861 * Copies all the hardware data from the given machine.
7862 *
7863 * Currently, only called when the VM is being restored from a snapshot. In
7864 * particular, this implies that the VM is not running during this method's
7865 * call.
7866 *
7867 * @note This method must be called from under this object's lock.
7868 *
7869 * @note This method doesn't call #commit(), so all data remains backed up and
7870 * unsaved.
7871 */
7872void Machine::copyFrom (Machine *aThat)
7873{
7874 AssertReturnVoid (mType == IsMachine || mType == IsSessionMachine);
7875 AssertReturnVoid (aThat->mType == IsSnapshotMachine);
7876
7877 AssertReturnVoid (!Global::IsOnline (mData->mMachineState));
7878
7879 mHWData.assignCopy (aThat->mHWData);
7880
7881 // create copies of all shared folders (mHWData after attiching a copy
7882 // contains just references to original objects)
7883 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
7884 it != mHWData->mSharedFolders.end();
7885 ++it)
7886 {
7887 ComObjPtr<SharedFolder> folder;
7888 folder.createObject();
7889 HRESULT rc = folder->initCopy (machine(), *it);
7890 AssertComRC (rc);
7891 *it = folder;
7892 }
7893
7894 mBIOSSettings->copyFrom (aThat->mBIOSSettings);
7895#ifdef VBOX_WITH_VRDP
7896 mVRDPServer->copyFrom (aThat->mVRDPServer);
7897#endif
7898 mAudioAdapter->copyFrom (aThat->mAudioAdapter);
7899 mUSBController->copyFrom (aThat->mUSBController);
7900
7901 /* create private copies of all controllers */
7902 mStorageControllers.backup();
7903 mStorageControllers->clear();
7904 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
7905 it != aThat->mStorageControllers->end();
7906 ++it)
7907 {
7908 ComObjPtr<StorageController> ctrl;
7909 ctrl.createObject();
7910 ctrl->initCopy (this, *it);
7911 mStorageControllers->push_back(ctrl);
7912 }
7913
7914 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
7915 mNetworkAdapters [slot]->copyFrom (aThat->mNetworkAdapters [slot]);
7916 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
7917 mSerialPorts [slot]->copyFrom (aThat->mSerialPorts [slot]);
7918 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
7919 mParallelPorts [slot]->copyFrom (aThat->mParallelPorts [slot]);
7920}
7921
7922#ifdef VBOX_WITH_RESOURCE_USAGE_API
7923void Machine::registerMetrics (PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
7924{
7925 pm::CollectorHAL *hal = aCollector->getHAL();
7926 /* Create sub metrics */
7927 pm::SubMetric *cpuLoadUser = new pm::SubMetric ("CPU/Load/User",
7928 "Percentage of processor time spent in user mode by VM process.");
7929 pm::SubMetric *cpuLoadKernel = new pm::SubMetric ("CPU/Load/Kernel",
7930 "Percentage of processor time spent in kernel mode by VM process.");
7931 pm::SubMetric *ramUsageUsed = new pm::SubMetric ("RAM/Usage/Used",
7932 "Size of resident portion of VM process in memory.");
7933 /* Create and register base metrics */
7934 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw (hal, aMachine, pid,
7935 cpuLoadUser, cpuLoadKernel);
7936 aCollector->registerBaseMetric (cpuLoad);
7937 pm::BaseMetric *ramUsage = new pm::MachineRamUsage (hal, aMachine, pid,
7938 ramUsageUsed);
7939 aCollector->registerBaseMetric (ramUsage);
7940
7941 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser, 0));
7942 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
7943 new pm::AggregateAvg()));
7944 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
7945 new pm::AggregateMin()));
7946 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
7947 new pm::AggregateMax()));
7948 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel, 0));
7949 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
7950 new pm::AggregateAvg()));
7951 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
7952 new pm::AggregateMin()));
7953 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
7954 new pm::AggregateMax()));
7955
7956 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed, 0));
7957 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
7958 new pm::AggregateAvg()));
7959 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
7960 new pm::AggregateMin()));
7961 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
7962 new pm::AggregateMax()));
7963};
7964
7965void Machine::unregisterMetrics (PerformanceCollector *aCollector, Machine *aMachine)
7966{
7967 aCollector->unregisterMetricsFor (aMachine);
7968 aCollector->unregisterBaseMetricsFor (aMachine);
7969};
7970#endif /* VBOX_WITH_RESOURCE_USAGE_API */
7971
7972
7973////////////////////////////////////////////////////////////////////////////////
7974
7975DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
7976
7977HRESULT SessionMachine::FinalConstruct()
7978{
7979 LogFlowThisFunc(("\n"));
7980
7981 /* set the proper type to indicate we're the SessionMachine instance */
7982 unconst(mType) = IsSessionMachine;
7983
7984#if defined(RT_OS_WINDOWS)
7985 mIPCSem = NULL;
7986#elif defined(RT_OS_OS2)
7987 mIPCSem = NULLHANDLE;
7988#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
7989 mIPCSem = -1;
7990#else
7991# error "Port me!"
7992#endif
7993
7994 return S_OK;
7995}
7996
7997void SessionMachine::FinalRelease()
7998{
7999 LogFlowThisFunc(("\n"));
8000
8001 uninit (Uninit::Unexpected);
8002}
8003
8004/**
8005 * @note Must be called only by Machine::openSession() from its own write lock.
8006 */
8007HRESULT SessionMachine::init (Machine *aMachine)
8008{
8009 LogFlowThisFuncEnter();
8010 LogFlowThisFunc(("mName={%ls}\n", aMachine->mUserData->mName.raw()));
8011
8012 AssertReturn(aMachine, E_INVALIDARG);
8013
8014 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
8015
8016 /* Enclose the state transition NotReady->InInit->Ready */
8017 AutoInitSpan autoInitSpan(this);
8018 AssertReturn(autoInitSpan.isOk(), E_FAIL);
8019
8020 /* create the interprocess semaphore */
8021#if defined(RT_OS_WINDOWS)
8022 mIPCSemName = aMachine->mData->m_strConfigFileFull;
8023 for (size_t i = 0; i < mIPCSemName.length(); i++)
8024 if (mIPCSemName[i] == '\\')
8025 mIPCSemName[i] = '/';
8026 mIPCSem = ::CreateMutex (NULL, FALSE, mIPCSemName);
8027 ComAssertMsgRet (mIPCSem,
8028 ("Cannot create IPC mutex '%ls', err=%d",
8029 mIPCSemName.raw(), ::GetLastError()),
8030 E_FAIL);
8031#elif defined(RT_OS_OS2)
8032 Utf8Str ipcSem = Utf8StrFmt ("\\SEM32\\VBOX\\VM\\{%RTuuid}",
8033 aMachine->mData->mUuid.raw());
8034 mIPCSemName = ipcSem;
8035 APIRET arc = ::DosCreateMutexSem ((PSZ) ipcSem.raw(), &mIPCSem, 0, FALSE);
8036 ComAssertMsgRet (arc == NO_ERROR,
8037 ("Cannot create IPC mutex '%s', arc=%ld",
8038 ipcSem.raw(), arc),
8039 E_FAIL);
8040#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8041# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8042# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
8043 /** @todo Check that this still works correctly. */
8044 AssertCompileSize(key_t, 8);
8045# else
8046 AssertCompileSize(key_t, 4);
8047# endif
8048 key_t key;
8049 mIPCSem = -1;
8050 mIPCKey = "0";
8051 for (uint32_t i = 0; i < 1 << 24; i++)
8052 {
8053 key = ((uint32_t)'V' << 24) | i;
8054 int sem = ::semget (key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
8055 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
8056 {
8057 mIPCSem = sem;
8058 if (sem >= 0)
8059 mIPCKey = BstrFmt ("%u", key);
8060 break;
8061 }
8062 }
8063# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8064 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
8065 char *pszSemName = NULL;
8066 RTStrUtf8ToCurrentCP (&pszSemName, semName);
8067 key_t key = ::ftok (pszSemName, 'V');
8068 RTStrFree (pszSemName);
8069
8070 mIPCSem = ::semget (key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
8071# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8072
8073 int errnoSave = errno;
8074 if (mIPCSem < 0 && errnoSave == ENOSYS)
8075 {
8076 setError(E_FAIL,
8077 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
8078 "support for SysV IPC. Check the host kernel configuration for "
8079 "CONFIG_SYSVIPC=y"));
8080 return E_FAIL;
8081 }
8082 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
8083 * the IPC semaphores */
8084 if (mIPCSem < 0 && errnoSave == ENOSPC)
8085 {
8086#ifdef RT_OS_LINUX
8087 setError(E_FAIL,
8088 tr("Cannot create IPC semaphore because the system limit for the "
8089 "maximum number of semaphore sets (SEMMNI), or the system wide "
8090 "maximum number of sempahores (SEMMNS) would be exceeded. The "
8091 "current set of SysV IPC semaphores can be determined from "
8092 "the file /proc/sysvipc/sem"));
8093#else
8094 setError(E_FAIL,
8095 tr("Cannot create IPC semaphore because the system-imposed limit "
8096 "on the maximum number of allowed semaphores or semaphore "
8097 "identifiers system-wide would be exceeded"));
8098#endif
8099 return E_FAIL;
8100 }
8101 ComAssertMsgRet (mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
8102 E_FAIL);
8103 /* set the initial value to 1 */
8104 int rv = ::semctl (mIPCSem, 0, SETVAL, 1);
8105 ComAssertMsgRet (rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
8106 E_FAIL);
8107#else
8108# error "Port me!"
8109#endif
8110
8111 /* memorize the peer Machine */
8112 unconst(mPeer) = aMachine;
8113 /* share the parent pointer */
8114 unconst(mParent) = aMachine->mParent;
8115
8116 /* take the pointers to data to share */
8117 mData.share (aMachine->mData);
8118 mSSData.share (aMachine->mSSData);
8119
8120 mUserData.share (aMachine->mUserData);
8121 mHWData.share (aMachine->mHWData);
8122 mMediaData.share(aMachine->mMediaData);
8123
8124 mStorageControllers.allocate();
8125 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
8126 it != aMachine->mStorageControllers->end();
8127 ++it)
8128 {
8129 ComObjPtr<StorageController> ctl;
8130 ctl.createObject();
8131 ctl->init(this, *it);
8132 mStorageControllers->push_back (ctl);
8133 }
8134
8135 unconst(mBIOSSettings).createObject();
8136 mBIOSSettings->init (this, aMachine->mBIOSSettings);
8137#ifdef VBOX_WITH_VRDP
8138 /* create another VRDPServer object that will be mutable */
8139 unconst(mVRDPServer).createObject();
8140 mVRDPServer->init (this, aMachine->mVRDPServer);
8141#endif
8142 /* create another audio adapter object that will be mutable */
8143 unconst(mAudioAdapter).createObject();
8144 mAudioAdapter->init (this, aMachine->mAudioAdapter);
8145 /* create a list of serial ports that will be mutable */
8146 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
8147 {
8148 unconst(mSerialPorts [slot]).createObject();
8149 mSerialPorts [slot]->init (this, aMachine->mSerialPorts [slot]);
8150 }
8151 /* create a list of parallel ports that will be mutable */
8152 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
8153 {
8154 unconst(mParallelPorts [slot]).createObject();
8155 mParallelPorts [slot]->init (this, aMachine->mParallelPorts [slot]);
8156 }
8157 /* create another USB controller object that will be mutable */
8158 unconst(mUSBController).createObject();
8159 mUSBController->init (this, aMachine->mUSBController);
8160
8161 /* create a list of network adapters that will be mutable */
8162 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
8163 {
8164 unconst(mNetworkAdapters [slot]).createObject();
8165 mNetworkAdapters [slot]->init (this, aMachine->mNetworkAdapters [slot]);
8166 }
8167
8168 /* default is to delete saved state on Saved -> PoweredOff transition */
8169 mRemoveSavedState = true;
8170
8171 /* Confirm a successful initialization when it's the case */
8172 autoInitSpan.setSucceeded();
8173
8174 LogFlowThisFuncLeave();
8175 return S_OK;
8176}
8177
8178/**
8179 * Uninitializes this session object. If the reason is other than
8180 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
8181 *
8182 * @param aReason uninitialization reason
8183 *
8184 * @note Locks mParent + this object for writing.
8185 */
8186void SessionMachine::uninit (Uninit::Reason aReason)
8187{
8188 LogFlowThisFuncEnter();
8189 LogFlowThisFunc(("reason=%d\n", aReason));
8190
8191 /*
8192 * Strongly reference ourselves to prevent this object deletion after
8193 * mData->mSession.mMachine.setNull() below (which can release the last
8194 * reference and call the destructor). Important: this must be done before
8195 * accessing any members (and before AutoUninitSpan that does it as well).
8196 * This self reference will be released as the very last step on return.
8197 */
8198 ComObjPtr<SessionMachine> selfRef = this;
8199
8200 /* Enclose the state transition Ready->InUninit->NotReady */
8201 AutoUninitSpan autoUninitSpan(this);
8202 if (autoUninitSpan.uninitDone())
8203 {
8204 LogFlowThisFunc(("Already uninitialized\n"));
8205 LogFlowThisFuncLeave();
8206 return;
8207 }
8208
8209 if (autoUninitSpan.initFailed())
8210 {
8211 /* We've been called by init() because it's failed. It's not really
8212 * necessary (nor it's safe) to perform the regular uninit sequense
8213 * below, the following is enough.
8214 */
8215 LogFlowThisFunc(("Initialization failed.\n"));
8216#if defined(RT_OS_WINDOWS)
8217 if (mIPCSem)
8218 ::CloseHandle (mIPCSem);
8219 mIPCSem = NULL;
8220#elif defined(RT_OS_OS2)
8221 if (mIPCSem != NULLHANDLE)
8222 ::DosCloseMutexSem (mIPCSem);
8223 mIPCSem = NULLHANDLE;
8224#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8225 if (mIPCSem >= 0)
8226 ::semctl (mIPCSem, 0, IPC_RMID);
8227 mIPCSem = -1;
8228# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8229 mIPCKey = "0";
8230# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
8231#else
8232# error "Port me!"
8233#endif
8234 uninitDataAndChildObjects();
8235 mData.free();
8236 unconst(mParent).setNull();
8237 unconst(mPeer).setNull();
8238 LogFlowThisFuncLeave();
8239 return;
8240 }
8241
8242 /* We need to lock this object in uninit() because the lock is shared
8243 * with mPeer (as well as data we modify below). mParent->addProcessToReap()
8244 * and others need mParent lock. */
8245 AutoMultiWriteLock2 alock (mParent, this);
8246
8247#ifdef VBOX_WITH_RESOURCE_USAGE_API
8248 unregisterMetrics (mParent->performanceCollector(), mPeer);
8249#endif /* VBOX_WITH_RESOURCE_USAGE_API */
8250
8251 MachineState_T lastState = mData->mMachineState;
8252 NOREF(lastState);
8253
8254 if (aReason == Uninit::Abnormal)
8255 {
8256 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
8257 Global::IsOnlineOrTransient (lastState)));
8258
8259 /* reset the state to Aborted */
8260 if (mData->mMachineState != MachineState_Aborted)
8261 setMachineState (MachineState_Aborted);
8262 }
8263
8264 if (isModified())
8265 {
8266 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
8267 rollback (false /* aNotify */);
8268 }
8269
8270 Assert(mSnapshotData.mStateFilePath.isEmpty() || !mSnapshotData.mSnapshot);
8271 if (!mSnapshotData.mStateFilePath.isEmpty())
8272 {
8273 LogWarningThisFunc(("canceling failed save state request!\n"));
8274 endSavingState (FALSE /* aSuccess */);
8275 }
8276 else if (!mSnapshotData.mSnapshot.isNull())
8277 {
8278 LogWarningThisFunc(("canceling untaken snapshot!\n"));
8279 endTakingSnapshot (FALSE /* aSuccess */);
8280 }
8281
8282#ifdef VBOX_WITH_USB
8283 /* release all captured USB devices */
8284 if (aReason == Uninit::Abnormal && Global::IsOnline (lastState))
8285 {
8286 /* Console::captureUSBDevices() is called in the VM process only after
8287 * setting the machine state to Starting or Restoring.
8288 * Console::detachAllUSBDevices() will be called upon successful
8289 * termination. So, we need to release USB devices only if there was
8290 * an abnormal termination of a running VM.
8291 *
8292 * This is identical to SessionMachine::DetachAllUSBDevices except
8293 * for the aAbnormal argument. */
8294 HRESULT rc = mUSBController->notifyProxy (false /* aInsertFilters */);
8295 AssertComRC(rc);
8296 NOREF (rc);
8297
8298 USBProxyService *service = mParent->host()->usbProxyService();
8299 if (service)
8300 service->detachAllDevicesFromVM (this, true /* aDone */, true /* aAbnormal */);
8301 }
8302#endif /* VBOX_WITH_USB */
8303
8304 if (!mData->mSession.mType.isNull())
8305 {
8306 /* mType is not null when this machine's process has been started by
8307 * VirtualBox::OpenRemoteSession(), therefore it is our child. We
8308 * need to queue the PID to reap the process (and avoid zombies on
8309 * Linux). */
8310 Assert (mData->mSession.mPid != NIL_RTPROCESS);
8311 mParent->addProcessToReap (mData->mSession.mPid);
8312 }
8313
8314 mData->mSession.mPid = NIL_RTPROCESS;
8315
8316 if (aReason == Uninit::Unexpected)
8317 {
8318 /* Uninitialization didn't come from #checkForDeath(), so tell the
8319 * client watcher thread to update the set of machines that have open
8320 * sessions. */
8321 mParent->updateClientWatcher();
8322 }
8323
8324 /* uninitialize all remote controls */
8325 if (mData->mSession.mRemoteControls.size())
8326 {
8327 LogFlowThisFunc(("Closing remote sessions (%d):\n",
8328 mData->mSession.mRemoteControls.size()));
8329
8330 Data::Session::RemoteControlList::iterator it =
8331 mData->mSession.mRemoteControls.begin();
8332 while (it != mData->mSession.mRemoteControls.end())
8333 {
8334 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
8335 HRESULT rc = (*it)->Uninitialize();
8336 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
8337 if (FAILED (rc))
8338 LogWarningThisFunc(("Forgot to close the remote session?\n"));
8339 ++it;
8340 }
8341 mData->mSession.mRemoteControls.clear();
8342 }
8343
8344 /*
8345 * An expected uninitialization can come only from #checkForDeath().
8346 * Otherwise it means that something's got really wrong (for examlple,
8347 * the Session implementation has released the VirtualBox reference
8348 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
8349 * etc). However, it's also possible, that the client releases the IPC
8350 * semaphore correctly (i.e. before it releases the VirtualBox reference),
8351 * but the VirtualBox release event comes first to the server process.
8352 * This case is practically possible, so we should not assert on an
8353 * unexpected uninit, just log a warning.
8354 */
8355
8356 if ((aReason == Uninit::Unexpected))
8357 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
8358
8359 if (aReason != Uninit::Normal)
8360 {
8361 mData->mSession.mDirectControl.setNull();
8362 }
8363 else
8364 {
8365 /* this must be null here (see #OnSessionEnd()) */
8366 Assert (mData->mSession.mDirectControl.isNull());
8367 Assert (mData->mSession.mState == SessionState_Closing);
8368 Assert (!mData->mSession.mProgress.isNull());
8369
8370 mData->mSession.mProgress->notifyComplete (S_OK);
8371 mData->mSession.mProgress.setNull();
8372 }
8373
8374 /* remove the association between the peer machine and this session machine */
8375 Assert (mData->mSession.mMachine == this ||
8376 aReason == Uninit::Unexpected);
8377
8378 /* reset the rest of session data */
8379 mData->mSession.mMachine.setNull();
8380 mData->mSession.mState = SessionState_Closed;
8381 mData->mSession.mType.setNull();
8382
8383 /* close the interprocess semaphore before leaving the exclusive lock */
8384#if defined(RT_OS_WINDOWS)
8385 if (mIPCSem)
8386 ::CloseHandle (mIPCSem);
8387 mIPCSem = NULL;
8388#elif defined(RT_OS_OS2)
8389 if (mIPCSem != NULLHANDLE)
8390 ::DosCloseMutexSem (mIPCSem);
8391 mIPCSem = NULLHANDLE;
8392#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8393 if (mIPCSem >= 0)
8394 ::semctl (mIPCSem, 0, IPC_RMID);
8395 mIPCSem = -1;
8396# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8397 mIPCKey = "0";
8398# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
8399#else
8400# error "Port me!"
8401#endif
8402
8403 /* fire an event */
8404 mParent->onSessionStateChange (mData->mUuid, SessionState_Closed);
8405
8406 uninitDataAndChildObjects();
8407
8408 /* free the essential data structure last */
8409 mData.free();
8410
8411 /* leave the exclusive lock before setting the below two to NULL */
8412 alock.leave();
8413
8414 unconst(mParent).setNull();
8415 unconst(mPeer).setNull();
8416
8417 LogFlowThisFuncLeave();
8418}
8419
8420// util::Lockable interface
8421////////////////////////////////////////////////////////////////////////////////
8422
8423/**
8424 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
8425 * with the primary Machine instance (mPeer).
8426 */
8427RWLockHandle *SessionMachine::lockHandle() const
8428{
8429 AssertReturn(!mPeer.isNull(), NULL);
8430 return mPeer->lockHandle();
8431}
8432
8433// IInternalMachineControl methods
8434////////////////////////////////////////////////////////////////////////////////
8435
8436/**
8437 * @note Locks this object for writing.
8438 */
8439STDMETHODIMP SessionMachine::SetRemoveSavedState(BOOL aRemove)
8440{
8441 AutoCaller autoCaller(this);
8442 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8443
8444 AutoWriteLock alock(this);
8445
8446 mRemoveSavedState = aRemove;
8447
8448 return S_OK;
8449}
8450
8451/**
8452 * @note Locks the same as #setMachineState() does.
8453 */
8454STDMETHODIMP SessionMachine::UpdateState (MachineState_T aMachineState)
8455{
8456 return setMachineState (aMachineState);
8457}
8458
8459/**
8460 * @note Locks this object for reading.
8461 */
8462STDMETHODIMP SessionMachine::GetIPCId (BSTR *aId)
8463{
8464 AutoCaller autoCaller(this);
8465 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8466
8467 AutoReadLock alock(this);
8468
8469#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8470 mIPCSemName.cloneTo(aId);
8471 return S_OK;
8472#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8473# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8474 mIPCKey.cloneTo(aId);
8475# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8476 mData->m_strConfigFileFull.cloneTo(aId);
8477# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8478 return S_OK;
8479#else
8480# error "Port me!"
8481#endif
8482}
8483
8484/**
8485 * Goes through the USB filters of the given machine to see if the given
8486 * device matches any filter or not.
8487 *
8488 * @note Locks the same as USBController::hasMatchingFilter() does.
8489 */
8490STDMETHODIMP SessionMachine::RunUSBDeviceFilters (IUSBDevice *aUSBDevice,
8491 BOOL *aMatched,
8492 ULONG *aMaskedIfs)
8493{
8494 LogFlowThisFunc(("\n"));
8495
8496 CheckComArgNotNull (aUSBDevice);
8497 CheckComArgOutPointerValid(aMatched);
8498
8499 AutoCaller autoCaller(this);
8500 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8501
8502#ifdef VBOX_WITH_USB
8503 *aMatched = mUSBController->hasMatchingFilter (aUSBDevice, aMaskedIfs);
8504#else
8505 NOREF(aUSBDevice);
8506 NOREF(aMaskedIfs);
8507 *aMatched = FALSE;
8508#endif
8509
8510 return S_OK;
8511}
8512
8513/**
8514 * @note Locks the same as Host::captureUSBDevice() does.
8515 */
8516STDMETHODIMP SessionMachine::CaptureUSBDevice (IN_BSTR aId)
8517{
8518 LogFlowThisFunc(("\n"));
8519
8520 AutoCaller autoCaller(this);
8521 AssertComRCReturnRC(autoCaller.rc());
8522
8523#ifdef VBOX_WITH_USB
8524 /* if captureDeviceForVM() fails, it must have set extended error info */
8525 MultiResult rc = mParent->host()->checkUSBProxyService();
8526 CheckComRCReturnRC(rc);
8527
8528 USBProxyService *service = mParent->host()->usbProxyService();
8529 AssertReturn(service, E_FAIL);
8530 return service->captureDeviceForVM (this, Guid(aId));
8531#else
8532 NOREF(aId);
8533 return E_NOTIMPL;
8534#endif
8535}
8536
8537/**
8538 * @note Locks the same as Host::detachUSBDevice() does.
8539 */
8540STDMETHODIMP SessionMachine::DetachUSBDevice (IN_BSTR aId, BOOL aDone)
8541{
8542 LogFlowThisFunc(("\n"));
8543
8544 AutoCaller autoCaller(this);
8545 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8546
8547#ifdef VBOX_WITH_USB
8548 USBProxyService *service = mParent->host()->usbProxyService();
8549 AssertReturn(service, E_FAIL);
8550 return service->detachDeviceFromVM (this, Guid(aId), !!aDone);
8551#else
8552 NOREF(aId);
8553 NOREF(aDone);
8554 return E_NOTIMPL;
8555#endif
8556}
8557
8558/**
8559 * Inserts all machine filters to the USB proxy service and then calls
8560 * Host::autoCaptureUSBDevices().
8561 *
8562 * Called by Console from the VM process upon VM startup.
8563 *
8564 * @note Locks what called methods lock.
8565 */
8566STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
8567{
8568 LogFlowThisFunc(("\n"));
8569
8570 AutoCaller autoCaller(this);
8571 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8572
8573#ifdef VBOX_WITH_USB
8574 HRESULT rc = mUSBController->notifyProxy (true /* aInsertFilters */);
8575 AssertComRC(rc);
8576 NOREF (rc);
8577
8578 USBProxyService *service = mParent->host()->usbProxyService();
8579 AssertReturn(service, E_FAIL);
8580 return service->autoCaptureDevicesForVM (this);
8581#else
8582 return S_OK;
8583#endif
8584}
8585
8586/**
8587 * Removes all machine filters from the USB proxy service and then calls
8588 * Host::detachAllUSBDevices().
8589 *
8590 * Called by Console from the VM process upon normal VM termination or by
8591 * SessionMachine::uninit() upon abnormal VM termination (from under the
8592 * Machine/SessionMachine lock).
8593 *
8594 * @note Locks what called methods lock.
8595 */
8596STDMETHODIMP SessionMachine::DetachAllUSBDevices (BOOL aDone)
8597{
8598 LogFlowThisFunc(("\n"));
8599
8600 AutoCaller autoCaller(this);
8601 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8602
8603#ifdef VBOX_WITH_USB
8604 HRESULT rc = mUSBController->notifyProxy (false /* aInsertFilters */);
8605 AssertComRC(rc);
8606 NOREF (rc);
8607
8608 USBProxyService *service = mParent->host()->usbProxyService();
8609 AssertReturn(service, E_FAIL);
8610 return service->detachAllDevicesFromVM (this, !!aDone, false /* aAbnormal */);
8611#else
8612 NOREF(aDone);
8613 return S_OK;
8614#endif
8615}
8616
8617/**
8618 * @note Locks this object for writing.
8619 */
8620STDMETHODIMP SessionMachine::OnSessionEnd (ISession *aSession,
8621 IProgress **aProgress)
8622{
8623 LogFlowThisFuncEnter();
8624
8625 AssertReturn(aSession, E_INVALIDARG);
8626 AssertReturn(aProgress, E_INVALIDARG);
8627
8628 AutoCaller autoCaller(this);
8629
8630 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
8631 /*
8632 * We don't assert below because it might happen that a non-direct session
8633 * informs us it is closed right after we've been uninitialized -- it's ok.
8634 */
8635 CheckComRCReturnRC(autoCaller.rc());
8636
8637 /* get IInternalSessionControl interface */
8638 ComPtr<IInternalSessionControl> control (aSession);
8639
8640 ComAssertRet (!control.isNull(), E_INVALIDARG);
8641
8642 /* Creating a Progress object requires the VirtualBox children lock, and
8643 * thus locking it here is required by the lock order rules. */
8644 AutoMultiWriteLock2 alock(mParent->childrenLock(), this->lockHandle());
8645
8646 if (control.equalsTo (mData->mSession.mDirectControl))
8647 {
8648 ComAssertRet (aProgress, E_POINTER);
8649
8650 /* The direct session is being normally closed by the client process
8651 * ----------------------------------------------------------------- */
8652
8653 /* go to the closing state (essential for all open*Session() calls and
8654 * for #checkForDeath()) */
8655 Assert (mData->mSession.mState == SessionState_Open);
8656 mData->mSession.mState = SessionState_Closing;
8657
8658 /* set direct control to NULL to release the remote instance */
8659 mData->mSession.mDirectControl.setNull();
8660 LogFlowThisFunc(("Direct control is set to NULL\n"));
8661
8662 /* Create the progress object the client will use to wait until
8663 * #checkForDeath() is called to uninitialize this session object after
8664 * it releases the IPC semaphore. */
8665 ComObjPtr<Progress> progress;
8666 progress.createObject();
8667 progress->init (mParent, static_cast <IMachine *> (mPeer),
8668 Bstr (tr ("Closing session")), FALSE /* aCancelable */);
8669 progress.queryInterfaceTo(aProgress);
8670 mData->mSession.mProgress = progress;
8671 }
8672 else
8673 {
8674 /* the remote session is being normally closed */
8675 Data::Session::RemoteControlList::iterator it =
8676 mData->mSession.mRemoteControls.begin();
8677 while (it != mData->mSession.mRemoteControls.end())
8678 {
8679 if (control.equalsTo (*it))
8680 break;
8681 ++it;
8682 }
8683 BOOL found = it != mData->mSession.mRemoteControls.end();
8684 ComAssertMsgRet (found, ("The session is not found in the session list!"),
8685 E_INVALIDARG);
8686 mData->mSession.mRemoteControls.remove (*it);
8687 }
8688
8689 LogFlowThisFuncLeave();
8690 return S_OK;
8691}
8692
8693/**
8694 * @note Locks this object for writing.
8695 */
8696STDMETHODIMP SessionMachine::BeginSavingState (IProgress *aProgress, BSTR *aStateFilePath)
8697{
8698 LogFlowThisFuncEnter();
8699
8700 AssertReturn(aProgress, E_INVALIDARG);
8701 AssertReturn(aStateFilePath, E_POINTER);
8702
8703 AutoCaller autoCaller(this);
8704 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8705
8706 AutoWriteLock alock(this);
8707
8708 AssertReturn( mData->mMachineState == MachineState_Paused
8709 && mSnapshotData.mLastState == MachineState_Null
8710 && mSnapshotData.mProgressId.isEmpty()
8711 && mSnapshotData.mStateFilePath.isEmpty(),
8712 E_FAIL);
8713
8714 /* memorize the progress ID and add it to the global collection */
8715 Bstr progressId;
8716 HRESULT rc = aProgress->COMGETTER(Id) (progressId.asOutParam());
8717 AssertComRCReturn (rc, rc);
8718 rc = mParent->addProgress (aProgress);
8719 AssertComRCReturn (rc, rc);
8720
8721 Bstr stateFilePath;
8722 /* stateFilePath is null when the machine is not running */
8723 if (mData->mMachineState == MachineState_Paused)
8724 {
8725 stateFilePath = Utf8StrFmt ("%ls%c{%RTuuid}.sav",
8726 mUserData->mSnapshotFolderFull.raw(),
8727 RTPATH_DELIMITER, mData->mUuid.raw());
8728 }
8729
8730 /* fill in the snapshot data */
8731 mSnapshotData.mLastState = mData->mMachineState;
8732 mSnapshotData.mProgressId = Guid(progressId);
8733 mSnapshotData.mStateFilePath = stateFilePath;
8734
8735 /* set the state to Saving (this is expected by Console::SaveState()) */
8736 setMachineState (MachineState_Saving);
8737
8738 stateFilePath.cloneTo(aStateFilePath);
8739
8740 return S_OK;
8741}
8742
8743/**
8744 * @note Locks mParent + this object for writing.
8745 */
8746STDMETHODIMP SessionMachine::EndSavingState (BOOL aSuccess)
8747{
8748 LogFlowThisFunc(("\n"));
8749
8750 AutoCaller autoCaller(this);
8751 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8752
8753 /* endSavingState() need mParent lock */
8754 AutoMultiWriteLock2 alock (mParent, this);
8755
8756 AssertReturn( mData->mMachineState == MachineState_Saving
8757 && mSnapshotData.mLastState != MachineState_Null
8758 && !mSnapshotData.mProgressId.isEmpty()
8759 && !mSnapshotData.mStateFilePath.isEmpty(),
8760 E_FAIL);
8761
8762 /*
8763 * on success, set the state to Saved;
8764 * on failure, set the state to the state we had when BeginSavingState() was
8765 * called (this is expected by Console::SaveState() and
8766 * Console::saveStateThread())
8767 */
8768 if (aSuccess)
8769 setMachineState (MachineState_Saved);
8770 else
8771 setMachineState (mSnapshotData.mLastState);
8772
8773 return endSavingState (aSuccess);
8774}
8775
8776/**
8777 * @note Locks this object for writing.
8778 */
8779STDMETHODIMP SessionMachine::AdoptSavedState (IN_BSTR aSavedStateFile)
8780{
8781 LogFlowThisFunc(("\n"));
8782
8783 AssertReturn(aSavedStateFile, E_INVALIDARG);
8784
8785 AutoCaller autoCaller(this);
8786 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8787
8788 AutoWriteLock alock(this);
8789
8790 AssertReturn( mData->mMachineState == MachineState_PoweredOff
8791 || mData->mMachineState == MachineState_Teleported
8792 || mData->mMachineState == MachineState_Aborted
8793 , E_FAIL); /** @todo setError. */
8794
8795 Utf8Str stateFilePathFull = aSavedStateFile;
8796 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8797 if (RT_FAILURE(vrc))
8798 return setError(VBOX_E_FILE_ERROR,
8799 tr("Invalid saved state file path '%ls' (%Rrc)"),
8800 aSavedStateFile,
8801 vrc);
8802
8803 mSSData->mStateFilePath = stateFilePathFull;
8804
8805 /* The below setMachineState() will detect the state transition and will
8806 * update the settings file */
8807
8808 return setMachineState (MachineState_Saved);
8809}
8810
8811STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
8812 ComSafeArrayOut(BSTR, aValues),
8813 ComSafeArrayOut(ULONG64, aTimestamps),
8814 ComSafeArrayOut(BSTR, aFlags))
8815{
8816 LogFlowThisFunc(("\n"));
8817
8818#ifdef VBOX_WITH_GUEST_PROPS
8819 using namespace guestProp;
8820
8821 AutoCaller autoCaller(this);
8822 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8823
8824 AutoReadLock alock(this);
8825
8826 AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
8827 AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
8828 AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
8829 AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
8830
8831 size_t cEntries = mHWData->mGuestProperties.size();
8832 com::SafeArray<BSTR> names (cEntries);
8833 com::SafeArray<BSTR> values (cEntries);
8834 com::SafeArray<ULONG64> timestamps (cEntries);
8835 com::SafeArray<BSTR> flags (cEntries);
8836 unsigned i = 0;
8837 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
8838 it != mHWData->mGuestProperties.end();
8839 ++it)
8840 {
8841 char szFlags[MAX_FLAGS_LEN + 1];
8842 it->strName.cloneTo(&names[i]);
8843 it->strValue.cloneTo(&values[i]);
8844 timestamps[i] = it->mTimestamp;
8845 /* If it is NULL, keep it NULL. */
8846 if (it->mFlags)
8847 {
8848 writeFlags(it->mFlags, szFlags);
8849 Bstr(szFlags).cloneTo(&flags[i]);
8850 }
8851 else
8852 flags[i] = NULL;
8853 ++i;
8854 }
8855 names.detachTo(ComSafeArrayOutArg(aNames));
8856 values.detachTo(ComSafeArrayOutArg(aValues));
8857 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
8858 flags.detachTo(ComSafeArrayOutArg(aFlags));
8859 mHWData->mPropertyServiceActive = true;
8860 return S_OK;
8861#else
8862 ReturnComNotImplemented();
8863#endif
8864}
8865
8866STDMETHODIMP SessionMachine::PushGuestProperties(ComSafeArrayIn (IN_BSTR, aNames),
8867 ComSafeArrayIn (IN_BSTR, aValues),
8868 ComSafeArrayIn (ULONG64, aTimestamps),
8869 ComSafeArrayIn (IN_BSTR, aFlags))
8870{
8871 LogFlowThisFunc(("\n"));
8872
8873#ifdef VBOX_WITH_GUEST_PROPS
8874 using namespace guestProp;
8875
8876 AutoCaller autoCaller(this);
8877 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
8878
8879 AutoWriteLock alock(this);
8880
8881 /* Temporarily reset the registered flag, so that our machine state
8882 * changes (i.e. mHWData.backup()) succeed. (isMutable() used in
8883 * all setters will return FALSE for a Machine instance if mRegistered
8884 * is TRUE). This is copied from registeredInit(), and may or may not be
8885 * the right way to handle this. */
8886 mData->mRegistered = FALSE;
8887 HRESULT rc = checkStateDependency(MutableStateDep);
8888 LogRel (("checkStateDependency(MutableStateDep) returned 0x%x\n", rc));
8889 CheckComRCReturnRC(rc);
8890
8891 // ComAssertRet (mData->mMachineState < MachineState_Running, E_FAIL);
8892
8893 AssertReturn(!ComSafeArrayInIsNull (aNames), E_POINTER);
8894 AssertReturn(!ComSafeArrayInIsNull (aValues), E_POINTER);
8895 AssertReturn(!ComSafeArrayInIsNull (aTimestamps), E_POINTER);
8896 AssertReturn(!ComSafeArrayInIsNull (aFlags), E_POINTER);
8897
8898 com::SafeArray<IN_BSTR> names (ComSafeArrayInArg (aNames));
8899 com::SafeArray<IN_BSTR> values (ComSafeArrayInArg (aValues));
8900 com::SafeArray<ULONG64> timestamps (ComSafeArrayInArg (aTimestamps));
8901 com::SafeArray<IN_BSTR> flags (ComSafeArrayInArg (aFlags));
8902 DiscardSettings();
8903 mHWData.backup();
8904 mHWData->mGuestProperties.erase (mHWData->mGuestProperties.begin(),
8905 mHWData->mGuestProperties.end());
8906 for (unsigned i = 0; i < names.size(); ++i)
8907 {
8908 uint32_t fFlags = NILFLAG;
8909 validateFlags (Utf8Str (flags[i]).raw(), &fFlags);
8910 HWData::GuestProperty property = { names[i], values[i], timestamps[i], fFlags };
8911 mHWData->mGuestProperties.push_back (property);
8912 }
8913 mHWData->mPropertyServiceActive = false;
8914 alock.unlock();
8915 SaveSettings();
8916 /* Restore the mRegistered flag. */
8917 mData->mRegistered = TRUE;
8918 return S_OK;
8919#else
8920 ReturnComNotImplemented();
8921#endif
8922}
8923
8924STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
8925 IN_BSTR aValue,
8926 ULONG64 aTimestamp,
8927 IN_BSTR aFlags)
8928{
8929 LogFlowThisFunc(("\n"));
8930
8931#ifdef VBOX_WITH_GUEST_PROPS
8932 using namespace guestProp;
8933
8934 CheckComArgNotNull (aName);
8935 if ((aValue != NULL) && (!VALID_PTR (aValue) || !VALID_PTR (aFlags)))
8936 return E_POINTER; /* aValue can be NULL to indicate deletion */
8937
8938 try
8939 {
8940 Utf8Str utf8Name(aName);
8941 Utf8Str utf8Flags(aFlags);
8942 Utf8Str utf8Patterns(mHWData->mGuestPropertyNotificationPatterns);
8943
8944 uint32_t fFlags = NILFLAG;
8945 if ((aFlags != NULL) && RT_FAILURE(validateFlags (utf8Flags.raw(), &fFlags)))
8946 return E_INVALIDARG;
8947
8948 bool matchAll = false;
8949 if (utf8Patterns.isEmpty())
8950 matchAll = true;
8951
8952 AutoCaller autoCaller(this);
8953 CheckComRCReturnRC(autoCaller.rc());
8954
8955 AutoWriteLock alock(this);
8956
8957 HRESULT rc = checkStateDependency(MutableStateDep);
8958 CheckComRCReturnRC(rc);
8959
8960 mHWData.backup();
8961 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
8962 iter != mHWData->mGuestProperties.end();
8963 ++iter)
8964 if (utf8Name == iter->strName)
8965 {
8966 mHWData->mGuestProperties.erase(iter);
8967 break;
8968 }
8969 if (aValue != NULL)
8970 {
8971 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
8972 mHWData->mGuestProperties.push_back (property);
8973 }
8974
8975 /* send a callback notification if appropriate */
8976 alock.leave();
8977 if ( matchAll
8978 || RTStrSimplePatternMultiMatch(utf8Patterns.raw(),
8979 RTSTR_MAX,
8980 utf8Name.raw(),
8981 RTSTR_MAX, NULL)
8982 )
8983 mParent->onGuestPropertyChange(mData->mUuid,
8984 aName,
8985 aValue,
8986 aFlags);
8987 }
8988 catch(std::bad_alloc &)
8989 {
8990 return E_OUTOFMEMORY;
8991 }
8992 return S_OK;
8993#else
8994 ReturnComNotImplemented();
8995#endif
8996}
8997
8998// public methods only for internal purposes
8999/////////////////////////////////////////////////////////////////////////////
9000
9001/**
9002 * Called from the client watcher thread to check for expected or unexpected
9003 * death of the client process that has a direct session to this machine.
9004 *
9005 * On Win32 and on OS/2, this method is called only when we've got the
9006 * mutex (i.e. the client has either died or terminated normally) so it always
9007 * returns @c true (the client is terminated, the session machine is
9008 * uninitialized).
9009 *
9010 * On other platforms, the method returns @c true if the client process has
9011 * terminated normally or abnormally and the session machine was uninitialized,
9012 * and @c false if the client process is still alive.
9013 *
9014 * @note Locks this object for writing.
9015 */
9016bool SessionMachine::checkForDeath()
9017{
9018 Uninit::Reason reason;
9019 bool terminated = false;
9020
9021 /* Enclose autoCaller with a block because calling uninit() from under it
9022 * will deadlock. */
9023 {
9024 AutoCaller autoCaller(this);
9025 if (!autoCaller.isOk())
9026 {
9027 /* return true if not ready, to cause the client watcher to exclude
9028 * the corresponding session from watching */
9029 LogFlowThisFunc(("Already uninitialized!\n"));
9030 return true;
9031 }
9032
9033 AutoWriteLock alock(this);
9034
9035 /* Determine the reason of death: if the session state is Closing here,
9036 * everything is fine. Otherwise it means that the client did not call
9037 * OnSessionEnd() before it released the IPC semaphore. This may happen
9038 * either because the client process has abnormally terminated, or
9039 * because it simply forgot to call ISession::Close() before exiting. We
9040 * threat the latter also as an abnormal termination (see
9041 * Session::uninit() for details). */
9042 reason = mData->mSession.mState == SessionState_Closing ?
9043 Uninit::Normal :
9044 Uninit::Abnormal;
9045
9046#if defined(RT_OS_WINDOWS)
9047
9048 AssertMsg (mIPCSem, ("semaphore must be created"));
9049
9050 /* release the IPC mutex */
9051 ::ReleaseMutex (mIPCSem);
9052
9053 terminated = true;
9054
9055#elif defined(RT_OS_OS2)
9056
9057 AssertMsg (mIPCSem, ("semaphore must be created"));
9058
9059 /* release the IPC mutex */
9060 ::DosReleaseMutexSem (mIPCSem);
9061
9062 terminated = true;
9063
9064#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9065
9066 AssertMsg (mIPCSem >= 0, ("semaphore must be created"));
9067
9068 int val = ::semctl (mIPCSem, 0, GETVAL);
9069 if (val > 0)
9070 {
9071 /* the semaphore is signaled, meaning the session is terminated */
9072 terminated = true;
9073 }
9074
9075#else
9076# error "Port me!"
9077#endif
9078
9079 } /* AutoCaller block */
9080
9081 if (terminated)
9082 uninit (reason);
9083
9084 return terminated;
9085}
9086
9087/**
9088 * @note Locks this object for reading.
9089 */
9090HRESULT SessionMachine::onNetworkAdapterChange (INetworkAdapter *networkAdapter, BOOL changeAdapter)
9091{
9092 LogFlowThisFunc(("\n"));
9093
9094 AutoCaller autoCaller(this);
9095 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9096
9097 ComPtr<IInternalSessionControl> directControl;
9098 {
9099 AutoReadLock alock(this);
9100 directControl = mData->mSession.mDirectControl;
9101 }
9102
9103 /* ignore notifications sent after #OnSessionEnd() is called */
9104 if (!directControl)
9105 return S_OK;
9106
9107 return directControl->OnNetworkAdapterChange (networkAdapter, changeAdapter);
9108}
9109
9110/**
9111 * @note Locks this object for reading.
9112 */
9113HRESULT SessionMachine::onSerialPortChange (ISerialPort *serialPort)
9114{
9115 LogFlowThisFunc(("\n"));
9116
9117 AutoCaller autoCaller(this);
9118 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9119
9120 ComPtr<IInternalSessionControl> directControl;
9121 {
9122 AutoReadLock alock(this);
9123 directControl = mData->mSession.mDirectControl;
9124 }
9125
9126 /* ignore notifications sent after #OnSessionEnd() is called */
9127 if (!directControl)
9128 return S_OK;
9129
9130 return directControl->OnSerialPortChange (serialPort);
9131}
9132
9133/**
9134 * @note Locks this object for reading.
9135 */
9136HRESULT SessionMachine::onParallelPortChange (IParallelPort *parallelPort)
9137{
9138 LogFlowThisFunc(("\n"));
9139
9140 AutoCaller autoCaller(this);
9141 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9142
9143 ComPtr<IInternalSessionControl> directControl;
9144 {
9145 AutoReadLock alock(this);
9146 directControl = mData->mSession.mDirectControl;
9147 }
9148
9149 /* ignore notifications sent after #OnSessionEnd() is called */
9150 if (!directControl)
9151 return S_OK;
9152
9153 return directControl->OnParallelPortChange (parallelPort);
9154}
9155
9156/**
9157 * @note Locks this object for reading.
9158 */
9159HRESULT SessionMachine::onStorageControllerChange ()
9160{
9161 LogFlowThisFunc(("\n"));
9162
9163 AutoCaller autoCaller(this);
9164 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9165
9166 ComPtr<IInternalSessionControl> directControl;
9167 {
9168 AutoReadLock alock(this);
9169 directControl = mData->mSession.mDirectControl;
9170 }
9171
9172 /* ignore notifications sent after #OnSessionEnd() is called */
9173 if (!directControl)
9174 return S_OK;
9175
9176 return directControl->OnStorageControllerChange ();
9177}
9178
9179/**
9180 * @note Locks this object for reading.
9181 */
9182HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment)
9183{
9184 LogFlowThisFunc(("\n"));
9185
9186 AutoCaller autoCaller(this);
9187 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9188
9189 ComPtr<IInternalSessionControl> directControl;
9190 {
9191 AutoReadLock alock(this);
9192 directControl = mData->mSession.mDirectControl;
9193 }
9194
9195 /* ignore notifications sent after #OnSessionEnd() is called */
9196 if (!directControl)
9197 return S_OK;
9198
9199 return directControl->OnMediumChange(aAttachment);
9200}
9201
9202/**
9203 * @note Locks this object for reading.
9204 */
9205HRESULT SessionMachine::onVRDPServerChange()
9206{
9207 LogFlowThisFunc(("\n"));
9208
9209 AutoCaller autoCaller(this);
9210 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9211
9212 ComPtr<IInternalSessionControl> directControl;
9213 {
9214 AutoReadLock alock(this);
9215 directControl = mData->mSession.mDirectControl;
9216 }
9217
9218 /* ignore notifications sent after #OnSessionEnd() is called */
9219 if (!directControl)
9220 return S_OK;
9221
9222 return directControl->OnVRDPServerChange();
9223}
9224
9225/**
9226 * @note Locks this object for reading.
9227 */
9228HRESULT SessionMachine::onUSBControllerChange()
9229{
9230 LogFlowThisFunc(("\n"));
9231
9232 AutoCaller autoCaller(this);
9233 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9234
9235 ComPtr<IInternalSessionControl> directControl;
9236 {
9237 AutoReadLock alock(this);
9238 directControl = mData->mSession.mDirectControl;
9239 }
9240
9241 /* ignore notifications sent after #OnSessionEnd() is called */
9242 if (!directControl)
9243 return S_OK;
9244
9245 return directControl->OnUSBControllerChange();
9246}
9247
9248/**
9249 * @note Locks this object for reading.
9250 */
9251HRESULT SessionMachine::onSharedFolderChange()
9252{
9253 LogFlowThisFunc(("\n"));
9254
9255 AutoCaller autoCaller(this);
9256 AssertComRCReturnRC(autoCaller.rc());
9257
9258 ComPtr<IInternalSessionControl> directControl;
9259 {
9260 AutoReadLock alock(this);
9261 directControl = mData->mSession.mDirectControl;
9262 }
9263
9264 /* ignore notifications sent after #OnSessionEnd() is called */
9265 if (!directControl)
9266 return S_OK;
9267
9268 return directControl->OnSharedFolderChange (FALSE /* aGlobal */);
9269}
9270
9271/**
9272 * Returns @c true if this machine's USB controller reports it has a matching
9273 * filter for the given USB device and @c false otherwise.
9274 *
9275 * @note Locks this object for reading.
9276 */
9277bool SessionMachine::hasMatchingUSBFilter (const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
9278{
9279 AutoCaller autoCaller(this);
9280 /* silently return if not ready -- this method may be called after the
9281 * direct machine session has been called */
9282 if (!autoCaller.isOk())
9283 return false;
9284
9285 AutoReadLock alock(this);
9286
9287#ifdef VBOX_WITH_USB
9288 switch (mData->mMachineState)
9289 {
9290 case MachineState_Starting:
9291 case MachineState_Restoring:
9292 case MachineState_TeleportingIn:
9293 case MachineState_Paused:
9294 case MachineState_Running:
9295 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
9296 * elsewhere... */
9297 return mUSBController->hasMatchingFilter (aDevice, aMaskedIfs);
9298 default: break;
9299 }
9300#else
9301 NOREF(aDevice);
9302 NOREF(aMaskedIfs);
9303#endif
9304 return false;
9305}
9306
9307/**
9308 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
9309 */
9310HRESULT SessionMachine::onUSBDeviceAttach (IUSBDevice *aDevice,
9311 IVirtualBoxErrorInfo *aError,
9312 ULONG aMaskedIfs)
9313{
9314 LogFlowThisFunc(("\n"));
9315
9316 AutoCaller autoCaller(this);
9317
9318 /* This notification may happen after the machine object has been
9319 * uninitialized (the session was closed), so don't assert. */
9320 CheckComRCReturnRC(autoCaller.rc());
9321
9322 ComPtr<IInternalSessionControl> directControl;
9323 {
9324 AutoReadLock alock(this);
9325 directControl = mData->mSession.mDirectControl;
9326 }
9327
9328 /* fail on notifications sent after #OnSessionEnd() is called, it is
9329 * expected by the caller */
9330 if (!directControl)
9331 return E_FAIL;
9332
9333 /* No locks should be held at this point. */
9334 AssertMsg (RTThreadGetWriteLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetWriteLockCount (RTThreadSelf())));
9335 AssertMsg (RTThreadGetReadLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetReadLockCount (RTThreadSelf())));
9336
9337 return directControl->OnUSBDeviceAttach (aDevice, aError, aMaskedIfs);
9338}
9339
9340/**
9341 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
9342 */
9343HRESULT SessionMachine::onUSBDeviceDetach (IN_BSTR aId,
9344 IVirtualBoxErrorInfo *aError)
9345{
9346 LogFlowThisFunc(("\n"));
9347
9348 AutoCaller autoCaller(this);
9349
9350 /* This notification may happen after the machine object has been
9351 * uninitialized (the session was closed), so don't assert. */
9352 CheckComRCReturnRC(autoCaller.rc());
9353
9354 ComPtr<IInternalSessionControl> directControl;
9355 {
9356 AutoReadLock alock(this);
9357 directControl = mData->mSession.mDirectControl;
9358 }
9359
9360 /* fail on notifications sent after #OnSessionEnd() is called, it is
9361 * expected by the caller */
9362 if (!directControl)
9363 return E_FAIL;
9364
9365 /* No locks should be held at this point. */
9366 AssertMsg (RTThreadGetWriteLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetWriteLockCount (RTThreadSelf())));
9367 AssertMsg (RTThreadGetReadLockCount (RTThreadSelf()) == 0, ("%d\n", RTThreadGetReadLockCount (RTThreadSelf())));
9368
9369 return directControl->OnUSBDeviceDetach (aId, aError);
9370}
9371
9372// protected methods
9373/////////////////////////////////////////////////////////////////////////////
9374
9375/**
9376 * Helper method to finalize saving the state.
9377 *
9378 * @note Must be called from under this object's lock.
9379 *
9380 * @param aSuccess TRUE if the snapshot has been taken successfully
9381 *
9382 * @note Locks mParent + this objects for writing.
9383 */
9384HRESULT SessionMachine::endSavingState (BOOL aSuccess)
9385{
9386 LogFlowThisFuncEnter();
9387
9388 AutoCaller autoCaller(this);
9389 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9390
9391 /* saveSettings() needs mParent lock */
9392 AutoMultiWriteLock2 alock (mParent, this);
9393
9394 HRESULT rc = S_OK;
9395
9396 if (aSuccess)
9397 {
9398 mSSData->mStateFilePath = mSnapshotData.mStateFilePath;
9399
9400 /* save all VM settings */
9401 rc = saveSettings();
9402 }
9403 else
9404 {
9405 /* delete the saved state file (it might have been already created) */
9406 RTFileDelete(mSnapshotData.mStateFilePath.c_str());
9407 }
9408
9409 /* remove the completed progress object */
9410 mParent->removeProgress(mSnapshotData.mProgressId);
9411
9412 /* clear out the temporary saved state data */
9413 mSnapshotData.mLastState = MachineState_Null;
9414 mSnapshotData.mProgressId.clear();
9415 mSnapshotData.mStateFilePath.setNull();
9416
9417 LogFlowThisFuncLeave();
9418 return rc;
9419}
9420
9421/**
9422 * Locks the attached media.
9423 *
9424 * All attached hard disks are locked for writing and DVD/floppy are locked for
9425 * reading. Parents of attached hard disks (if any) are locked for reading.
9426 *
9427 * This method also performs accessibility check of all media it locks: if some
9428 * media is inaccessible, the method will return a failure and a bunch of
9429 * extended error info objects per each inaccessible medium.
9430 *
9431 * Note that this method is atomic: if it returns a success, all media are
9432 * locked as described above; on failure no media is locked at all (all
9433 * succeeded individual locks will be undone).
9434 *
9435 * This method is intended to be called when the machine is in Starting or
9436 * Restoring state and asserts otherwise.
9437 *
9438 * The locks made by this method must be undone by calling #unlockMedia() when
9439 * no more needed.
9440 */
9441HRESULT SessionMachine::lockMedia()
9442{
9443 AutoCaller autoCaller(this);
9444 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9445
9446 AutoWriteLock alock(this);
9447
9448 AssertReturn( mData->mMachineState == MachineState_Starting
9449 || mData->mMachineState == MachineState_Restoring
9450 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
9451
9452 typedef std::list <ComPtr<IMedium> > MediaList;
9453
9454 try
9455 {
9456 HRESULT rc = S_OK;
9457
9458 ErrorInfoKeeper eik(true /* aIsNull */);
9459 MultiResult mrc(S_OK);
9460
9461 /* Lock all medium objects attached to the VM.
9462 * Get status for inaccessible media as well. */
9463 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
9464 it != mMediaData->mAttachments.end();
9465 ++it)
9466 {
9467 DeviceType_T devType = (*it)->type();
9468 ComObjPtr<Medium> medium = (*it)->medium();
9469
9470 bool first = true;
9471
9472 /** @todo split out the media locking, and put it into
9473 * MediumImpl.cpp, as it needs this functionality too. */
9474 while (!medium.isNull())
9475 {
9476 MediumState_T mediumState = medium->state();
9477
9478 /* accessibility check must be first, otherwise locking
9479 * interferes with getting the medium state. */
9480 if (mediumState == MediumState_Inaccessible)
9481 {
9482 rc = medium->RefreshState(&mediumState);
9483 CheckComRCThrowRC(rc);
9484
9485 if (mediumState == MediumState_Inaccessible)
9486 {
9487 Bstr error;
9488 rc = medium->COMGETTER(LastAccessError)(error.asOutParam());
9489 CheckComRCThrowRC(rc);
9490
9491 Bstr loc;
9492 rc = medium->COMGETTER(Location)(loc.asOutParam());
9493 CheckComRCThrowRC(rc);
9494
9495 /* collect multiple errors */
9496 eik.restore();
9497
9498 /* be in sync with MediumBase::setStateError() */
9499 Assert(!error.isEmpty());
9500 mrc = setError(E_FAIL,
9501 tr("Medium '%ls' is not accessible. %ls"),
9502 loc.raw(),
9503 error.raw());
9504
9505 eik.fetch();
9506 }
9507 }
9508
9509 if (first)
9510 {
9511 if (devType != DeviceType_DVD)
9512 {
9513 /* HardDisk and Floppy medium must be locked for writing */
9514 rc = medium->LockWrite(NULL);
9515 CheckComRCThrowRC(rc);
9516 }
9517 else
9518 {
9519 /* DVD medium must be locked for reading */
9520 rc = medium->LockRead(NULL);
9521 CheckComRCThrowRC(rc);
9522 }
9523
9524 mData->mSession.mLockedMedia.push_back(
9525 Data::Session::LockedMedia::value_type(
9526 ComPtr<IMedium>(medium), true));
9527
9528 first = false;
9529 }
9530 else
9531 {
9532 rc = medium->LockRead(NULL);
9533 CheckComRCThrowRC(rc);
9534
9535 mData->mSession.mLockedMedia.push_back(
9536 Data::Session::LockedMedia::value_type(
9537 ComPtr<IMedium>(medium), false));
9538 }
9539
9540
9541 /* no locks or callers here since there should be no way to
9542 * change the hard disk parent at this point (as it is still
9543 * attached to the machine) */
9544 medium = medium->parent();
9545 }
9546 }
9547
9548 eik.restore();
9549 CheckComRCThrowRC((HRESULT)mrc);
9550 }
9551 catch (HRESULT aRC)
9552 {
9553 /* Unlock all locked media on failure */
9554 unlockMedia();
9555 return aRC;
9556 }
9557
9558 return S_OK;
9559}
9560
9561/**
9562 * Undoes the locks made by by #lockMedia().
9563 */
9564void SessionMachine::unlockMedia()
9565{
9566 AutoCaller autoCaller(this);
9567 AssertComRCReturnVoid (autoCaller.rc());
9568
9569 AutoWriteLock alock(this);
9570
9571 /* we may be holding important error info on the current thread;
9572 * preserve it */
9573 ErrorInfoKeeper eik;
9574
9575 HRESULT rc = S_OK;
9576
9577 for (Data::Session::LockedMedia::const_iterator
9578 it = mData->mSession.mLockedMedia.begin();
9579 it != mData->mSession.mLockedMedia.end(); ++it)
9580 {
9581 MediumState_T state;
9582 if (it->second)
9583 rc = it->first->UnlockWrite (&state);
9584 else
9585 rc = it->first->UnlockRead (&state);
9586
9587 /* The second can happen if an object was re-locked in
9588 * Machine::fixupMedia(). The last can happen when e.g a DVD/Floppy
9589 * image was unmounted at runtime. */
9590 Assert (SUCCEEDED(rc) || state == MediumState_LockedRead || state == MediumState_Created);
9591 }
9592
9593 mData->mSession.mLockedMedia.clear();
9594}
9595
9596/**
9597 * Helper to change the machine state (reimplementation).
9598 *
9599 * @note Locks this object for writing.
9600 */
9601HRESULT SessionMachine::setMachineState (MachineState_T aMachineState)
9602{
9603 LogFlowThisFuncEnter();
9604 LogFlowThisFunc(("aMachineState=%d\n", aMachineState));
9605
9606 AutoCaller autoCaller(this);
9607 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9608
9609 AutoWriteLock alock(this);
9610
9611 MachineState_T oldMachineState = mData->mMachineState;
9612
9613 AssertMsgReturn (oldMachineState != aMachineState,
9614 ("oldMachineState=%d, aMachineState=%d\n",
9615 oldMachineState, aMachineState), E_FAIL);
9616
9617 HRESULT rc = S_OK;
9618
9619 int stsFlags = 0;
9620 bool deleteSavedState = false;
9621
9622 /* detect some state transitions */
9623
9624 if ( ( oldMachineState == MachineState_Saved
9625 && aMachineState == MachineState_Restoring)
9626 || ( ( oldMachineState == MachineState_PoweredOff
9627 || oldMachineState == MachineState_Teleported
9628 || oldMachineState == MachineState_Aborted
9629 )
9630 && ( aMachineState == MachineState_TeleportingIn
9631 || aMachineState == MachineState_Starting
9632 )
9633 )
9634 )
9635 {
9636 /* The EMT thread is about to start */
9637
9638 /* Nothing to do here for now... */
9639
9640 /// @todo NEWMEDIA don't let mDVDDrive and other children
9641 /// change anything when in the Starting/Restoring state
9642 }
9643 else if ( ( oldMachineState == MachineState_Running
9644 || oldMachineState == MachineState_Paused
9645 || oldMachineState == MachineState_Teleporting
9646 || oldMachineState == MachineState_LiveSnapshotting
9647 || oldMachineState == MachineState_Stuck
9648 || oldMachineState == MachineState_Starting
9649 || oldMachineState == MachineState_Stopping
9650 || oldMachineState == MachineState_Saving
9651 || oldMachineState == MachineState_Restoring
9652 || oldMachineState == MachineState_TeleportingPausedVM
9653 || oldMachineState == MachineState_TeleportingIn
9654 )
9655 && ( aMachineState == MachineState_PoweredOff
9656 || aMachineState == MachineState_Saved
9657 || aMachineState == MachineState_Teleported
9658 || aMachineState == MachineState_Aborted
9659 )
9660 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
9661 * snapshot */
9662 && ( mSnapshotData.mSnapshot.isNull()
9663 || mSnapshotData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
9664 )
9665 )
9666 {
9667 /* The EMT thread has just stopped, unlock attached media. Note that as
9668 * opposed to locking that is done from Console, we do unlocking here
9669 * because the VM process may have aborted before having a chance to
9670 * properly unlock all media it locked. */
9671
9672 unlockMedia();
9673 }
9674
9675 if (oldMachineState == MachineState_Restoring)
9676 {
9677 if (aMachineState != MachineState_Saved)
9678 {
9679 /*
9680 * delete the saved state file once the machine has finished
9681 * restoring from it (note that Console sets the state from
9682 * Restoring to Saved if the VM couldn't restore successfully,
9683 * to give the user an ability to fix an error and retry --
9684 * we keep the saved state file in this case)
9685 */
9686 deleteSavedState = true;
9687 }
9688 }
9689 else if ( oldMachineState == MachineState_Saved
9690 && ( aMachineState == MachineState_PoweredOff
9691 || aMachineState == MachineState_Aborted
9692 || aMachineState == MachineState_Teleported
9693 )
9694 )
9695 {
9696 /*
9697 * delete the saved state after Console::DiscardSavedState() is called
9698 * or if the VM process (owning a direct VM session) crashed while the
9699 * VM was Saved
9700 */
9701
9702 /// @todo (dmik)
9703 // Not sure that deleting the saved state file just because of the
9704 // client death before it attempted to restore the VM is a good
9705 // thing. But when it crashes we need to go to the Aborted state
9706 // which cannot have the saved state file associated... The only
9707 // way to fix this is to make the Aborted condition not a VM state
9708 // but a bool flag: i.e., when a crash occurs, set it to true and
9709 // change the state to PoweredOff or Saved depending on the
9710 // saved state presence.
9711
9712 deleteSavedState = true;
9713 mData->mCurrentStateModified = TRUE;
9714 stsFlags |= SaveSTS_CurStateModified;
9715 }
9716
9717 if ( aMachineState == MachineState_Starting
9718 || aMachineState == MachineState_Restoring
9719 || aMachineState == MachineState_TeleportingIn
9720 )
9721 {
9722 /* set the current state modified flag to indicate that the current
9723 * state is no more identical to the state in the
9724 * current snapshot */
9725 if (!mData->mCurrentSnapshot.isNull())
9726 {
9727 mData->mCurrentStateModified = TRUE;
9728 stsFlags |= SaveSTS_CurStateModified;
9729 }
9730 }
9731
9732 if (deleteSavedState)
9733 {
9734 if (mRemoveSavedState)
9735 {
9736 Assert(!mSSData->mStateFilePath.isEmpty());
9737 RTFileDelete(mSSData->mStateFilePath.c_str());
9738 }
9739 mSSData->mStateFilePath.setNull();
9740 stsFlags |= SaveSTS_StateFilePath;
9741 }
9742
9743 /* redirect to the underlying peer machine */
9744 mPeer->setMachineState (aMachineState);
9745
9746 if ( aMachineState == MachineState_PoweredOff
9747 || aMachineState == MachineState_Teleported
9748 || aMachineState == MachineState_Aborted
9749 || aMachineState == MachineState_Saved)
9750 {
9751 /* the machine has stopped execution
9752 * (or the saved state file was adopted) */
9753 stsFlags |= SaveSTS_StateTimeStamp;
9754 }
9755
9756 if ( ( oldMachineState == MachineState_PoweredOff
9757 || oldMachineState == MachineState_Aborted
9758 || oldMachineState == MachineState_Teleported
9759 )
9760 && aMachineState == MachineState_Saved)
9761 {
9762 /* the saved state file was adopted */
9763 Assert(!mSSData->mStateFilePath.isEmpty());
9764 stsFlags |= SaveSTS_StateFilePath;
9765 }
9766
9767 rc = saveStateSettings (stsFlags);
9768
9769 if ( ( oldMachineState != MachineState_PoweredOff
9770 && oldMachineState != MachineState_Aborted
9771 && oldMachineState != MachineState_Teleported
9772 )
9773 && ( aMachineState == MachineState_PoweredOff
9774 || aMachineState == MachineState_Aborted
9775 || aMachineState == MachineState_Teleported
9776 )
9777 )
9778 {
9779 /* we've been shut down for any reason */
9780 /* no special action so far */
9781 }
9782
9783 LogFlowThisFunc(("rc=%08X\n", rc));
9784 LogFlowThisFuncLeave();
9785 return rc;
9786}
9787
9788/**
9789 * Sends the current machine state value to the VM process.
9790 *
9791 * @note Locks this object for reading, then calls a client process.
9792 */
9793HRESULT SessionMachine::updateMachineStateOnClient()
9794{
9795 AutoCaller autoCaller(this);
9796 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9797
9798 ComPtr<IInternalSessionControl> directControl;
9799 {
9800 AutoReadLock alock(this);
9801 AssertReturn(!!mData, E_FAIL);
9802 directControl = mData->mSession.mDirectControl;
9803
9804 /* directControl may be already set to NULL here in #OnSessionEnd()
9805 * called too early by the direct session process while there is still
9806 * some operation (like discarding the snapshot) in progress. The client
9807 * process in this case is waiting inside Session::close() for the
9808 * "end session" process object to complete, while #uninit() called by
9809 * #checkForDeath() on the Watcher thread is waiting for the pending
9810 * operation to complete. For now, we accept this inconsitent behavior
9811 * and simply do nothing here. */
9812
9813 if (mData->mSession.mState == SessionState_Closing)
9814 return S_OK;
9815
9816 AssertReturn(!directControl.isNull(), E_FAIL);
9817 }
9818
9819 return directControl->UpdateMachineState (mData->mMachineState);
9820}
Note: See TracBrowser for help on using the repository browser.

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