VirtualBox

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

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

API: cleanup of previous changes

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