VirtualBox

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

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

Main: fix restoreSnapshot() which updated the current snapshot only for online snapshots

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