VirtualBox

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

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

Main/Machine: fix returned error code if opening the vm config file doesn't work

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