VirtualBox

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

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

Backed out 59261 & 59266

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

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