VirtualBox

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

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

fix for xTracker #3762

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