VirtualBox

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

Last change on this file since 19412 was 19280, checked in by vboxsync, 15 years ago

Main: Build fix for FreeBSD AMD64. key_t is 8 bytes big on amd64

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