VirtualBox

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

Last change on this file since 23570 was 23567, checked in by vboxsync, 15 years ago

Main: Enabled the live snapshot code (TakeSnapshot).

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

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