VirtualBox

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

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

Added vim modelines to aid following coding guidelines, like no tabs,
similar to what is already in the xidl file.

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

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