VirtualBox

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

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

API: big medium handling change and lots of assorted other cleanups and fixes

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

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