VirtualBox

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

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

Main/Machine: when opening a new session, don't just fail of the previous session wasn't closed completely. Better wait a little, as normally it takes much less than half a second to clean it up. The client could retry, but usually that would be much slower as the client has no knowledge about when the session is closed.

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

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