VirtualBox

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

Last change on this file since 22171 was 22143, checked in by vboxsync, 15 years ago

video 2d accel: Main & ui settings

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