VirtualBox

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

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

Main: bump machine settings version only if new features are really requested

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