VirtualBox

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

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

Main: fix both SetExtraData() implementations to call onExtraDataCanChange() outside of any locks

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

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