VirtualBox

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

Last change on this file since 26497 was 26459, checked in by vboxsync, 15 years ago

Main: HPET machine property, cleanups

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

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