VirtualBox

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

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

#3285: Improve error handling API to include unique error numbers

The mega commit that implements Main-wide usage of new CheckCom*
macros, mostly CheckComArgNotNull, CheckComArgStrNotEmptyOrNull,
CheckComArgOutSafeArrayPointerValid, CheckComArgExpr.
Note that some methods incorrectly returned E_INVALIDARG where they
should have returned E_POINTER and vice versa. If any higher level
function tests these, they will behave differently now...

Special thanks to: vi macros, making it easy to semi-automatically
find and replace several hundred instances of if (!aName) ...

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

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