VirtualBox

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

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

Main/Machine: fix error message, very partial whitespace cleanup

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

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