VirtualBox

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

Last change on this file since 23674 was 23674, checked in by vboxsync, 16 years ago

Windows build fix

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

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