VirtualBox

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

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

Main: move huge amounts of snapshot code from MachineImpl.cpp to SnapshotImpl.cpp; no functional change

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