VirtualBox

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

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

CPU hotplug: Merge 4th patch. Implements the Main API and a VBoxManage interface to turn CPU hotplug on/off and to add/remove CPUs while the VM is running

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