VirtualBox

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

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

Extra comment (todo)

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

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