VirtualBox

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

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

Main: Bstr makeover (second attempt) -- make Bstr(NULL) and Bstr() behave the same; resulting cleanup; make some more internal methods use Utf8Str instead of Bstr; fix a lot of CheckComArgNotNull?() usage

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