VirtualBox

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

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

Main: make restoreSnapshot() work with the lock validator; take saveSettings() out of a lot of functions and instead return a flag to the caller so the caller can make that call; as a side effect, this no longer calls saveSettings multiple times in several code paths (e.g. restoreSnapshot()) and cleans up locking in medium tasks

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 343.4 KB
Line 
1/* $Id: MachineImpl.cpp 25903 2010-01-18 18:15:43Z 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 COMMA_LOCKVAL_SRC_POS);
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 COMMA_LOCKVAL_SRC_POS);
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 // if this becomes true then we need to call saveSettings in the end
2429 // @todo r=dj there is no error handling so far...
2430 bool fNeedsSaveSettings = false;
2431
2432 /* protect the media tree all the while we're in here, as well as our member variables */
2433 AutoMultiWriteLock2 alock(this->lockHandle(), &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2434
2435 HRESULT rc = checkStateDependency(MutableStateDep);
2436 if (FAILED(rc)) return rc;
2437
2438 /// @todo NEWMEDIA implicit machine registration
2439 if (!mData->mRegistered)
2440 return setError(VBOX_E_INVALID_OBJECT_STATE,
2441 tr("Cannot attach storage devices to an unregistered machine"));
2442
2443 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
2444
2445 if (Global::IsOnlineOrTransient(mData->mMachineState))
2446 return setError(VBOX_E_INVALID_VM_STATE,
2447 tr("Invalid machine state: %s"),
2448 Global::stringifyMachineState(mData->mMachineState));
2449
2450 /* Check for an existing controller. */
2451 ComObjPtr<StorageController> ctl;
2452 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
2453 if (FAILED(rc)) return rc;
2454
2455 /* check that the port and device are not out of range. */
2456 ULONG portCount;
2457 ULONG devicesPerPort;
2458 rc = ctl->COMGETTER(PortCount)(&portCount);
2459 if (FAILED(rc)) return rc;
2460 rc = ctl->COMGETTER(MaxDevicesPerPortCount)(&devicesPerPort);
2461 if (FAILED(rc)) return rc;
2462
2463 if ( (aControllerPort < 0)
2464 || (aControllerPort >= (LONG)portCount)
2465 || (aDevice < 0)
2466 || (aDevice >= (LONG)devicesPerPort)
2467 )
2468 return setError(E_INVALIDARG,
2469 tr("The port and/or count parameter are out of range [%lu:%lu]"),
2470 portCount,
2471 devicesPerPort);
2472
2473 /* check if the device slot is already busy */
2474 MediumAttachment *pAttachTemp;
2475 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
2476 aControllerName,
2477 aControllerPort,
2478 aDevice)))
2479 {
2480 Medium *pMedium = pAttachTemp->getMedium();
2481 if (pMedium)
2482 {
2483 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
2484 return setError(VBOX_E_OBJECT_IN_USE,
2485 tr("Medium '%s' is already attached to device slot %d on port %d of controller '%ls' of this virtual machine"),
2486 pMedium->getLocationFull().raw(),
2487 aDevice,
2488 aControllerPort,
2489 aControllerName);
2490 }
2491 else
2492 return setError(VBOX_E_OBJECT_IN_USE,
2493 tr("Device is already attached to slot %d on port %d of controller '%ls' of this virtual machine"),
2494 aDevice, aControllerPort, aControllerName);
2495 }
2496
2497 Guid uuid(aId);
2498
2499 ComObjPtr<Medium> medium;
2500 switch (aType)
2501 {
2502 case DeviceType_HardDisk:
2503 /* find a hard disk by UUID */
2504 rc = mParent->findHardDisk(&uuid, NULL, true /* aSetError */, &medium);
2505 if (FAILED(rc)) return rc;
2506 break;
2507
2508 case DeviceType_DVD: // @todo r=dj eliminate this, replace with findDVDImage
2509 if (!uuid.isEmpty())
2510 {
2511 /* first search for host drive */
2512 SafeIfaceArray<IMedium> drivevec;
2513 rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
2514 if (SUCCEEDED(rc))
2515 {
2516 for (size_t i = 0; i < drivevec.size(); ++i)
2517 {
2518 /// @todo eliminate this conversion
2519 ComObjPtr<Medium> med = (Medium *)drivevec[i];
2520 if (med->getId() == uuid)
2521 {
2522 medium = med;
2523 break;
2524 }
2525 }
2526 }
2527
2528 if (medium.isNull())
2529 {
2530 /* find a DVD image by UUID */
2531 rc = mParent->findDVDImage(&uuid, NULL, true /* aSetError */, &medium);
2532 if (FAILED(rc)) return rc;
2533 }
2534 }
2535 else
2536 {
2537 /* null UUID means null medium, which needs no code */
2538 }
2539 break;
2540
2541 case DeviceType_Floppy: // @todo r=dj eliminate this, replace with findFloppyImage
2542 if (!uuid.isEmpty())
2543 {
2544 /* first search for host drive */
2545 SafeIfaceArray<IMedium> drivevec;
2546 rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
2547 if (SUCCEEDED(rc))
2548 {
2549 for (size_t i = 0; i < drivevec.size(); ++i)
2550 {
2551 /// @todo eliminate this conversion
2552 ComObjPtr<Medium> med = (Medium *)drivevec[i];
2553 if (med->getId() == uuid)
2554 {
2555 medium = med;
2556 break;
2557 }
2558 }
2559 }
2560
2561 if (medium.isNull())
2562 {
2563 /* find a floppy image by UUID */
2564 rc = mParent->findFloppyImage(&uuid, NULL, true /* aSetError */, &medium);
2565 if (FAILED(rc)) return rc;
2566 }
2567 }
2568 else
2569 {
2570 /* null UUID means null medium, which needs no code */
2571 }
2572 break;
2573
2574 default:
2575 return setError(E_INVALIDARG,
2576 tr("The device type %d is not recognized"),
2577 (int)aType);
2578 }
2579
2580 AutoCaller mediumCaller(medium);
2581 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
2582
2583 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
2584
2585 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
2586 && !medium.isNull()
2587 )
2588 return setError(VBOX_E_OBJECT_IN_USE,
2589 tr("Medium '%s' is already attached to this virtual machine"),
2590 medium->getLocationFull().raw());
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 &fNeedsSaveSettings);
2778 if (FAILED(rc)) return rc;
2779
2780 /* make sure the hard disk is not modified before createDiffStorage() */
2781 rc = medium->LockRead(NULL);
2782 if (FAILED(rc)) return rc;
2783
2784 /* will leave the lock before the potentially lengthy operation, so
2785 * protect with the special state */
2786 MachineState_T oldState = mData->mMachineState;
2787 setMachineState(MachineState_SettingUp);
2788
2789 mediumLock.leave();
2790 alock.leave();
2791
2792 rc = medium->createDiffStorageAndWait(diff, MediumVariant_Standard, &fNeedsSaveSettings);
2793
2794 alock.enter();
2795 mediumLock.enter();
2796
2797 setMachineState(oldState);
2798
2799 medium->UnlockRead(NULL);
2800
2801 if (FAILED(rc)) return rc;
2802
2803 /* use the created diff for the actual attachment */
2804 medium = diff;
2805 mediumCaller.attach(medium);
2806 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
2807 mediumLock.attach(medium);
2808 }
2809 while (0);
2810
2811 ComObjPtr<MediumAttachment> attachment;
2812 attachment.createObject();
2813 rc = attachment->init(this, medium, aControllerName, aControllerPort, aDevice, aType, indirect);
2814 if (FAILED(rc)) return rc;
2815
2816 if (associate && !medium.isNull())
2817 {
2818 /* as the last step, associate the medium to the VM */
2819 rc = medium->attachTo(mData->mUuid);
2820 /* here we can fail because of Deleting, or being in process of
2821 * creating a Diff */
2822 if (FAILED(rc)) return rc;
2823 }
2824
2825 /* success: finally remember the attachment */
2826 mMediaData.backup();
2827 mMediaData->mAttachments.push_back(attachment);
2828
2829 if (fNeedsSaveSettings)
2830 {
2831 mediumLock.release();
2832 alock.release();
2833
2834 AutoWriteLock(mParent COMMA_LOCKVAL_SRC_POS);
2835 mParent->saveSettings();
2836 }
2837
2838 return rc;
2839}
2840
2841STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
2842 LONG aDevice)
2843{
2844 CheckComArgNotNull(aControllerName);
2845
2846 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
2847 aControllerName, aControllerPort, aDevice));
2848
2849 AutoCaller autoCaller(this);
2850 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2851
2852 bool fNeedsSaveSettings = false;
2853
2854 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2855
2856 HRESULT rc = checkStateDependency(MutableStateDep);
2857 if (FAILED(rc)) return rc;
2858
2859 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
2860
2861 if (Global::IsOnlineOrTransient(mData->mMachineState))
2862 return setError(VBOX_E_INVALID_VM_STATE,
2863 tr("Invalid machine state: %s"),
2864 Global::stringifyMachineState(mData->mMachineState));
2865
2866 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
2867 aControllerName,
2868 aControllerPort,
2869 aDevice);
2870 if (!pAttach)
2871 return setError(VBOX_E_OBJECT_NOT_FOUND,
2872 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
2873 aDevice, aControllerPort, aControllerName);
2874
2875 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
2876 DeviceType_T mediumType = pAttach->getType();
2877
2878 if (pAttach->isImplicit())
2879 {
2880 /* attempt to implicitly delete the implicitly created diff */
2881
2882 /// @todo move the implicit flag from MediumAttachment to Medium
2883 /// and forbid any hard disk operation when it is implicit. Or maybe
2884 /// a special media state for it to make it even more simple.
2885
2886 Assert(mMediaData.isBackedUp());
2887
2888 /* will leave the lock before the potentially lengthy operation, so
2889 * protect with the special state */
2890 MachineState_T oldState = mData->mMachineState;
2891 setMachineState(MachineState_SettingUp);
2892
2893 alock.leave();
2894
2895 rc = oldmedium->deleteStorageAndWait(NULL /*aProgress*/, &fNeedsSaveSettings);
2896
2897 alock.enter();
2898
2899 setMachineState(oldState);
2900
2901 if (FAILED(rc)) return rc;
2902 }
2903
2904 mMediaData.backup();
2905
2906 /* we cannot use erase (it) below because backup() above will create
2907 * a copy of the list and make this copy active, but the iterator
2908 * still refers to the original and is not valid for the copy */
2909 mMediaData->mAttachments.remove(pAttach);
2910
2911 /* For non-hard disk media, detach straight away. */
2912 if (mediumType != DeviceType_HardDisk && !oldmedium.isNull())
2913 oldmedium->detachFrom(mData->mUuid);
2914
2915 if (fNeedsSaveSettings)
2916 {
2917 alock.release();
2918 AutoWriteLock vboxlock(this COMMA_LOCKVAL_SRC_POS);
2919 saveSettings();
2920 }
2921
2922 return S_OK;
2923}
2924
2925STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
2926 LONG aDevice, BOOL aPassthrough)
2927{
2928 CheckComArgNotNull(aControllerName);
2929
2930 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aPassthrough=%d\n",
2931 aControllerName, aControllerPort, aDevice, aPassthrough));
2932
2933 AutoCaller autoCaller(this);
2934 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2935
2936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2937
2938 HRESULT rc = checkStateDependency(MutableStateDep);
2939 if (FAILED(rc)) return rc;
2940
2941 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
2942
2943 if (Global::IsOnlineOrTransient(mData->mMachineState))
2944 return setError(VBOX_E_INVALID_VM_STATE,
2945 tr("Invalid machine state: %s"),
2946 Global::stringifyMachineState(mData->mMachineState));
2947
2948 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
2949 aControllerName,
2950 aControllerPort,
2951 aDevice);
2952 if (!pAttach)
2953 return setError(VBOX_E_OBJECT_NOT_FOUND,
2954 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
2955 aDevice, aControllerPort, aControllerName);
2956
2957
2958 mMediaData.backup();
2959
2960 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
2961
2962 if (pAttach->getType() != DeviceType_DVD)
2963 return setError(E_INVALIDARG,
2964 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
2965 aDevice, aControllerPort, aControllerName);
2966 pAttach->updatePassthrough(!!aPassthrough);
2967
2968 return S_OK;
2969}
2970
2971STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
2972 LONG aControllerPort,
2973 LONG aDevice,
2974 IN_BSTR aId,
2975 BOOL aForce)
2976{
2977 int rc = S_OK;
2978 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aForce=%d\n",
2979 aControllerName, aControllerPort, aDevice, aForce));
2980
2981 CheckComArgNotNull(aControllerName);
2982 CheckComArgNotNull(aId);
2983
2984 AutoCaller autoCaller(this);
2985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2986
2987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2988
2989 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
2990 aControllerName,
2991 aControllerPort,
2992 aDevice);
2993 if (pAttach.isNull())
2994 return setError(VBOX_E_OBJECT_NOT_FOUND,
2995 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
2996 aDevice, aControllerPort, aControllerName);
2997
2998 /* Remember previously mounted medium. The medium before taking the
2999 * backup is not necessarily the same thing. */
3000 ComObjPtr<Medium> oldmedium;
3001 oldmedium = pAttach->getMedium();
3002
3003 Guid uuid(aId);
3004 ComObjPtr<Medium> medium;
3005 DeviceType_T mediumType = pAttach->getType();
3006 switch (mediumType)
3007 {
3008 case DeviceType_DVD:
3009 if (!uuid.isEmpty())
3010 {
3011 /* find a DVD by host device UUID */
3012 SafeIfaceArray<IMedium> drivevec;
3013 rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
3014 if (SUCCEEDED(rc))
3015 {
3016 for (size_t i = 0; i < drivevec.size(); ++i)
3017 {
3018 /// @todo eliminate this conversion
3019 ComObjPtr<Medium> med = (Medium *)drivevec[i];
3020 if (uuid == med->getId())
3021 {
3022 medium = med;
3023 break;
3024 }
3025 }
3026 }
3027 /* find a DVD by UUID */
3028 if (medium.isNull())
3029 rc = mParent->findDVDImage(&uuid, NULL, true /* aDoSetError */, &medium);
3030 }
3031 if (FAILED(rc)) return rc;
3032 break;
3033 case DeviceType_Floppy:
3034 if (!uuid.isEmpty())
3035 {
3036 /* find a Floppy by host device UUID */
3037 SafeIfaceArray<IMedium> drivevec;
3038 rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
3039 if (SUCCEEDED(rc))
3040 {
3041 for (size_t i = 0; i < drivevec.size(); ++i)
3042 {
3043 /// @todo eliminate this conversion
3044 ComObjPtr<Medium> med = (Medium *)drivevec[i];
3045 if (uuid == med->getId())
3046 {
3047 medium = med;
3048 break;
3049 }
3050 }
3051 }
3052 /* find a Floppy by UUID */
3053 if (medium.isNull())
3054 rc = mParent->findFloppyImage(&uuid, NULL, true /* aDoSetError */, &medium);
3055 }
3056 if (FAILED(rc)) return rc;
3057 break;
3058 default:
3059 return setError(VBOX_E_INVALID_OBJECT_STATE,
3060 tr("Cannot change medium attached to device slot %d on port %d of controller '%ls'"),
3061 aDevice, aControllerPort, aControllerName);
3062 }
3063
3064 if (SUCCEEDED(rc))
3065 {
3066
3067 mMediaData.backup();
3068 /* The backup operation makes the pAttach reference point to the
3069 * old settings. Re-get the correct reference. */
3070 pAttach = findAttachment(mMediaData->mAttachments,
3071 aControllerName,
3072 aControllerPort,
3073 aDevice);
3074 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3075 /* For non-hard disk media, detach straight away. */
3076 if (mediumType != DeviceType_HardDisk && !oldmedium.isNull())
3077 oldmedium->detachFrom(mData->mUuid);
3078 if (!medium.isNull())
3079 medium->attachTo(mData->mUuid);
3080 pAttach->updateMedium(medium, false /* aImplicit */);
3081 }
3082
3083 alock.leave();
3084 rc = onMediumChange(pAttach, aForce);
3085 alock.enter();
3086
3087 /* On error roll back this change only. */
3088 if (FAILED(rc))
3089 {
3090 if (!medium.isNull())
3091 medium->detachFrom(mData->mUuid);
3092 pAttach = findAttachment(mMediaData->mAttachments,
3093 aControllerName,
3094 aControllerPort,
3095 aDevice);
3096 /* If the attachment is gone in the mean time, bail out. */
3097 if (pAttach.isNull())
3098 return rc;
3099 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3100 /* For non-hard disk media, re-attach straight away. */
3101 if (mediumType != DeviceType_HardDisk && !oldmedium.isNull())
3102 oldmedium->attachTo(mData->mUuid);
3103 pAttach->updateMedium(oldmedium, false /* aImplicit */);
3104 }
3105
3106 return rc;
3107}
3108
3109STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
3110 LONG aControllerPort,
3111 LONG aDevice,
3112 IMedium **aMedium)
3113{
3114 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
3115 aControllerName, aControllerPort, aDevice));
3116
3117 CheckComArgNotNull(aControllerName);
3118 CheckComArgOutPointerValid(aMedium);
3119
3120 AutoCaller autoCaller(this);
3121 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3122
3123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3124
3125 *aMedium = NULL;
3126
3127 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
3128 aControllerName,
3129 aControllerPort,
3130 aDevice);
3131 if (pAttach.isNull())
3132 return setError(VBOX_E_OBJECT_NOT_FOUND,
3133 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3134 aDevice, aControllerPort, aControllerName);
3135
3136 pAttach->getMedium().queryInterfaceTo(aMedium);
3137
3138 return S_OK;
3139}
3140
3141STDMETHODIMP Machine::GetSerialPort (ULONG slot, ISerialPort **port)
3142{
3143 CheckComArgOutPointerValid(port);
3144 CheckComArgExpr (slot, slot < RT_ELEMENTS (mSerialPorts));
3145
3146 AutoCaller autoCaller(this);
3147 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3148
3149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3150
3151 mSerialPorts [slot].queryInterfaceTo(port);
3152
3153 return S_OK;
3154}
3155
3156STDMETHODIMP Machine::GetParallelPort (ULONG slot, IParallelPort **port)
3157{
3158 CheckComArgOutPointerValid(port);
3159 CheckComArgExpr (slot, slot < RT_ELEMENTS (mParallelPorts));
3160
3161 AutoCaller autoCaller(this);
3162 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3163
3164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3165
3166 mParallelPorts [slot].queryInterfaceTo(port);
3167
3168 return S_OK;
3169}
3170
3171STDMETHODIMP Machine::GetNetworkAdapter (ULONG slot, INetworkAdapter **adapter)
3172{
3173 CheckComArgOutPointerValid(adapter);
3174 CheckComArgExpr (slot, slot < RT_ELEMENTS (mNetworkAdapters));
3175
3176 AutoCaller autoCaller(this);
3177 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3178
3179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3180
3181 mNetworkAdapters[slot].queryInterfaceTo(adapter);
3182
3183 return S_OK;
3184}
3185
3186STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
3187{
3188 if (ComSafeArrayOutIsNull(aKeys))
3189 return E_POINTER;
3190
3191 AutoCaller autoCaller(this);
3192 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3193
3194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3195
3196 com::SafeArray<BSTR> saKeys(mData->m_pMachineConfigFile->mapExtraDataItems.size());
3197 int i = 0;
3198 for (settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.begin();
3199 it != mData->m_pMachineConfigFile->mapExtraDataItems.end();
3200 ++it, ++i)
3201 {
3202 const Utf8Str &strKey = it->first;
3203 strKey.cloneTo(&saKeys[i]);
3204 }
3205 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
3206
3207 return S_OK;
3208 }
3209
3210 /**
3211 * @note Locks this object for reading.
3212 */
3213STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
3214 BSTR *aValue)
3215{
3216 CheckComArgNotNull(aKey);
3217 CheckComArgOutPointerValid(aValue);
3218
3219 AutoCaller autoCaller(this);
3220 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3221
3222 /* start with nothing found */
3223 Bstr bstrResult("");
3224
3225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3226
3227 settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
3228 if (it != mData->m_pMachineConfigFile->mapExtraDataItems.end())
3229 // found:
3230 bstrResult = it->second; // source is a Utf8Str
3231
3232 /* return the result to caller (may be empty) */
3233 bstrResult.cloneTo(aValue);
3234
3235 return S_OK;
3236}
3237
3238 /**
3239 * @note Locks mParent for writing + this object for writing.
3240 */
3241STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
3242{
3243 CheckComArgNotNull(aKey);
3244
3245 AutoCaller autoCaller(this);
3246 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3247
3248 Utf8Str strKey(aKey);
3249 Utf8Str strValue(aValue);
3250 Utf8Str strOldValue; // empty
3251
3252 // locking note: we only hold the read lock briefly to look up the old value,
3253 // then release it and call the onExtraCanChange callbacks. There is a small
3254 // chance of a race insofar as the callback might be called twice if two callers
3255 // change the same key at the same time, but that's a much better solution
3256 // than the deadlock we had here before. The actual changing of the extradata
3257 // is then performed under the write lock and race-free.
3258
3259 // look up the old value first; if nothing's changed then we need not do anything
3260 {
3261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
3262 settings::ExtraDataItemsMap::const_iterator it = mData->m_pMachineConfigFile->mapExtraDataItems.find(strKey);
3263 if (it != mData->m_pMachineConfigFile->mapExtraDataItems.end())
3264 strOldValue = it->second;
3265 }
3266
3267 bool fChanged;
3268 if ((fChanged = (strOldValue != strValue)))
3269 {
3270 // ask for permission from all listeners outside the locks;
3271 // onExtraDataCanChange() only briefly requests the VirtualBox
3272 // lock to copy the list of callbacks to invoke
3273 Bstr error;
3274 Bstr bstrValue;
3275 if (aValue)
3276 bstrValue = aValue;
3277 else
3278 bstrValue = (const char *)"";
3279
3280 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue, error))
3281 {
3282 const char *sep = error.isEmpty() ? "" : ": ";
3283 CBSTR err = error.isNull() ? (CBSTR) L"" : error.raw();
3284 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
3285 sep, err));
3286 return setError(E_ACCESSDENIED,
3287 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
3288 aKey,
3289 bstrValue.raw(),
3290 sep,
3291 err);
3292 }
3293
3294 // data is changing and change not vetoed: then write it out under the locks
3295
3296 // saveSettings() needs VirtualBox write lock
3297 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
3298
3299 if (getClassID() == clsidSnapshotMachine)
3300 {
3301 HRESULT rc = checkStateDependency(MutableStateDep);
3302 if (FAILED(rc)) return rc;
3303 }
3304
3305 if (strValue.isEmpty())
3306 mData->m_pMachineConfigFile->mapExtraDataItems.erase(strKey);
3307 else
3308 mData->m_pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
3309 // creates a new key if needed
3310
3311 /* save settings on success */
3312 HRESULT rc = saveSettings();
3313 if (FAILED(rc)) return rc;
3314 }
3315
3316 // fire notification outside the lock
3317 if (fChanged)
3318 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
3319
3320 return S_OK;
3321}
3322
3323STDMETHODIMP Machine::SaveSettings()
3324{
3325 AutoCaller autoCaller(this);
3326 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3327
3328 /* saveSettings() needs mParent lock */
3329 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
3330
3331 /* when there was auto-conversion, we want to save the file even if
3332 * the VM is saved */
3333 HRESULT rc = checkStateDependency(MutableStateDep);
3334 if (FAILED(rc)) return rc;
3335
3336 /* the settings file path may never be null */
3337 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
3338
3339 /* save all VM data excluding snapshots */
3340 return saveSettings();
3341}
3342
3343STDMETHODIMP Machine::DiscardSettings()
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 /*
3354 * during this rollback, the session will be notified if data has
3355 * been actually changed
3356 */
3357 rollback (true /* aNotify */);
3358
3359 return S_OK;
3360}
3361
3362STDMETHODIMP Machine::DeleteSettings()
3363{
3364 AutoCaller autoCaller(this);
3365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3366
3367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3368
3369 HRESULT rc = checkStateDependency(MutableStateDep);
3370 if (FAILED(rc)) return rc;
3371
3372 if (mData->mRegistered)
3373 return setError(VBOX_E_INVALID_VM_STATE,
3374 tr("Cannot delete settings of a registered machine"));
3375
3376 /* delete the settings only when the file actually exists */
3377 if (mData->m_pMachineConfigFile->fileExists())
3378 {
3379 int vrc = RTFileDelete(mData->m_strConfigFileFull.c_str());
3380 if (RT_FAILURE(vrc))
3381 return setError(VBOX_E_IPRT_ERROR,
3382 tr("Could not delete the settings file '%s' (%Rrc)"),
3383 mData->m_strConfigFileFull.raw(),
3384 vrc);
3385
3386 /* delete the Logs folder, nothing important should be left
3387 * there (we don't check for errors because the user might have
3388 * some private files there that we don't want to delete) */
3389 Utf8Str logFolder;
3390 getLogFolder(logFolder);
3391 Assert(logFolder.length());
3392 if (RTDirExists(logFolder.c_str()))
3393 {
3394 /* Delete all VBox.log[.N] files from the Logs folder
3395 * (this must be in sync with the rotation logic in
3396 * Console::powerUpThread()). Also, delete the VBox.png[.N]
3397 * files that may have been created by the GUI. */
3398 Utf8Str log = Utf8StrFmt("%s/VBox.log", logFolder.raw());
3399 RTFileDelete(log.c_str());
3400 log = Utf8StrFmt("%s/VBox.png", logFolder.raw());
3401 RTFileDelete(log.c_str());
3402 for (int i = 3; i >= 0; i--)
3403 {
3404 log = Utf8StrFmt("%s/VBox.log.%d", logFolder.raw(), i);
3405 RTFileDelete(log.c_str());
3406 log = Utf8StrFmt("%s/VBox.png.%d", logFolder.raw(), i);
3407 RTFileDelete(log.c_str());
3408 }
3409
3410 RTDirRemove(logFolder.c_str());
3411 }
3412
3413 /* delete the Snapshots folder, nothing important should be left
3414 * there (we don't check for errors because the user might have
3415 * some private files there that we don't want to delete) */
3416 Utf8Str snapshotFolder(mUserData->mSnapshotFolderFull);
3417 Assert(snapshotFolder.length());
3418 if (RTDirExists(snapshotFolder.c_str()))
3419 RTDirRemove(snapshotFolder.c_str());
3420
3421 /* delete the directory that contains the settings file, but only
3422 * if it matches the VM name (i.e. a structure created by default in
3423 * prepareSaveSettings()) */
3424 {
3425 Utf8Str settingsDir;
3426 if (isInOwnDir(&settingsDir))
3427 RTDirRemove(settingsDir.c_str());
3428 }
3429 }
3430
3431 return S_OK;
3432}
3433
3434STDMETHODIMP Machine::GetSnapshot (IN_BSTR aId, ISnapshot **aSnapshot)
3435{
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 Guid uuid(aId);
3444 ComObjPtr<Snapshot> snapshot;
3445
3446 HRESULT rc = findSnapshot(uuid, snapshot, true /* aSetError */);
3447 snapshot.queryInterfaceTo(aSnapshot);
3448
3449 return rc;
3450}
3451
3452STDMETHODIMP Machine::FindSnapshot (IN_BSTR aName, ISnapshot **aSnapshot)
3453{
3454 CheckComArgNotNull (aName);
3455 CheckComArgOutPointerValid(aSnapshot);
3456
3457 AutoCaller autoCaller(this);
3458 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3459
3460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3461
3462 ComObjPtr<Snapshot> snapshot;
3463
3464 HRESULT rc = findSnapshot(aName, snapshot, true /* aSetError */);
3465 snapshot.queryInterfaceTo(aSnapshot);
3466
3467 return rc;
3468}
3469
3470STDMETHODIMP Machine::SetCurrentSnapshot (IN_BSTR /* aId */)
3471{
3472 /// @todo (dmik) don't forget to set
3473 // mData->mCurrentStateModified to FALSE
3474
3475 return setError (E_NOTIMPL, "Not implemented");
3476}
3477
3478STDMETHODIMP Machine::CreateSharedFolder (IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable)
3479{
3480 CheckComArgNotNull(aName);
3481 CheckComArgNotNull(aHostPath);
3482
3483 AutoCaller autoCaller(this);
3484 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3485
3486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3487
3488 HRESULT rc = checkStateDependency(MutableStateDep);
3489 if (FAILED(rc)) return rc;
3490
3491 ComObjPtr<SharedFolder> sharedFolder;
3492 rc = findSharedFolder (aName, sharedFolder, false /* aSetError */);
3493 if (SUCCEEDED(rc))
3494 return setError(VBOX_E_OBJECT_IN_USE,
3495 tr("Shared folder named '%ls' already exists"),
3496 aName);
3497
3498 sharedFolder.createObject();
3499 rc = sharedFolder->init(getMachine(), aName, aHostPath, aWritable);
3500 if (FAILED(rc)) return rc;
3501
3502 mHWData.backup();
3503 mHWData->mSharedFolders.push_back (sharedFolder);
3504
3505 /* inform the direct session if any */
3506 alock.leave();
3507 onSharedFolderChange();
3508
3509 return S_OK;
3510}
3511
3512STDMETHODIMP Machine::RemoveSharedFolder (IN_BSTR aName)
3513{
3514 CheckComArgNotNull (aName);
3515
3516 AutoCaller autoCaller(this);
3517 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3518
3519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3520
3521 HRESULT rc = checkStateDependency(MutableStateDep);
3522 if (FAILED(rc)) return rc;
3523
3524 ComObjPtr<SharedFolder> sharedFolder;
3525 rc = findSharedFolder (aName, sharedFolder, true /* aSetError */);
3526 if (FAILED(rc)) return rc;
3527
3528 mHWData.backup();
3529 mHWData->mSharedFolders.remove (sharedFolder);
3530
3531 /* inform the direct session if any */
3532 alock.leave();
3533 onSharedFolderChange();
3534
3535 return S_OK;
3536}
3537
3538STDMETHODIMP Machine::CanShowConsoleWindow (BOOL *aCanShow)
3539{
3540 CheckComArgOutPointerValid(aCanShow);
3541
3542 /* start with No */
3543 *aCanShow = FALSE;
3544
3545 AutoCaller autoCaller(this);
3546 AssertComRCReturnRC(autoCaller.rc());
3547
3548 ComPtr<IInternalSessionControl> directControl;
3549 {
3550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3551
3552 if (mData->mSession.mState != SessionState_Open)
3553 return setError(VBOX_E_INVALID_VM_STATE,
3554 tr("Machine session is not open (session state: %s)"),
3555 Global::stringifySessionState(mData->mSession.mState));
3556
3557 directControl = mData->mSession.mDirectControl;
3558 }
3559
3560 /* ignore calls made after #OnSessionEnd() is called */
3561 if (!directControl)
3562 return S_OK;
3563
3564 ULONG64 dummy;
3565 return directControl->OnShowWindow (TRUE /* aCheck */, aCanShow, &dummy);
3566}
3567
3568STDMETHODIMP Machine::ShowConsoleWindow (ULONG64 *aWinId)
3569{
3570 CheckComArgOutPointerValid(aWinId);
3571
3572 AutoCaller autoCaller(this);
3573 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3574
3575 ComPtr<IInternalSessionControl> directControl;
3576 {
3577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3578
3579 if (mData->mSession.mState != SessionState_Open)
3580 return setError(E_FAIL,
3581 tr("Machine session is not open (session state: %s)"),
3582 Global::stringifySessionState(mData->mSession.mState));
3583
3584 directControl = mData->mSession.mDirectControl;
3585 }
3586
3587 /* ignore calls made after #OnSessionEnd() is called */
3588 if (!directControl)
3589 return S_OK;
3590
3591 BOOL dummy;
3592 return directControl->OnShowWindow (FALSE /* aCheck */, &dummy, aWinId);
3593}
3594
3595STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
3596 BSTR *aValue,
3597 ULONG64 *aTimestamp,
3598 BSTR *aFlags)
3599{
3600#if !defined (VBOX_WITH_GUEST_PROPS)
3601 ReturnComNotImplemented();
3602#else
3603 CheckComArgNotNull(aName);
3604 CheckComArgOutPointerValid(aValue);
3605 CheckComArgOutPointerValid(aTimestamp);
3606 CheckComArgOutPointerValid(aFlags);
3607
3608 AutoCaller autoCaller(this);
3609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3610
3611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3612
3613 using namespace guestProp;
3614 HRESULT rc = E_FAIL;
3615
3616 Utf8Str strName(aName);
3617
3618 if (!mHWData->mPropertyServiceActive)
3619 {
3620 bool found = false;
3621 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
3622 (it != mHWData->mGuestProperties.end()) && !found;
3623 ++it)
3624 {
3625 if (it->strName == strName)
3626 {
3627 char szFlags[MAX_FLAGS_LEN + 1];
3628 it->strValue.cloneTo(aValue);
3629 *aTimestamp = it->mTimestamp;
3630 writeFlags(it->mFlags, szFlags);
3631 Bstr(szFlags).cloneTo(aFlags);
3632 found = true;
3633 }
3634 }
3635 rc = S_OK;
3636 }
3637 else
3638 {
3639 ComPtr<IInternalSessionControl> directControl =
3640 mData->mSession.mDirectControl;
3641
3642 /* just be on the safe side when calling another process */
3643 alock.release();
3644
3645 /* fail if we were called after #OnSessionEnd() is called. This is a
3646 * silly race condition. */
3647
3648 if (!directControl)
3649 rc = E_FAIL;
3650 else
3651 rc = directControl->AccessGuestProperty (aName, NULL, NULL,
3652 false /* isSetter */,
3653 aValue, aTimestamp, aFlags);
3654 }
3655 return rc;
3656#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
3657}
3658
3659STDMETHODIMP Machine::GetGuestPropertyValue (IN_BSTR aName, BSTR *aValue)
3660{
3661 ULONG64 dummyTimestamp;
3662 BSTR dummyFlags;
3663 return GetGuestProperty (aName, aValue, &dummyTimestamp, &dummyFlags);
3664}
3665
3666STDMETHODIMP Machine::GetGuestPropertyTimestamp (IN_BSTR aName, ULONG64 *aTimestamp)
3667{
3668 BSTR dummyValue;
3669 BSTR dummyFlags;
3670 return GetGuestProperty (aName, &dummyValue, aTimestamp, &dummyFlags);
3671}
3672
3673STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName,
3674 IN_BSTR aValue,
3675 IN_BSTR aFlags)
3676{
3677#if !defined (VBOX_WITH_GUEST_PROPS)
3678 ReturnComNotImplemented();
3679#else
3680 using namespace guestProp;
3681
3682 CheckComArgNotNull(aName);
3683 CheckComArgNotNull(aValue);
3684 if ((aFlags != NULL) && !VALID_PTR (aFlags))
3685 return E_INVALIDARG;
3686
3687 HRESULT rc = S_OK;
3688
3689 try
3690 {
3691 Utf8Str utf8Name(aName);
3692 Utf8Str utf8Flags(aFlags);
3693
3694 AutoCaller autoCaller(this);
3695 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3696
3697 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3698
3699 rc = checkStateDependency(MutableStateDep);
3700 if (FAILED(rc)) return rc;
3701
3702 rc = S_OK;
3703 uint32_t fFlags = NILFLAG;
3704 if ( (aFlags != NULL)
3705 && RT_FAILURE(validateFlags (utf8Flags.raw(), &fFlags))
3706 )
3707 return setError(E_INVALIDARG,
3708 tr("Invalid flag values: '%ls'"),
3709 aFlags);
3710
3711 if (!mHWData->mPropertyServiceActive)
3712 {
3713 bool found = false;
3714 HWData::GuestProperty property;
3715 property.mFlags = NILFLAG;
3716
3717 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I know,
3718 * this is simple and do an OK job atm.) */
3719 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
3720 it != mHWData->mGuestProperties.end();
3721 ++it)
3722 if (it->strName == utf8Name)
3723 {
3724 property = *it;
3725 if (it->mFlags & (RDONLYHOST))
3726 rc = setError(E_ACCESSDENIED,
3727 tr("The property '%ls' cannot be changed by the host"),
3728 aName);
3729 else
3730 {
3731 mHWData.backup();
3732 /* The backup() operation invalidates our iterator, so
3733 * get a new one. */
3734 for (it = mHWData->mGuestProperties.begin();
3735 it->strName != utf8Name;
3736 ++it)
3737 ;
3738 mHWData->mGuestProperties.erase (it);
3739 }
3740 found = true;
3741 break;
3742 }
3743 if (found && SUCCEEDED(rc))
3744 {
3745 if (*aValue)
3746 {
3747 RTTIMESPEC time;
3748 property.strValue = aValue;
3749 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
3750 if (aFlags != NULL)
3751 property.mFlags = fFlags;
3752 mHWData->mGuestProperties.push_back (property);
3753 }
3754 }
3755 else if (SUCCEEDED(rc) && *aValue)
3756 {
3757 RTTIMESPEC time;
3758 mHWData.backup();
3759 property.strName = aName;
3760 property.strValue = aValue;
3761 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
3762 property.mFlags = fFlags;
3763 mHWData->mGuestProperties.push_back (property);
3764 }
3765 if ( SUCCEEDED(rc)
3766 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
3767 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.raw(), RTSTR_MAX,
3768 utf8Name.raw(), RTSTR_MAX, NULL) )
3769 )
3770 {
3771 /** @todo r=bird: Why aren't we leaving the lock here? The
3772 * same code in PushGuestProperty does... */
3773 mParent->onGuestPropertyChange(mData->mUuid, aName, aValue, aFlags);
3774 }
3775 }
3776 else
3777 {
3778 ComPtr<IInternalSessionControl> directControl =
3779 mData->mSession.mDirectControl;
3780
3781 /* just be on the safe side when calling another process */
3782 alock.leave();
3783
3784 BSTR dummy = NULL;
3785 ULONG64 dummy64;
3786 if (!directControl)
3787 rc = E_FAIL;
3788 else
3789 rc = directControl->AccessGuestProperty(aName,
3790 *aValue ? aValue : NULL, /** @todo Fix when adding DeleteGuestProperty(), see defect. */
3791 aFlags,
3792 true /* isSetter */,
3793 &dummy, &dummy64, &dummy);
3794 }
3795 }
3796 catch (std::bad_alloc &)
3797 {
3798 rc = E_OUTOFMEMORY;
3799 }
3800
3801 return rc;
3802#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
3803}
3804
3805STDMETHODIMP Machine::SetGuestPropertyValue (IN_BSTR aName, IN_BSTR aValue)
3806{
3807 return SetGuestProperty (aName, aValue, NULL);
3808}
3809
3810STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
3811 ComSafeArrayOut(BSTR, aNames),
3812 ComSafeArrayOut(BSTR, aValues),
3813 ComSafeArrayOut(ULONG64, aTimestamps),
3814 ComSafeArrayOut(BSTR, aFlags))
3815{
3816#if !defined (VBOX_WITH_GUEST_PROPS)
3817 ReturnComNotImplemented();
3818#else
3819 if (!VALID_PTR (aPatterns) && (aPatterns != NULL))
3820 return E_POINTER;
3821
3822 CheckComArgOutSafeArrayPointerValid(aNames);
3823 CheckComArgOutSafeArrayPointerValid(aValues);
3824 CheckComArgOutSafeArrayPointerValid(aTimestamps);
3825 CheckComArgOutSafeArrayPointerValid(aFlags);
3826
3827 AutoCaller autoCaller(this);
3828 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3829
3830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3831
3832 using namespace guestProp;
3833 HRESULT rc = E_FAIL;
3834
3835 Utf8Str strPatterns(aPatterns);
3836
3837 if (!mHWData->mPropertyServiceActive)
3838 {
3839
3840 /*
3841 * Look for matching patterns and build up a list.
3842 */
3843 HWData::GuestPropertyList propList;
3844 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
3845 it != mHWData->mGuestProperties.end();
3846 ++it)
3847 if ( strPatterns.isEmpty()
3848 || RTStrSimplePatternMultiMatch(strPatterns.raw(),
3849 RTSTR_MAX,
3850 it->strName.raw(),
3851 RTSTR_MAX, NULL)
3852 )
3853 propList.push_back(*it);
3854
3855 /*
3856 * And build up the arrays for returning the property information.
3857 */
3858 size_t cEntries = propList.size();
3859 SafeArray<BSTR> names (cEntries);
3860 SafeArray<BSTR> values (cEntries);
3861 SafeArray<ULONG64> timestamps (cEntries);
3862 SafeArray<BSTR> flags (cEntries);
3863 size_t iProp = 0;
3864 for (HWData::GuestPropertyList::iterator it = propList.begin();
3865 it != propList.end();
3866 ++it)
3867 {
3868 char szFlags[MAX_FLAGS_LEN + 1];
3869 it->strName.cloneTo(&names[iProp]);
3870 it->strValue.cloneTo(&values[iProp]);
3871 timestamps[iProp] = it->mTimestamp;
3872 writeFlags(it->mFlags, szFlags);
3873 Bstr(szFlags).cloneTo(&flags[iProp]);
3874 ++iProp;
3875 }
3876 names.detachTo(ComSafeArrayOutArg(aNames));
3877 values.detachTo(ComSafeArrayOutArg(aValues));
3878 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
3879 flags.detachTo(ComSafeArrayOutArg(aFlags));
3880 rc = S_OK;
3881 }
3882 else
3883 {
3884 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
3885
3886 /* just be on the safe side when calling another process */
3887 alock.release();
3888
3889 if (!directControl)
3890 rc = E_FAIL;
3891 else
3892 rc = directControl->EnumerateGuestProperties(aPatterns,
3893 ComSafeArrayOutArg(aNames),
3894 ComSafeArrayOutArg(aValues),
3895 ComSafeArrayOutArg(aTimestamps),
3896 ComSafeArrayOutArg(aFlags));
3897 }
3898 return rc;
3899#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
3900}
3901
3902STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
3903 ComSafeArrayOut(IMediumAttachment*, aAttachments))
3904{
3905 MediaData::AttachmentList atts;
3906
3907 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
3908 if (FAILED(rc)) return rc;
3909
3910 SafeIfaceArray<IMediumAttachment> attachments(atts);
3911 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
3912
3913 return S_OK;
3914}
3915
3916STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
3917 LONG aControllerPort,
3918 LONG aDevice,
3919 IMediumAttachment **aAttachment)
3920{
3921 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
3922 aControllerName, aControllerPort, aDevice));
3923
3924 CheckComArgNotNull(aControllerName);
3925 CheckComArgOutPointerValid(aAttachment);
3926
3927 AutoCaller autoCaller(this);
3928 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3929
3930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3931
3932 *aAttachment = NULL;
3933
3934 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
3935 aControllerName,
3936 aControllerPort,
3937 aDevice);
3938 if (pAttach.isNull())
3939 return setError(VBOX_E_OBJECT_NOT_FOUND,
3940 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3941 aDevice, aControllerPort, aControllerName);
3942
3943 pAttach.queryInterfaceTo(aAttachment);
3944
3945 return S_OK;
3946}
3947
3948STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
3949 StorageBus_T aConnectionType,
3950 IStorageController **controller)
3951{
3952 CheckComArgStrNotEmptyOrNull(aName);
3953
3954 if ( (aConnectionType <= StorageBus_Null)
3955 || (aConnectionType > StorageBus_SAS))
3956 return setError (E_INVALIDARG,
3957 tr ("Invalid connection type: %d"),
3958 aConnectionType);
3959
3960 AutoCaller autoCaller(this);
3961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3962
3963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3964
3965 HRESULT rc = checkStateDependency(MutableStateDep);
3966 if (FAILED(rc)) return rc;
3967
3968 /* try to find one with the name first. */
3969 ComObjPtr<StorageController> ctrl;
3970
3971 rc = getStorageControllerByName (aName, ctrl, false /* aSetError */);
3972 if (SUCCEEDED(rc))
3973 return setError (VBOX_E_OBJECT_IN_USE,
3974 tr ("Storage controller named '%ls' already exists"), aName);
3975
3976 ctrl.createObject();
3977
3978 /* get a new instance number for the storage controller */
3979 ULONG ulInstance = 0;
3980 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
3981 it != mStorageControllers->end();
3982 ++it)
3983 {
3984 if ((*it)->getStorageBus() == aConnectionType)
3985 {
3986 ULONG ulCurInst = (*it)->getInstance();
3987
3988 if (ulCurInst >= ulInstance)
3989 ulInstance = ulCurInst + 1;
3990 }
3991 }
3992
3993 rc = ctrl->init(this, aName, aConnectionType, ulInstance);
3994 if (FAILED(rc)) return rc;
3995
3996 mStorageControllers.backup();
3997 mStorageControllers->push_back (ctrl);
3998
3999 ctrl.queryInterfaceTo(controller);
4000
4001 /* inform the direct session if any */
4002 alock.leave();
4003 onStorageControllerChange();
4004
4005 return S_OK;
4006}
4007
4008STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
4009 IStorageController **aStorageController)
4010{
4011 CheckComArgStrNotEmptyOrNull(aName);
4012
4013 AutoCaller autoCaller(this);
4014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4015
4016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4017
4018 ComObjPtr<StorageController> ctrl;
4019
4020 HRESULT rc = getStorageControllerByName (aName, ctrl, true /* aSetError */);
4021 if (SUCCEEDED(rc))
4022 ctrl.queryInterfaceTo(aStorageController);
4023
4024 return rc;
4025}
4026
4027STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
4028 IStorageController **aStorageController)
4029{
4030 AutoCaller autoCaller(this);
4031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4032
4033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4034
4035 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
4036 it != mStorageControllers->end();
4037 ++it)
4038 {
4039 if ((*it)->getInstance() == aInstance)
4040 {
4041 (*it).queryInterfaceTo(aStorageController);
4042 return S_OK;
4043 }
4044 }
4045
4046 return setError(VBOX_E_OBJECT_NOT_FOUND,
4047 tr("Could not find a storage controller with instance number '%lu'"),
4048 aInstance);
4049}
4050
4051STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
4052{
4053 CheckComArgStrNotEmptyOrNull(aName);
4054
4055 AutoCaller autoCaller(this);
4056 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4057
4058 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4059
4060 HRESULT rc = checkStateDependency(MutableStateDep);
4061 if (FAILED(rc)) return rc;
4062
4063 ComObjPtr<StorageController> ctrl;
4064 rc = getStorageControllerByName (aName, ctrl, true /* aSetError */);
4065 if (FAILED(rc)) return rc;
4066
4067 /* We can remove the controller only if there is no device attached. */
4068 /* check if the device slot is already busy */
4069 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
4070 it != mMediaData->mAttachments.end();
4071 ++it)
4072 {
4073 if ((*it)->getControllerName() == aName)
4074 return setError(VBOX_E_OBJECT_IN_USE,
4075 tr("Storage controller named '%ls' has still devices attached"),
4076 aName);
4077 }
4078
4079 /* We can remove it now. */
4080 mStorageControllers.backup();
4081
4082 ctrl->unshare();
4083
4084 mStorageControllers->remove (ctrl);
4085
4086 /* inform the direct session if any */
4087 alock.leave();
4088 onStorageControllerChange();
4089
4090 return S_OK;
4091}
4092
4093/* @todo where is the right place for this? */
4094#define sSSMDisplayScreenshotVer 0x00010001
4095
4096static int readSavedDisplayScreenshot(Utf8Str *pStateFilePath, uint32_t u32Type, uint8_t **ppu8Data, uint32_t *pcbData, uint32_t *pu32Width, uint32_t *pu32Height)
4097{
4098 LogFlowFunc(("u32Type = %d [%s]\n", u32Type, pStateFilePath->raw()));
4099
4100 /* @todo cache read data */
4101 if (pStateFilePath->isEmpty())
4102 {
4103 /* No saved state data. */
4104 return VERR_NOT_SUPPORTED;
4105 }
4106
4107 uint8_t *pu8Data = NULL;
4108 uint32_t cbData = 0;
4109 uint32_t u32Width = 0;
4110 uint32_t u32Height = 0;
4111
4112 PSSMHANDLE pSSM;
4113 int rc = SSMR3Open(pStateFilePath->raw(), 0 /*fFlags*/, &pSSM);
4114 if (RT_SUCCESS(rc))
4115 {
4116 uint32_t uVersion;
4117 rc = SSMR3Seek(pSSM, "DisplayScreenshot", 1100 /*iInstance*/, &uVersion);
4118 if (RT_SUCCESS(rc))
4119 {
4120 if (uVersion == sSSMDisplayScreenshotVer)
4121 {
4122 uint32_t cBlocks;
4123 rc = SSMR3GetU32(pSSM, &cBlocks);
4124 AssertRCReturn(rc, rc);
4125
4126 for (uint32_t i = 0; i < cBlocks; i++)
4127 {
4128 uint32_t cbBlock;
4129 rc = SSMR3GetU32(pSSM, &cbBlock);
4130 AssertRCBreak(rc);
4131
4132 uint32_t typeOfBlock;
4133 rc = SSMR3GetU32(pSSM, &typeOfBlock);
4134 AssertRCBreak(rc);
4135
4136 LogFlowFunc(("[%d] type %d, size %d bytes\n", i, typeOfBlock, cbBlock));
4137
4138 if (typeOfBlock == u32Type)
4139 {
4140 if (cbBlock > 2 * sizeof (uint32_t))
4141 {
4142 cbData = cbBlock - 2 * sizeof (uint32_t);
4143 pu8Data = (uint8_t *)RTMemAlloc(cbData);
4144 if (pu8Data == NULL)
4145 {
4146 rc = VERR_NO_MEMORY;
4147 break;
4148 }
4149
4150 rc = SSMR3GetU32(pSSM, &u32Width);
4151 AssertRCBreak(rc);
4152 rc = SSMR3GetU32(pSSM, &u32Height);
4153 AssertRCBreak(rc);
4154 rc = SSMR3GetMem(pSSM, pu8Data, cbData);
4155 AssertRCBreak(rc);
4156 }
4157 else
4158 {
4159 /* No saved state data. */
4160 rc = VERR_NOT_SUPPORTED;
4161 }
4162
4163 break;
4164 }
4165 else
4166 {
4167 if (cbBlock != 0)
4168 {
4169 rc = SSMR3Skip(pSSM, cbBlock);
4170 AssertRCBreak(rc);
4171 }
4172 }
4173 }
4174 }
4175 else
4176 {
4177 rc = VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4178 }
4179 }
4180
4181 SSMR3Close(pSSM);
4182 }
4183
4184 if (RT_SUCCESS(rc))
4185 {
4186 if (u32Type == 0 && cbData % 4 != 0)
4187 {
4188 /* Bitmap is 32bpp, so data is invalid. */
4189 rc = VERR_SSM_UNEXPECTED_DATA;
4190 }
4191 }
4192
4193 if (RT_SUCCESS(rc))
4194 {
4195 *ppu8Data = pu8Data;
4196 *pcbData = cbData;
4197 *pu32Width = u32Width;
4198 *pu32Height = u32Height;
4199 LogFlowFunc(("cbData %d, u32Width %d, u32Height %d\n", cbData, u32Width, u32Height));
4200 }
4201
4202 LogFlowFunc(("rc %Rrc\n", rc));
4203 return rc;
4204}
4205
4206static void freeSavedDisplayScreenshot(uint8_t *pu8Data)
4207{
4208 /* @todo not necessary when caching is implemented. */
4209 RTMemFree(pu8Data);
4210}
4211
4212STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
4213{
4214 LogFlowThisFunc(("\n"));
4215
4216 CheckComArgNotNull(aSize);
4217 CheckComArgNotNull(aWidth);
4218 CheckComArgNotNull(aHeight);
4219
4220 AutoCaller autoCaller(this);
4221 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4222
4223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4224
4225 uint8_t *pu8Data = NULL;
4226 uint32_t cbData = 0;
4227 uint32_t u32Width = 0;
4228 uint32_t u32Height = 0;
4229
4230 int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4231
4232 if (RT_FAILURE(vrc))
4233 return setError (VBOX_E_IPRT_ERROR,
4234 tr("Saved screenshot data is not available (%Rrc)"), vrc);
4235
4236 *aSize = cbData;
4237 *aWidth = u32Width;
4238 *aHeight = u32Height;
4239
4240 freeSavedDisplayScreenshot(pu8Data);
4241
4242 return S_OK;
4243}
4244
4245STDMETHODIMP Machine::ReadSavedThumbnailToArray(BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
4246{
4247 LogFlowThisFunc(("\n"));
4248
4249 CheckComArgNotNull(aWidth);
4250 CheckComArgNotNull(aHeight);
4251 CheckComArgExpr(aData, !ComSafeArrayOutIsNull(aData));
4252
4253 AutoCaller autoCaller(this);
4254 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4255
4256 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4257
4258 uint8_t *pu8Data = NULL;
4259 uint32_t cbData = 0;
4260 uint32_t u32Width = 0;
4261 uint32_t u32Height = 0;
4262
4263 int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4264
4265 if (RT_FAILURE(vrc))
4266 return setError (VBOX_E_IPRT_ERROR,
4267 tr("Saved screenshot data is not available (%Rrc)"), vrc);
4268
4269 *aWidth = u32Width;
4270 *aHeight = u32Height;
4271
4272 com::SafeArray<BYTE> bitmap(cbData);
4273 /* Convert pixels to format expected by the API caller. */
4274 if (aBGR)
4275 {
4276 /* [0] B, [1] G, [2] R, [3] A. */
4277 for (unsigned i = 0; i < cbData; i += 4)
4278 {
4279 bitmap[i] = pu8Data[i];
4280 bitmap[i + 1] = pu8Data[i + 1];
4281 bitmap[i + 2] = pu8Data[i + 2];
4282 bitmap[i + 3] = 0xff;
4283 }
4284 }
4285 else
4286 {
4287 /* [0] R, [1] G, [2] B, [3] A. */
4288 for (unsigned i = 0; i < cbData; i += 4)
4289 {
4290 bitmap[i] = pu8Data[i + 2];
4291 bitmap[i + 1] = pu8Data[i + 1];
4292 bitmap[i + 2] = pu8Data[i];
4293 bitmap[i + 3] = 0xff;
4294 }
4295 }
4296 bitmap.detachTo(ComSafeArrayOutArg(aData));
4297
4298 freeSavedDisplayScreenshot(pu8Data);
4299
4300 return S_OK;
4301}
4302
4303STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
4304{
4305 LogFlowThisFunc(("\n"));
4306
4307 CheckComArgNotNull(aSize);
4308 CheckComArgNotNull(aWidth);
4309 CheckComArgNotNull(aHeight);
4310
4311 AutoCaller autoCaller(this);
4312 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4313
4314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4315
4316 uint8_t *pu8Data = NULL;
4317 uint32_t cbData = 0;
4318 uint32_t u32Width = 0;
4319 uint32_t u32Height = 0;
4320
4321 int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4322
4323 if (RT_FAILURE(vrc))
4324 return setError (VBOX_E_IPRT_ERROR,
4325 tr("Saved screenshot data is not available (%Rrc)"), vrc);
4326
4327 *aSize = cbData;
4328 *aWidth = u32Width;
4329 *aHeight = u32Height;
4330
4331 freeSavedDisplayScreenshot(pu8Data);
4332
4333 return S_OK;
4334}
4335
4336STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
4337{
4338 LogFlowThisFunc(("\n"));
4339
4340 CheckComArgNotNull(aWidth);
4341 CheckComArgNotNull(aHeight);
4342 CheckComArgExpr(aData, !ComSafeArrayOutIsNull(aData));
4343
4344 AutoCaller autoCaller(this);
4345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4346
4347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4348
4349 uint8_t *pu8Data = NULL;
4350 uint32_t cbData = 0;
4351 uint32_t u32Width = 0;
4352 uint32_t u32Height = 0;
4353
4354 int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4355
4356 if (RT_FAILURE(vrc))
4357 return setError (VBOX_E_IPRT_ERROR,
4358 tr("Saved screenshot data is not available (%Rrc)"), vrc);
4359
4360 *aWidth = u32Width;
4361 *aHeight = u32Height;
4362
4363 com::SafeArray<BYTE> png(cbData);
4364 for (unsigned i = 0; i < cbData; i++)
4365 png[i] = pu8Data[i];
4366 png.detachTo(ComSafeArrayOutArg(aData));
4367
4368 freeSavedDisplayScreenshot(pu8Data);
4369
4370 return S_OK;
4371}
4372
4373STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
4374{
4375 HRESULT rc = S_OK;
4376 LogFlowThisFunc(("\n"));
4377
4378 AutoCaller autoCaller(this);
4379 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4380
4381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4382
4383 if (!mHWData->mCPUHotPlugEnabled)
4384 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
4385
4386 if (aCpu >= mHWData->mCPUCount)
4387 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
4388
4389 if (mHWData->mCPUAttached[aCpu])
4390 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
4391
4392 alock.leave();
4393 rc = onCPUChange(aCpu, false);
4394 alock.enter();
4395 if (FAILED(rc)) return rc;
4396
4397 mHWData.backup();
4398 mHWData->mCPUAttached[aCpu] = true;
4399
4400 /* Save settings if online */
4401 if (Global::IsOnline(mData->mMachineState))
4402 SaveSettings();
4403
4404 return S_OK;
4405}
4406
4407STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
4408{
4409 HRESULT rc = S_OK;
4410 LogFlowThisFunc(("\n"));
4411
4412 AutoCaller autoCaller(this);
4413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4414
4415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4416
4417 if (!mHWData->mCPUHotPlugEnabled)
4418 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
4419
4420 if (aCpu >= SchemaDefs::MaxCPUCount)
4421 return setError(E_INVALIDARG,
4422 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
4423 SchemaDefs::MaxCPUCount);
4424
4425 if (!mHWData->mCPUAttached[aCpu])
4426 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
4427
4428 /* CPU 0 can't be detached */
4429 if (aCpu == 0)
4430 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
4431
4432 alock.leave();
4433 rc = onCPUChange(aCpu, true);
4434 alock.enter();
4435 if (FAILED(rc)) return rc;
4436
4437 mHWData.backup();
4438 mHWData->mCPUAttached[aCpu] = false;
4439
4440 /* Save settings if online */
4441 if (Global::IsOnline(mData->mMachineState))
4442 SaveSettings();
4443
4444 return S_OK;
4445}
4446
4447STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
4448{
4449 LogFlowThisFunc(("\n"));
4450
4451 CheckComArgNotNull(aCpuAttached);
4452
4453 *aCpuAttached = false;
4454
4455 AutoCaller autoCaller(this);
4456 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4457
4458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4459
4460 /* If hotplug is enabled the CPU is always enabled. */
4461 if (!mHWData->mCPUHotPlugEnabled)
4462 {
4463 if (aCpu < mHWData->mCPUCount)
4464 *aCpuAttached = true;
4465 }
4466 else
4467 {
4468 if (aCpu < SchemaDefs::MaxCPUCount)
4469 *aCpuAttached = mHWData->mCPUAttached[aCpu];
4470 }
4471
4472 return S_OK;
4473}
4474
4475// public methods for internal purposes
4476/////////////////////////////////////////////////////////////////////////////
4477
4478/**
4479 * Saves the registry entry of this machine to the given configuration node.
4480 *
4481 * @param aEntryNode Node to save the registry entry to.
4482 *
4483 * @note locks this object for reading.
4484 */
4485HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
4486{
4487 AutoLimitedCaller autoCaller(this);
4488 AssertComRCReturnRC(autoCaller.rc());
4489
4490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4491
4492 data.uuid = mData->mUuid;
4493 data.strSettingsFile = mData->m_strConfigFile;
4494
4495 return S_OK;
4496}
4497
4498/**
4499 * Calculates the absolute path of the given path taking the directory of the
4500 * machine settings file as the current directory.
4501 *
4502 * @param aPath Path to calculate the absolute path for.
4503 * @param aResult Where to put the result (used only on success, can be the
4504 * same Utf8Str instance as passed in @a aPath).
4505 * @return IPRT result.
4506 *
4507 * @note Locks this object for reading.
4508 */
4509int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
4510{
4511 AutoCaller autoCaller(this);
4512 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4513
4514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4515
4516 AssertReturn (!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
4517
4518 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
4519
4520 strSettingsDir.stripFilename();
4521 char folder[RTPATH_MAX];
4522 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
4523 if (RT_SUCCESS(vrc))
4524 aResult = folder;
4525
4526 return vrc;
4527}
4528
4529/**
4530 * Tries to calculate the relative path of the given absolute path using the
4531 * directory of the machine settings file as the base directory.
4532 *
4533 * @param aPath Absolute path to calculate the relative path for.
4534 * @param aResult Where to put the result (used only when it's possible to
4535 * make a relative path from the given absolute path; otherwise
4536 * left untouched).
4537 *
4538 * @note Locks this object for reading.
4539 */
4540void Machine::calculateRelativePath(const Utf8Str &strPath, Utf8Str &aResult)
4541{
4542 AutoCaller autoCaller(this);
4543 AssertComRCReturn (autoCaller.rc(), (void) 0);
4544
4545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4546
4547 AssertReturnVoid (!mData->m_strConfigFileFull.isEmpty());
4548
4549 Utf8Str settingsDir = mData->m_strConfigFileFull;
4550
4551 settingsDir.stripFilename();
4552 if (RTPathStartsWith(strPath.c_str(), settingsDir.c_str()))
4553 {
4554 /* when assigning, we create a separate Utf8Str instance because both
4555 * aPath and aResult can point to the same memory location when this
4556 * func is called (if we just do aResult = aPath, aResult will be freed
4557 * first, and since its the same as aPath, an attempt to copy garbage
4558 * will be made. */
4559 aResult = Utf8Str(strPath.c_str() + settingsDir.length() + 1);
4560 }
4561}
4562
4563/**
4564 * Returns the full path to the machine's log folder in the
4565 * \a aLogFolder argument.
4566 */
4567void Machine::getLogFolder (Utf8Str &aLogFolder)
4568{
4569 AutoCaller autoCaller(this);
4570 AssertComRCReturnVoid (autoCaller.rc());
4571
4572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4573
4574 Utf8Str settingsDir;
4575 if (isInOwnDir (&settingsDir))
4576 {
4577 /* Log folder is <Machines>/<VM_Name>/Logs */
4578 aLogFolder = Utf8StrFmt ("%s%cLogs", settingsDir.raw(), RTPATH_DELIMITER);
4579 }
4580 else
4581 {
4582 /* Log folder is <Machines>/<VM_SnapshotFolder>/Logs */
4583 Assert (!mUserData->mSnapshotFolderFull.isEmpty());
4584 aLogFolder = Utf8StrFmt ("%ls%cLogs", mUserData->mSnapshotFolderFull.raw(),
4585 RTPATH_DELIMITER);
4586 }
4587}
4588
4589/**
4590 * @note Locks this object for writing, calls the client process (outside the
4591 * lock).
4592 */
4593HRESULT Machine::openSession(IInternalSessionControl *aControl)
4594{
4595 LogFlowThisFuncEnter();
4596
4597 AssertReturn(aControl, E_FAIL);
4598
4599 AutoCaller autoCaller(this);
4600 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4601
4602 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4603
4604 if (!mData->mRegistered)
4605 return setError(E_UNEXPECTED,
4606 tr("The machine '%ls' is not registered"),
4607 mUserData->mName.raw());
4608
4609 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4610
4611 /* Hack: in case the session is closing and there is a progress object
4612 * which allows waiting for the session to be closed, take the opportunity
4613 * and do a limited wait (max. 1 second). This helps a lot when the system
4614 * is busy and thus session closing can take a little while. */
4615 if ( mData->mSession.mState == SessionState_Closing
4616 && mData->mSession.mProgress)
4617 {
4618 alock.leave();
4619 mData->mSession.mProgress->WaitForCompletion(1000);
4620 alock.enter();
4621 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4622 }
4623
4624 if (mData->mSession.mState == SessionState_Open ||
4625 mData->mSession.mState == SessionState_Closing)
4626 return setError(VBOX_E_INVALID_OBJECT_STATE,
4627 tr("A session for the machine '%ls' is currently open (or being closed)"),
4628 mUserData->mName.raw());
4629
4630 /* may not be busy */
4631 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
4632
4633 /* get the session PID */
4634 RTPROCESS pid = NIL_RTPROCESS;
4635 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
4636 aControl->GetPID((ULONG *) &pid);
4637 Assert(pid != NIL_RTPROCESS);
4638
4639 if (mData->mSession.mState == SessionState_Spawning)
4640 {
4641 /* This machine is awaiting for a spawning session to be opened, so
4642 * reject any other open attempts from processes other than one
4643 * started by #openRemoteSession(). */
4644
4645 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n",
4646 mData->mSession.mPid, mData->mSession.mPid));
4647 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
4648
4649 if (mData->mSession.mPid != pid)
4650 return setError(E_ACCESSDENIED,
4651 tr("An unexpected process (PID=0x%08X) has tried to open a direct "
4652 "session with the machine named '%ls', while only a process "
4653 "started by OpenRemoteSession (PID=0x%08X) is allowed"),
4654 pid, mUserData->mName.raw(), mData->mSession.mPid);
4655 }
4656
4657 /* create a SessionMachine object */
4658 ComObjPtr<SessionMachine> sessionMachine;
4659 sessionMachine.createObject();
4660 HRESULT rc = sessionMachine->init(this);
4661 AssertComRC(rc);
4662
4663 /* NOTE: doing return from this function after this point but
4664 * before the end is forbidden since it may call SessionMachine::uninit()
4665 * (through the ComObjPtr's destructor) which requests the VirtualBox write
4666 * lock while still holding the Machine lock in alock so that a deadlock
4667 * is possible due to the wrong lock order. */
4668
4669 if (SUCCEEDED(rc))
4670 {
4671#ifdef VBOX_WITH_RESOURCE_USAGE_API
4672 registerMetrics(mParent->performanceCollector(), this, pid);
4673#endif /* VBOX_WITH_RESOURCE_USAGE_API */
4674
4675 /*
4676 * Set the session state to Spawning to protect against subsequent
4677 * attempts to open a session and to unregister the machine after
4678 * we leave the lock.
4679 */
4680 SessionState_T origState = mData->mSession.mState;
4681 mData->mSession.mState = SessionState_Spawning;
4682
4683 /*
4684 * Leave the lock before calling the client process -- it will call
4685 * Machine/SessionMachine methods. Leaving the lock here is quite safe
4686 * because the state is Spawning, so that openRemotesession() and
4687 * openExistingSession() calls will fail. This method, called before we
4688 * enter the lock again, will fail because of the wrong PID.
4689 *
4690 * Note that mData->mSession.mRemoteControls accessed outside
4691 * the lock may not be modified when state is Spawning, so it's safe.
4692 */
4693 alock.leave();
4694
4695 LogFlowThisFunc(("Calling AssignMachine()...\n"));
4696 rc = aControl->AssignMachine(sessionMachine);
4697 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
4698
4699 /* The failure may occur w/o any error info (from RPC), so provide one */
4700 if (FAILED(rc))
4701 setError(VBOX_E_VM_ERROR,
4702 tr("Failed to assign the machine to the session (%Rrc)"), rc);
4703
4704 if (SUCCEEDED(rc) && origState == SessionState_Spawning)
4705 {
4706 /* complete the remote session initialization */
4707
4708 /* get the console from the direct session */
4709 ComPtr<IConsole> console;
4710 rc = aControl->GetRemoteConsole(console.asOutParam());
4711 ComAssertComRC(rc);
4712
4713 if (SUCCEEDED(rc) && !console)
4714 {
4715 ComAssert(!!console);
4716 rc = E_FAIL;
4717 }
4718
4719 /* assign machine & console to the remote session */
4720 if (SUCCEEDED(rc))
4721 {
4722 /*
4723 * after openRemoteSession(), the first and the only
4724 * entry in remoteControls is that remote session
4725 */
4726 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
4727 rc = mData->mSession.mRemoteControls.front()->
4728 AssignRemoteMachine(sessionMachine, console);
4729 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
4730
4731 /* The failure may occur w/o any error info (from RPC), so provide one */
4732 if (FAILED(rc))
4733 setError(VBOX_E_VM_ERROR,
4734 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
4735 }
4736
4737 if (FAILED(rc))
4738 aControl->Uninitialize();
4739 }
4740
4741 /* enter the lock again */
4742 alock.enter();
4743
4744 /* Restore the session state */
4745 mData->mSession.mState = origState;
4746 }
4747
4748 /* finalize spawning anyway (this is why we don't return on errors above) */
4749 if (mData->mSession.mState == SessionState_Spawning)
4750 {
4751 /* Note that the progress object is finalized later */
4752
4753 /* We don't reset mSession.mPid here because it is necessary for
4754 * SessionMachine::uninit() to reap the child process later. */
4755
4756 if (FAILED(rc))
4757 {
4758 /* Close the remote session, remove the remote control from the list
4759 * and reset session state to Closed (@note keep the code in sync
4760 * with the relevant part in openSession()). */
4761
4762 Assert (mData->mSession.mRemoteControls.size() == 1);
4763 if (mData->mSession.mRemoteControls.size() == 1)
4764 {
4765 ErrorInfoKeeper eik;
4766 mData->mSession.mRemoteControls.front()->Uninitialize();
4767 }
4768
4769 mData->mSession.mRemoteControls.clear();
4770 mData->mSession.mState = SessionState_Closed;
4771 }
4772 }
4773 else
4774 {
4775 /* memorize PID of the directly opened session */
4776 if (SUCCEEDED(rc))
4777 mData->mSession.mPid = pid;
4778 }
4779
4780 if (SUCCEEDED(rc))
4781 {
4782 /* memorize the direct session control and cache IUnknown for it */
4783 mData->mSession.mDirectControl = aControl;
4784 mData->mSession.mState = SessionState_Open;
4785 /* associate the SessionMachine with this Machine */
4786 mData->mSession.mMachine = sessionMachine;
4787
4788 /* request an IUnknown pointer early from the remote party for later
4789 * identity checks (it will be internally cached within mDirectControl
4790 * at least on XPCOM) */
4791 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
4792 NOREF(unk);
4793 }
4794
4795 if (mData->mSession.mProgress)
4796 {
4797 /* finalize the progress after setting the state, for consistency */
4798 mData->mSession.mProgress->notifyComplete(rc);
4799 mData->mSession.mProgress.setNull();
4800 }
4801
4802 /* Leave the lock since SessionMachine::uninit() locks VirtualBox which
4803 * would break the lock order */
4804 alock.leave();
4805
4806 /* uninitialize the created session machine on failure */
4807 if (FAILED(rc))
4808 sessionMachine->uninit();
4809
4810 LogFlowThisFunc(("rc=%08X\n", rc));
4811 LogFlowThisFuncLeave();
4812 return rc;
4813}
4814
4815/**
4816 * @note Locks this object for writing, calls the client process
4817 * (inside the lock).
4818 */
4819HRESULT Machine::openRemoteSession(IInternalSessionControl *aControl,
4820 IN_BSTR aType,
4821 IN_BSTR aEnvironment,
4822 Progress *aProgress)
4823{
4824 LogFlowThisFuncEnter();
4825
4826 AssertReturn(aControl, E_FAIL);
4827 AssertReturn(aProgress, E_FAIL);
4828
4829 AutoCaller autoCaller(this);
4830 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4831
4832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4833
4834 if (!mData->mRegistered)
4835 return setError(E_UNEXPECTED,
4836 tr("The machine '%ls' is not registered"),
4837 mUserData->mName.raw());
4838
4839 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4840
4841 if (mData->mSession.mState == SessionState_Open ||
4842 mData->mSession.mState == SessionState_Spawning ||
4843 mData->mSession.mState == SessionState_Closing)
4844 return setError(VBOX_E_INVALID_OBJECT_STATE,
4845 tr("A session for the machine '%ls' is currently open (or being opened or closed)"),
4846 mUserData->mName.raw());
4847
4848 /* may not be busy */
4849 AssertReturn(!Global::IsOnlineOrTransient (mData->mMachineState), E_FAIL);
4850
4851 /* get the path to the executable */
4852 char szPath[RTPATH_MAX];
4853 RTPathAppPrivateArch(szPath, RTPATH_MAX);
4854 size_t sz = strlen(szPath);
4855 szPath[sz++] = RTPATH_DELIMITER;
4856 szPath[sz] = 0;
4857 char *cmd = szPath + sz;
4858 sz = RTPATH_MAX - sz;
4859
4860 int vrc = VINF_SUCCESS;
4861 RTPROCESS pid = NIL_RTPROCESS;
4862
4863 RTENV env = RTENV_DEFAULT;
4864
4865 if (aEnvironment != NULL && *aEnvironment)
4866 {
4867 char *newEnvStr = NULL;
4868
4869 do
4870 {
4871 /* clone the current environment */
4872 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
4873 AssertRCBreakStmt(vrc2, vrc = vrc2);
4874
4875 newEnvStr = RTStrDup(Utf8Str(aEnvironment).c_str());
4876 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
4877
4878 /* put new variables to the environment
4879 * (ignore empty variable names here since RTEnv API
4880 * intentionally doesn't do that) */
4881 char *var = newEnvStr;
4882 for (char *p = newEnvStr; *p; ++p)
4883 {
4884 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
4885 {
4886 *p = '\0';
4887 if (*var)
4888 {
4889 char *val = strchr (var, '=');
4890 if (val)
4891 {
4892 *val++ = '\0';
4893 vrc2 = RTEnvSetEx (env, var, val);
4894 }
4895 else
4896 vrc2 = RTEnvUnsetEx (env, var);
4897 if (RT_FAILURE(vrc2))
4898 break;
4899 }
4900 var = p + 1;
4901 }
4902 }
4903 if (RT_SUCCESS(vrc2) && *var)
4904 vrc2 = RTEnvPutEx (env, var);
4905
4906 AssertRCBreakStmt (vrc2, vrc = vrc2);
4907 }
4908 while (0);
4909
4910 if (newEnvStr != NULL)
4911 RTStrFree(newEnvStr);
4912 }
4913
4914 Utf8Str strType(aType);
4915
4916 /* Qt is default */
4917#ifdef VBOX_WITH_QTGUI
4918 if (strType == "gui" || strType == "GUI/Qt")
4919 {
4920# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
4921 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
4922# else
4923 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
4924# endif
4925 Assert (sz >= sizeof (VirtualBox_exe));
4926 strcpy (cmd, VirtualBox_exe);
4927
4928 Utf8Str idStr = mData->mUuid.toString();
4929# ifdef RT_OS_WINDOWS /** @todo drop this once the RTProcCreate bug has been fixed */
4930 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0 };
4931# else
4932 Utf8Str strName = mUserData->mName;
4933 const char * args[] = {szPath, "--comment", strName.c_str(), "--startvm", idStr.c_str(), 0 };
4934# endif
4935 vrc = RTProcCreate(szPath, args, env, 0, &pid);
4936 }
4937#else /* !VBOX_WITH_QTGUI */
4938 if (0)
4939 ;
4940#endif /* VBOX_WITH_QTGUI */
4941
4942 else
4943
4944#ifdef VBOX_WITH_VBOXSDL
4945 if (strType == "sdl" || strType == "GUI/SDL")
4946 {
4947 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
4948 Assert (sz >= sizeof (VBoxSDL_exe));
4949 strcpy (cmd, VBoxSDL_exe);
4950
4951 Utf8Str idStr = mData->mUuid.toString();
4952# ifdef RT_OS_WINDOWS
4953 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0 };
4954# else
4955 Utf8Str strName = mUserData->mName;
4956 const char * args[] = {szPath, "--comment", strName.c_str(), "--startvm", idStr.c_str(), 0 };
4957# endif
4958 vrc = RTProcCreate(szPath, args, env, 0, &pid);
4959 }
4960#else /* !VBOX_WITH_VBOXSDL */
4961 if (0)
4962 ;
4963#endif /* !VBOX_WITH_VBOXSDL */
4964
4965 else
4966
4967#ifdef VBOX_WITH_HEADLESS
4968 if ( strType == "headless"
4969 || strType == "capture"
4970#ifdef VBOX_WITH_VRDP
4971 || strType == "vrdp"
4972#endif
4973 )
4974 {
4975 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
4976 Assert (sz >= sizeof (VBoxHeadless_exe));
4977 strcpy (cmd, VBoxHeadless_exe);
4978
4979 Utf8Str idStr = mData->mUuid.toString();
4980 /* Leave space for 2 args, as "headless" needs --vrdp off on non-OSE. */
4981# ifdef RT_OS_WINDOWS
4982 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0, 0, 0 };
4983# else
4984 Utf8Str strName = mUserData->mName;
4985 const char * args[] ={szPath, "--comment", strName.c_str(), "--startvm", idStr.c_str(), 0, 0, 0 };
4986# endif
4987#ifdef VBOX_WITH_VRDP
4988 if (strType == "headless")
4989 {
4990 unsigned pos = RT_ELEMENTS(args) - 3;
4991 args[pos++] = "--vrdp";
4992 args[pos] = "off";
4993 }
4994#endif
4995 if (strType == "capture")
4996 {
4997 unsigned pos = RT_ELEMENTS(args) - 3;
4998 args[pos] = "--capture";
4999 }
5000 vrc = RTProcCreate(szPath, args, env, 0, &pid);
5001 }
5002#else /* !VBOX_WITH_HEADLESS */
5003 if (0)
5004 ;
5005#endif /* !VBOX_WITH_HEADLESS */
5006 else
5007 {
5008 RTEnvDestroy (env);
5009 return setError(E_INVALIDARG,
5010 tr("Invalid session type: '%s'"),
5011 strType.c_str());
5012 }
5013
5014 RTEnvDestroy (env);
5015
5016 if (RT_FAILURE(vrc))
5017 return setError(VBOX_E_IPRT_ERROR,
5018 tr("Could not launch a process for the machine '%ls' (%Rrc)"),
5019 mUserData->mName.raw(), vrc);
5020
5021 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
5022
5023 /*
5024 * Note that we don't leave the lock here before calling the client,
5025 * because it doesn't need to call us back if called with a NULL argument.
5026 * Leaving the lock herer is dangerous because we didn't prepare the
5027 * launch data yet, but the client we've just started may happen to be
5028 * too fast and call openSession() that will fail (because of PID, etc.),
5029 * so that the Machine will never get out of the Spawning session state.
5030 */
5031
5032 /* inform the session that it will be a remote one */
5033 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
5034 HRESULT rc = aControl->AssignMachine (NULL);
5035 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
5036
5037 if (FAILED(rc))
5038 {
5039 /* restore the session state */
5040 mData->mSession.mState = SessionState_Closed;
5041 /* The failure may occur w/o any error info (from RPC), so provide one */
5042 return setError(VBOX_E_VM_ERROR,
5043 tr("Failed to assign the machine to the session (%Rrc)"), rc);
5044 }
5045
5046 /* attach launch data to the machine */
5047 Assert (mData->mSession.mPid == NIL_RTPROCESS);
5048 mData->mSession.mRemoteControls.push_back (aControl);
5049 mData->mSession.mProgress = aProgress;
5050 mData->mSession.mPid = pid;
5051 mData->mSession.mState = SessionState_Spawning;
5052 mData->mSession.mType = strType;
5053
5054 LogFlowThisFuncLeave();
5055 return S_OK;
5056}
5057
5058/**
5059 * @note Locks this object for writing, calls the client process
5060 * (outside the lock).
5061 */
5062HRESULT Machine::openExistingSession (IInternalSessionControl *aControl)
5063{
5064 LogFlowThisFuncEnter();
5065
5066 AssertReturn(aControl, E_FAIL);
5067
5068 AutoCaller autoCaller(this);
5069 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5070
5071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5072
5073 if (!mData->mRegistered)
5074 return setError (E_UNEXPECTED,
5075 tr ("The machine '%ls' is not registered"), mUserData->mName.raw());
5076
5077 LogFlowThisFunc(("mSession.state=%s\n", Global::stringifySessionState(mData->mSession.mState)));
5078
5079 if (mData->mSession.mState != SessionState_Open)
5080 return setError (VBOX_E_INVALID_SESSION_STATE,
5081 tr ("The machine '%ls' does not have an open session"),
5082 mUserData->mName.raw());
5083
5084 ComAssertRet (!mData->mSession.mDirectControl.isNull(), E_FAIL);
5085
5086 /*
5087 * Get the console from the direct session (note that we don't leave the
5088 * lock here because GetRemoteConsole must not call us back).
5089 */
5090 ComPtr<IConsole> console;
5091 HRESULT rc = mData->mSession.mDirectControl->
5092 GetRemoteConsole (console.asOutParam());
5093 if (FAILED (rc))
5094 {
5095 /* The failure may occur w/o any error info (from RPC), so provide one */
5096 return setError (VBOX_E_VM_ERROR,
5097 tr ("Failed to get a console object from the direct session (%Rrc)"), rc);
5098 }
5099
5100 ComAssertRet (!console.isNull(), E_FAIL);
5101
5102 ComObjPtr<SessionMachine> sessionMachine = mData->mSession.mMachine;
5103 AssertReturn(!sessionMachine.isNull(), E_FAIL);
5104
5105 /*
5106 * Leave the lock before calling the client process. It's safe here
5107 * since the only thing to do after we get the lock again is to add
5108 * the remote control to the list (which doesn't directly influence
5109 * anything).
5110 */
5111 alock.leave();
5112
5113 /* attach the remote session to the machine */
5114 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
5115 rc = aControl->AssignRemoteMachine (sessionMachine, console);
5116 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
5117
5118 /* The failure may occur w/o any error info (from RPC), so provide one */
5119 if (FAILED(rc))
5120 return setError(VBOX_E_VM_ERROR,
5121 tr("Failed to assign the machine to the session (%Rrc)"),
5122 rc);
5123
5124 alock.enter();
5125
5126 /* need to revalidate the state after entering the lock again */
5127 if (mData->mSession.mState != SessionState_Open)
5128 {
5129 aControl->Uninitialize();
5130
5131 return setError(VBOX_E_INVALID_SESSION_STATE,
5132 tr("The machine '%ls' does not have an open session"),
5133 mUserData->mName.raw());
5134 }
5135
5136 /* store the control in the list */
5137 mData->mSession.mRemoteControls.push_back (aControl);
5138
5139 LogFlowThisFuncLeave();
5140 return S_OK;
5141}
5142
5143/**
5144 * Returns @c true if the given machine has an open direct session and returns
5145 * the session machine instance and additional session data (on some platforms)
5146 * if so.
5147 *
5148 * Note that when the method returns @c false, the arguments remain unchanged.
5149 *
5150 * @param aMachine Session machine object.
5151 * @param aControl Direct session control object (optional).
5152 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
5153 *
5154 * @note locks this object for reading.
5155 */
5156#if defined (RT_OS_WINDOWS)
5157bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
5158 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
5159 HANDLE *aIPCSem /*= NULL*/,
5160 bool aAllowClosing /*= false*/)
5161#elif defined (RT_OS_OS2)
5162bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
5163 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
5164 HMTX *aIPCSem /*= NULL*/,
5165 bool aAllowClosing /*= false*/)
5166#else
5167bool Machine::isSessionOpen (ComObjPtr<SessionMachine> &aMachine,
5168 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
5169 bool aAllowClosing /*= false*/)
5170#endif
5171{
5172 AutoLimitedCaller autoCaller(this);
5173 AssertComRCReturn (autoCaller.rc(), false);
5174
5175 /* just return false for inaccessible machines */
5176 if (autoCaller.state() != Ready)
5177 return false;
5178
5179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5180
5181 if (mData->mSession.mState == SessionState_Open ||
5182 (aAllowClosing && mData->mSession.mState == SessionState_Closing))
5183 {
5184 AssertReturn(!mData->mSession.mMachine.isNull(), false);
5185
5186 aMachine = mData->mSession.mMachine;
5187
5188 if (aControl != NULL)
5189 *aControl = mData->mSession.mDirectControl;
5190
5191#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
5192 /* Additional session data */
5193 if (aIPCSem != NULL)
5194 *aIPCSem = aMachine->mIPCSem;
5195#endif
5196 return true;
5197 }
5198
5199 return false;
5200}
5201
5202/**
5203 * Returns @c true if the given machine has an spawning direct session and
5204 * returns and additional session data (on some platforms) if so.
5205 *
5206 * Note that when the method returns @c false, the arguments remain unchanged.
5207 *
5208 * @param aPID PID of the spawned direct session process.
5209 *
5210 * @note locks this object for reading.
5211 */
5212#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
5213bool Machine::isSessionSpawning (RTPROCESS *aPID /*= NULL*/)
5214#else
5215bool Machine::isSessionSpawning()
5216#endif
5217{
5218 AutoLimitedCaller autoCaller(this);
5219 AssertComRCReturn (autoCaller.rc(), false);
5220
5221 /* just return false for inaccessible machines */
5222 if (autoCaller.state() != Ready)
5223 return false;
5224
5225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5226
5227 if (mData->mSession.mState == SessionState_Spawning)
5228 {
5229#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
5230 /* Additional session data */
5231 if (aPID != NULL)
5232 {
5233 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
5234 *aPID = mData->mSession.mPid;
5235 }
5236#endif
5237 return true;
5238 }
5239
5240 return false;
5241}
5242
5243/**
5244 * Called from the client watcher thread to check for unexpected client process
5245 * death during Session_Spawning state (e.g. before it successfully opened a
5246 * direct session).
5247 *
5248 * On Win32 and on OS/2, this method is called only when we've got the
5249 * direct client's process termination notification, so it always returns @c
5250 * true.
5251 *
5252 * On other platforms, this method returns @c true if the client process is
5253 * terminated and @c false if it's still alive.
5254 *
5255 * @note Locks this object for writing.
5256 */
5257bool Machine::checkForSpawnFailure()
5258{
5259 AutoCaller autoCaller(this);
5260 if (!autoCaller.isOk())
5261 {
5262 /* nothing to do */
5263 LogFlowThisFunc(("Already uninitialized!\n"));
5264 return true;
5265 }
5266
5267 /* VirtualBox::addProcessToReap() needs a write lock */
5268 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
5269
5270 if (mData->mSession.mState != SessionState_Spawning)
5271 {
5272 /* nothing to do */
5273 LogFlowThisFunc(("Not spawning any more!\n"));
5274 return true;
5275 }
5276
5277 HRESULT rc = S_OK;
5278
5279#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5280
5281 /* the process was already unexpectedly terminated, we just need to set an
5282 * error and finalize session spawning */
5283 rc = setError(E_FAIL,
5284 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
5285 getName().raw());
5286#else
5287
5288 /* PID not yet initialized, skip check. */
5289 if (mData->mSession.mPid == NIL_RTPROCESS)
5290 return false;
5291
5292 RTPROCSTATUS status;
5293 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
5294 &status);
5295
5296 if (vrc != VERR_PROCESS_RUNNING)
5297 rc = setError(E_FAIL,
5298 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
5299 getName().raw());
5300#endif
5301
5302 if (FAILED(rc))
5303 {
5304 /* Close the remote session, remove the remote control from the list
5305 * and reset session state to Closed (@note keep the code in sync with
5306 * the relevant part in checkForSpawnFailure()). */
5307
5308 Assert(mData->mSession.mRemoteControls.size() == 1);
5309 if (mData->mSession.mRemoteControls.size() == 1)
5310 {
5311 ErrorInfoKeeper eik;
5312 mData->mSession.mRemoteControls.front()->Uninitialize();
5313 }
5314
5315 mData->mSession.mRemoteControls.clear();
5316 mData->mSession.mState = SessionState_Closed;
5317
5318 /* finalize the progress after setting the state, for consistency */
5319 if (!mData->mSession.mProgress.isNull())
5320 {
5321 mData->mSession.mProgress->notifyComplete(rc);
5322 mData->mSession.mProgress.setNull();
5323 }
5324
5325 mParent->addProcessToReap(mData->mSession.mPid);
5326 mData->mSession.mPid = NIL_RTPROCESS;
5327
5328 mParent->onSessionStateChange(mData->mUuid, SessionState_Closed);
5329 return true;
5330 }
5331
5332 return false;
5333}
5334
5335/**
5336 * Checks that the registered flag of the machine can be set according to
5337 * the argument and sets it. On success, commits and saves all settings.
5338 *
5339 * @note When this machine is inaccessible, the only valid value for \a
5340 * aRegistered is FALSE (i.e. unregister the machine) because unregistered
5341 * inaccessible machines are not currently supported. Note that unregistering
5342 * an inaccessible machine will \b uninitialize this machine object. Therefore,
5343 * the caller must make sure there are no active Machine::addCaller() calls
5344 * on the current thread because this will block Machine::uninit().
5345 *
5346 * @note Must be called from mParent's write lock. Locks this object and
5347 * children for writing.
5348 */
5349HRESULT Machine::trySetRegistered(BOOL argNewRegistered)
5350{
5351 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
5352
5353 AutoLimitedCaller autoCaller(this);
5354 AssertComRCReturnRC(autoCaller.rc());
5355
5356 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5357
5358 /* wait for state dependants to drop to zero */
5359 ensureNoStateDependencies();
5360
5361 ComAssertRet(mData->mRegistered != argNewRegistered, E_FAIL);
5362
5363 if (!mData->mAccessible)
5364 {
5365 /* A special case: the machine is not accessible. */
5366
5367 /* inaccessible machines can only be unregistered */
5368 AssertReturn(!argNewRegistered, E_FAIL);
5369
5370 /* Uninitialize ourselves here because currently there may be no
5371 * unregistered that are inaccessible (this state combination is not
5372 * supported). Note releasing the caller and leaving the lock before
5373 * calling uninit() */
5374
5375 alock.leave();
5376 autoCaller.release();
5377
5378 uninit();
5379
5380 return S_OK;
5381 }
5382
5383 AssertReturn(autoCaller.state() == Ready, E_FAIL);
5384
5385 if (argNewRegistered)
5386 {
5387 if (mData->mRegistered)
5388 return setError(VBOX_E_INVALID_OBJECT_STATE,
5389 tr("The machine '%ls' with UUID {%s} is already registered"),
5390 mUserData->mName.raw(),
5391 mData->mUuid.toString().raw());
5392 }
5393 else
5394 {
5395 if (mData->mMachineState == MachineState_Saved)
5396 return setError(VBOX_E_INVALID_VM_STATE,
5397 tr("Cannot unregister the machine '%ls' because it is in the Saved state"),
5398 mUserData->mName.raw());
5399
5400 size_t snapshotCount = 0;
5401 if (mData->mFirstSnapshot)
5402 snapshotCount = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5403 if (snapshotCount)
5404 return setError(VBOX_E_INVALID_OBJECT_STATE,
5405 tr("Cannot unregister the machine '%ls' because it has %d snapshots"),
5406 mUserData->mName.raw(), snapshotCount);
5407
5408 if (mData->mSession.mState != SessionState_Closed)
5409 return setError(VBOX_E_INVALID_OBJECT_STATE,
5410 tr("Cannot unregister the machine '%ls' because it has an open session"),
5411 mUserData->mName.raw());
5412
5413 if (mMediaData->mAttachments.size() != 0)
5414 return setError(VBOX_E_INVALID_OBJECT_STATE,
5415 tr("Cannot unregister the machine '%ls' because it has %d medium attachments"),
5416 mUserData->mName.raw(),
5417 mMediaData->mAttachments.size());
5418
5419 /* Note that we do not prevent unregistration of a DVD or Floppy image
5420 * is attached: as opposed to hard disks detaching such an image
5421 * implicitly in this method (which we will do below) won't have any
5422 * side effects (like detached orphan base and diff hard disks etc).*/
5423 }
5424
5425 HRESULT rc = S_OK;
5426
5427 /* Ensure the settings are saved. If we are going to be registered and
5428 * isConfigLocked() is FALSE then it means that no config file exists yet,
5429 * so create it by calling saveSettings() too. */
5430 if ( isModified()
5431 || (argNewRegistered && !mData->m_pMachineConfigFile->fileExists())
5432 )
5433 {
5434 rc = saveSettings();
5435 if (FAILED(rc)) return rc;
5436 }
5437
5438 /* more config checking goes here */
5439
5440 if (SUCCEEDED(rc))
5441 {
5442 /* we may have had implicit modifications we want to fix on success */
5443 commit();
5444
5445 mData->mRegistered = argNewRegistered;
5446 }
5447 else
5448 {
5449 /* we may have had implicit modifications we want to cancel on failure*/
5450 rollback (false /* aNotify */);
5451 }
5452
5453 return rc;
5454}
5455
5456/**
5457 * Increases the number of objects dependent on the machine state or on the
5458 * registered state. Guarantees that these two states will not change at least
5459 * until #releaseStateDependency() is called.
5460 *
5461 * Depending on the @a aDepType value, additional state checks may be made.
5462 * These checks will set extended error info on failure. See
5463 * #checkStateDependency() for more info.
5464 *
5465 * If this method returns a failure, the dependency is not added and the caller
5466 * is not allowed to rely on any particular machine state or registration state
5467 * value and may return the failed result code to the upper level.
5468 *
5469 * @param aDepType Dependency type to add.
5470 * @param aState Current machine state (NULL if not interested).
5471 * @param aRegistered Current registered state (NULL if not interested).
5472 *
5473 * @note Locks this object for writing.
5474 */
5475HRESULT Machine::addStateDependency (StateDependency aDepType /* = AnyStateDep */,
5476 MachineState_T *aState /* = NULL */,
5477 BOOL *aRegistered /* = NULL */)
5478{
5479 AutoCaller autoCaller(this);
5480 AssertComRCReturnRC(autoCaller.rc());
5481
5482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5483
5484 HRESULT rc = checkStateDependency(aDepType);
5485 if (FAILED(rc)) return rc;
5486
5487 {
5488 if (mData->mMachineStateChangePending != 0)
5489 {
5490 /* ensureNoStateDependencies() is waiting for state dependencies to
5491 * drop to zero so don't add more. It may make sense to wait a bit
5492 * and retry before reporting an error (since the pending state
5493 * transition should be really quick) but let's just assert for
5494 * now to see if it ever happens on practice. */
5495
5496 AssertFailed();
5497
5498 return setError(E_ACCESSDENIED,
5499 tr("Machine state change is in progress. Please retry the operation later."));
5500 }
5501
5502 ++mData->mMachineStateDeps;
5503 Assert (mData->mMachineStateDeps != 0 /* overflow */);
5504 }
5505
5506 if (aState)
5507 *aState = mData->mMachineState;
5508 if (aRegistered)
5509 *aRegistered = mData->mRegistered;
5510
5511 return S_OK;
5512}
5513
5514/**
5515 * Decreases the number of objects dependent on the machine state.
5516 * Must always complete the #addStateDependency() call after the state
5517 * dependency is no more necessary.
5518 */
5519void Machine::releaseStateDependency()
5520{
5521 AutoCaller autoCaller(this);
5522 AssertComRCReturnVoid (autoCaller.rc());
5523
5524 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5525
5526 AssertReturnVoid (mData->mMachineStateDeps != 0
5527 /* releaseStateDependency() w/o addStateDependency()? */);
5528 -- mData->mMachineStateDeps;
5529
5530 if (mData->mMachineStateDeps == 0)
5531 {
5532 /* inform ensureNoStateDependencies() that there are no more deps */
5533 if (mData->mMachineStateChangePending != 0)
5534 {
5535 Assert (mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
5536 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
5537 }
5538 }
5539}
5540
5541// protected methods
5542/////////////////////////////////////////////////////////////////////////////
5543
5544/**
5545 * Performs machine state checks based on the @a aDepType value. If a check
5546 * fails, this method will set extended error info, otherwise it will return
5547 * S_OK. It is supposed, that on failure, the caller will immedieately return
5548 * the return value of this method to the upper level.
5549 *
5550 * When @a aDepType is AnyStateDep, this method always returns S_OK.
5551 *
5552 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
5553 * current state of this machine object allows to change settings of the
5554 * machine (i.e. the machine is not registered, or registered but not running
5555 * and not saved). It is useful to call this method from Machine setters
5556 * before performing any change.
5557 *
5558 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
5559 * as for MutableStateDep except that if the machine is saved, S_OK is also
5560 * returned. This is useful in setters which allow changing machine
5561 * properties when it is in the saved state.
5562 *
5563 * @param aDepType Dependency type to check.
5564 *
5565 * @note Non Machine based classes should use #addStateDependency() and
5566 * #releaseStateDependency() methods or the smart AutoStateDependency
5567 * template.
5568 *
5569 * @note This method must be called from under this object's read or write
5570 * lock.
5571 */
5572HRESULT Machine::checkStateDependency(StateDependency aDepType)
5573{
5574 switch (aDepType)
5575 {
5576 case AnyStateDep:
5577 {
5578 break;
5579 }
5580 case MutableStateDep:
5581 {
5582 if ( mData->mRegistered
5583 && ( getClassID() != clsidSessionMachine /** @todo This was just convered raw; Check if Running and Paused should actually be included here... (Live Migration) */
5584 || ( mData->mMachineState != MachineState_Paused
5585 && mData->mMachineState != MachineState_Running
5586 && mData->mMachineState != MachineState_Aborted
5587 && mData->mMachineState != MachineState_Teleported
5588 && mData->mMachineState != MachineState_PoweredOff
5589 )
5590 )
5591 )
5592 return setError(VBOX_E_INVALID_VM_STATE,
5593 tr("The machine is not mutable (state is %s)"),
5594 Global::stringifyMachineState(mData->mMachineState));
5595 break;
5596 }
5597 case MutableOrSavedStateDep:
5598 {
5599 if ( mData->mRegistered
5600 && ( getClassID() != clsidSessionMachine /** @todo This was just convered raw; Check if Running and Paused should actually be included here... (Live Migration) */
5601 || ( mData->mMachineState != MachineState_Paused
5602 && mData->mMachineState != MachineState_Running
5603 && mData->mMachineState != MachineState_Aborted
5604 && mData->mMachineState != MachineState_Teleported
5605 && mData->mMachineState != MachineState_Saved
5606 && mData->mMachineState != MachineState_PoweredOff
5607 )
5608 )
5609 )
5610 return setError(VBOX_E_INVALID_VM_STATE,
5611 tr("The machine is not mutable (state is %s)"),
5612 Global::stringifyMachineState(mData->mMachineState));
5613 break;
5614 }
5615 }
5616
5617 return S_OK;
5618}
5619
5620/**
5621 * Helper to initialize all associated child objects and allocate data
5622 * structures.
5623 *
5624 * This method must be called as a part of the object's initialization procedure
5625 * (usually done in the #init() method).
5626 *
5627 * @note Must be called only from #init() or from #registeredInit().
5628 */
5629HRESULT Machine::initDataAndChildObjects()
5630{
5631 AutoCaller autoCaller(this);
5632 AssertComRCReturnRC(autoCaller.rc());
5633 AssertComRCReturn (autoCaller.state() == InInit ||
5634 autoCaller.state() == Limited, E_FAIL);
5635
5636 AssertReturn(!mData->mAccessible, E_FAIL);
5637
5638 /* allocate data structures */
5639 mSSData.allocate();
5640 mUserData.allocate();
5641 mHWData.allocate();
5642 mMediaData.allocate();
5643 mStorageControllers.allocate();
5644
5645 /* initialize mOSTypeId */
5646 mUserData->mOSTypeId = mParent->getUnknownOSType()->id();
5647
5648 /* create associated BIOS settings object */
5649 unconst(mBIOSSettings).createObject();
5650 mBIOSSettings->init (this);
5651
5652#ifdef VBOX_WITH_VRDP
5653 /* create an associated VRDPServer object (default is disabled) */
5654 unconst(mVRDPServer).createObject();
5655 mVRDPServer->init (this);
5656#endif
5657
5658 /* create associated serial port objects */
5659 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
5660 {
5661 unconst(mSerialPorts [slot]).createObject();
5662 mSerialPorts [slot]->init (this, slot);
5663 }
5664
5665 /* create associated parallel port objects */
5666 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
5667 {
5668 unconst(mParallelPorts [slot]).createObject();
5669 mParallelPorts [slot]->init (this, slot);
5670 }
5671
5672 /* create the audio adapter object (always present, default is disabled) */
5673 unconst(mAudioAdapter).createObject();
5674 mAudioAdapter->init (this);
5675
5676 /* create the USB controller object (always present, default is disabled) */
5677 unconst(mUSBController).createObject();
5678 mUSBController->init (this);
5679
5680 /* create associated network adapter objects */
5681 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
5682 {
5683 unconst(mNetworkAdapters [slot]).createObject();
5684 mNetworkAdapters [slot]->init (this, slot);
5685 }
5686
5687 return S_OK;
5688}
5689
5690/**
5691 * Helper to uninitialize all associated child objects and to free all data
5692 * structures.
5693 *
5694 * This method must be called as a part of the object's uninitialization
5695 * procedure (usually done in the #uninit() method).
5696 *
5697 * @note Must be called only from #uninit() or from #registeredInit().
5698 */
5699void Machine::uninitDataAndChildObjects()
5700{
5701 AutoCaller autoCaller(this);
5702 AssertComRCReturnVoid (autoCaller.rc());
5703 AssertComRCReturnVoid (autoCaller.state() == InUninit ||
5704 autoCaller.state() == Limited);
5705
5706 /* uninit all children using addDependentChild()/removeDependentChild()
5707 * in their init()/uninit() methods */
5708 uninitDependentChildren();
5709
5710 /* tell all our other child objects we've been uninitialized */
5711
5712 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
5713 {
5714 if (mNetworkAdapters[slot])
5715 {
5716 mNetworkAdapters[slot]->uninit();
5717 unconst(mNetworkAdapters[slot]).setNull();
5718 }
5719 }
5720
5721 if (mUSBController)
5722 {
5723 mUSBController->uninit();
5724 unconst(mUSBController).setNull();
5725 }
5726
5727 if (mAudioAdapter)
5728 {
5729 mAudioAdapter->uninit();
5730 unconst(mAudioAdapter).setNull();
5731 }
5732
5733 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
5734 {
5735 if (mParallelPorts[slot])
5736 {
5737 mParallelPorts[slot]->uninit();
5738 unconst(mParallelPorts[slot]).setNull();
5739 }
5740 }
5741
5742 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
5743 {
5744 if (mSerialPorts[slot])
5745 {
5746 mSerialPorts[slot]->uninit();
5747 unconst(mSerialPorts[slot]).setNull();
5748 }
5749 }
5750
5751#ifdef VBOX_WITH_VRDP
5752 if (mVRDPServer)
5753 {
5754 mVRDPServer->uninit();
5755 unconst(mVRDPServer).setNull();
5756 }
5757#endif
5758
5759 if (mBIOSSettings)
5760 {
5761 mBIOSSettings->uninit();
5762 unconst(mBIOSSettings).setNull();
5763 }
5764
5765 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
5766 * instance is uninitialized; SessionMachine instances refer to real
5767 * Machine hard disks). This is necessary for a clean re-initialization of
5768 * the VM after successfully re-checking the accessibility state. Note
5769 * that in case of normal Machine or SnapshotMachine uninitialization (as
5770 * a result of unregistering or discarding the snapshot), outdated hard
5771 * disk attachments will already be uninitialized and deleted, so this
5772 * code will not affect them. */
5773 if (!!mMediaData && (getClassID() == clsidMachine || getClassID() == clsidSnapshotMachine))
5774 {
5775 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
5776 it != mMediaData->mAttachments.end();
5777 ++it)
5778 {
5779 ComObjPtr<Medium> hd = (*it)->getMedium();
5780 if (hd.isNull())
5781 continue;
5782 HRESULT rc = hd->detachFrom(mData->mUuid, getSnapshotId());
5783 AssertComRC (rc);
5784 }
5785 }
5786
5787 if (getClassID() == clsidMachine)
5788 {
5789 /* reset some important fields of mData */
5790 mData->mCurrentSnapshot.setNull();
5791 mData->mFirstSnapshot.setNull();
5792 }
5793
5794 /* free data structures (the essential mData structure is not freed here
5795 * since it may be still in use) */
5796 mMediaData.free();
5797 mStorageControllers.free();
5798 mHWData.free();
5799 mUserData.free();
5800 mSSData.free();
5801}
5802
5803/**
5804 * Makes sure that there are no machine state dependants. If necessary, waits
5805 * for the number of dependants to drop to zero.
5806 *
5807 * Make sure this method is called from under this object's write lock to
5808 * guarantee that no new dependants may be added when this method returns
5809 * control to the caller.
5810 *
5811 * @note Locks this object for writing. The lock will be released while waiting
5812 * (if necessary).
5813 *
5814 * @warning To be used only in methods that change the machine state!
5815 */
5816void Machine::ensureNoStateDependencies()
5817{
5818 AssertReturnVoid (isWriteLockOnCurrentThread());
5819
5820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5821
5822 /* Wait for all state dependants if necessary */
5823 if (mData->mMachineStateDeps != 0)
5824 {
5825 /* lazy semaphore creation */
5826 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
5827 RTSemEventMultiCreate (&mData->mMachineStateDepsSem);
5828
5829 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
5830 mData->mMachineStateDeps));
5831
5832 ++mData->mMachineStateChangePending;
5833
5834 /* reset the semaphore before waiting, the last dependant will signal
5835 * it */
5836 RTSemEventMultiReset (mData->mMachineStateDepsSem);
5837
5838 alock.leave();
5839
5840 RTSemEventMultiWait (mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
5841
5842 alock.enter();
5843
5844 -- mData->mMachineStateChangePending;
5845 }
5846}
5847
5848/**
5849 * Changes the machine state and informs callbacks.
5850 *
5851 * This method is not intended to fail so it either returns S_OK or asserts (and
5852 * returns a failure).
5853 *
5854 * @note Locks this object for writing.
5855 */
5856HRESULT Machine::setMachineState (MachineState_T aMachineState)
5857{
5858 LogFlowThisFuncEnter();
5859 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
5860
5861 AutoCaller autoCaller(this);
5862 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
5863
5864 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5865
5866 /* wait for state dependants to drop to zero */
5867 ensureNoStateDependencies();
5868
5869 if (mData->mMachineState != aMachineState)
5870 {
5871 mData->mMachineState = aMachineState;
5872
5873 RTTimeNow (&mData->mLastStateChange);
5874
5875 mParent->onMachineStateChange(mData->mUuid, aMachineState);
5876 }
5877
5878 LogFlowThisFuncLeave();
5879 return S_OK;
5880}
5881
5882/**
5883 * Searches for a shared folder with the given logical name
5884 * in the collection of shared folders.
5885 *
5886 * @param aName logical name of the shared folder
5887 * @param aSharedFolder where to return the found object
5888 * @param aSetError whether to set the error info if the folder is
5889 * not found
5890 * @return
5891 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
5892 *
5893 * @note
5894 * must be called from under the object's lock!
5895 */
5896HRESULT Machine::findSharedFolder (CBSTR aName,
5897 ComObjPtr<SharedFolder> &aSharedFolder,
5898 bool aSetError /* = false */)
5899{
5900 bool found = false;
5901 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
5902 !found && it != mHWData->mSharedFolders.end();
5903 ++it)
5904 {
5905 AutoWriteLock alock(*it COMMA_LOCKVAL_SRC_POS);
5906 found = (*it)->getName() == aName;
5907 if (found)
5908 aSharedFolder = *it;
5909 }
5910
5911 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
5912
5913 if (aSetError && !found)
5914 setError(rc, tr("Could not find a shared folder named '%ls'"), aName);
5915
5916 return rc;
5917}
5918
5919/**
5920 * Loads all the VM settings by walking down the <Machine> node.
5921 *
5922 * @param aRegistered true when the machine is being loaded on VirtualBox
5923 * startup
5924 *
5925 * @note This method is intended to be called only from init(), so it assumes
5926 * all machine data fields have appropriate default values when it is called.
5927 *
5928 * @note Doesn't lock any objects.
5929 */
5930HRESULT Machine::loadSettings(bool aRegistered)
5931{
5932 LogFlowThisFuncEnter();
5933 AssertReturn(getClassID() == clsidMachine, E_FAIL);
5934
5935 AutoCaller autoCaller(this);
5936 AssertReturn(autoCaller.state() == InInit, E_FAIL);
5937
5938 HRESULT rc = S_OK;
5939
5940 try
5941 {
5942 Assert(mData->m_pMachineConfigFile == NULL);
5943
5944 // load and parse machine XML; this will throw on XML or logic errors
5945 mData->m_pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
5946
5947 /* If the stored UUID is not empty, it means the registered machine
5948 * is being loaded. Compare the loaded UUID with the stored one taken
5949 * from the global registry. */
5950 if (!mData->mUuid.isEmpty())
5951 {
5952 if (mData->mUuid != mData->m_pMachineConfigFile->uuid)
5953 {
5954 throw setError(E_FAIL,
5955 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
5956 mData->m_pMachineConfigFile->uuid.raw(),
5957 mData->m_strConfigFileFull.raw(),
5958 mData->mUuid.toString().raw(),
5959 mParent->settingsFilePath().raw());
5960 }
5961 }
5962 else
5963 unconst (mData->mUuid) = mData->m_pMachineConfigFile->uuid;
5964
5965 /* name (required) */
5966 mUserData->mName = mData->m_pMachineConfigFile->strName;
5967
5968 /* nameSync (optional, default is true) */
5969 mUserData->mNameSync = mData->m_pMachineConfigFile->fNameSync;
5970
5971 mUserData->mDescription = mData->m_pMachineConfigFile->strDescription;
5972
5973 // guest OS type
5974 mUserData->mOSTypeId = mData->m_pMachineConfigFile->strOsType;
5975 /* look up the object by Id to check it is valid */
5976 ComPtr<IGuestOSType> guestOSType;
5977 rc = mParent->GetGuestOSType(mUserData->mOSTypeId,
5978 guestOSType.asOutParam());
5979 if (FAILED(rc)) throw rc;
5980
5981 // stateFile (optional)
5982 if (mData->m_pMachineConfigFile->strStateFile.isEmpty())
5983 mSSData->mStateFilePath.setNull();
5984 else
5985 {
5986 Utf8Str stateFilePathFull(mData->m_pMachineConfigFile->strStateFile);
5987 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
5988 if (RT_FAILURE(vrc))
5989 throw setError(E_FAIL,
5990 tr("Invalid saved state file path '%s' (%Rrc)"),
5991 mData->m_pMachineConfigFile->strStateFile.raw(),
5992 vrc);
5993 mSSData->mStateFilePath = stateFilePathFull;
5994 }
5995
5996 /* snapshotFolder (optional) */
5997 rc = COMSETTER(SnapshotFolder)(Bstr(mData->m_pMachineConfigFile->strSnapshotFolder));
5998 if (FAILED(rc)) throw rc;
5999
6000 /* currentStateModified (optional, default is true) */
6001 mData->mCurrentStateModified = mData->m_pMachineConfigFile->fCurrentStateModified;
6002
6003 mData->mLastStateChange = mData->m_pMachineConfigFile->timeLastStateChange;
6004
6005 /* teleportation */
6006 mUserData->mTeleporterEnabled = mData->m_pMachineConfigFile->fTeleporterEnabled;
6007 mUserData->mTeleporterPort = mData->m_pMachineConfigFile->uTeleporterPort;
6008 mUserData->mTeleporterAddress = mData->m_pMachineConfigFile->strTeleporterAddress;
6009 mUserData->mTeleporterPassword = mData->m_pMachineConfigFile->strTeleporterPassword;
6010
6011 /* RTC */
6012 mUserData->mRTCUseUTC = mData->m_pMachineConfigFile->fRTCUseUTC;
6013
6014 /*
6015 * note: all mUserData members must be assigned prior this point because
6016 * we need to commit changes in order to let mUserData be shared by all
6017 * snapshot machine instances.
6018 */
6019 mUserData.commitCopy();
6020
6021 /* Snapshot node (optional) */
6022 if (mData->m_pMachineConfigFile->llFirstSnapshot.size())
6023 {
6024 // there can only be one root snapshot
6025 Assert(mData->m_pMachineConfigFile->llFirstSnapshot.size() == 1);
6026
6027 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
6028
6029 rc = loadSnapshot(snap,
6030 mData->m_pMachineConfigFile->uuidCurrentSnapshot,
6031 NULL); // no parent == first snapshot
6032 if (FAILED(rc)) throw rc;
6033 }
6034
6035 /* Hardware node (required) */
6036 rc = loadHardware(mData->m_pMachineConfigFile->hardwareMachine);
6037 if (FAILED(rc)) throw rc;
6038
6039 /* Load storage controllers */
6040 rc = loadStorageControllers(mData->m_pMachineConfigFile->storageMachine, aRegistered);
6041 if (FAILED(rc)) throw rc;
6042
6043 /*
6044 * NOTE: the assignment below must be the last thing to do,
6045 * otherwise it will be not possible to change the settings
6046 * somewehere in the code above because all setters will be
6047 * blocked by checkStateDependency(MutableStateDep).
6048 */
6049
6050 /* set the machine state to Aborted or Saved when appropriate */
6051 if (mData->m_pMachineConfigFile->fAborted)
6052 {
6053 Assert(!mSSData->mStateFilePath.isEmpty());
6054 mSSData->mStateFilePath.setNull();
6055
6056 /* no need to use setMachineState() during init() */
6057 mData->mMachineState = MachineState_Aborted;
6058 }
6059 else if (!mSSData->mStateFilePath.isEmpty())
6060 {
6061 /* no need to use setMachineState() during init() */
6062 mData->mMachineState = MachineState_Saved;
6063 }
6064 }
6065 catch (HRESULT err)
6066 {
6067 /* we assume that error info is set by the thrower */
6068 rc = err;
6069 }
6070 catch (...)
6071 {
6072 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
6073 }
6074
6075 LogFlowThisFuncLeave();
6076 return rc;
6077}
6078
6079/**
6080 * Recursively loads all snapshots starting from the given.
6081 *
6082 * @param aNode <Snapshot> node.
6083 * @param aCurSnapshotId Current snapshot ID from the settings file.
6084 * @param aParentSnapshot Parent snapshot.
6085 */
6086HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
6087 const Guid &aCurSnapshotId,
6088 Snapshot *aParentSnapshot)
6089{
6090 AssertReturn(getClassID() == clsidMachine, E_FAIL);
6091
6092 HRESULT rc = S_OK;
6093
6094 Utf8Str strStateFile;
6095 if (!data.strStateFile.isEmpty())
6096 {
6097 /* optional */
6098 strStateFile = data.strStateFile;
6099 int vrc = calculateFullPath(strStateFile, strStateFile);
6100 if (RT_FAILURE(vrc))
6101 return setError(E_FAIL,
6102 tr("Invalid saved state file path '%s' (%Rrc)"),
6103 strStateFile.raw(),
6104 vrc);
6105 }
6106
6107 /* create a snapshot machine object */
6108 ComObjPtr<SnapshotMachine> pSnapshotMachine;
6109 pSnapshotMachine.createObject();
6110 rc = pSnapshotMachine->init(this,
6111 data.hardware,
6112 data.storage,
6113 data.uuid,
6114 strStateFile);
6115 if (FAILED(rc)) return rc;
6116
6117 /* create a snapshot object */
6118 ComObjPtr<Snapshot> pSnapshot;
6119 pSnapshot.createObject();
6120 /* initialize the snapshot */
6121 rc = pSnapshot->init(mParent, // VirtualBox object
6122 data.uuid,
6123 data.strName,
6124 data.strDescription,
6125 data.timestamp,
6126 pSnapshotMachine,
6127 aParentSnapshot);
6128 if (FAILED(rc)) return rc;
6129
6130 /* memorize the first snapshot if necessary */
6131 if (!mData->mFirstSnapshot)
6132 mData->mFirstSnapshot = pSnapshot;
6133
6134 /* memorize the current snapshot when appropriate */
6135 if ( !mData->mCurrentSnapshot
6136 && pSnapshot->getId() == aCurSnapshotId
6137 )
6138 mData->mCurrentSnapshot = pSnapshot;
6139
6140 // now create the children
6141 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
6142 it != data.llChildSnapshots.end();
6143 ++it)
6144 {
6145 const settings::Snapshot &childData = *it;
6146 // recurse
6147 rc = loadSnapshot(childData,
6148 aCurSnapshotId,
6149 pSnapshot); // parent = the one we created above
6150 if (FAILED(rc)) return rc;
6151 }
6152
6153 return rc;
6154}
6155
6156/**
6157 * @param aNode <Hardware> node.
6158 */
6159HRESULT Machine::loadHardware(const settings::Hardware &data)
6160{
6161 AssertReturn(getClassID() == clsidMachine || getClassID() == clsidSnapshotMachine, E_FAIL);
6162
6163 HRESULT rc = S_OK;
6164
6165 try
6166 {
6167 /* The hardware version attribute (optional). */
6168 mHWData->mHWVersion = data.strVersion;
6169 mHWData->mHardwareUUID = data.uuid;
6170
6171 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
6172 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
6173 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
6174 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
6175 mHWData->mPAEEnabled = data.fPAE;
6176 mHWData->mSyntheticCpu = data.fSyntheticCpu;
6177
6178 mHWData->mCPUCount = data.cCPUs;
6179 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
6180
6181 // cpu
6182 if (mHWData->mCPUHotPlugEnabled)
6183 {
6184 for (settings::CpuList::const_iterator it = data.llCpus.begin();
6185 it != data.llCpus.end();
6186 ++it)
6187 {
6188 const settings::Cpu &cpu = *it;
6189
6190 mHWData->mCPUAttached[cpu.ulId] = true;
6191 }
6192 }
6193
6194 // cpuid leafs
6195 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
6196 it != data.llCpuIdLeafs.end();
6197 ++it)
6198 {
6199 const settings::CpuIdLeaf &leaf = *it;
6200
6201 switch (leaf.ulId)
6202 {
6203 case 0x0:
6204 case 0x1:
6205 case 0x2:
6206 case 0x3:
6207 case 0x4:
6208 case 0x5:
6209 case 0x6:
6210 case 0x7:
6211 case 0x8:
6212 case 0x9:
6213 case 0xA:
6214 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
6215 break;
6216
6217 case 0x80000000:
6218 case 0x80000001:
6219 case 0x80000002:
6220 case 0x80000003:
6221 case 0x80000004:
6222 case 0x80000005:
6223 case 0x80000006:
6224 case 0x80000007:
6225 case 0x80000008:
6226 case 0x80000009:
6227 case 0x8000000A:
6228 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
6229 break;
6230
6231 default:
6232 /* just ignore */
6233 break;
6234 }
6235 }
6236
6237 mHWData->mMemorySize = data.ulMemorySizeMB;
6238
6239 // boot order
6240 for (size_t i = 0;
6241 i < RT_ELEMENTS(mHWData->mBootOrder);
6242 i++)
6243 {
6244 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
6245 if (it == data.mapBootOrder.end())
6246 mHWData->mBootOrder[i] = DeviceType_Null;
6247 else
6248 mHWData->mBootOrder[i] = it->second;
6249 }
6250
6251 mHWData->mVRAMSize = data.ulVRAMSizeMB;
6252 mHWData->mMonitorCount = data.cMonitors;
6253 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
6254 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
6255 mHWData->mFirmwareType = data.firmwareType;
6256
6257#ifdef VBOX_WITH_VRDP
6258 /* RemoteDisplay */
6259 rc = mVRDPServer->loadSettings(data.vrdpSettings);
6260 if (FAILED(rc)) return rc;
6261#endif
6262
6263 /* BIOS */
6264 rc = mBIOSSettings->loadSettings(data.biosSettings);
6265 if (FAILED(rc)) return rc;
6266
6267 /* USB Controller */
6268 rc = mUSBController->loadSettings(data.usbController);
6269 if (FAILED(rc)) return rc;
6270
6271 // network adapters
6272 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
6273 it != data.llNetworkAdapters.end();
6274 ++it)
6275 {
6276 const settings::NetworkAdapter &nic = *it;
6277
6278 /* slot unicity is guaranteed by XML Schema */
6279 AssertBreak(nic.ulSlot < RT_ELEMENTS(mNetworkAdapters));
6280 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(nic);
6281 if (FAILED(rc)) return rc;
6282 }
6283
6284 // serial ports
6285 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
6286 it != data.llSerialPorts.end();
6287 ++it)
6288 {
6289 const settings::SerialPort &s = *it;
6290
6291 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
6292 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
6293 if (FAILED(rc)) return rc;
6294 }
6295
6296 // parallel ports (optional)
6297 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
6298 it != data.llParallelPorts.end();
6299 ++it)
6300 {
6301 const settings::ParallelPort &p = *it;
6302
6303 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
6304 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
6305 if (FAILED(rc)) return rc;
6306 }
6307
6308 /* AudioAdapter */
6309 rc = mAudioAdapter->loadSettings(data.audioAdapter);
6310 if (FAILED(rc)) return rc;
6311
6312 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
6313 it != data.llSharedFolders.end();
6314 ++it)
6315 {
6316 const settings::SharedFolder &sf = *it;
6317 rc = CreateSharedFolder(Bstr(sf.strName), Bstr(sf.strHostPath), sf.fWritable);
6318 if (FAILED(rc)) return rc;
6319 }
6320
6321 // Clipboard
6322 mHWData->mClipboardMode = data.clipboardMode;
6323
6324 // guest settings
6325 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
6326 mHWData->mStatisticsUpdateInterval = data.ulStatisticsUpdateInterval;
6327
6328#ifdef VBOX_WITH_GUEST_PROPS
6329 /* Guest properties (optional) */
6330 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
6331 it != data.llGuestProperties.end();
6332 ++it)
6333 {
6334 const settings::GuestProperty &prop = *it;
6335 uint32_t fFlags = guestProp::NILFLAG;
6336 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
6337 HWData::GuestProperty property = { prop.strName, prop.strValue, prop.timestamp, fFlags };
6338 mHWData->mGuestProperties.push_back(property);
6339 }
6340
6341 mHWData->mPropertyServiceActive = false;
6342 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
6343#endif /* VBOX_WITH_GUEST_PROPS defined */
6344 }
6345 catch(std::bad_alloc &)
6346 {
6347 return E_OUTOFMEMORY;
6348 }
6349
6350 AssertComRC(rc);
6351 return rc;
6352}
6353
6354 /**
6355 * @param aNode <StorageControllers> node.
6356 */
6357HRESULT Machine::loadStorageControllers(const settings::Storage &data,
6358 bool aRegistered,
6359 const Guid *aSnapshotId /* = NULL */)
6360{
6361 AssertReturn(getClassID() == clsidMachine || getClassID() == clsidSnapshotMachine, E_FAIL);
6362
6363 HRESULT rc = S_OK;
6364
6365 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
6366 it != data.llStorageControllers.end();
6367 ++it)
6368 {
6369 const settings::StorageController &ctlData = *it;
6370
6371 ComObjPtr<StorageController> pCtl;
6372 /* Try to find one with the name first. */
6373 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
6374 if (SUCCEEDED(rc))
6375 return setError(VBOX_E_OBJECT_IN_USE,
6376 tr("Storage controller named '%s' already exists"),
6377 ctlData.strName.raw());
6378
6379 pCtl.createObject();
6380 rc = pCtl->init(this,
6381 ctlData.strName,
6382 ctlData.storageBus,
6383 ctlData.ulInstance);
6384 if (FAILED(rc)) return rc;
6385
6386 mStorageControllers->push_back(pCtl);
6387
6388 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
6389 if (FAILED(rc)) return rc;
6390
6391 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
6392 if (FAILED(rc)) return rc;
6393
6394 /* Set IDE emulation settings (only for AHCI controller). */
6395 if (ctlData.controllerType == StorageControllerType_IntelAhci)
6396 {
6397 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
6398 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
6399 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
6400 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
6401 )
6402 return rc;
6403 }
6404
6405 /* Load the attached devices now. */
6406 rc = loadStorageDevices(pCtl,
6407 ctlData,
6408 aRegistered,
6409 aSnapshotId);
6410 if (FAILED(rc)) return rc;
6411 }
6412
6413 return S_OK;
6414}
6415
6416/**
6417 * @param aNode <HardDiskAttachments> node.
6418 * @param aRegistered true when the machine is being loaded on VirtualBox
6419 * startup, or when a snapshot is being loaded (wchich
6420 * currently can happen on startup only)
6421 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
6422 *
6423 * @note Lock mParent for reading and hard disks for writing before calling.
6424 */
6425HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
6426 const settings::StorageController &data,
6427 bool aRegistered,
6428 const Guid *aSnapshotId /*= NULL*/)
6429{
6430 AssertReturn( (getClassID() == clsidMachine && aSnapshotId == NULL)
6431 || (getClassID() == clsidSnapshotMachine && aSnapshotId != NULL),
6432 E_FAIL);
6433
6434 HRESULT rc = S_OK;
6435
6436 if (!aRegistered && data.llAttachedDevices.size() > 0)
6437 /* when the machine is being loaded (opened) from a file, it cannot
6438 * have hard disks attached (this should not happen normally,
6439 * because we don't allow to attach hard disks to an unregistered
6440 * VM at all */
6441 return setError(E_FAIL,
6442 tr("Unregistered machine '%ls' cannot have storage devices attached (found %d attachments)"),
6443 mUserData->mName.raw(),
6444 data.llAttachedDevices.size());
6445
6446 /* paranoia: detect duplicate attachments */
6447 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
6448 it != data.llAttachedDevices.end();
6449 ++it)
6450 {
6451 for (settings::AttachedDevicesList::const_iterator it2 = it;
6452 it2 != data.llAttachedDevices.end();
6453 ++it2)
6454 {
6455 if (it == it2)
6456 continue;
6457
6458 if ( (*it).lPort == (*it2).lPort
6459 && (*it).lDevice == (*it2).lDevice)
6460 {
6461 return setError(E_FAIL,
6462 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%ls'"),
6463 aStorageController->getName().raw(), (*it).lPort, (*it).lDevice, mUserData->mName.raw());
6464 }
6465 }
6466 }
6467
6468 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
6469 it != data.llAttachedDevices.end();
6470 ++it)
6471 {
6472 const settings::AttachedDevice &dev = *it;
6473 ComObjPtr<Medium> medium;
6474
6475 switch (dev.deviceType)
6476 {
6477 case DeviceType_Floppy:
6478 /* find a floppy by UUID */
6479 if (!dev.uuid.isEmpty())
6480 rc = mParent->findFloppyImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
6481 /* find a floppy by host device name */
6482 else if (!dev.strHostDriveSrc.isEmpty())
6483 {
6484 SafeIfaceArray<IMedium> drivevec;
6485 rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
6486 if (SUCCEEDED(rc))
6487 {
6488 for (size_t i = 0; i < drivevec.size(); ++i)
6489 {
6490 /// @todo eliminate this conversion
6491 ComObjPtr<Medium> med = (Medium *)drivevec[i];
6492 if ( dev.strHostDriveSrc == med->getName()
6493 || dev.strHostDriveSrc == med->getLocation())
6494 {
6495 medium = med;
6496 break;
6497 }
6498 }
6499 }
6500 }
6501 break;
6502
6503 case DeviceType_DVD:
6504 /* find a DVD by UUID */
6505 if (!dev.uuid.isEmpty())
6506 rc = mParent->findDVDImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
6507 /* find a DVD by host device name */
6508 else if (!dev.strHostDriveSrc.isEmpty())
6509 {
6510 SafeIfaceArray<IMedium> drivevec;
6511 rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
6512 if (SUCCEEDED(rc))
6513 {
6514 for (size_t i = 0; i < drivevec.size(); ++i)
6515 {
6516 Bstr hostDriveSrc(dev.strHostDriveSrc);
6517 /// @todo eliminate this conversion
6518 ComObjPtr<Medium> med = (Medium *)drivevec[i];
6519 if ( hostDriveSrc == med->getName()
6520 || hostDriveSrc == med->getLocation())
6521 {
6522 medium = med;
6523 break;
6524 }
6525 }
6526 }
6527 }
6528 break;
6529
6530 case DeviceType_HardDisk:
6531 {
6532 /* find a hard disk by UUID */
6533 rc = mParent->findHardDisk(&dev.uuid, NULL, true /* aDoSetError */, &medium);
6534 if (FAILED(rc))
6535 {
6536 if (getClassID() == clsidSnapshotMachine)
6537 {
6538 // wrap another error message around the "cannot find hard disk" set by findHardDisk
6539 // so the user knows that the bad disk is in a snapshot somewhere
6540 com::ErrorInfo info;
6541 return setError(E_FAIL,
6542 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
6543 aSnapshotId->raw(),
6544 info.getText().raw());
6545 }
6546 else
6547 return rc;
6548 }
6549
6550 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
6551
6552 if (medium->getType() == MediumType_Immutable)
6553 {
6554 if (getClassID() == clsidSnapshotMachine)
6555 return setError(E_FAIL,
6556 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
6557 "of the virtual machine '%ls' ('%s')"),
6558 medium->getLocationFull().raw(),
6559 dev.uuid.raw(),
6560 aSnapshotId->raw(),
6561 mUserData->mName.raw(),
6562 mData->m_strConfigFileFull.raw());
6563
6564 return setError(E_FAIL,
6565 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s')"),
6566 medium->getLocationFull().raw(),
6567 dev.uuid.raw(),
6568 mUserData->mName.raw(),
6569 mData->m_strConfigFileFull.raw());
6570 }
6571
6572 if ( getClassID() != clsidSnapshotMachine
6573 && medium->getChildren().size() != 0
6574 )
6575 return setError(E_FAIL,
6576 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s') "
6577 "because it has %d differencing child hard disks"),
6578 medium->getLocationFull().raw(),
6579 dev.uuid.raw(),
6580 mUserData->mName.raw(),
6581 mData->m_strConfigFileFull.raw(),
6582 medium->getChildren().size());
6583
6584 if (findAttachment(mMediaData->mAttachments,
6585 medium))
6586 return setError(E_FAIL,
6587 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%ls' ('%s')"),
6588 medium->getLocationFull().raw(),
6589 dev.uuid.raw(),
6590 mUserData->mName.raw(),
6591 mData->m_strConfigFileFull.raw());
6592
6593 break;
6594 }
6595
6596 default:
6597 return setError(E_FAIL,
6598 tr("Device with unknown type is attached to the virtual machine '%s' ('%s')"),
6599 medium->getLocationFull().raw(),
6600 mUserData->mName.raw(),
6601 mData->m_strConfigFileFull.raw());
6602 }
6603
6604 if (FAILED(rc))
6605 break;
6606
6607 const Bstr controllerName = aStorageController->getName();
6608 ComObjPtr<MediumAttachment> pAttachment;
6609 pAttachment.createObject();
6610 rc = pAttachment->init(this,
6611 medium,
6612 controllerName,
6613 dev.lPort,
6614 dev.lDevice,
6615 dev.deviceType,
6616 dev.fPassThrough);
6617 if (FAILED(rc)) break;
6618
6619 /* associate the medium with this machine and snapshot */
6620 if (!medium.isNull())
6621 {
6622 if (getClassID() == clsidSnapshotMachine)
6623 rc = medium->attachTo(mData->mUuid, *aSnapshotId);
6624 else
6625 rc = medium->attachTo(mData->mUuid);
6626 }
6627 if (FAILED(rc))
6628 break;
6629
6630 /* backup mMediaData to let registeredInit() properly rollback on failure
6631 * (= limited accessibility) */
6632
6633 mMediaData.backup();
6634 mMediaData->mAttachments.push_back(pAttachment);
6635 }
6636
6637 return rc;
6638}
6639
6640/**
6641 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
6642 *
6643 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
6644 * @param aSnapshot where to return the found snapshot
6645 * @param aSetError true to set extended error info on failure
6646 */
6647HRESULT Machine::findSnapshot(const Guid &aId,
6648 ComObjPtr<Snapshot> &aSnapshot,
6649 bool aSetError /* = false */)
6650{
6651 AutoReadLock chlock(snapshotsTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6652
6653 if (!mData->mFirstSnapshot)
6654 {
6655 if (aSetError)
6656 return setError(E_FAIL,
6657 tr("This machine does not have any snapshots"));
6658 return E_FAIL;
6659 }
6660
6661 if (aId.isEmpty())
6662 aSnapshot = mData->mFirstSnapshot;
6663 else
6664 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId);
6665
6666 if (!aSnapshot)
6667 {
6668 if (aSetError)
6669 return setError(E_FAIL,
6670 tr("Could not find a snapshot with UUID {%s}"),
6671 aId.toString().raw());
6672 return E_FAIL;
6673 }
6674
6675 return S_OK;
6676}
6677
6678/**
6679 * Returns the snapshot with the given name or fails of no such snapshot.
6680 *
6681 * @param aName snapshot name to find
6682 * @param aSnapshot where to return the found snapshot
6683 * @param aSetError true to set extended error info on failure
6684 */
6685HRESULT Machine::findSnapshot(IN_BSTR aName,
6686 ComObjPtr<Snapshot> &aSnapshot,
6687 bool aSetError /* = false */)
6688{
6689 AssertReturn(aName, E_INVALIDARG);
6690
6691 AutoReadLock chlock(snapshotsTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6692
6693 if (!mData->mFirstSnapshot)
6694 {
6695 if (aSetError)
6696 return setError(VBOX_E_OBJECT_NOT_FOUND,
6697 tr("This machine does not have any snapshots"));
6698 return VBOX_E_OBJECT_NOT_FOUND;
6699 }
6700
6701 aSnapshot = mData->mFirstSnapshot->findChildOrSelf (aName);
6702
6703 if (!aSnapshot)
6704 {
6705 if (aSetError)
6706 return setError(VBOX_E_OBJECT_NOT_FOUND,
6707 tr("Could not find a snapshot named '%ls'"), aName);
6708 return VBOX_E_OBJECT_NOT_FOUND;
6709 }
6710
6711 return S_OK;
6712}
6713
6714/**
6715 * Returns a storage controller object with the given name.
6716 *
6717 * @param aName storage controller name to find
6718 * @param aStorageController where to return the found storage controller
6719 * @param aSetError true to set extended error info on failure
6720 */
6721HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
6722 ComObjPtr<StorageController> &aStorageController,
6723 bool aSetError /* = false */)
6724{
6725 AssertReturn (!aName.isEmpty(), E_INVALIDARG);
6726
6727 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6728 it != mStorageControllers->end();
6729 ++it)
6730 {
6731 if ((*it)->getName() == aName)
6732 {
6733 aStorageController = (*it);
6734 return S_OK;
6735 }
6736 }
6737
6738 if (aSetError)
6739 return setError(VBOX_E_OBJECT_NOT_FOUND,
6740 tr("Could not find a storage controller named '%s'"),
6741 aName.raw());
6742 return VBOX_E_OBJECT_NOT_FOUND;
6743}
6744
6745HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
6746 MediaData::AttachmentList &atts)
6747{
6748 AutoCaller autoCaller(this);
6749 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6750
6751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6752
6753 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
6754 it != mMediaData->mAttachments.end();
6755 ++it)
6756 {
6757 if ((*it)->getControllerName() == aName)
6758 atts.push_back(*it);
6759 }
6760
6761 return S_OK;
6762}
6763
6764/**
6765 * Helper for #saveSettings. Cares about renaming the settings directory and
6766 * file if the machine name was changed and about creating a new settings file
6767 * if this is a new machine.
6768 *
6769 * @note Must be never called directly but only from #saveSettings().
6770 *
6771 * @param aRenamed receives |true| if the name was changed and the settings
6772 * file was renamed as a result, or |false| otherwise. The
6773 * value makes sense only on success.
6774 * @param aNew receives |true| if a virgin settings file was created.
6775 */
6776HRESULT Machine::prepareSaveSettings(bool &aRenamed,
6777 bool &aNew)
6778{
6779 /* Note: tecnhically, mParent needs to be locked only when the machine is
6780 * registered (see prepareSaveSettings() for details) but we don't
6781 * currently differentiate it in callers of saveSettings() so we don't
6782 * make difference here too. */
6783 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
6784 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6785
6786 HRESULT rc = S_OK;
6787
6788 aRenamed = false;
6789
6790 /* if we're ready and isConfigLocked() is FALSE then it means
6791 * that no config file exists yet (we will create a virgin one) */
6792 aNew = !mData->m_pMachineConfigFile->fileExists();
6793
6794 /* attempt to rename the settings file if machine name is changed */
6795 if ( mUserData->mNameSync
6796 && mUserData.isBackedUp()
6797 && mUserData.backedUpData()->mName != mUserData->mName
6798 )
6799 {
6800 aRenamed = true;
6801
6802 bool dirRenamed = false;
6803 bool fileRenamed = false;
6804
6805 Utf8Str configFile, newConfigFile;
6806 Utf8Str configDir, newConfigDir;
6807
6808 do
6809 {
6810 int vrc = VINF_SUCCESS;
6811
6812 Utf8Str name = mUserData.backedUpData()->mName;
6813 Utf8Str newName = mUserData->mName;
6814
6815 configFile = mData->m_strConfigFileFull;
6816
6817 /* first, rename the directory if it matches the machine name */
6818 configDir = configFile;
6819 configDir.stripFilename();
6820 newConfigDir = configDir;
6821 if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str()))
6822 {
6823 newConfigDir.stripFilename();
6824 newConfigDir = Utf8StrFmt ("%s%c%s",
6825 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
6826 /* new dir and old dir cannot be equal here because of 'if'
6827 * above and because name != newName */
6828 Assert (configDir != newConfigDir);
6829 if (!aNew)
6830 {
6831 /* perform real rename only if the machine is not new */
6832 vrc = RTPathRename (configDir.raw(), newConfigDir.raw(), 0);
6833 if (RT_FAILURE(vrc))
6834 {
6835 rc = setError(E_FAIL,
6836 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
6837 configDir.raw(),
6838 newConfigDir.raw(),
6839 vrc);
6840 break;
6841 }
6842 dirRenamed = true;
6843 }
6844 }
6845
6846 newConfigFile = Utf8StrFmt ("%s%c%s.xml",
6847 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
6848
6849 /* then try to rename the settings file itself */
6850 if (newConfigFile != configFile)
6851 {
6852 /* get the path to old settings file in renamed directory */
6853 configFile = Utf8StrFmt("%s%c%s",
6854 newConfigDir.raw(),
6855 RTPATH_DELIMITER,
6856 RTPathFilename(configFile.c_str()));
6857 if (!aNew)
6858 {
6859 /* perform real rename only if the machine is not new */
6860 vrc = RTFileRename (configFile.raw(), newConfigFile.raw(), 0);
6861 if (RT_FAILURE(vrc))
6862 {
6863 rc = setError(E_FAIL,
6864 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
6865 configFile.raw(),
6866 newConfigFile.raw(),
6867 vrc);
6868 break;
6869 }
6870 fileRenamed = true;
6871 }
6872 }
6873
6874 /* update m_strConfigFileFull amd mConfigFile */
6875 Utf8Str oldConfigFileFull = mData->m_strConfigFileFull;
6876 Utf8Str oldConfigFile = mData->m_strConfigFile;
6877 mData->m_strConfigFileFull = newConfigFile;
6878 /* try to get the relative path for mConfigFile */
6879 Utf8Str path = newConfigFile;
6880 mParent->calculateRelativePath (path, path);
6881 mData->m_strConfigFile = path;
6882
6883 /* last, try to update the global settings with the new path */
6884 if (mData->mRegistered)
6885 {
6886 rc = mParent->updateSettings(configDir.c_str(), newConfigDir.c_str());
6887 if (FAILED(rc))
6888 {
6889 /* revert to old values */
6890 mData->m_strConfigFileFull = oldConfigFileFull;
6891 mData->m_strConfigFile = oldConfigFile;
6892 break;
6893 }
6894 }
6895
6896 /* update the snapshot folder */
6897 path = mUserData->mSnapshotFolderFull;
6898 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
6899 {
6900 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
6901 path.raw() + configDir.length());
6902 mUserData->mSnapshotFolderFull = path;
6903 calculateRelativePath (path, path);
6904 mUserData->mSnapshotFolder = path;
6905 }
6906
6907 /* update the saved state file path */
6908 path = mSSData->mStateFilePath;
6909 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
6910 {
6911 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
6912 path.raw() + configDir.length());
6913 mSSData->mStateFilePath = path;
6914 }
6915
6916 /* Update saved state file paths of all online snapshots.
6917 * Note that saveSettings() will recognize name change
6918 * and will save all snapshots in this case. */
6919 if (mData->mFirstSnapshot)
6920 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
6921 newConfigDir.c_str());
6922 }
6923 while (0);
6924
6925 if (FAILED(rc))
6926 {
6927 /* silently try to rename everything back */
6928 if (fileRenamed)
6929 RTFileRename(newConfigFile.raw(), configFile.raw(), 0);
6930 if (dirRenamed)
6931 RTPathRename(newConfigDir.raw(), configDir.raw(), 0);
6932 }
6933
6934 if (FAILED(rc)) return rc;
6935 }
6936
6937 if (aNew)
6938 {
6939 /* create a virgin config file */
6940 int vrc = VINF_SUCCESS;
6941
6942 /* ensure the settings directory exists */
6943 Utf8Str path(mData->m_strConfigFileFull);
6944 path.stripFilename();
6945 if (!RTDirExists(path.c_str()))
6946 {
6947 vrc = RTDirCreateFullPath(path.c_str(), 0777);
6948 if (RT_FAILURE(vrc))
6949 {
6950 return setError(E_FAIL,
6951 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
6952 path.raw(),
6953 vrc);
6954 }
6955 }
6956
6957 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
6958 path = Utf8Str(mData->m_strConfigFileFull);
6959 vrc = RTFileOpen(&mData->mHandleCfgFile, path.c_str(),
6960 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
6961 if (RT_FAILURE(vrc))
6962 {
6963 mData->mHandleCfgFile = NIL_RTFILE;
6964 return setError(E_FAIL,
6965 tr("Could not create the settings file '%s' (%Rrc)"),
6966 path.raw(),
6967 vrc);
6968 }
6969 RTFileClose(mData->mHandleCfgFile);
6970 }
6971
6972 return rc;
6973}
6974
6975/**
6976 * Saves and commits machine data, user data and hardware data.
6977 *
6978 * Note that on failure, the data remains uncommitted.
6979 *
6980 * @a aFlags may combine the following flags:
6981 *
6982 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
6983 * Used when saving settings after an operation that makes them 100%
6984 * correspond to the settings from the current snapshot.
6985 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
6986 * #isReallyModified() returns false. This is necessary for cases when we
6987 * change machine data directly, not through the backup()/commit() mechanism.
6988 *
6989 * @note Must be called from under mParent write lock (sometimes needed by
6990 * #prepareSaveSettings()) and this object's write lock. Locks children for
6991 * writing. There is one exception when mParent is unused and therefore may be
6992 * left unlocked: if this machine is an unregistered one.
6993 */
6994HRESULT Machine::saveSettings(int aFlags /*= 0*/)
6995{
6996 LogFlowThisFuncEnter();
6997
6998 /* Note: tecnhically, mParent needs to be locked only when the machine is
6999 * registered (see prepareSaveSettings() for details) but we don't
7000 * currently differentiate it in callers of saveSettings() so we don't
7001 * make difference here too. */
7002 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7003 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7004
7005 /* make sure child objects are unable to modify the settings while we are
7006 * saving them */
7007 ensureNoStateDependencies();
7008
7009 AssertReturn(getClassID() == clsidMachine || getClassID() == clsidSessionMachine, E_FAIL);
7010
7011 BOOL currentStateModified = mData->mCurrentStateModified;
7012 bool settingsModified;
7013
7014 if (!(aFlags & SaveS_ResetCurStateModified) && !currentStateModified)
7015 {
7016 /* We ignore changes to user data when setting mCurrentStateModified
7017 * because the current state will not differ from the current snapshot
7018 * if only user data has been changed (user data is shared by all
7019 * snapshots). */
7020 currentStateModified = isReallyModified (true /* aIgnoreUserData */);
7021 settingsModified = mUserData.hasActualChanges() || currentStateModified;
7022 }
7023 else
7024 {
7025 if (aFlags & SaveS_ResetCurStateModified)
7026 currentStateModified = FALSE;
7027 settingsModified = isReallyModified();
7028 }
7029
7030 HRESULT rc = S_OK;
7031
7032 /* First, prepare to save settings. It will care about renaming the
7033 * settings directory and file if the machine name was changed and about
7034 * creating a new settings file if this is a new machine. */
7035 bool isRenamed = false;
7036 bool isNew = false;
7037 rc = prepareSaveSettings(isRenamed, isNew);
7038 if (FAILED(rc)) return rc;
7039
7040 try
7041 {
7042 mData->m_pMachineConfigFile->uuid = mData->mUuid;
7043 mData->m_pMachineConfigFile->strName = mUserData->mName;
7044 mData->m_pMachineConfigFile->fNameSync = !!mUserData->mNameSync;
7045 mData->m_pMachineConfigFile->strDescription = mUserData->mDescription;
7046 mData->m_pMachineConfigFile->strOsType = mUserData->mOSTypeId;
7047
7048 if ( mData->mMachineState == MachineState_Saved
7049 || mData->mMachineState == MachineState_Restoring
7050 // when deleting a snapshot we may or may not have a saved state in the current state,
7051 // so let's not assert here please
7052 || ( (mData->mMachineState == MachineState_DeletingSnapshot)
7053 && (!mSSData->mStateFilePath.isEmpty())
7054 )
7055 )
7056 {
7057 Assert(!mSSData->mStateFilePath.isEmpty());
7058 /* try to make the file name relative to the settings file dir */
7059 calculateRelativePath(mSSData->mStateFilePath, mData->m_pMachineConfigFile->strStateFile);
7060 }
7061 else
7062 {
7063 Assert(mSSData->mStateFilePath.isEmpty());
7064 mData->m_pMachineConfigFile->strStateFile.setNull();
7065 }
7066
7067 if (mData->mCurrentSnapshot)
7068 mData->m_pMachineConfigFile->uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
7069 else
7070 mData->m_pMachineConfigFile->uuidCurrentSnapshot.clear();
7071
7072 mData->m_pMachineConfigFile->strSnapshotFolder = mUserData->mSnapshotFolder;
7073 mData->m_pMachineConfigFile->fCurrentStateModified = !!currentStateModified;
7074 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
7075 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
7076/// @todo Live Migration: mData->m_pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
7077
7078 mData->m_pMachineConfigFile->fTeleporterEnabled = !!mUserData->mTeleporterEnabled;
7079 mData->m_pMachineConfigFile->uTeleporterPort = mUserData->mTeleporterPort;
7080 mData->m_pMachineConfigFile->strTeleporterAddress = mUserData->mTeleporterAddress;
7081 mData->m_pMachineConfigFile->strTeleporterPassword = mUserData->mTeleporterPassword;
7082
7083 mData->m_pMachineConfigFile->fRTCUseUTC = !!mUserData->mRTCUseUTC;
7084
7085 rc = saveHardware(mData->m_pMachineConfigFile->hardwareMachine);
7086 if (FAILED(rc)) throw rc;
7087
7088 rc = saveStorageControllers(mData->m_pMachineConfigFile->storageMachine);
7089 if (FAILED(rc)) throw rc;
7090
7091 // save snapshots
7092 rc = saveAllSnapshots();
7093 if (FAILED(rc)) throw rc;
7094
7095 // now spit it all out
7096 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
7097 }
7098 catch (HRESULT err)
7099 {
7100 /* we assume that error info is set by the thrower */
7101 rc = err;
7102 }
7103 catch (...)
7104 {
7105 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
7106 }
7107
7108 if (SUCCEEDED(rc))
7109 {
7110 commit();
7111
7112 /* memorize the new modified state */
7113 mData->mCurrentStateModified = currentStateModified;
7114 }
7115
7116 if (settingsModified || (aFlags & SaveS_InformCallbacksAnyway))
7117 {
7118 /* Fire the data change event, even on failure (since we've already
7119 * committed all data). This is done only for SessionMachines because
7120 * mutable Machine instances are always not registered (i.e. private
7121 * to the client process that creates them) and thus don't need to
7122 * inform callbacks. */
7123 if (getClassID() == clsidSessionMachine)
7124 mParent->onMachineDataChange(mData->mUuid);
7125 }
7126
7127 LogFlowThisFunc(("rc=%08X\n", rc));
7128 LogFlowThisFuncLeave();
7129 return rc;
7130}
7131
7132HRESULT Machine::saveAllSnapshots()
7133{
7134 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
7135
7136 HRESULT rc = S_OK;
7137
7138 try
7139 {
7140 mData->m_pMachineConfigFile->llFirstSnapshot.clear();
7141
7142 if (mData->mFirstSnapshot)
7143 {
7144 settings::Snapshot snapNew;
7145 mData->m_pMachineConfigFile->llFirstSnapshot.push_back(snapNew);
7146
7147 // get reference to the fresh copy of the snapshot on the list and
7148 // work on that copy directly to avoid excessive copying later
7149 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
7150
7151 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
7152 if (FAILED(rc)) throw rc;
7153 }
7154
7155// if (mType == IsSessionMachine)
7156// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
7157
7158 }
7159 catch (HRESULT err)
7160 {
7161 /* we assume that error info is set by the thrower */
7162 rc = err;
7163 }
7164 catch (...)
7165 {
7166 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
7167 }
7168
7169 return rc;
7170}
7171
7172/**
7173 * Saves the VM hardware configuration. It is assumed that the
7174 * given node is empty.
7175 *
7176 * @param aNode <Hardware> node to save the VM hardware confguration to.
7177 */
7178HRESULT Machine::saveHardware(settings::Hardware &data)
7179{
7180 HRESULT rc = S_OK;
7181
7182 try
7183 {
7184 /* The hardware version attribute (optional).
7185 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
7186 if ( mHWData->mHWVersion == "1"
7187 && mSSData->mStateFilePath.isEmpty()
7188 )
7189 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. */
7190
7191 data.strVersion = mHWData->mHWVersion;
7192 data.uuid = mHWData->mHardwareUUID;
7193
7194 // CPU
7195 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
7196 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
7197 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
7198 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
7199 data.fPAE = !!mHWData->mPAEEnabled;
7200 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
7201
7202 /* Standard and Extended CPUID leafs. */
7203 data.llCpuIdLeafs.clear();
7204 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
7205 {
7206 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
7207 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
7208 }
7209 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
7210 {
7211 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
7212 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
7213 }
7214
7215 data.cCPUs = mHWData->mCPUCount;
7216 data.fCpuHotPlug = mHWData->mCPUHotPlugEnabled;
7217
7218 data.llCpus.clear();
7219 if (data.fCpuHotPlug)
7220 {
7221 for (unsigned idx = 0; idx < data.cCPUs; idx++)
7222 {
7223 if (mHWData->mCPUAttached[idx])
7224 {
7225 settings::Cpu cpu;
7226 cpu.ulId = idx;
7227 data.llCpus.push_back(cpu);
7228 }
7229 }
7230 }
7231
7232 // memory
7233 data.ulMemorySizeMB = mHWData->mMemorySize;
7234
7235 // firmware
7236 data.firmwareType = mHWData->mFirmwareType;
7237
7238 // boot order
7239 data.mapBootOrder.clear();
7240 for (size_t i = 0;
7241 i < RT_ELEMENTS(mHWData->mBootOrder);
7242 ++i)
7243 data.mapBootOrder[i] = mHWData->mBootOrder[i];
7244
7245 // display
7246 data.ulVRAMSizeMB = mHWData->mVRAMSize;
7247 data.cMonitors = mHWData->mMonitorCount;
7248 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
7249 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
7250
7251#ifdef VBOX_WITH_VRDP
7252 /* VRDP settings (optional) */
7253 rc = mVRDPServer->saveSettings(data.vrdpSettings);
7254 if (FAILED(rc)) throw rc;
7255#endif
7256
7257 /* BIOS (required) */
7258 rc = mBIOSSettings->saveSettings(data.biosSettings);
7259 if (FAILED(rc)) throw rc;
7260
7261 /* USB Controller (required) */
7262 rc = mUSBController->saveSettings(data.usbController);
7263 if (FAILED(rc)) throw rc;
7264
7265 /* Network adapters (required) */
7266 data.llNetworkAdapters.clear();
7267 for (ULONG slot = 0;
7268 slot < RT_ELEMENTS(mNetworkAdapters);
7269 ++slot)
7270 {
7271 settings::NetworkAdapter nic;
7272 nic.ulSlot = slot;
7273 rc = mNetworkAdapters[slot]->saveSettings(nic);
7274 if (FAILED(rc)) throw rc;
7275
7276 data.llNetworkAdapters.push_back(nic);
7277 }
7278
7279 /* Serial ports */
7280 data.llSerialPorts.clear();
7281 for (ULONG slot = 0;
7282 slot < RT_ELEMENTS(mSerialPorts);
7283 ++slot)
7284 {
7285 settings::SerialPort s;
7286 s.ulSlot = slot;
7287 rc = mSerialPorts[slot]->saveSettings(s);
7288 if (FAILED(rc)) return rc;
7289
7290 data.llSerialPorts.push_back(s);
7291 }
7292
7293 /* Parallel ports */
7294 data.llParallelPorts.clear();
7295 for (ULONG slot = 0;
7296 slot < RT_ELEMENTS(mParallelPorts);
7297 ++slot)
7298 {
7299 settings::ParallelPort p;
7300 p.ulSlot = slot;
7301 rc = mParallelPorts[slot]->saveSettings(p);
7302 if (FAILED(rc)) return rc;
7303
7304 data.llParallelPorts.push_back(p);
7305 }
7306
7307 /* Audio adapter */
7308 rc = mAudioAdapter->saveSettings(data.audioAdapter);
7309 if (FAILED(rc)) return rc;
7310
7311 /* Shared folders */
7312 data.llSharedFolders.clear();
7313 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
7314 it != mHWData->mSharedFolders.end();
7315 ++it)
7316 {
7317 ComObjPtr<SharedFolder> pFolder = *it;
7318 settings::SharedFolder sf;
7319 sf.strName = pFolder->getName();
7320 sf.strHostPath = pFolder->getHostPath();
7321 sf.fWritable = !!pFolder->isWritable();
7322
7323 data.llSharedFolders.push_back(sf);
7324 }
7325
7326 // clipboard
7327 data.clipboardMode = mHWData->mClipboardMode;
7328
7329 /* Guest */
7330 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
7331 data.ulStatisticsUpdateInterval = mHWData->mStatisticsUpdateInterval;
7332
7333 // guest properties
7334 data.llGuestProperties.clear();
7335#ifdef VBOX_WITH_GUEST_PROPS
7336 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
7337 it != mHWData->mGuestProperties.end();
7338 ++it)
7339 {
7340 HWData::GuestProperty property = *it;
7341
7342 settings::GuestProperty prop;
7343 prop.strName = property.strName;
7344 prop.strValue = property.strValue;
7345 prop.timestamp = property.mTimestamp;
7346 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
7347 guestProp::writeFlags(property.mFlags, szFlags);
7348 prop.strFlags = szFlags;
7349
7350 data.llGuestProperties.push_back(prop);
7351 }
7352
7353 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
7354#endif /* VBOX_WITH_GUEST_PROPS defined */
7355 }
7356 catch(std::bad_alloc &)
7357 {
7358 return E_OUTOFMEMORY;
7359 }
7360
7361 AssertComRC(rc);
7362 return rc;
7363}
7364
7365/**
7366 * Saves the storage controller configuration.
7367 *
7368 * @param aNode <StorageControllers> node to save the VM hardware confguration to.
7369 */
7370HRESULT Machine::saveStorageControllers(settings::Storage &data)
7371{
7372 data.llStorageControllers.clear();
7373
7374 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
7375 it != mStorageControllers->end();
7376 ++it)
7377 {
7378 HRESULT rc;
7379 ComObjPtr<StorageController> pCtl = *it;
7380
7381 settings::StorageController ctl;
7382 ctl.strName = pCtl->getName();
7383 ctl.controllerType = pCtl->getControllerType();
7384 ctl.storageBus = pCtl->getStorageBus();
7385 ctl.ulInstance = pCtl->getInstance();
7386
7387 /* Save the port count. */
7388 ULONG portCount;
7389 rc = pCtl->COMGETTER(PortCount)(&portCount);
7390 ComAssertComRCRet(rc, rc);
7391 ctl.ulPortCount = portCount;
7392
7393 /* Save IDE emulation settings. */
7394 if (ctl.controllerType == StorageControllerType_IntelAhci)
7395 {
7396 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
7397 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
7398 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
7399 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
7400 )
7401 ComAssertComRCRet(rc, rc);
7402 }
7403
7404 /* save the devices now. */
7405 rc = saveStorageDevices(pCtl, ctl);
7406 ComAssertComRCRet(rc, rc);
7407
7408 data.llStorageControllers.push_back(ctl);
7409 }
7410
7411 return S_OK;
7412}
7413
7414/**
7415 * Saves the hard disk confguration.
7416 */
7417HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
7418 settings::StorageController &data)
7419{
7420 MediaData::AttachmentList atts;
7421
7422 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()), atts);
7423 if (FAILED(rc)) return rc;
7424
7425 data.llAttachedDevices.clear();
7426 for (MediaData::AttachmentList::const_iterator it = atts.begin();
7427 it != atts.end();
7428 ++it)
7429 {
7430 settings::AttachedDevice dev;
7431
7432 MediumAttachment *pAttach = *it;
7433 Medium *pMedium = pAttach->getMedium();
7434
7435 dev.deviceType = pAttach->getType();
7436 dev.lPort = pAttach->getPort();
7437 dev.lDevice = pAttach->getDevice();
7438 if (pMedium)
7439 {
7440 BOOL fHostDrive = FALSE;
7441 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
7442 if (FAILED(rc))
7443 return rc;
7444 if (fHostDrive)
7445 dev.strHostDriveSrc = pMedium->getLocation();
7446 else
7447 dev.uuid = pMedium->getId();
7448 dev.fPassThrough = pAttach->getPassthrough();
7449 }
7450
7451 data.llAttachedDevices.push_back(dev);
7452 }
7453
7454 return S_OK;
7455}
7456
7457/**
7458 * Saves machine state settings as defined by aFlags
7459 * (SaveSTS_* values).
7460 *
7461 * @param aFlags Combination of SaveSTS_* flags.
7462 *
7463 * @note Locks objects for writing.
7464 */
7465HRESULT Machine::saveStateSettings(int aFlags)
7466{
7467 if (aFlags == 0)
7468 return S_OK;
7469
7470 AutoCaller autoCaller(this);
7471 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7472
7473 /* This object's write lock is also necessary to serialize file access
7474 * (prevent concurrent reads and writes) */
7475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7476
7477 HRESULT rc = S_OK;
7478
7479 Assert(mData->m_pMachineConfigFile);
7480
7481 try
7482 {
7483 if (aFlags & SaveSTS_CurStateModified)
7484 mData->m_pMachineConfigFile->fCurrentStateModified = true;
7485
7486 if (aFlags & SaveSTS_StateFilePath)
7487 {
7488 if (!mSSData->mStateFilePath.isEmpty())
7489 /* try to make the file name relative to the settings file dir */
7490 calculateRelativePath(mSSData->mStateFilePath, mData->m_pMachineConfigFile->strStateFile);
7491 else
7492 mData->m_pMachineConfigFile->strStateFile.setNull();
7493 }
7494
7495 if (aFlags & SaveSTS_StateTimeStamp)
7496 {
7497 Assert( mData->mMachineState != MachineState_Aborted
7498 || mSSData->mStateFilePath.isEmpty());
7499
7500 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
7501
7502 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
7503//@todo live migration mData->m_pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
7504 }
7505
7506 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
7507 }
7508 catch (...)
7509 {
7510 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
7511 }
7512
7513 return rc;
7514}
7515
7516/**
7517 * Creates differencing hard disks for all normal hard disks attached to this
7518 * machine and a new set of attachments to refer to created disks.
7519 *
7520 * Used when taking a snapshot or when discarding the current state.
7521 *
7522 * This method assumes that mMediaData contains the original hard disk attachments
7523 * it needs to create diffs for. On success, these attachments will be replaced
7524 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
7525 * called to delete created diffs which will also rollback mMediaData and restore
7526 * whatever was backed up before calling this method.
7527 *
7528 * Attachments with non-normal hard disks are left as is.
7529 *
7530 * If @a aOnline is @c false then the original hard disks that require implicit
7531 * diffs will be locked for reading. Otherwise it is assumed that they are
7532 * already locked for writing (when the VM was started). Note that in the latter
7533 * case it is responsibility of the caller to lock the newly created diffs for
7534 * writing if this method succeeds.
7535 *
7536 * @param aFolder Folder where to create diff hard disks.
7537 * @param aProgress Progress object to run (must contain at least as
7538 * many operations left as the number of hard disks
7539 * attached).
7540 * @param aOnline Whether the VM was online prior to this operation.
7541 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
7542 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
7543 *
7544 * @note The progress object is not marked as completed, neither on success nor
7545 * on failure. This is a responsibility of the caller.
7546 *
7547 * @note Locks this object for writing.
7548 */
7549HRESULT Machine::createImplicitDiffs(const Bstr &aFolder,
7550 IProgress *aProgress,
7551 ULONG aWeight,
7552 bool aOnline,
7553 bool *pfNeedsSaveSettings)
7554{
7555 AssertReturn(!aFolder.isEmpty(), E_FAIL);
7556
7557 LogFlowThisFunc(("aFolder='%ls', aOnline=%d\n", aFolder.raw(), aOnline));
7558
7559 AutoCaller autoCaller(this);
7560 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7561
7562 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7563
7564 /* must be in a protective state because we leave the lock below */
7565 AssertReturn( mData->mMachineState == MachineState_Saving
7566 || mData->mMachineState == MachineState_LiveSnapshotting
7567 || mData->mMachineState == MachineState_RestoringSnapshot
7568 || mData->mMachineState == MachineState_DeletingSnapshot
7569 , E_FAIL);
7570
7571 HRESULT rc = S_OK;
7572
7573 MediaList lockedMedia;
7574
7575 try
7576 {
7577 if (!aOnline)
7578 {
7579 /* lock all attached hard disks early to detect "in use"
7580 * situations before creating actual diffs */
7581 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7582 it != mMediaData->mAttachments.end();
7583 ++it)
7584 {
7585 MediumAttachment* pAtt = *it;
7586 if (pAtt->getType() == DeviceType_HardDisk)
7587 {
7588 Medium* pHD = pAtt->getMedium();
7589 Assert(pHD);
7590 rc = pHD->LockRead (NULL);
7591 if (FAILED(rc)) throw rc;
7592 lockedMedia.push_back(pHD);
7593 }
7594 }
7595 }
7596
7597 /* remember the current list (note that we don't use backup() since
7598 * mMediaData may be already backed up) */
7599 MediaData::AttachmentList atts = mMediaData->mAttachments;
7600
7601 /* start from scratch */
7602 mMediaData->mAttachments.clear();
7603
7604 /* go through remembered attachments and create diffs for normal hard
7605 * disks and attach them */
7606 for (MediaData::AttachmentList::const_iterator it = atts.begin();
7607 it != atts.end();
7608 ++it)
7609 {
7610 MediumAttachment* pAtt = *it;
7611
7612 DeviceType_T devType = pAtt->getType();
7613 Medium* medium = pAtt->getMedium();
7614
7615 if ( devType != DeviceType_HardDisk
7616 || medium == NULL
7617 || medium->getType() != MediumType_Normal)
7618 {
7619 /* copy the attachment as is */
7620
7621 /** @todo the progress object created in Console::TakeSnaphot
7622 * only expects operations for hard disks. Later other
7623 * device types need to show up in the progress as well. */
7624 if (devType == DeviceType_HardDisk)
7625 {
7626 if (medium == NULL)
7627 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")),
7628 aWeight); // weight
7629 else
7630 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
7631 medium->getBase()->getName().raw()),
7632 aWeight); // weight
7633 }
7634
7635 mMediaData->mAttachments.push_back(pAtt);
7636 continue;
7637 }
7638
7639 /* need a diff */
7640 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
7641 medium->getBase()->getName().raw()),
7642 aWeight); // weight
7643
7644 ComObjPtr<Medium> diff;
7645 diff.createObject();
7646 rc = diff->init(mParent,
7647 medium->preferredDiffFormat().raw(),
7648 BstrFmt("%ls"RTPATH_SLASH_STR,
7649 mUserData->mSnapshotFolderFull.raw()).raw(),
7650 pfNeedsSaveSettings);
7651 if (FAILED(rc)) throw rc;
7652
7653 /* leave the lock before the potentially lengthy operation */
7654 alock.leave();
7655
7656 rc = medium->createDiffStorageAndWait(diff,
7657 MediumVariant_Standard,
7658 pfNeedsSaveSettings);
7659
7660 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
7661 * the push_back? Looks like we're going to leave medium with the
7662 * wrong kind of lock (general issue with if we fail anywhere at all)
7663 * and an orphaned VDI in the snapshots folder. */
7664 // at this point, the old image is still locked for writing, but instead
7665 // we need the new diff image locked for writing and lock the previously
7666 // current one for reading only
7667 if (aOnline)
7668 {
7669 diff->LockWrite(NULL);
7670 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(diff), true));
7671 medium->UnlockWrite(NULL);
7672 medium->LockRead(NULL);
7673 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(medium), false));
7674 }
7675
7676 if (FAILED(rc)) throw rc;
7677
7678 alock.enter();
7679
7680 rc = diff->attachTo(mData->mUuid);
7681 AssertComRCThrowRC(rc);
7682
7683 /* add a new attachment */
7684 ComObjPtr<MediumAttachment> attachment;
7685 attachment.createObject();
7686 rc = attachment->init(this,
7687 diff,
7688 pAtt->getControllerName(),
7689 pAtt->getPort(),
7690 pAtt->getDevice(),
7691 DeviceType_HardDisk,
7692 true /* aImplicit */);
7693 if (FAILED(rc)) throw rc;
7694
7695 mMediaData->mAttachments.push_back(attachment);
7696 }
7697 }
7698 catch (HRESULT aRC) { rc = aRC; }
7699
7700 /* unlock all hard disks we locked */
7701 if (!aOnline)
7702 {
7703 ErrorInfoKeeper eik;
7704
7705 for (MediaList::const_iterator it = lockedMedia.begin();
7706 it != lockedMedia.end();
7707 ++it)
7708 {
7709 HRESULT rc2 = (*it)->UnlockRead(NULL);
7710 AssertComRC(rc2);
7711 }
7712 }
7713
7714 if (FAILED(rc))
7715 {
7716 MultiResultRef mrc (rc);
7717
7718 mrc = deleteImplicitDiffs(pfNeedsSaveSettings);
7719 }
7720
7721 return rc;
7722}
7723
7724/**
7725 * Deletes implicit differencing hard disks created either by
7726 * #createImplicitDiffs() or by #AttachMedium() and rolls back mMediaData.
7727 *
7728 * Note that to delete hard disks created by #AttachMedium() this method is
7729 * called from #fixupMedia() when the changes are rolled back.
7730 *
7731 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
7732 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
7733 *
7734 * @note Locks this object for writing.
7735 */
7736HRESULT Machine::deleteImplicitDiffs(bool *pfNeedsSaveSettings)
7737{
7738 AutoCaller autoCaller(this);
7739 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
7740
7741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7742 LogFlowThisFuncEnter();
7743
7744 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
7745
7746 HRESULT rc = S_OK;
7747
7748 MediaData::AttachmentList implicitAtts;
7749
7750 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
7751
7752 /* enumerate new attachments */
7753 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7754 it != mMediaData->mAttachments.end();
7755 ++it)
7756 {
7757 ComObjPtr<Medium> hd = (*it)->getMedium();
7758 if (hd.isNull())
7759 continue;
7760
7761 if ((*it)->isImplicit())
7762 {
7763 /* deassociate and mark for deletion */
7764 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
7765 rc = hd->detachFrom(mData->mUuid);
7766 AssertComRC(rc);
7767 implicitAtts.push_back (*it);
7768 continue;
7769 }
7770
7771 /* was this hard disk attached before? */
7772 if (!findAttachment(oldAtts, hd))
7773 {
7774 /* no: de-associate */
7775 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
7776 rc = hd->detachFrom(mData->mUuid);
7777 AssertComRC(rc);
7778 continue;
7779 }
7780 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
7781 }
7782
7783 /* rollback hard disk changes */
7784 mMediaData.rollback();
7785
7786 MultiResult mrc (S_OK);
7787
7788 /* delete unused implicit diffs */
7789 if (implicitAtts.size() != 0)
7790 {
7791 /* will leave the lock before the potentially lengthy
7792 * operation, so protect with the special state (unless already
7793 * protected) */
7794 MachineState_T oldState = mData->mMachineState;
7795 if ( oldState != MachineState_Saving
7796 && oldState != MachineState_LiveSnapshotting
7797 && oldState != MachineState_RestoringSnapshot
7798 && oldState != MachineState_DeletingSnapshot
7799 )
7800 setMachineState (MachineState_SettingUp);
7801
7802 alock.leave();
7803
7804 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
7805 it != implicitAtts.end();
7806 ++it)
7807 {
7808 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
7809 ComObjPtr<Medium> hd = (*it)->getMedium();
7810
7811 rc = hd->deleteStorageAndWait(NULL /*aProgress*/, pfNeedsSaveSettings);
7812#if 1 /* HACK ALERT: Just make it kind of work */ /** @todo Fix this hack properly. The LockWrite / UnlockWrite / LockRead changes aren't undone! */
7813 if (rc == VBOX_E_INVALID_OBJECT_STATE)
7814 {
7815 LogFlowFunc(("Applying unlock hack on '%s'! FIXME!\n", (*it)->getLogName()));
7816 hd->UnlockWrite(NULL);
7817 rc = hd->deleteStorageAndWait(NULL /*aProgress*/, pfNeedsSaveSettings);
7818 }
7819#endif
7820 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
7821 mrc = rc;
7822 }
7823
7824 alock.enter();
7825
7826 if (mData->mMachineState == MachineState_SettingUp)
7827 {
7828 setMachineState (oldState);
7829 }
7830 }
7831
7832 return mrc;
7833}
7834
7835/**
7836 * Looks through the given list of media attachments for one with the given parameters
7837 * and returns it, or NULL if not found. The list is a parameter so that backup lists
7838 * can be searched as well if needed.
7839 *
7840 * @param list
7841 * @param aControllerName
7842 * @param aControllerPort
7843 * @param aDevice
7844 * @return
7845 */
7846MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
7847 IN_BSTR aControllerName,
7848 LONG aControllerPort,
7849 LONG aDevice)
7850{
7851 for (MediaData::AttachmentList::const_iterator it = ll.begin();
7852 it != ll.end();
7853 ++it)
7854 {
7855 MediumAttachment *pAttach = *it;
7856 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
7857 return pAttach;
7858 }
7859
7860 return NULL;
7861}
7862
7863/**
7864 * Looks through the given list of media attachments for one with the given parameters
7865 * and returns it, or NULL if not found. The list is a parameter so that backup lists
7866 * can be searched as well if needed.
7867 *
7868 * @param list
7869 * @param aControllerName
7870 * @param aControllerPort
7871 * @param aDevice
7872 * @return
7873 */
7874MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
7875 ComObjPtr<Medium> pMedium)
7876{
7877 for (MediaData::AttachmentList::const_iterator it = ll.begin();
7878 it != ll.end();
7879 ++it)
7880 {
7881 MediumAttachment *pAttach = *it;
7882 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
7883 if (pMediumThis.equalsTo(pMedium))
7884 return pAttach;
7885 }
7886
7887 return NULL;
7888}
7889
7890/**
7891 * Looks through the given list of media attachments for one with the given parameters
7892 * and returns it, or NULL if not found. The list is a parameter so that backup lists
7893 * can be searched as well if needed.
7894 *
7895 * @param list
7896 * @param aControllerName
7897 * @param aControllerPort
7898 * @param aDevice
7899 * @return
7900 */
7901MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
7902 Guid &id)
7903{
7904 for (MediaData::AttachmentList::const_iterator it = ll.begin();
7905 it != ll.end();
7906 ++it)
7907 {
7908 MediumAttachment *pAttach = *it;
7909 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
7910 if (pMediumThis->getId() == id)
7911 return pAttach;
7912 }
7913
7914 return NULL;
7915}
7916
7917/**
7918 * Perform deferred hard disk detachments.
7919 *
7920 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
7921 * backed up).
7922 *
7923 * If @a aOnline is @c true then this method will also unlock the old hard disks
7924 * for which the new implicit diffs were created and will lock these new diffs for
7925 * writing.
7926 *
7927 * @param aOnline Whether the VM was online prior to this operation.
7928 *
7929 * @note Locks this object for writing!
7930 */
7931void Machine::commitMedia(bool aOnline /*= false*/)
7932{
7933 AutoCaller autoCaller(this);
7934 AssertComRCReturnVoid (autoCaller.rc());
7935
7936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7937
7938 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
7939
7940 HRESULT rc = S_OK;
7941
7942 /* no attach/detach operations -- nothing to do */
7943 if (!mMediaData.isBackedUp())
7944 return;
7945
7946 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
7947
7948 /* enumerate new attachments */
7949 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7950 it != mMediaData->mAttachments.end();
7951 ++it)
7952 {
7953 MediumAttachment *pAttach = *it;
7954
7955 pAttach->commit();
7956
7957 Medium* pMedium = pAttach->getMedium();
7958 bool fImplicit = pAttach->isImplicit();
7959
7960 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
7961 (pMedium) ? pMedium->getName().raw() : "NULL",
7962 fImplicit));
7963
7964 /** @todo convert all this Machine-based voodoo to MediumAttachment
7965 * based commit logic. */
7966 if (fImplicit)
7967 {
7968 /* convert implicit attachment to normal */
7969 pAttach->setImplicit(false);
7970
7971 if ( aOnline
7972 && pMedium
7973 && pAttach->getType() == DeviceType_HardDisk
7974 )
7975 {
7976 rc = pMedium->LockWrite(NULL);
7977 AssertComRC(rc);
7978
7979 mData->mSession.mLockedMedia.push_back(
7980 Data::Session::LockedMedia::value_type(
7981 ComPtr<IMedium>(pMedium), true));
7982
7983 /* also, relock the old hard disk which is a base for the
7984 * new diff for reading if the VM is online */
7985
7986 ComObjPtr<Medium> parent = pMedium->getParent();
7987 /* make the relock atomic */
7988 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
7989 rc = parent->UnlockWrite(NULL);
7990 AssertComRC(rc);
7991 rc = parent->LockRead(NULL);
7992 AssertComRC(rc);
7993
7994 /* XXX actually we should replace the old entry in that
7995 * vector (write lock => read lock) but this would take
7996 * some effort. So lets just ignore the error code in
7997 * SessionMachine::unlockMedia(). */
7998 mData->mSession.mLockedMedia.push_back(
7999 Data::Session::LockedMedia::value_type (
8000 ComPtr<IMedium>(parent), false));
8001 }
8002
8003 continue;
8004 }
8005
8006 if (pMedium)
8007 {
8008 /* was this medium attached before? */
8009 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
8010 oldIt != oldAtts.end();
8011 ++oldIt)
8012 {
8013 MediumAttachment *pOldAttach = *oldIt;
8014 if (pOldAttach->getMedium().equalsTo(pMedium))
8015 {
8016 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().raw()));
8017
8018 /* yes: remove from old to avoid de-association */
8019 oldAtts.erase(oldIt);
8020 break;
8021 }
8022 }
8023 }
8024 }
8025
8026 /* enumerate remaining old attachments and de-associate from the
8027 * current machine state */
8028 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
8029 it != oldAtts.end();
8030 ++it)
8031 {
8032 MediumAttachment *pAttach = *it;
8033 Medium* pMedium = pAttach->getMedium();
8034
8035 /* Detach only hard disks, since DVD/floppy media is detached
8036 * instantly in MountMedium. */
8037 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
8038 {
8039 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().raw()));
8040
8041 /* now de-associate from the current machine state */
8042 rc = pMedium->detachFrom(mData->mUuid);
8043 AssertComRC(rc);
8044
8045 if ( aOnline
8046 && pAttach->getType() == DeviceType_HardDisk)
8047 {
8048 /* unlock since not used anymore */
8049 MediumState_T state;
8050 rc = pMedium->UnlockWrite(&state);
8051 /* the disk may be alredy relocked for reading above */
8052 Assert (SUCCEEDED(rc) || state == MediumState_LockedRead);
8053 }
8054 }
8055 }
8056
8057 /* commit the hard disk changes */
8058 mMediaData.commit();
8059
8060 if (getClassID() == clsidSessionMachine)
8061 {
8062 /* attach new data to the primary machine and reshare it */
8063 mPeer->mMediaData.attach(mMediaData);
8064 }
8065
8066 return;
8067}
8068
8069/**
8070 * Perform deferred deletion of implicitly created diffs.
8071 *
8072 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
8073 * backed up).
8074 *
8075 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
8076 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
8077 *
8078 * @note Locks this object for writing!
8079 */
8080void Machine::rollbackMedia()
8081{
8082 AutoCaller autoCaller(this);
8083 AssertComRCReturnVoid (autoCaller.rc());
8084
8085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8086
8087 LogFlowThisFunc(("Entering\n"));
8088
8089 HRESULT rc = S_OK;
8090
8091 /* no attach/detach operations -- nothing to do */
8092 if (!mMediaData.isBackedUp())
8093 return;
8094
8095 /* enumerate new attachments */
8096 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8097 it != mMediaData->mAttachments.end();
8098 ++it)
8099 {
8100 MediumAttachment *pAttach = *it;
8101 /* Fix up the backrefs for DVD/floppy media. */
8102 if (pAttach->getType() != DeviceType_HardDisk)
8103 {
8104 Medium* pMedium = pAttach->getMedium();
8105 if (pMedium)
8106 {
8107 rc = pMedium->detachFrom(mData->mUuid);
8108 AssertComRC(rc);
8109 }
8110 }
8111
8112 (*it)->rollback();
8113
8114 pAttach = *it;
8115 /* Fix up the backrefs for DVD/floppy media. */
8116 if (pAttach->getType() != DeviceType_HardDisk)
8117 {
8118 Medium* pMedium = pAttach->getMedium();
8119 if (pMedium)
8120 {
8121 rc = pMedium->attachTo(mData->mUuid);
8122 AssertComRC(rc);
8123 }
8124 }
8125 }
8126
8127 /** @todo convert all this Machine-based voodoo to MediumAttachment
8128 * based rollback logic. */
8129 // @todo r=dj the below totally fails if this gets called from Machine::rollback(),
8130 // which gets called if Machine::registeredInit() fails...
8131 deleteImplicitDiffs(NULL /*pfNeedsSaveSettings*/);
8132
8133 return;
8134}
8135
8136/**
8137 * Returns true if the settings file is located in the directory named exactly
8138 * as the machine. This will be true if the machine settings structure was
8139 * created by default in #openConfigLoader().
8140 *
8141 * @param aSettingsDir if not NULL, the full machine settings file directory
8142 * name will be assigned there.
8143 *
8144 * @note Doesn't lock anything.
8145 * @note Not thread safe (must be called from this object's lock).
8146 */
8147bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */)
8148{
8149 Utf8Str settingsDir = mData->m_strConfigFileFull;
8150 settingsDir.stripFilename();
8151 char *dirName = RTPathFilename(settingsDir.c_str());
8152
8153 AssertReturn(dirName, false);
8154
8155 /* if we don't rename anything on name change, return false shorlty */
8156 if (!mUserData->mNameSync)
8157 return false;
8158
8159 if (aSettingsDir)
8160 *aSettingsDir = settingsDir;
8161
8162 return Bstr (dirName) == mUserData->mName;
8163}
8164
8165/**
8166 * @note Locks objects for reading!
8167 */
8168bool Machine::isModified()
8169{
8170 AutoCaller autoCaller(this);
8171 AssertComRCReturn (autoCaller.rc(), false);
8172
8173 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8174
8175 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
8176 if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isModified())
8177 return true;
8178
8179 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
8180 if (mSerialPorts [slot] && mSerialPorts [slot]->isModified())
8181 return true;
8182
8183 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
8184 if (mParallelPorts [slot] && mParallelPorts [slot]->isModified())
8185 return true;
8186
8187 if (!mStorageControllers.isNull())
8188 {
8189 for (StorageControllerList::const_iterator it =
8190 mStorageControllers->begin();
8191 it != mStorageControllers->end();
8192 ++it)
8193 {
8194 if ((*it)->isModified())
8195 return true;
8196 }
8197 }
8198
8199 return
8200 mUserData.isBackedUp() ||
8201 mHWData.isBackedUp() ||
8202 mMediaData.isBackedUp() ||
8203 mStorageControllers.isBackedUp() ||
8204#ifdef VBOX_WITH_VRDP
8205 (mVRDPServer && mVRDPServer->isModified()) ||
8206#endif
8207 (mAudioAdapter && mAudioAdapter->isModified()) ||
8208 (mUSBController && mUSBController->isModified()) ||
8209 (mBIOSSettings && mBIOSSettings->isModified());
8210}
8211
8212/**
8213 * Returns the logical OR of data.hasActualChanges() of this and all child
8214 * objects.
8215 *
8216 * @param aIgnoreUserData @c true to ignore changes to mUserData
8217 *
8218 * @note Locks objects for reading!
8219 */
8220bool Machine::isReallyModified (bool aIgnoreUserData /* = false */)
8221{
8222 AutoCaller autoCaller(this);
8223 AssertComRCReturn (autoCaller.rc(), false);
8224
8225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8226
8227 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
8228 if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isReallyModified())
8229 return true;
8230
8231 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
8232 if (mSerialPorts [slot] && mSerialPorts [slot]->isReallyModified())
8233 return true;
8234
8235 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
8236 if (mParallelPorts [slot] && mParallelPorts [slot]->isReallyModified())
8237 return true;
8238
8239 if (!mStorageControllers.isBackedUp())
8240 {
8241 /* see whether any of the devices has changed its data */
8242 for (StorageControllerList::const_iterator
8243 it = mStorageControllers->begin();
8244 it != mStorageControllers->end();
8245 ++it)
8246 {
8247 if ((*it)->isReallyModified())
8248 return true;
8249 }
8250 }
8251 else
8252 {
8253 if (mStorageControllers->size() != mStorageControllers.backedUpData()->size())
8254 return true;
8255 }
8256
8257 return
8258 (!aIgnoreUserData && mUserData.hasActualChanges()) ||
8259 mHWData.hasActualChanges() ||
8260 mMediaData.hasActualChanges() ||
8261 mStorageControllers.hasActualChanges() ||
8262#ifdef VBOX_WITH_VRDP
8263 (mVRDPServer && mVRDPServer->isReallyModified()) ||
8264#endif
8265 (mAudioAdapter && mAudioAdapter->isReallyModified()) ||
8266 (mUSBController && mUSBController->isReallyModified()) ||
8267 (mBIOSSettings && mBIOSSettings->isReallyModified());
8268}
8269
8270/**
8271 * Discards all changes to machine settings.
8272 *
8273 * @param aNotify Whether to notify the direct session about changes or not.
8274 *
8275 * @note Locks objects for writing!
8276 */
8277void Machine::rollback (bool aNotify)
8278{
8279 AutoCaller autoCaller(this);
8280 AssertComRCReturn (autoCaller.rc(), (void) 0);
8281
8282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8283
8284 /* check for changes in own data */
8285
8286 bool sharedFoldersChanged = false, storageChanged = false;
8287
8288 if (aNotify && mHWData.isBackedUp())
8289 {
8290 if (mHWData->mSharedFolders.size() !=
8291 mHWData.backedUpData()->mSharedFolders.size())
8292 sharedFoldersChanged = true;
8293 else
8294 {
8295 for (HWData::SharedFolderList::iterator rit =
8296 mHWData->mSharedFolders.begin();
8297 rit != mHWData->mSharedFolders.end() && !sharedFoldersChanged;
8298 ++rit)
8299 {
8300 for (HWData::SharedFolderList::iterator cit =
8301 mHWData.backedUpData()->mSharedFolders.begin();
8302 cit != mHWData.backedUpData()->mSharedFolders.end();
8303 ++cit)
8304 {
8305 if ((*cit)->getName() != (*rit)->getName() ||
8306 (*cit)->getHostPath() != (*rit)->getHostPath())
8307 {
8308 sharedFoldersChanged = true;
8309 break;
8310 }
8311 }
8312 }
8313 }
8314 }
8315
8316 if (!mStorageControllers.isNull())
8317 {
8318 if (mStorageControllers.isBackedUp())
8319 {
8320 /* unitialize all new devices (absent in the backed up list). */
8321 StorageControllerList::const_iterator it = mStorageControllers->begin();
8322 StorageControllerList *backedList = mStorageControllers.backedUpData();
8323 while (it != mStorageControllers->end())
8324 {
8325 if (std::find (backedList->begin(), backedList->end(), *it ) ==
8326 backedList->end())
8327 {
8328 (*it)->uninit();
8329 }
8330 ++it;
8331 }
8332
8333 /* restore the list */
8334 mStorageControllers.rollback();
8335 }
8336
8337 /* rollback any changes to devices after restoring the list */
8338 StorageControllerList::const_iterator it = mStorageControllers->begin();
8339 while (it != mStorageControllers->end())
8340 {
8341 if ((*it)->isModified())
8342 (*it)->rollback();
8343
8344 ++it;
8345 }
8346 }
8347
8348 mUserData.rollback();
8349
8350 mHWData.rollback();
8351
8352 if (mMediaData.isBackedUp())
8353 rollbackMedia();
8354
8355 /* check for changes in child objects */
8356
8357 bool vrdpChanged = false, usbChanged = false;
8358
8359 ComPtr<INetworkAdapter> networkAdapters [RT_ELEMENTS (mNetworkAdapters)];
8360 ComPtr<ISerialPort> serialPorts [RT_ELEMENTS (mSerialPorts)];
8361 ComPtr<IParallelPort> parallelPorts [RT_ELEMENTS (mParallelPorts)];
8362
8363 if (mBIOSSettings)
8364 mBIOSSettings->rollback();
8365
8366#ifdef VBOX_WITH_VRDP
8367 if (mVRDPServer)
8368 vrdpChanged = mVRDPServer->rollback();
8369#endif
8370
8371 if (mAudioAdapter)
8372 mAudioAdapter->rollback();
8373
8374 if (mUSBController)
8375 usbChanged = mUSBController->rollback();
8376
8377 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
8378 if (mNetworkAdapters [slot])
8379 if (mNetworkAdapters [slot]->rollback())
8380 networkAdapters [slot] = mNetworkAdapters [slot];
8381
8382 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
8383 if (mSerialPorts [slot])
8384 if (mSerialPorts [slot]->rollback())
8385 serialPorts [slot] = mSerialPorts [slot];
8386
8387 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
8388 if (mParallelPorts [slot])
8389 if (mParallelPorts [slot]->rollback())
8390 parallelPorts [slot] = mParallelPorts [slot];
8391
8392 if (aNotify)
8393 {
8394 /* inform the direct session about changes */
8395
8396 ComObjPtr<Machine> that = this;
8397 alock.leave();
8398
8399 if (sharedFoldersChanged)
8400 that->onSharedFolderChange();
8401
8402 if (vrdpChanged)
8403 that->onVRDPServerChange();
8404 if (usbChanged)
8405 that->onUSBControllerChange();
8406
8407 for (ULONG slot = 0; slot < RT_ELEMENTS (networkAdapters); slot ++)
8408 if (networkAdapters [slot])
8409 that->onNetworkAdapterChange (networkAdapters [slot], FALSE);
8410 for (ULONG slot = 0; slot < RT_ELEMENTS (serialPorts); slot ++)
8411 if (serialPorts [slot])
8412 that->onSerialPortChange (serialPorts [slot]);
8413 for (ULONG slot = 0; slot < RT_ELEMENTS (parallelPorts); slot ++)
8414 if (parallelPorts [slot])
8415 that->onParallelPortChange (parallelPorts [slot]);
8416
8417 if (storageChanged)
8418 that->onStorageControllerChange();
8419 }
8420}
8421
8422/**
8423 * Commits all the changes to machine settings.
8424 *
8425 * Note that this operation is supposed to never fail.
8426 *
8427 * @note Locks this object and children for writing.
8428 */
8429void Machine::commit()
8430{
8431 AutoCaller autoCaller(this);
8432 AssertComRCReturnVoid (autoCaller.rc());
8433
8434 AutoCaller peerCaller (mPeer);
8435 AssertComRCReturnVoid (peerCaller.rc());
8436
8437 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
8438
8439 /*
8440 * use safe commit to ensure Snapshot machines (that share mUserData)
8441 * will still refer to a valid memory location
8442 */
8443 mUserData.commitCopy();
8444
8445 mHWData.commit();
8446
8447 if (mMediaData.isBackedUp())
8448 commitMedia();
8449
8450 mBIOSSettings->commit();
8451#ifdef VBOX_WITH_VRDP
8452 mVRDPServer->commit();
8453#endif
8454 mAudioAdapter->commit();
8455 mUSBController->commit();
8456
8457 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
8458 mNetworkAdapters [slot]->commit();
8459 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
8460 mSerialPorts [slot]->commit();
8461 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
8462 mParallelPorts [slot]->commit();
8463
8464 bool commitStorageControllers = false;
8465
8466 if (mStorageControllers.isBackedUp())
8467 {
8468 mStorageControllers.commit();
8469
8470 if (mPeer)
8471 {
8472 AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);
8473
8474 /* Commit all changes to new controllers (this will reshare data with
8475 * peers for thos who have peers) */
8476 StorageControllerList *newList = new StorageControllerList();
8477 StorageControllerList::const_iterator it = mStorageControllers->begin();
8478 while (it != mStorageControllers->end())
8479 {
8480 (*it)->commit();
8481
8482 /* look if this controller has a peer device */
8483 ComObjPtr<StorageController> peer = (*it)->getPeer();
8484 if (!peer)
8485 {
8486 /* no peer means the device is a newly created one;
8487 * create a peer owning data this device share it with */
8488 peer.createObject();
8489 peer->init (mPeer, *it, true /* aReshare */);
8490 }
8491 else
8492 {
8493 /* remove peer from the old list */
8494 mPeer->mStorageControllers->remove (peer);
8495 }
8496 /* and add it to the new list */
8497 newList->push_back(peer);
8498
8499 ++it;
8500 }
8501
8502 /* uninit old peer's controllers that are left */
8503 it = mPeer->mStorageControllers->begin();
8504 while (it != mPeer->mStorageControllers->end())
8505 {
8506 (*it)->uninit();
8507 ++it;
8508 }
8509
8510 /* attach new list of controllers to our peer */
8511 mPeer->mStorageControllers.attach (newList);
8512 }
8513 else
8514 {
8515 /* we have no peer (our parent is the newly created machine);
8516 * just commit changes to devices */
8517 commitStorageControllers = true;
8518 }
8519 }
8520 else
8521 {
8522 /* the list of controllers itself is not changed,
8523 * just commit changes to controllers themselves */
8524 commitStorageControllers = true;
8525 }
8526
8527 if (commitStorageControllers)
8528 {
8529 StorageControllerList::const_iterator it = mStorageControllers->begin();
8530 while (it != mStorageControllers->end())
8531 {
8532 (*it)->commit();
8533 ++it;
8534 }
8535 }
8536
8537 if (getClassID() == clsidSessionMachine)
8538 {
8539 /* attach new data to the primary machine and reshare it */
8540 mPeer->mUserData.attach (mUserData);
8541 mPeer->mHWData.attach (mHWData);
8542 /* mMediaData is reshared by fixupMedia */
8543 // mPeer->mMediaData.attach(mMediaData);
8544 Assert(mPeer->mMediaData.data() == mMediaData.data());
8545 }
8546}
8547
8548/**
8549 * Copies all the hardware data from the given machine.
8550 *
8551 * Currently, only called when the VM is being restored from a snapshot. In
8552 * particular, this implies that the VM is not running during this method's
8553 * call.
8554 *
8555 * @note This method must be called from under this object's lock.
8556 *
8557 * @note This method doesn't call #commit(), so all data remains backed up and
8558 * unsaved.
8559 */
8560void Machine::copyFrom(Machine *aThat)
8561{
8562 AssertReturnVoid(getClassID() == clsidMachine || getClassID() == clsidSessionMachine);
8563 AssertReturnVoid(aThat->getClassID() == clsidSnapshotMachine);
8564
8565 AssertReturnVoid(!Global::IsOnline (mData->mMachineState));
8566
8567 mHWData.assignCopy(aThat->mHWData);
8568
8569 // create copies of all shared folders (mHWData after attiching a copy
8570 // contains just references to original objects)
8571 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
8572 it != mHWData->mSharedFolders.end();
8573 ++it)
8574 {
8575 ComObjPtr<SharedFolder> folder;
8576 folder.createObject();
8577 HRESULT rc = folder->initCopy(getMachine(), *it);
8578 AssertComRC (rc);
8579 *it = folder;
8580 }
8581
8582 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
8583#ifdef VBOX_WITH_VRDP
8584 mVRDPServer->copyFrom(aThat->mVRDPServer);
8585#endif
8586 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
8587 mUSBController->copyFrom(aThat->mUSBController);
8588
8589 /* create private copies of all controllers */
8590 mStorageControllers.backup();
8591 mStorageControllers->clear();
8592 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
8593 it != aThat->mStorageControllers->end();
8594 ++it)
8595 {
8596 ComObjPtr<StorageController> ctrl;
8597 ctrl.createObject();
8598 ctrl->initCopy (this, *it);
8599 mStorageControllers->push_back(ctrl);
8600 }
8601
8602 for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
8603 mNetworkAdapters [slot]->copyFrom (aThat->mNetworkAdapters [slot]);
8604 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
8605 mSerialPorts [slot]->copyFrom (aThat->mSerialPorts [slot]);
8606 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
8607 mParallelPorts [slot]->copyFrom (aThat->mParallelPorts [slot]);
8608}
8609
8610#ifdef VBOX_WITH_RESOURCE_USAGE_API
8611void Machine::registerMetrics (PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
8612{
8613 pm::CollectorHAL *hal = aCollector->getHAL();
8614 /* Create sub metrics */
8615 pm::SubMetric *cpuLoadUser = new pm::SubMetric ("CPU/Load/User",
8616 "Percentage of processor time spent in user mode by VM process.");
8617 pm::SubMetric *cpuLoadKernel = new pm::SubMetric ("CPU/Load/Kernel",
8618 "Percentage of processor time spent in kernel mode by VM process.");
8619 pm::SubMetric *ramUsageUsed = new pm::SubMetric ("RAM/Usage/Used",
8620 "Size of resident portion of VM process in memory.");
8621 /* Create and register base metrics */
8622 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw (hal, aMachine, pid,
8623 cpuLoadUser, cpuLoadKernel);
8624 aCollector->registerBaseMetric (cpuLoad);
8625 pm::BaseMetric *ramUsage = new pm::MachineRamUsage (hal, aMachine, pid,
8626 ramUsageUsed);
8627 aCollector->registerBaseMetric (ramUsage);
8628
8629 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser, 0));
8630 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
8631 new pm::AggregateAvg()));
8632 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
8633 new pm::AggregateMin()));
8634 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadUser,
8635 new pm::AggregateMax()));
8636 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel, 0));
8637 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
8638 new pm::AggregateAvg()));
8639 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
8640 new pm::AggregateMin()));
8641 aCollector->registerMetric (new pm::Metric (cpuLoad, cpuLoadKernel,
8642 new pm::AggregateMax()));
8643
8644 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed, 0));
8645 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
8646 new pm::AggregateAvg()));
8647 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
8648 new pm::AggregateMin()));
8649 aCollector->registerMetric (new pm::Metric (ramUsage, ramUsageUsed,
8650 new pm::AggregateMax()));
8651};
8652
8653void Machine::unregisterMetrics (PerformanceCollector *aCollector, Machine *aMachine)
8654{
8655 aCollector->unregisterMetricsFor (aMachine);
8656 aCollector->unregisterBaseMetricsFor (aMachine);
8657};
8658#endif /* VBOX_WITH_RESOURCE_USAGE_API */
8659
8660
8661////////////////////////////////////////////////////////////////////////////////
8662
8663DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
8664
8665HRESULT SessionMachine::FinalConstruct()
8666{
8667 LogFlowThisFunc(("\n"));
8668
8669#if defined(RT_OS_WINDOWS)
8670 mIPCSem = NULL;
8671#elif defined(RT_OS_OS2)
8672 mIPCSem = NULLHANDLE;
8673#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8674 mIPCSem = -1;
8675#else
8676# error "Port me!"
8677#endif
8678
8679 return S_OK;
8680}
8681
8682void SessionMachine::FinalRelease()
8683{
8684 LogFlowThisFunc(("\n"));
8685
8686 uninit (Uninit::Unexpected);
8687}
8688
8689/**
8690 * @note Must be called only by Machine::openSession() from its own write lock.
8691 */
8692HRESULT SessionMachine::init (Machine *aMachine)
8693{
8694 LogFlowThisFuncEnter();
8695 LogFlowThisFunc(("mName={%ls}\n", aMachine->mUserData->mName.raw()));
8696
8697 AssertReturn(aMachine, E_INVALIDARG);
8698
8699 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
8700
8701 /* Enclose the state transition NotReady->InInit->Ready */
8702 AutoInitSpan autoInitSpan(this);
8703 AssertReturn(autoInitSpan.isOk(), E_FAIL);
8704
8705 /* create the interprocess semaphore */
8706#if defined(RT_OS_WINDOWS)
8707 mIPCSemName = aMachine->mData->m_strConfigFileFull;
8708 for (size_t i = 0; i < mIPCSemName.length(); i++)
8709 if (mIPCSemName[i] == '\\')
8710 mIPCSemName[i] = '/';
8711 mIPCSem = ::CreateMutex (NULL, FALSE, mIPCSemName);
8712 ComAssertMsgRet (mIPCSem,
8713 ("Cannot create IPC mutex '%ls', err=%d",
8714 mIPCSemName.raw(), ::GetLastError()),
8715 E_FAIL);
8716#elif defined(RT_OS_OS2)
8717 Utf8Str ipcSem = Utf8StrFmt ("\\SEM32\\VBOX\\VM\\{%RTuuid}",
8718 aMachine->mData->mUuid.raw());
8719 mIPCSemName = ipcSem;
8720 APIRET arc = ::DosCreateMutexSem ((PSZ) ipcSem.raw(), &mIPCSem, 0, FALSE);
8721 ComAssertMsgRet (arc == NO_ERROR,
8722 ("Cannot create IPC mutex '%s', arc=%ld",
8723 ipcSem.raw(), arc),
8724 E_FAIL);
8725#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8726# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8727# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
8728 /** @todo Check that this still works correctly. */
8729 AssertCompileSize(key_t, 8);
8730# else
8731 AssertCompileSize(key_t, 4);
8732# endif
8733 key_t key;
8734 mIPCSem = -1;
8735 mIPCKey = "0";
8736 for (uint32_t i = 0; i < 1 << 24; i++)
8737 {
8738 key = ((uint32_t)'V' << 24) | i;
8739 int sem = ::semget (key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
8740 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
8741 {
8742 mIPCSem = sem;
8743 if (sem >= 0)
8744 mIPCKey = BstrFmt ("%u", key);
8745 break;
8746 }
8747 }
8748# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8749 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
8750 char *pszSemName = NULL;
8751 RTStrUtf8ToCurrentCP (&pszSemName, semName);
8752 key_t key = ::ftok (pszSemName, 'V');
8753 RTStrFree (pszSemName);
8754
8755 mIPCSem = ::semget (key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
8756# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8757
8758 int errnoSave = errno;
8759 if (mIPCSem < 0 && errnoSave == ENOSYS)
8760 {
8761 setError(E_FAIL,
8762 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
8763 "support for SysV IPC. Check the host kernel configuration for "
8764 "CONFIG_SYSVIPC=y"));
8765 return E_FAIL;
8766 }
8767 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
8768 * the IPC semaphores */
8769 if (mIPCSem < 0 && errnoSave == ENOSPC)
8770 {
8771#ifdef RT_OS_LINUX
8772 setError(E_FAIL,
8773 tr("Cannot create IPC semaphore because the system limit for the "
8774 "maximum number of semaphore sets (SEMMNI), or the system wide "
8775 "maximum number of sempahores (SEMMNS) would be exceeded. The "
8776 "current set of SysV IPC semaphores can be determined from "
8777 "the file /proc/sysvipc/sem"));
8778#else
8779 setError(E_FAIL,
8780 tr("Cannot create IPC semaphore because the system-imposed limit "
8781 "on the maximum number of allowed semaphores or semaphore "
8782 "identifiers system-wide would be exceeded"));
8783#endif
8784 return E_FAIL;
8785 }
8786 ComAssertMsgRet (mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
8787 E_FAIL);
8788 /* set the initial value to 1 */
8789 int rv = ::semctl (mIPCSem, 0, SETVAL, 1);
8790 ComAssertMsgRet (rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
8791 E_FAIL);
8792#else
8793# error "Port me!"
8794#endif
8795
8796 /* memorize the peer Machine */
8797 unconst(mPeer) = aMachine;
8798 /* share the parent pointer */
8799 unconst(mParent) = aMachine->mParent;
8800
8801 /* take the pointers to data to share */
8802 mData.share (aMachine->mData);
8803 mSSData.share (aMachine->mSSData);
8804
8805 mUserData.share (aMachine->mUserData);
8806 mHWData.share (aMachine->mHWData);
8807 mMediaData.share(aMachine->mMediaData);
8808
8809 mStorageControllers.allocate();
8810 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
8811 it != aMachine->mStorageControllers->end();
8812 ++it)
8813 {
8814 ComObjPtr<StorageController> ctl;
8815 ctl.createObject();
8816 ctl->init(this, *it);
8817 mStorageControllers->push_back (ctl);
8818 }
8819
8820 unconst(mBIOSSettings).createObject();
8821 mBIOSSettings->init (this, aMachine->mBIOSSettings);
8822#ifdef VBOX_WITH_VRDP
8823 /* create another VRDPServer object that will be mutable */
8824 unconst(mVRDPServer).createObject();
8825 mVRDPServer->init (this, aMachine->mVRDPServer);
8826#endif
8827 /* create another audio adapter object that will be mutable */
8828 unconst(mAudioAdapter).createObject();
8829 mAudioAdapter->init (this, aMachine->mAudioAdapter);
8830 /* create a list of serial ports that will be mutable */
8831 for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
8832 {
8833 unconst(mSerialPorts [slot]).createObject();
8834 mSerialPorts [slot]->init (this, aMachine->mSerialPorts [slot]);
8835 }
8836 /* create a list of parallel ports that will be mutable */
8837 for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
8838 {
8839 unconst(mParallelPorts [slot]).createObject();
8840 mParallelPorts [slot]->init (this, aMachine->mParallelPorts [slot]);
8841 }
8842 /* create another USB controller object that will be mutable */
8843 unconst(mUSBController).createObject();
8844 mUSBController->init(this, aMachine->mUSBController);
8845
8846 /* create a list of network adapters that will be mutable */
8847 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
8848 {
8849 unconst(mNetworkAdapters [slot]).createObject();
8850 mNetworkAdapters[slot]->init (this, aMachine->mNetworkAdapters [slot]);
8851 }
8852
8853 /* default is to delete saved state on Saved -> PoweredOff transition */
8854 mRemoveSavedState = true;
8855
8856 /* Confirm a successful initialization when it's the case */
8857 autoInitSpan.setSucceeded();
8858
8859 LogFlowThisFuncLeave();
8860 return S_OK;
8861}
8862
8863/**
8864 * Uninitializes this session object. If the reason is other than
8865 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
8866 *
8867 * @param aReason uninitialization reason
8868 *
8869 * @note Locks mParent + this object for writing.
8870 */
8871void SessionMachine::uninit (Uninit::Reason aReason)
8872{
8873 LogFlowThisFuncEnter();
8874 LogFlowThisFunc(("reason=%d\n", aReason));
8875
8876 /*
8877 * Strongly reference ourselves to prevent this object deletion after
8878 * mData->mSession.mMachine.setNull() below (which can release the last
8879 * reference and call the destructor). Important: this must be done before
8880 * accessing any members (and before AutoUninitSpan that does it as well).
8881 * This self reference will be released as the very last step on return.
8882 */
8883 ComObjPtr<SessionMachine> selfRef = this;
8884
8885 /* Enclose the state transition Ready->InUninit->NotReady */
8886 AutoUninitSpan autoUninitSpan(this);
8887 if (autoUninitSpan.uninitDone())
8888 {
8889 LogFlowThisFunc(("Already uninitialized\n"));
8890 LogFlowThisFuncLeave();
8891 return;
8892 }
8893
8894 if (autoUninitSpan.initFailed())
8895 {
8896 /* We've been called by init() because it's failed. It's not really
8897 * necessary (nor it's safe) to perform the regular uninit sequense
8898 * below, the following is enough.
8899 */
8900 LogFlowThisFunc(("Initialization failed.\n"));
8901#if defined(RT_OS_WINDOWS)
8902 if (mIPCSem)
8903 ::CloseHandle (mIPCSem);
8904 mIPCSem = NULL;
8905#elif defined(RT_OS_OS2)
8906 if (mIPCSem != NULLHANDLE)
8907 ::DosCloseMutexSem (mIPCSem);
8908 mIPCSem = NULLHANDLE;
8909#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8910 if (mIPCSem >= 0)
8911 ::semctl (mIPCSem, 0, IPC_RMID);
8912 mIPCSem = -1;
8913# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8914 mIPCKey = "0";
8915# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
8916#else
8917# error "Port me!"
8918#endif
8919 uninitDataAndChildObjects();
8920 mData.free();
8921 unconst(mParent).setNull();
8922 unconst(mPeer).setNull();
8923 LogFlowThisFuncLeave();
8924 return;
8925 }
8926
8927 /* We need to lock this object in uninit() because the lock is shared
8928 * with mPeer (as well as data we modify below). mParent->addProcessToReap()
8929 * and others need mParent lock. */
8930 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
8931
8932#ifdef VBOX_WITH_RESOURCE_USAGE_API
8933 unregisterMetrics (mParent->performanceCollector(), mPeer);
8934#endif /* VBOX_WITH_RESOURCE_USAGE_API */
8935
8936 MachineState_T lastState = mData->mMachineState;
8937 NOREF(lastState);
8938
8939 if (aReason == Uninit::Abnormal)
8940 {
8941 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
8942 Global::IsOnlineOrTransient (lastState)));
8943
8944 /* reset the state to Aborted */
8945 if (mData->mMachineState != MachineState_Aborted)
8946 setMachineState (MachineState_Aborted);
8947 }
8948
8949 if (isModified())
8950 {
8951 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
8952 rollback (false /* aNotify */);
8953 }
8954
8955 Assert(mSnapshotData.mStateFilePath.isEmpty() || !mSnapshotData.mSnapshot);
8956 if (!mSnapshotData.mStateFilePath.isEmpty())
8957 {
8958 LogWarningThisFunc(("canceling failed save state request!\n"));
8959 endSavingState(FALSE /* aSuccess */);
8960 }
8961 else if (!mSnapshotData.mSnapshot.isNull())
8962 {
8963 LogWarningThisFunc(("canceling untaken snapshot!\n"));
8964
8965 /* delete all differencing hard disks created (this will also attach
8966 * their parents back by rolling back mMediaData) */
8967 rollbackMedia();
8968 /* delete the saved state file (it might have been already created) */
8969 if (mSnapshotData.mSnapshot->stateFilePath().length())
8970 RTFileDelete(mSnapshotData.mSnapshot->stateFilePath().c_str());
8971
8972 mSnapshotData.mSnapshot->uninit();
8973 }
8974
8975#ifdef VBOX_WITH_USB
8976 /* release all captured USB devices */
8977 if (aReason == Uninit::Abnormal && Global::IsOnline (lastState))
8978 {
8979 /* Console::captureUSBDevices() is called in the VM process only after
8980 * setting the machine state to Starting or Restoring.
8981 * Console::detachAllUSBDevices() will be called upon successful
8982 * termination. So, we need to release USB devices only if there was
8983 * an abnormal termination of a running VM.
8984 *
8985 * This is identical to SessionMachine::DetachAllUSBDevices except
8986 * for the aAbnormal argument. */
8987 HRESULT rc = mUSBController->notifyProxy (false /* aInsertFilters */);
8988 AssertComRC(rc);
8989 NOREF (rc);
8990
8991 USBProxyService *service = mParent->host()->usbProxyService();
8992 if (service)
8993 service->detachAllDevicesFromVM (this, true /* aDone */, true /* aAbnormal */);
8994 }
8995#endif /* VBOX_WITH_USB */
8996
8997 if (!mData->mSession.mType.isNull())
8998 {
8999 /* mType is not null when this machine's process has been started by
9000 * VirtualBox::OpenRemoteSession(), therefore it is our child. We
9001 * need to queue the PID to reap the process (and avoid zombies on
9002 * Linux). */
9003 Assert (mData->mSession.mPid != NIL_RTPROCESS);
9004 mParent->addProcessToReap (mData->mSession.mPid);
9005 }
9006
9007 mData->mSession.mPid = NIL_RTPROCESS;
9008
9009 if (aReason == Uninit::Unexpected)
9010 {
9011 /* Uninitialization didn't come from #checkForDeath(), so tell the
9012 * client watcher thread to update the set of machines that have open
9013 * sessions. */
9014 mParent->updateClientWatcher();
9015 }
9016
9017 /* uninitialize all remote controls */
9018 if (mData->mSession.mRemoteControls.size())
9019 {
9020 LogFlowThisFunc(("Closing remote sessions (%d):\n",
9021 mData->mSession.mRemoteControls.size()));
9022
9023 Data::Session::RemoteControlList::iterator it =
9024 mData->mSession.mRemoteControls.begin();
9025 while (it != mData->mSession.mRemoteControls.end())
9026 {
9027 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
9028 HRESULT rc = (*it)->Uninitialize();
9029 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
9030 if (FAILED (rc))
9031 LogWarningThisFunc(("Forgot to close the remote session?\n"));
9032 ++it;
9033 }
9034 mData->mSession.mRemoteControls.clear();
9035 }
9036
9037 /*
9038 * An expected uninitialization can come only from #checkForDeath().
9039 * Otherwise it means that something's got really wrong (for examlple,
9040 * the Session implementation has released the VirtualBox reference
9041 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
9042 * etc). However, it's also possible, that the client releases the IPC
9043 * semaphore correctly (i.e. before it releases the VirtualBox reference),
9044 * but the VirtualBox release event comes first to the server process.
9045 * This case is practically possible, so we should not assert on an
9046 * unexpected uninit, just log a warning.
9047 */
9048
9049 if ((aReason == Uninit::Unexpected))
9050 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
9051
9052 if (aReason != Uninit::Normal)
9053 {
9054 mData->mSession.mDirectControl.setNull();
9055 }
9056 else
9057 {
9058 /* this must be null here (see #OnSessionEnd()) */
9059 Assert (mData->mSession.mDirectControl.isNull());
9060 Assert (mData->mSession.mState == SessionState_Closing);
9061 Assert (!mData->mSession.mProgress.isNull());
9062
9063 mData->mSession.mProgress->notifyComplete (S_OK);
9064 mData->mSession.mProgress.setNull();
9065 }
9066
9067 /* remove the association between the peer machine and this session machine */
9068 Assert (mData->mSession.mMachine == this ||
9069 aReason == Uninit::Unexpected);
9070
9071 /* reset the rest of session data */
9072 mData->mSession.mMachine.setNull();
9073 mData->mSession.mState = SessionState_Closed;
9074 mData->mSession.mType.setNull();
9075
9076 /* close the interprocess semaphore before leaving the exclusive lock */
9077#if defined(RT_OS_WINDOWS)
9078 if (mIPCSem)
9079 ::CloseHandle (mIPCSem);
9080 mIPCSem = NULL;
9081#elif defined(RT_OS_OS2)
9082 if (mIPCSem != NULLHANDLE)
9083 ::DosCloseMutexSem (mIPCSem);
9084 mIPCSem = NULLHANDLE;
9085#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9086 if (mIPCSem >= 0)
9087 ::semctl (mIPCSem, 0, IPC_RMID);
9088 mIPCSem = -1;
9089# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9090 mIPCKey = "0";
9091# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
9092#else
9093# error "Port me!"
9094#endif
9095
9096 /* fire an event */
9097 mParent->onSessionStateChange (mData->mUuid, SessionState_Closed);
9098
9099 uninitDataAndChildObjects();
9100
9101 /* free the essential data structure last */
9102 mData.free();
9103
9104 /* leave the exclusive lock before setting the below two to NULL */
9105 alock.leave();
9106
9107 unconst(mParent).setNull();
9108 unconst(mPeer).setNull();
9109
9110 LogFlowThisFuncLeave();
9111}
9112
9113// util::Lockable interface
9114////////////////////////////////////////////////////////////////////////////////
9115
9116/**
9117 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
9118 * with the primary Machine instance (mPeer).
9119 */
9120RWLockHandle *SessionMachine::lockHandle() const
9121{
9122 AssertReturn(!mPeer.isNull(), NULL);
9123 return mPeer->lockHandle();
9124}
9125
9126// IInternalMachineControl methods
9127////////////////////////////////////////////////////////////////////////////////
9128
9129/**
9130 * @note Locks this object for writing.
9131 */
9132STDMETHODIMP SessionMachine::SetRemoveSavedState(BOOL aRemove)
9133{
9134 AutoCaller autoCaller(this);
9135 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9136
9137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9138
9139 mRemoveSavedState = aRemove;
9140
9141 return S_OK;
9142}
9143
9144/**
9145 * @note Locks the same as #setMachineState() does.
9146 */
9147STDMETHODIMP SessionMachine::UpdateState (MachineState_T aMachineState)
9148{
9149 return setMachineState (aMachineState);
9150}
9151
9152/**
9153 * @note Locks this object for reading.
9154 */
9155STDMETHODIMP SessionMachine::GetIPCId (BSTR *aId)
9156{
9157 AutoCaller autoCaller(this);
9158 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9159
9160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9161
9162#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
9163 mIPCSemName.cloneTo(aId);
9164 return S_OK;
9165#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9166# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9167 mIPCKey.cloneTo(aId);
9168# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
9169 mData->m_strConfigFileFull.cloneTo(aId);
9170# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
9171 return S_OK;
9172#else
9173# error "Port me!"
9174#endif
9175}
9176
9177/**
9178 * Goes through the USB filters of the given machine to see if the given
9179 * device matches any filter or not.
9180 *
9181 * @note Locks the same as USBController::hasMatchingFilter() does.
9182 */
9183STDMETHODIMP SessionMachine::RunUSBDeviceFilters (IUSBDevice *aUSBDevice,
9184 BOOL *aMatched,
9185 ULONG *aMaskedIfs)
9186{
9187 LogFlowThisFunc(("\n"));
9188
9189 CheckComArgNotNull (aUSBDevice);
9190 CheckComArgOutPointerValid(aMatched);
9191
9192 AutoCaller autoCaller(this);
9193 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9194
9195#ifdef VBOX_WITH_USB
9196 *aMatched = mUSBController->hasMatchingFilter (aUSBDevice, aMaskedIfs);
9197#else
9198 NOREF(aUSBDevice);
9199 NOREF(aMaskedIfs);
9200 *aMatched = FALSE;
9201#endif
9202
9203 return S_OK;
9204}
9205
9206/**
9207 * @note Locks the same as Host::captureUSBDevice() does.
9208 */
9209STDMETHODIMP SessionMachine::CaptureUSBDevice (IN_BSTR aId)
9210{
9211 LogFlowThisFunc(("\n"));
9212
9213 AutoCaller autoCaller(this);
9214 AssertComRCReturnRC(autoCaller.rc());
9215
9216#ifdef VBOX_WITH_USB
9217 /* if captureDeviceForVM() fails, it must have set extended error info */
9218 MultiResult rc = mParent->host()->checkUSBProxyService();
9219 if (FAILED(rc)) return rc;
9220
9221 USBProxyService *service = mParent->host()->usbProxyService();
9222 AssertReturn(service, E_FAIL);
9223 return service->captureDeviceForVM (this, Guid(aId));
9224#else
9225 NOREF(aId);
9226 return E_NOTIMPL;
9227#endif
9228}
9229
9230/**
9231 * @note Locks the same as Host::detachUSBDevice() does.
9232 */
9233STDMETHODIMP SessionMachine::DetachUSBDevice (IN_BSTR aId, BOOL aDone)
9234{
9235 LogFlowThisFunc(("\n"));
9236
9237 AutoCaller autoCaller(this);
9238 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9239
9240#ifdef VBOX_WITH_USB
9241 USBProxyService *service = mParent->host()->usbProxyService();
9242 AssertReturn(service, E_FAIL);
9243 return service->detachDeviceFromVM (this, Guid(aId), !!aDone);
9244#else
9245 NOREF(aId);
9246 NOREF(aDone);
9247 return E_NOTIMPL;
9248#endif
9249}
9250
9251/**
9252 * Inserts all machine filters to the USB proxy service and then calls
9253 * Host::autoCaptureUSBDevices().
9254 *
9255 * Called by Console from the VM process upon VM startup.
9256 *
9257 * @note Locks what called methods lock.
9258 */
9259STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
9260{
9261 LogFlowThisFunc(("\n"));
9262
9263 AutoCaller autoCaller(this);
9264 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9265
9266#ifdef VBOX_WITH_USB
9267 HRESULT rc = mUSBController->notifyProxy (true /* aInsertFilters */);
9268 AssertComRC(rc);
9269 NOREF (rc);
9270
9271 USBProxyService *service = mParent->host()->usbProxyService();
9272 AssertReturn(service, E_FAIL);
9273 return service->autoCaptureDevicesForVM (this);
9274#else
9275 return S_OK;
9276#endif
9277}
9278
9279/**
9280 * Removes all machine filters from the USB proxy service and then calls
9281 * Host::detachAllUSBDevices().
9282 *
9283 * Called by Console from the VM process upon normal VM termination or by
9284 * SessionMachine::uninit() upon abnormal VM termination (from under the
9285 * Machine/SessionMachine lock).
9286 *
9287 * @note Locks what called methods lock.
9288 */
9289STDMETHODIMP SessionMachine::DetachAllUSBDevices (BOOL aDone)
9290{
9291 LogFlowThisFunc(("\n"));
9292
9293 AutoCaller autoCaller(this);
9294 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9295
9296#ifdef VBOX_WITH_USB
9297 HRESULT rc = mUSBController->notifyProxy (false /* aInsertFilters */);
9298 AssertComRC(rc);
9299 NOREF (rc);
9300
9301 USBProxyService *service = mParent->host()->usbProxyService();
9302 AssertReturn(service, E_FAIL);
9303 return service->detachAllDevicesFromVM (this, !!aDone, false /* aAbnormal */);
9304#else
9305 NOREF(aDone);
9306 return S_OK;
9307#endif
9308}
9309
9310/**
9311 * @note Locks this object for writing.
9312 */
9313STDMETHODIMP SessionMachine::OnSessionEnd (ISession *aSession,
9314 IProgress **aProgress)
9315{
9316 LogFlowThisFuncEnter();
9317
9318 AssertReturn(aSession, E_INVALIDARG);
9319 AssertReturn(aProgress, E_INVALIDARG);
9320
9321 AutoCaller autoCaller(this);
9322
9323 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
9324 /*
9325 * We don't assert below because it might happen that a non-direct session
9326 * informs us it is closed right after we've been uninitialized -- it's ok.
9327 */
9328 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9329
9330 /* get IInternalSessionControl interface */
9331 ComPtr<IInternalSessionControl> control (aSession);
9332
9333 ComAssertRet (!control.isNull(), E_INVALIDARG);
9334
9335 /* Creating a Progress object requires the VirtualBox lock, and
9336 * thus locking it here is required by the lock order rules. */
9337 AutoMultiWriteLock2 alock(mParent->lockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
9338
9339 if (control.equalsTo(mData->mSession.mDirectControl))
9340 {
9341 ComAssertRet (aProgress, E_POINTER);
9342
9343 /* The direct session is being normally closed by the client process
9344 * ----------------------------------------------------------------- */
9345
9346 /* go to the closing state (essential for all open*Session() calls and
9347 * for #checkForDeath()) */
9348 Assert (mData->mSession.mState == SessionState_Open);
9349 mData->mSession.mState = SessionState_Closing;
9350
9351 /* set direct control to NULL to release the remote instance */
9352 mData->mSession.mDirectControl.setNull();
9353 LogFlowThisFunc(("Direct control is set to NULL\n"));
9354
9355 /* Create the progress object the client will use to wait until
9356 * #checkForDeath() is called to uninitialize this session object after
9357 * it releases the IPC semaphore. */
9358 ComObjPtr<Progress> progress;
9359 progress.createObject();
9360 progress->init (mParent, static_cast <IMachine *> (mPeer),
9361 Bstr (tr ("Closing session")), FALSE /* aCancelable */);
9362 progress.queryInterfaceTo(aProgress);
9363 mData->mSession.mProgress = progress;
9364 }
9365 else
9366 {
9367 /* the remote session is being normally closed */
9368 Data::Session::RemoteControlList::iterator it =
9369 mData->mSession.mRemoteControls.begin();
9370 while (it != mData->mSession.mRemoteControls.end())
9371 {
9372 if (control.equalsTo (*it))
9373 break;
9374 ++it;
9375 }
9376 BOOL found = it != mData->mSession.mRemoteControls.end();
9377 ComAssertMsgRet (found, ("The session is not found in the session list!"),
9378 E_INVALIDARG);
9379 mData->mSession.mRemoteControls.remove (*it);
9380 }
9381
9382 LogFlowThisFuncLeave();
9383 return S_OK;
9384}
9385
9386/**
9387 * @note Locks this object for writing.
9388 */
9389STDMETHODIMP SessionMachine::BeginSavingState (IProgress *aProgress, BSTR *aStateFilePath)
9390{
9391 LogFlowThisFuncEnter();
9392
9393 AssertReturn(aProgress, E_INVALIDARG);
9394 AssertReturn(aStateFilePath, E_POINTER);
9395
9396 AutoCaller autoCaller(this);
9397 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9398
9399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9400
9401 AssertReturn( mData->mMachineState == MachineState_Paused
9402 && mSnapshotData.mLastState == MachineState_Null
9403 && mSnapshotData.mProgressId.isEmpty()
9404 && mSnapshotData.mStateFilePath.isEmpty(),
9405 E_FAIL);
9406
9407 /* memorize the progress ID and add it to the global collection */
9408 Bstr progressId;
9409 HRESULT rc = aProgress->COMGETTER(Id) (progressId.asOutParam());
9410 AssertComRCReturn (rc, rc);
9411 rc = mParent->addProgress (aProgress);
9412 AssertComRCReturn (rc, rc);
9413
9414 Bstr stateFilePath;
9415 /* stateFilePath is null when the machine is not running */
9416 if (mData->mMachineState == MachineState_Paused)
9417 {
9418 stateFilePath = Utf8StrFmt ("%ls%c{%RTuuid}.sav",
9419 mUserData->mSnapshotFolderFull.raw(),
9420 RTPATH_DELIMITER, mData->mUuid.raw());
9421 }
9422
9423 /* fill in the snapshot data */
9424 mSnapshotData.mLastState = mData->mMachineState;
9425 mSnapshotData.mProgressId = Guid(progressId);
9426 mSnapshotData.mStateFilePath = stateFilePath;
9427
9428 /* set the state to Saving (this is expected by Console::SaveState()) */
9429 setMachineState (MachineState_Saving);
9430
9431 stateFilePath.cloneTo(aStateFilePath);
9432
9433 return S_OK;
9434}
9435
9436/**
9437 * @note Locks mParent + this object for writing.
9438 */
9439STDMETHODIMP SessionMachine::EndSavingState (BOOL aSuccess)
9440{
9441 LogFlowThisFunc(("\n"));
9442
9443 AutoCaller autoCaller(this);
9444 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9445
9446 /* endSavingState() need mParent lock */
9447 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
9448
9449 AssertReturn( mData->mMachineState == MachineState_Saving
9450 && mSnapshotData.mLastState != MachineState_Null
9451 && !mSnapshotData.mProgressId.isEmpty()
9452 && !mSnapshotData.mStateFilePath.isEmpty(),
9453 E_FAIL);
9454
9455 /*
9456 * on success, set the state to Saved;
9457 * on failure, set the state to the state we had when BeginSavingState() was
9458 * called (this is expected by Console::SaveState() and
9459 * Console::saveStateThread())
9460 */
9461 if (aSuccess)
9462 setMachineState (MachineState_Saved);
9463 else
9464 setMachineState (mSnapshotData.mLastState);
9465
9466 return endSavingState (aSuccess);
9467}
9468
9469/**
9470 * @note Locks this object for writing.
9471 */
9472STDMETHODIMP SessionMachine::AdoptSavedState (IN_BSTR aSavedStateFile)
9473{
9474 LogFlowThisFunc(("\n"));
9475
9476 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
9477
9478 AutoCaller autoCaller(this);
9479 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9480
9481 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9482
9483 AssertReturn( mData->mMachineState == MachineState_PoweredOff
9484 || mData->mMachineState == MachineState_Teleported
9485 || mData->mMachineState == MachineState_Aborted
9486 , E_FAIL); /** @todo setError. */
9487
9488 Utf8Str stateFilePathFull = aSavedStateFile;
9489 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
9490 if (RT_FAILURE(vrc))
9491 return setError(VBOX_E_FILE_ERROR,
9492 tr("Invalid saved state file path '%ls' (%Rrc)"),
9493 aSavedStateFile,
9494 vrc);
9495
9496 mSSData->mStateFilePath = stateFilePathFull;
9497
9498 /* The below setMachineState() will detect the state transition and will
9499 * update the settings file */
9500
9501 return setMachineState (MachineState_Saved);
9502}
9503
9504STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
9505 ComSafeArrayOut(BSTR, aValues),
9506 ComSafeArrayOut(ULONG64, aTimestamps),
9507 ComSafeArrayOut(BSTR, aFlags))
9508{
9509 LogFlowThisFunc(("\n"));
9510
9511#ifdef VBOX_WITH_GUEST_PROPS
9512 using namespace guestProp;
9513
9514 AutoCaller autoCaller(this);
9515 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9516
9517 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9518
9519 AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
9520 AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
9521 AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
9522 AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
9523
9524 size_t cEntries = mHWData->mGuestProperties.size();
9525 com::SafeArray<BSTR> names (cEntries);
9526 com::SafeArray<BSTR> values (cEntries);
9527 com::SafeArray<ULONG64> timestamps (cEntries);
9528 com::SafeArray<BSTR> flags (cEntries);
9529 unsigned i = 0;
9530 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
9531 it != mHWData->mGuestProperties.end();
9532 ++it)
9533 {
9534 char szFlags[MAX_FLAGS_LEN + 1];
9535 it->strName.cloneTo(&names[i]);
9536 it->strValue.cloneTo(&values[i]);
9537 timestamps[i] = it->mTimestamp;
9538 /* If it is NULL, keep it NULL. */
9539 if (it->mFlags)
9540 {
9541 writeFlags(it->mFlags, szFlags);
9542 Bstr(szFlags).cloneTo(&flags[i]);
9543 }
9544 else
9545 flags[i] = NULL;
9546 ++i;
9547 }
9548 names.detachTo(ComSafeArrayOutArg(aNames));
9549 values.detachTo(ComSafeArrayOutArg(aValues));
9550 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
9551 flags.detachTo(ComSafeArrayOutArg(aFlags));
9552 mHWData->mPropertyServiceActive = true;
9553 return S_OK;
9554#else
9555 ReturnComNotImplemented();
9556#endif
9557}
9558
9559STDMETHODIMP SessionMachine::PushGuestProperties(ComSafeArrayIn(IN_BSTR, aNames),
9560 ComSafeArrayIn(IN_BSTR, aValues),
9561 ComSafeArrayIn(ULONG64, aTimestamps),
9562 ComSafeArrayIn(IN_BSTR, aFlags))
9563{
9564 LogFlowThisFunc(("\n"));
9565
9566#ifdef VBOX_WITH_GUEST_PROPS
9567 using namespace guestProp;
9568
9569 AssertReturn(!ComSafeArrayInIsNull(aNames), E_POINTER);
9570 AssertReturn(!ComSafeArrayInIsNull(aValues), E_POINTER);
9571 AssertReturn(!ComSafeArrayInIsNull(aTimestamps), E_POINTER);
9572 AssertReturn(!ComSafeArrayInIsNull(aFlags), E_POINTER);
9573
9574 AutoCaller autoCaller(this);
9575 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9576
9577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9578
9579 /*
9580 * Temporarily reset the registered flag, so that our machine state
9581 * changes (i.e. mHWData.backup()) succeed. (isMutable() used in all
9582 * setters will return FALSE for a Machine instance if mRegistered is TRUE).
9583 *
9584 * This is copied from registeredInit(), and may or may not be the right
9585 * way to handle this.
9586 */
9587 Assert(mData->mRegistered);
9588 mData->mRegistered = FALSE;
9589
9590 HRESULT rc = checkStateDependency(MutableStateDep);
9591 AssertLogRelMsgReturn(SUCCEEDED(rc), ("%Rhrc\n", rc), rc);
9592
9593 com::SafeArray<IN_BSTR> names( ComSafeArrayInArg(aNames));
9594 com::SafeArray<IN_BSTR> values( ComSafeArrayInArg(aValues));
9595 com::SafeArray<ULONG64> timestamps(ComSafeArrayInArg(aTimestamps));
9596 com::SafeArray<IN_BSTR> flags( ComSafeArrayInArg(aFlags));
9597
9598 DiscardSettings();
9599 mHWData.backup();
9600
9601 mHWData->mGuestProperties.erase(mHWData->mGuestProperties.begin(),
9602 mHWData->mGuestProperties.end());
9603 for (unsigned i = 0; i < names.size(); ++i)
9604 {
9605 uint32_t fFlags = NILFLAG;
9606 validateFlags(Utf8Str(flags[i]).raw(), &fFlags);
9607 HWData::GuestProperty property = { names[i], values[i], timestamps[i], fFlags };
9608 mHWData->mGuestProperties.push_back(property);
9609 }
9610
9611 mHWData->mPropertyServiceActive = false;
9612
9613 alock.release();
9614 SaveSettings();
9615
9616 /* Restore the mRegistered flag. */
9617 alock.acquire();
9618 mData->mRegistered = TRUE;
9619
9620 return S_OK;
9621#else
9622 ReturnComNotImplemented();
9623#endif
9624}
9625
9626STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
9627 IN_BSTR aValue,
9628 ULONG64 aTimestamp,
9629 IN_BSTR aFlags)
9630{
9631 LogFlowThisFunc(("\n"));
9632
9633#ifdef VBOX_WITH_GUEST_PROPS
9634 using namespace guestProp;
9635
9636 CheckComArgNotNull(aName);
9637 if (aValue != NULL && (!VALID_PTR(aValue) || !VALID_PTR(aFlags)))
9638 return E_POINTER; /* aValue can be NULL to indicate deletion */
9639
9640 try
9641 {
9642 /*
9643 * Convert input up front.
9644 */
9645 Utf8Str utf8Name(aName);
9646 uint32_t fFlags = NILFLAG;
9647 if (aFlags)
9648 {
9649 Utf8Str utf8Flags(aFlags);
9650 int vrc = validateFlags(utf8Flags.raw(), &fFlags);
9651 AssertRCReturn(vrc, E_INVALIDARG);
9652 }
9653
9654 /*
9655 * Now grab the object lock, validate the state and do the update.
9656 */
9657 AutoCaller autoCaller(this);
9658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9659
9660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9661
9662 AssertReturn(mHWData->mPropertyServiceActive, VBOX_E_INVALID_OBJECT_STATE);
9663 switch (mData->mMachineState)
9664 {
9665 case MachineState_Paused:
9666 case MachineState_Running:
9667 case MachineState_Teleporting:
9668 case MachineState_TeleportingPausedVM:
9669 case MachineState_LiveSnapshotting:
9670 case MachineState_Saving:
9671 break;
9672
9673 default:
9674 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
9675 VBOX_E_INVALID_VM_STATE);
9676 }
9677
9678 mHWData.backup();
9679
9680 /** @todo r=bird: The careful memory handling doesn't work out here because
9681 * the catch block won't undo any damange we've done. So, if push_back throws
9682 * bad_alloc then you've lost the value.
9683 *
9684 * Another thing. Doing a linear search here isn't extremely efficient, esp.
9685 * since values that changes actually bubbles to the end of the list. Using
9686 * something that has an efficient lookup and can tollerate a bit of updates
9687 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
9688 * combination of RTStrCache (for sharing names and getting uniqueness into
9689 * the bargain) and hash/tree is another. */
9690 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
9691 iter != mHWData->mGuestProperties.end();
9692 ++iter)
9693 if (utf8Name == iter->strName)
9694 {
9695 mHWData->mGuestProperties.erase(iter);
9696 break;
9697 }
9698 if (aValue != NULL)
9699 {
9700 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
9701 mHWData->mGuestProperties.push_back(property);
9702 }
9703
9704 /*
9705 * Send a callback notification if appropriate
9706 */
9707 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
9708 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.raw(),
9709 RTSTR_MAX,
9710 utf8Name.raw(),
9711 RTSTR_MAX, NULL)
9712 )
9713 {
9714 alock.leave();
9715
9716 mParent->onGuestPropertyChange(mData->mUuid,
9717 aName,
9718 aValue,
9719 aFlags);
9720 }
9721 }
9722 catch (...)
9723 {
9724 return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
9725 }
9726 return S_OK;
9727#else
9728 ReturnComNotImplemented();
9729#endif
9730}
9731
9732// public methods only for internal purposes
9733/////////////////////////////////////////////////////////////////////////////
9734
9735/**
9736 * Called from the client watcher thread to check for expected or unexpected
9737 * death of the client process that has a direct session to this machine.
9738 *
9739 * On Win32 and on OS/2, this method is called only when we've got the
9740 * mutex (i.e. the client has either died or terminated normally) so it always
9741 * returns @c true (the client is terminated, the session machine is
9742 * uninitialized).
9743 *
9744 * On other platforms, the method returns @c true if the client process has
9745 * terminated normally or abnormally and the session machine was uninitialized,
9746 * and @c false if the client process is still alive.
9747 *
9748 * @note Locks this object for writing.
9749 */
9750bool SessionMachine::checkForDeath()
9751{
9752 Uninit::Reason reason;
9753 bool terminated = false;
9754
9755 /* Enclose autoCaller with a block because calling uninit() from under it
9756 * will deadlock. */
9757 {
9758 AutoCaller autoCaller(this);
9759 if (!autoCaller.isOk())
9760 {
9761 /* return true if not ready, to cause the client watcher to exclude
9762 * the corresponding session from watching */
9763 LogFlowThisFunc(("Already uninitialized!\n"));
9764 return true;
9765 }
9766
9767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9768
9769 /* Determine the reason of death: if the session state is Closing here,
9770 * everything is fine. Otherwise it means that the client did not call
9771 * OnSessionEnd() before it released the IPC semaphore. This may happen
9772 * either because the client process has abnormally terminated, or
9773 * because it simply forgot to call ISession::Close() before exiting. We
9774 * threat the latter also as an abnormal termination (see
9775 * Session::uninit() for details). */
9776 reason = mData->mSession.mState == SessionState_Closing ?
9777 Uninit::Normal :
9778 Uninit::Abnormal;
9779
9780#if defined(RT_OS_WINDOWS)
9781
9782 AssertMsg (mIPCSem, ("semaphore must be created"));
9783
9784 /* release the IPC mutex */
9785 ::ReleaseMutex (mIPCSem);
9786
9787 terminated = true;
9788
9789#elif defined(RT_OS_OS2)
9790
9791 AssertMsg (mIPCSem, ("semaphore must be created"));
9792
9793 /* release the IPC mutex */
9794 ::DosReleaseMutexSem (mIPCSem);
9795
9796 terminated = true;
9797
9798#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9799
9800 AssertMsg (mIPCSem >= 0, ("semaphore must be created"));
9801
9802 int val = ::semctl (mIPCSem, 0, GETVAL);
9803 if (val > 0)
9804 {
9805 /* the semaphore is signaled, meaning the session is terminated */
9806 terminated = true;
9807 }
9808
9809#else
9810# error "Port me!"
9811#endif
9812
9813 } /* AutoCaller block */
9814
9815 if (terminated)
9816 uninit (reason);
9817
9818 return terminated;
9819}
9820
9821/**
9822 * @note Locks this object for reading.
9823 */
9824HRESULT SessionMachine::onNetworkAdapterChange (INetworkAdapter *networkAdapter, BOOL changeAdapter)
9825{
9826 LogFlowThisFunc(("\n"));
9827
9828 AutoCaller autoCaller(this);
9829 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9830
9831 ComPtr<IInternalSessionControl> directControl;
9832 {
9833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9834 directControl = mData->mSession.mDirectControl;
9835 }
9836
9837 /* ignore notifications sent after #OnSessionEnd() is called */
9838 if (!directControl)
9839 return S_OK;
9840
9841 return directControl->OnNetworkAdapterChange (networkAdapter, changeAdapter);
9842}
9843
9844/**
9845 * @note Locks this object for reading.
9846 */
9847HRESULT SessionMachine::onSerialPortChange (ISerialPort *serialPort)
9848{
9849 LogFlowThisFunc(("\n"));
9850
9851 AutoCaller autoCaller(this);
9852 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9853
9854 ComPtr<IInternalSessionControl> directControl;
9855 {
9856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9857 directControl = mData->mSession.mDirectControl;
9858 }
9859
9860 /* ignore notifications sent after #OnSessionEnd() is called */
9861 if (!directControl)
9862 return S_OK;
9863
9864 return directControl->OnSerialPortChange (serialPort);
9865}
9866
9867/**
9868 * @note Locks this object for reading.
9869 */
9870HRESULT SessionMachine::onParallelPortChange (IParallelPort *parallelPort)
9871{
9872 LogFlowThisFunc(("\n"));
9873
9874 AutoCaller autoCaller(this);
9875 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9876
9877 ComPtr<IInternalSessionControl> directControl;
9878 {
9879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9880 directControl = mData->mSession.mDirectControl;
9881 }
9882
9883 /* ignore notifications sent after #OnSessionEnd() is called */
9884 if (!directControl)
9885 return S_OK;
9886
9887 return directControl->OnParallelPortChange (parallelPort);
9888}
9889
9890/**
9891 * @note Locks this object for reading.
9892 */
9893HRESULT SessionMachine::onStorageControllerChange ()
9894{
9895 LogFlowThisFunc(("\n"));
9896
9897 AutoCaller autoCaller(this);
9898 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9899
9900 ComPtr<IInternalSessionControl> directControl;
9901 {
9902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9903 directControl = mData->mSession.mDirectControl;
9904 }
9905
9906 /* ignore notifications sent after #OnSessionEnd() is called */
9907 if (!directControl)
9908 return S_OK;
9909
9910 return directControl->OnStorageControllerChange ();
9911}
9912
9913/**
9914 * @note Locks this object for reading.
9915 */
9916HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
9917{
9918 LogFlowThisFunc(("\n"));
9919
9920 AutoCaller autoCaller(this);
9921 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9922
9923 ComPtr<IInternalSessionControl> directControl;
9924 {
9925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9926 directControl = mData->mSession.mDirectControl;
9927 }
9928
9929 /* ignore notifications sent after #OnSessionEnd() is called */
9930 if (!directControl)
9931 return S_OK;
9932
9933 return directControl->OnMediumChange(aAttachment, aForce);
9934}
9935
9936/**
9937 * @note Locks this object for reading.
9938 */
9939HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
9940{
9941 LogFlowThisFunc(("\n"));
9942
9943 AutoCaller autoCaller(this);
9944 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9945
9946 ComPtr<IInternalSessionControl> directControl;
9947 {
9948 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9949 directControl = mData->mSession.mDirectControl;
9950 }
9951
9952 /* ignore notifications sent after #OnSessionEnd() is called */
9953 if (!directControl)
9954 return S_OK;
9955
9956 return directControl->OnCPUChange(aCPU, aRemove);
9957}
9958
9959/**
9960 * @note Locks this object for reading.
9961 */
9962HRESULT SessionMachine::onVRDPServerChange()
9963{
9964 LogFlowThisFunc(("\n"));
9965
9966 AutoCaller autoCaller(this);
9967 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9968
9969 ComPtr<IInternalSessionControl> directControl;
9970 {
9971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9972 directControl = mData->mSession.mDirectControl;
9973 }
9974
9975 /* ignore notifications sent after #OnSessionEnd() is called */
9976 if (!directControl)
9977 return S_OK;
9978
9979 return directControl->OnVRDPServerChange();
9980}
9981
9982/**
9983 * @note Locks this object for reading.
9984 */
9985HRESULT SessionMachine::onUSBControllerChange()
9986{
9987 LogFlowThisFunc(("\n"));
9988
9989 AutoCaller autoCaller(this);
9990 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
9991
9992 ComPtr<IInternalSessionControl> directControl;
9993 {
9994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9995 directControl = mData->mSession.mDirectControl;
9996 }
9997
9998 /* ignore notifications sent after #OnSessionEnd() is called */
9999 if (!directControl)
10000 return S_OK;
10001
10002 return directControl->OnUSBControllerChange();
10003}
10004
10005/**
10006 * @note Locks this object for reading.
10007 */
10008HRESULT SessionMachine::onSharedFolderChange()
10009{
10010 LogFlowThisFunc(("\n"));
10011
10012 AutoCaller autoCaller(this);
10013 AssertComRCReturnRC(autoCaller.rc());
10014
10015 ComPtr<IInternalSessionControl> directControl;
10016 {
10017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10018 directControl = mData->mSession.mDirectControl;
10019 }
10020
10021 /* ignore notifications sent after #OnSessionEnd() is called */
10022 if (!directControl)
10023 return S_OK;
10024
10025 return directControl->OnSharedFolderChange (FALSE /* aGlobal */);
10026}
10027
10028/**
10029 * Returns @c true if this machine's USB controller reports it has a matching
10030 * filter for the given USB device and @c false otherwise.
10031 *
10032 * @note Caller must have requested machine read lock.
10033 */
10034bool SessionMachine::hasMatchingUSBFilter (const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
10035{
10036 AutoCaller autoCaller(this);
10037 /* silently return if not ready -- this method may be called after the
10038 * direct machine session has been called */
10039 if (!autoCaller.isOk())
10040 return false;
10041
10042 AssertReturn(isWriteLockOnCurrentThread(), false);
10043
10044#ifdef VBOX_WITH_USB
10045 switch (mData->mMachineState)
10046 {
10047 case MachineState_Starting:
10048 case MachineState_Restoring:
10049 case MachineState_TeleportingIn:
10050 case MachineState_Paused:
10051 case MachineState_Running:
10052 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
10053 * elsewhere... */
10054 return mUSBController->hasMatchingFilter (aDevice, aMaskedIfs);
10055 default: break;
10056 }
10057#else
10058 NOREF(aDevice);
10059 NOREF(aMaskedIfs);
10060#endif
10061 return false;
10062}
10063
10064/**
10065 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
10066 */
10067HRESULT SessionMachine::onUSBDeviceAttach (IUSBDevice *aDevice,
10068 IVirtualBoxErrorInfo *aError,
10069 ULONG aMaskedIfs)
10070{
10071 LogFlowThisFunc(("\n"));
10072
10073 AutoCaller autoCaller(this);
10074
10075 /* This notification may happen after the machine object has been
10076 * uninitialized (the session was closed), so don't assert. */
10077 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10078
10079 ComPtr<IInternalSessionControl> directControl;
10080 {
10081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10082 directControl = mData->mSession.mDirectControl;
10083 }
10084
10085 /* fail on notifications sent after #OnSessionEnd() is called, it is
10086 * expected by the caller */
10087 if (!directControl)
10088 return E_FAIL;
10089
10090 /* No locks should be held at this point. */
10091 AssertMsg (RTLockValidatorWriteLockGetCount (RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount (RTThreadSelf())));
10092 AssertMsg (RTLockValidatorReadLockGetCount (RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount (RTThreadSelf())));
10093
10094 return directControl->OnUSBDeviceAttach (aDevice, aError, aMaskedIfs);
10095}
10096
10097/**
10098 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
10099 */
10100HRESULT SessionMachine::onUSBDeviceDetach (IN_BSTR aId,
10101 IVirtualBoxErrorInfo *aError)
10102{
10103 LogFlowThisFunc(("\n"));
10104
10105 AutoCaller autoCaller(this);
10106
10107 /* This notification may happen after the machine object has been
10108 * uninitialized (the session was closed), so don't assert. */
10109 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10110
10111 ComPtr<IInternalSessionControl> directControl;
10112 {
10113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10114 directControl = mData->mSession.mDirectControl;
10115 }
10116
10117 /* fail on notifications sent after #OnSessionEnd() is called, it is
10118 * expected by the caller */
10119 if (!directControl)
10120 return E_FAIL;
10121
10122 /* No locks should be held at this point. */
10123 AssertMsg (RTLockValidatorWriteLockGetCount (RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount (RTThreadSelf())));
10124 AssertMsg (RTLockValidatorReadLockGetCount (RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount (RTThreadSelf())));
10125
10126 return directControl->OnUSBDeviceDetach (aId, aError);
10127}
10128
10129// protected methods
10130/////////////////////////////////////////////////////////////////////////////
10131
10132/**
10133 * Helper method to finalize saving the state.
10134 *
10135 * @note Must be called from under this object's lock.
10136 *
10137 * @param aSuccess TRUE if the snapshot has been taken successfully
10138 *
10139 * @note Locks mParent + this objects for writing.
10140 */
10141HRESULT SessionMachine::endSavingState (BOOL aSuccess)
10142{
10143 LogFlowThisFuncEnter();
10144
10145 AutoCaller autoCaller(this);
10146 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10147
10148 /* saveSettings() needs mParent lock */
10149 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
10150
10151 HRESULT rc = S_OK;
10152
10153 if (aSuccess)
10154 {
10155 mSSData->mStateFilePath = mSnapshotData.mStateFilePath;
10156
10157 /* save all VM settings */
10158 rc = saveSettings();
10159 }
10160 else
10161 {
10162 /* delete the saved state file (it might have been already created) */
10163 RTFileDelete(mSnapshotData.mStateFilePath.c_str());
10164 }
10165
10166 /* remove the completed progress object */
10167 mParent->removeProgress(mSnapshotData.mProgressId);
10168
10169 /* clear out the temporary saved state data */
10170 mSnapshotData.mLastState = MachineState_Null;
10171 mSnapshotData.mProgressId.clear();
10172 mSnapshotData.mStateFilePath.setNull();
10173
10174 LogFlowThisFuncLeave();
10175 return rc;
10176}
10177
10178/**
10179 * Locks the attached media.
10180 *
10181 * All attached hard disks are locked for writing and DVD/floppy are locked for
10182 * reading. Parents of attached hard disks (if any) are locked for reading.
10183 *
10184 * This method also performs accessibility check of all media it locks: if some
10185 * media is inaccessible, the method will return a failure and a bunch of
10186 * extended error info objects per each inaccessible medium.
10187 *
10188 * Note that this method is atomic: if it returns a success, all media are
10189 * locked as described above; on failure no media is locked at all (all
10190 * succeeded individual locks will be undone).
10191 *
10192 * This method is intended to be called when the machine is in Starting or
10193 * Restoring state and asserts otherwise.
10194 *
10195 * The locks made by this method must be undone by calling #unlockMedia() when
10196 * no more needed.
10197 */
10198HRESULT SessionMachine::lockMedia()
10199{
10200 AutoCaller autoCaller(this);
10201 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10202
10203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10204
10205 AssertReturn( mData->mMachineState == MachineState_Starting
10206 || mData->mMachineState == MachineState_Restoring
10207 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
10208
10209 typedef std::list <ComPtr<IMedium> > MediaList;
10210
10211 try
10212 {
10213 HRESULT rc = S_OK;
10214
10215 ErrorInfoKeeper eik(true /* aIsNull */);
10216 MultiResult mrc(S_OK);
10217
10218 /* Lock all medium objects attached to the VM.
10219 * Get status for inaccessible media as well. */
10220 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10221 it != mMediaData->mAttachments.end();
10222 ++it)
10223 {
10224 DeviceType_T devType = (*it)->getType();
10225 ComObjPtr<Medium> medium = (*it)->getMedium();
10226
10227 bool first = true;
10228
10229 /** @todo split out the media locking, and put it into
10230 * MediumImpl.cpp, as it needs this functionality too. */
10231 while (!medium.isNull())
10232 {
10233 MediumState_T mediumState = medium->getState();
10234
10235 /* accessibility check must be first, otherwise locking
10236 * interferes with getting the medium state. */
10237 if (mediumState == MediumState_Inaccessible)
10238 {
10239 rc = medium->RefreshState(&mediumState);
10240 if (FAILED(rc)) throw rc;
10241
10242 if (mediumState == MediumState_Inaccessible)
10243 {
10244 Bstr error;
10245 rc = medium->COMGETTER(LastAccessError)(error.asOutParam());
10246 if (FAILED(rc)) throw rc;
10247
10248 Bstr loc;
10249 rc = medium->COMGETTER(Location)(loc.asOutParam());
10250 if (FAILED(rc)) throw rc;
10251
10252 /* collect multiple errors */
10253 eik.restore();
10254
10255 /* be in sync with MediumBase::setStateError() */
10256 Assert(!error.isEmpty());
10257 mrc = setError(E_FAIL,
10258 tr("Medium '%ls' is not accessible. %ls"),
10259 loc.raw(),
10260 error.raw());
10261
10262 eik.fetch();
10263 }
10264 }
10265
10266 if (first)
10267 {
10268 if (devType != DeviceType_DVD)
10269 {
10270 /* HardDisk and Floppy medium must be locked for writing */
10271 rc = medium->LockWrite(NULL);
10272 if (FAILED(rc)) throw rc;
10273 }
10274 else
10275 {
10276 /* DVD medium must be locked for reading */
10277 rc = medium->LockRead(NULL);
10278 if (FAILED(rc)) throw rc;
10279 }
10280
10281 mData->mSession.mLockedMedia.push_back(
10282 Data::Session::LockedMedia::value_type(
10283 ComPtr<IMedium>(medium), true));
10284
10285 first = false;
10286 }
10287 else
10288 {
10289 rc = medium->LockRead(NULL);
10290 if (FAILED(rc)) throw rc;
10291
10292 mData->mSession.mLockedMedia.push_back(
10293 Data::Session::LockedMedia::value_type(
10294 ComPtr<IMedium>(medium), false));
10295 }
10296
10297
10298 /* no locks or callers here since there should be no way to
10299 * change the hard disk parent at this point (as it is still
10300 * attached to the machine) */
10301 medium = medium->getParent();
10302 }
10303 }
10304
10305 /* @todo r=dj is this correct? first restoring the eik and then throwing? */
10306 eik.restore();
10307 HRESULT rc2 = (HRESULT)mrc;
10308 if (FAILED(rc2)) throw rc2;
10309 }
10310 catch (HRESULT aRC)
10311 {
10312 /* Unlock all locked media on failure */
10313 unlockMedia();
10314 return aRC;
10315 }
10316
10317 return S_OK;
10318}
10319
10320/**
10321 * Undoes the locks made by by #lockMedia().
10322 */
10323void SessionMachine::unlockMedia()
10324{
10325 AutoCaller autoCaller(this);
10326 AssertComRCReturnVoid (autoCaller.rc());
10327
10328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10329
10330 /* we may be holding important error info on the current thread;
10331 * preserve it */
10332 ErrorInfoKeeper eik;
10333
10334 HRESULT rc = S_OK;
10335
10336 for (Data::Session::LockedMedia::const_iterator
10337 it = mData->mSession.mLockedMedia.begin();
10338 it != mData->mSession.mLockedMedia.end(); ++it)
10339 {
10340 MediumState_T state;
10341 if (it->second)
10342 rc = it->first->UnlockWrite (&state);
10343 else
10344 rc = it->first->UnlockRead (&state);
10345
10346 /* The second can happen if an object was re-locked in
10347 * Machine::fixupMedia(). The last can happen when e.g a DVD/Floppy
10348 * image was unmounted at runtime. */
10349 Assert (SUCCEEDED(rc) || state == MediumState_LockedRead || state == MediumState_Created);
10350 }
10351
10352 mData->mSession.mLockedMedia.clear();
10353}
10354
10355/**
10356 * Helper to change the machine state (reimplementation).
10357 *
10358 * @note Locks this object for writing.
10359 */
10360HRESULT SessionMachine::setMachineState (MachineState_T aMachineState)
10361{
10362 LogFlowThisFuncEnter();
10363 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
10364
10365 AutoCaller autoCaller(this);
10366 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10367
10368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10369
10370 MachineState_T oldMachineState = mData->mMachineState;
10371
10372 AssertMsgReturn(oldMachineState != aMachineState,
10373 ("oldMachineState=%s, aMachineState=%s\n",
10374 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
10375 E_FAIL);
10376
10377 HRESULT rc = S_OK;
10378
10379 int stsFlags = 0;
10380 bool deleteSavedState = false;
10381
10382 /* detect some state transitions */
10383
10384 if ( ( oldMachineState == MachineState_Saved
10385 && aMachineState == MachineState_Restoring)
10386 || ( ( oldMachineState == MachineState_PoweredOff
10387 || oldMachineState == MachineState_Teleported
10388 || oldMachineState == MachineState_Aborted
10389 )
10390 && ( aMachineState == MachineState_TeleportingIn
10391 || aMachineState == MachineState_Starting
10392 )
10393 )
10394 )
10395 {
10396 /* The EMT thread is about to start */
10397
10398 /* Nothing to do here for now... */
10399
10400 /// @todo NEWMEDIA don't let mDVDDrive and other children
10401 /// change anything when in the Starting/Restoring state
10402 }
10403 else if ( ( oldMachineState == MachineState_Running
10404 || oldMachineState == MachineState_Paused
10405 || oldMachineState == MachineState_Teleporting
10406 || oldMachineState == MachineState_LiveSnapshotting
10407 || oldMachineState == MachineState_Stuck
10408 || oldMachineState == MachineState_Starting
10409 || oldMachineState == MachineState_Stopping
10410 || oldMachineState == MachineState_Saving
10411 || oldMachineState == MachineState_Restoring
10412 || oldMachineState == MachineState_TeleportingPausedVM
10413 || oldMachineState == MachineState_TeleportingIn
10414 )
10415 && ( aMachineState == MachineState_PoweredOff
10416 || aMachineState == MachineState_Saved
10417 || aMachineState == MachineState_Teleported
10418 || aMachineState == MachineState_Aborted
10419 )
10420 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
10421 * snapshot */
10422 && ( mSnapshotData.mSnapshot.isNull()
10423 || mSnapshotData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
10424 )
10425 )
10426 {
10427 /* The EMT thread has just stopped, unlock attached media. Note that as
10428 * opposed to locking that is done from Console, we do unlocking here
10429 * because the VM process may have aborted before having a chance to
10430 * properly unlock all media it locked. */
10431
10432 unlockMedia();
10433 }
10434
10435 if (oldMachineState == MachineState_Restoring)
10436 {
10437 if (aMachineState != MachineState_Saved)
10438 {
10439 /*
10440 * delete the saved state file once the machine has finished
10441 * restoring from it (note that Console sets the state from
10442 * Restoring to Saved if the VM couldn't restore successfully,
10443 * to give the user an ability to fix an error and retry --
10444 * we keep the saved state file in this case)
10445 */
10446 deleteSavedState = true;
10447 }
10448 }
10449 else if ( oldMachineState == MachineState_Saved
10450 && ( aMachineState == MachineState_PoweredOff
10451 || aMachineState == MachineState_Aborted
10452 || aMachineState == MachineState_Teleported
10453 )
10454 )
10455 {
10456 /*
10457 * delete the saved state after Console::DiscardSavedState() is called
10458 * or if the VM process (owning a direct VM session) crashed while the
10459 * VM was Saved
10460 */
10461
10462 /// @todo (dmik)
10463 // Not sure that deleting the saved state file just because of the
10464 // client death before it attempted to restore the VM is a good
10465 // thing. But when it crashes we need to go to the Aborted state
10466 // which cannot have the saved state file associated... The only
10467 // way to fix this is to make the Aborted condition not a VM state
10468 // but a bool flag: i.e., when a crash occurs, set it to true and
10469 // change the state to PoweredOff or Saved depending on the
10470 // saved state presence.
10471
10472 deleteSavedState = true;
10473 mData->mCurrentStateModified = TRUE;
10474 stsFlags |= SaveSTS_CurStateModified;
10475 }
10476
10477 if ( aMachineState == MachineState_Starting
10478 || aMachineState == MachineState_Restoring
10479 || aMachineState == MachineState_TeleportingIn
10480 )
10481 {
10482 /* set the current state modified flag to indicate that the current
10483 * state is no more identical to the state in the
10484 * current snapshot */
10485 if (!mData->mCurrentSnapshot.isNull())
10486 {
10487 mData->mCurrentStateModified = TRUE;
10488 stsFlags |= SaveSTS_CurStateModified;
10489 }
10490 }
10491
10492 if (deleteSavedState)
10493 {
10494 if (mRemoveSavedState)
10495 {
10496 Assert(!mSSData->mStateFilePath.isEmpty());
10497 RTFileDelete(mSSData->mStateFilePath.c_str());
10498 }
10499 mSSData->mStateFilePath.setNull();
10500 stsFlags |= SaveSTS_StateFilePath;
10501 }
10502
10503 /* redirect to the underlying peer machine */
10504 mPeer->setMachineState (aMachineState);
10505
10506 if ( aMachineState == MachineState_PoweredOff
10507 || aMachineState == MachineState_Teleported
10508 || aMachineState == MachineState_Aborted
10509 || aMachineState == MachineState_Saved)
10510 {
10511 /* the machine has stopped execution
10512 * (or the saved state file was adopted) */
10513 stsFlags |= SaveSTS_StateTimeStamp;
10514 }
10515
10516 if ( ( oldMachineState == MachineState_PoweredOff
10517 || oldMachineState == MachineState_Aborted
10518 || oldMachineState == MachineState_Teleported
10519 )
10520 && aMachineState == MachineState_Saved)
10521 {
10522 /* the saved state file was adopted */
10523 Assert(!mSSData->mStateFilePath.isEmpty());
10524 stsFlags |= SaveSTS_StateFilePath;
10525 }
10526
10527 rc = saveStateSettings (stsFlags);
10528
10529 if ( ( oldMachineState != MachineState_PoweredOff
10530 && oldMachineState != MachineState_Aborted
10531 && oldMachineState != MachineState_Teleported
10532 )
10533 && ( aMachineState == MachineState_PoweredOff
10534 || aMachineState == MachineState_Aborted
10535 || aMachineState == MachineState_Teleported
10536 )
10537 )
10538 {
10539 /* we've been shut down for any reason */
10540 /* no special action so far */
10541 }
10542
10543 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
10544 LogFlowThisFuncLeave();
10545 return rc;
10546}
10547
10548/**
10549 * Sends the current machine state value to the VM process.
10550 *
10551 * @note Locks this object for reading, then calls a client process.
10552 */
10553HRESULT SessionMachine::updateMachineStateOnClient()
10554{
10555 AutoCaller autoCaller(this);
10556 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10557
10558 ComPtr<IInternalSessionControl> directControl;
10559 {
10560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10561 AssertReturn(!!mData, E_FAIL);
10562 directControl = mData->mSession.mDirectControl;
10563
10564 /* directControl may be already set to NULL here in #OnSessionEnd()
10565 * called too early by the direct session process while there is still
10566 * some operation (like discarding the snapshot) in progress. The client
10567 * process in this case is waiting inside Session::close() for the
10568 * "end session" process object to complete, while #uninit() called by
10569 * #checkForDeath() on the Watcher thread is waiting for the pending
10570 * operation to complete. For now, we accept this inconsitent behavior
10571 * and simply do nothing here. */
10572
10573 if (mData->mSession.mState == SessionState_Closing)
10574 return S_OK;
10575
10576 AssertReturn(!directControl.isNull(), E_FAIL);
10577 }
10578
10579 return directControl->UpdateMachineState (mData->mMachineState);
10580}
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