VirtualBox

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

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

ConsoleImpl,MachineImpl: Re-did the live snapshot bits.

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