VirtualBox

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

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

Main: document XML settings versions correctly; bring back DHCP server default config with new or upgraded main settings file; add mechanism to bump machine settings version if new features are requested

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

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