VirtualBox

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

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

handle required settings upgrade transparently in the settings backend only

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