VirtualBox

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

Last change on this file since 22142 was 21961, checked in by vboxsync, 15 years ago

NetworkAttachment: trigger the network attachment change only when AttachTo* API are called

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