VirtualBox

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

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

Main: fix gcc warnings.

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