VirtualBox

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

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

Main: Cleaned up the long standing const BSTR = const (OLECHAR *) on WIn32 vs (const PRunichar) * on XPCOM clash. Cleaned up BSTR/GUID macros (IN_BSTR replaces INPTR BSTR, IN_GUID replaces INPTR GUIDPARAM, OUT_GUID replaces GUIDPARAMOUT).

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