VirtualBox

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

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

Main: fix missing machine settings save in some takeSnapshot() situations (recent trunk regression)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 352.3 KB
Line 
1/* $Id: MachineImpl.cpp 27644 2010-03-23 16:07:49Z 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
4710// public methods for internal purposes
4711/////////////////////////////////////////////////////////////////////////////
4712
4713/**
4714 * Adds the given IsModified_* flag to the dirty flags of the machine.
4715 * This must be called either during loadSettings or under the machine write lock.
4716 * @param fl
4717 */
4718void Machine::setModified(uint32_t fl)
4719{
4720 m_flModifications |= fl;
4721}
4722
4723/**
4724 * Saves the registry entry of this machine to the given configuration node.
4725 *
4726 * @param aEntryNode Node to save the registry entry to.
4727 *
4728 * @note locks this object for reading.
4729 */
4730HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
4731{
4732 AutoLimitedCaller autoCaller(this);
4733 AssertComRCReturnRC(autoCaller.rc());
4734
4735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4736
4737 data.uuid = mData->mUuid;
4738 data.strSettingsFile = mData->m_strConfigFile;
4739
4740 return S_OK;
4741}
4742
4743/**
4744 * Calculates the absolute path of the given path taking the directory of the
4745 * machine settings file as the current directory.
4746 *
4747 * @param aPath Path to calculate the absolute path for.
4748 * @param aResult Where to put the result (used only on success, can be the
4749 * same Utf8Str instance as passed in @a aPath).
4750 * @return IPRT result.
4751 *
4752 * @note Locks this object for reading.
4753 */
4754int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
4755{
4756 AutoCaller autoCaller(this);
4757 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4758
4759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4760
4761 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
4762
4763 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
4764
4765 strSettingsDir.stripFilename();
4766 char folder[RTPATH_MAX];
4767 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
4768 if (RT_SUCCESS(vrc))
4769 aResult = folder;
4770
4771 return vrc;
4772}
4773
4774/**
4775 * Tries to calculate the relative path of the given absolute path using the
4776 * directory of the machine settings file as the base directory.
4777 *
4778 * @param aPath Absolute path to calculate the relative path for.
4779 * @param aResult Where to put the result (used only when it's possible to
4780 * make a relative path from the given absolute path; otherwise
4781 * left untouched).
4782 *
4783 * @note Locks this object for reading.
4784 */
4785void Machine::calculateRelativePath(const Utf8Str &strPath, Utf8Str &aResult)
4786{
4787 AutoCaller autoCaller(this);
4788 AssertComRCReturn(autoCaller.rc(), (void)0);
4789
4790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4791
4792 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
4793
4794 Utf8Str settingsDir = mData->m_strConfigFileFull;
4795
4796 settingsDir.stripFilename();
4797 if (RTPathStartsWith(strPath.c_str(), settingsDir.c_str()))
4798 {
4799 /* when assigning, we create a separate Utf8Str instance because both
4800 * aPath and aResult can point to the same memory location when this
4801 * func is called (if we just do aResult = aPath, aResult will be freed
4802 * first, and since its the same as aPath, an attempt to copy garbage
4803 * will be made. */
4804 aResult = Utf8Str(strPath.c_str() + settingsDir.length() + 1);
4805 }
4806}
4807
4808/**
4809 * Returns the full path to the machine's log folder in the
4810 * \a aLogFolder argument.
4811 */
4812void Machine::getLogFolder(Utf8Str &aLogFolder)
4813{
4814 AutoCaller autoCaller(this);
4815 AssertComRCReturnVoid(autoCaller.rc());
4816
4817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4818
4819 Utf8Str settingsDir;
4820 if (isInOwnDir(&settingsDir))
4821 {
4822 /* Log folder is <Machines>/<VM_Name>/Logs */
4823 aLogFolder = Utf8StrFmt("%s%cLogs", settingsDir.raw(), RTPATH_DELIMITER);
4824 }
4825 else
4826 {
4827 /* Log folder is <Machines>/<VM_SnapshotFolder>/Logs */
4828 Assert(!mUserData->mSnapshotFolderFull.isEmpty());
4829 aLogFolder = Utf8StrFmt ("%ls%cLogs", mUserData->mSnapshotFolderFull.raw(),
4830 RTPATH_DELIMITER);
4831 }
4832}
4833
4834/**
4835 * @note Locks this object for writing, calls the client process (outside the
4836 * lock).
4837 */
4838HRESULT Machine::openSession(IInternalSessionControl *aControl)
4839{
4840 LogFlowThisFuncEnter();
4841
4842 AssertReturn(aControl, E_FAIL);
4843
4844 AutoCaller autoCaller(this);
4845 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4846
4847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4848
4849 if (!mData->mRegistered)
4850 return setError(E_UNEXPECTED,
4851 tr("The machine '%ls' is not registered"),
4852 mUserData->mName.raw());
4853
4854 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4855
4856 /* Hack: in case the session is closing and there is a progress object
4857 * which allows waiting for the session to be closed, take the opportunity
4858 * and do a limited wait (max. 1 second). This helps a lot when the system
4859 * is busy and thus session closing can take a little while. */
4860 if ( mData->mSession.mState == SessionState_Closing
4861 && mData->mSession.mProgress)
4862 {
4863 alock.leave();
4864 mData->mSession.mProgress->WaitForCompletion(1000);
4865 alock.enter();
4866 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
4867 }
4868
4869 if (mData->mSession.mState == SessionState_Open ||
4870 mData->mSession.mState == SessionState_Closing)
4871 return setError(VBOX_E_INVALID_OBJECT_STATE,
4872 tr("A session for the machine '%ls' is currently open (or being closed)"),
4873 mUserData->mName.raw());
4874
4875 /* may not be busy */
4876 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
4877
4878 /* get the session PID */
4879 RTPROCESS pid = NIL_RTPROCESS;
4880 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
4881 aControl->GetPID((ULONG *) &pid);
4882 Assert(pid != NIL_RTPROCESS);
4883
4884 if (mData->mSession.mState == SessionState_Spawning)
4885 {
4886 /* This machine is awaiting for a spawning session to be opened, so
4887 * reject any other open attempts from processes other than one
4888 * started by #openRemoteSession(). */
4889
4890 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n",
4891 mData->mSession.mPid, mData->mSession.mPid));
4892 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
4893
4894 if (mData->mSession.mPid != pid)
4895 return setError(E_ACCESSDENIED,
4896 tr("An unexpected process (PID=0x%08X) has tried to open a direct "
4897 "session with the machine named '%ls', while only a process "
4898 "started by OpenRemoteSession (PID=0x%08X) is allowed"),
4899 pid, mUserData->mName.raw(), mData->mSession.mPid);
4900 }
4901
4902 /* create a SessionMachine object */
4903 ComObjPtr<SessionMachine> sessionMachine;
4904 sessionMachine.createObject();
4905 HRESULT rc = sessionMachine->init(this);
4906 AssertComRC(rc);
4907
4908 /* NOTE: doing return from this function after this point but
4909 * before the end is forbidden since it may call SessionMachine::uninit()
4910 * (through the ComObjPtr's destructor) which requests the VirtualBox write
4911 * lock while still holding the Machine lock in alock so that a deadlock
4912 * is possible due to the wrong lock order. */
4913
4914 if (SUCCEEDED(rc))
4915 {
4916#ifdef VBOX_WITH_RESOURCE_USAGE_API
4917 registerMetrics(mParent->performanceCollector(), this, pid);
4918#endif /* VBOX_WITH_RESOURCE_USAGE_API */
4919
4920 /*
4921 * Set the session state to Spawning to protect against subsequent
4922 * attempts to open a session and to unregister the machine after
4923 * we leave the lock.
4924 */
4925 SessionState_T origState = mData->mSession.mState;
4926 mData->mSession.mState = SessionState_Spawning;
4927
4928 /*
4929 * Leave the lock before calling the client process -- it will call
4930 * Machine/SessionMachine methods. Leaving the lock here is quite safe
4931 * because the state is Spawning, so that openRemotesession() and
4932 * openExistingSession() calls will fail. This method, called before we
4933 * enter the lock again, will fail because of the wrong PID.
4934 *
4935 * Note that mData->mSession.mRemoteControls accessed outside
4936 * the lock may not be modified when state is Spawning, so it's safe.
4937 */
4938 alock.leave();
4939
4940 LogFlowThisFunc(("Calling AssignMachine()...\n"));
4941 rc = aControl->AssignMachine(sessionMachine);
4942 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
4943
4944 /* The failure may occur w/o any error info (from RPC), so provide one */
4945 if (FAILED(rc))
4946 setError(VBOX_E_VM_ERROR,
4947 tr("Failed to assign the machine to the session (%Rrc)"), rc);
4948
4949 if (SUCCEEDED(rc) && origState == SessionState_Spawning)
4950 {
4951 /* complete the remote session initialization */
4952
4953 /* get the console from the direct session */
4954 ComPtr<IConsole> console;
4955 rc = aControl->GetRemoteConsole(console.asOutParam());
4956 ComAssertComRC(rc);
4957
4958 if (SUCCEEDED(rc) && !console)
4959 {
4960 ComAssert(!!console);
4961 rc = E_FAIL;
4962 }
4963
4964 /* assign machine & console to the remote session */
4965 if (SUCCEEDED(rc))
4966 {
4967 /*
4968 * after openRemoteSession(), the first and the only
4969 * entry in remoteControls is that remote session
4970 */
4971 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
4972 rc = mData->mSession.mRemoteControls.front()->
4973 AssignRemoteMachine(sessionMachine, console);
4974 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
4975
4976 /* The failure may occur w/o any error info (from RPC), so provide one */
4977 if (FAILED(rc))
4978 setError(VBOX_E_VM_ERROR,
4979 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
4980 }
4981
4982 if (FAILED(rc))
4983 aControl->Uninitialize();
4984 }
4985
4986 /* enter the lock again */
4987 alock.enter();
4988
4989 /* Restore the session state */
4990 mData->mSession.mState = origState;
4991 }
4992
4993 /* finalize spawning anyway (this is why we don't return on errors above) */
4994 if (mData->mSession.mState == SessionState_Spawning)
4995 {
4996 /* Note that the progress object is finalized later */
4997
4998 /* We don't reset mSession.mPid here because it is necessary for
4999 * SessionMachine::uninit() to reap the child process later. */
5000
5001 if (FAILED(rc))
5002 {
5003 /* Close the remote session, remove the remote control from the list
5004 * and reset session state to Closed (@note keep the code in sync
5005 * with the relevant part in openSession()). */
5006
5007 Assert(mData->mSession.mRemoteControls.size() == 1);
5008 if (mData->mSession.mRemoteControls.size() == 1)
5009 {
5010 ErrorInfoKeeper eik;
5011 mData->mSession.mRemoteControls.front()->Uninitialize();
5012 }
5013
5014 mData->mSession.mRemoteControls.clear();
5015 mData->mSession.mState = SessionState_Closed;
5016 }
5017 }
5018 else
5019 {
5020 /* memorize PID of the directly opened session */
5021 if (SUCCEEDED(rc))
5022 mData->mSession.mPid = pid;
5023 }
5024
5025 if (SUCCEEDED(rc))
5026 {
5027 /* memorize the direct session control and cache IUnknown for it */
5028 mData->mSession.mDirectControl = aControl;
5029 mData->mSession.mState = SessionState_Open;
5030 /* associate the SessionMachine with this Machine */
5031 mData->mSession.mMachine = sessionMachine;
5032
5033 /* request an IUnknown pointer early from the remote party for later
5034 * identity checks (it will be internally cached within mDirectControl
5035 * at least on XPCOM) */
5036 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
5037 NOREF(unk);
5038 }
5039
5040 /* Leave the lock since SessionMachine::uninit() locks VirtualBox which
5041 * would break the lock order */
5042 alock.leave();
5043
5044 /* uninitialize the created session machine on failure */
5045 if (FAILED(rc))
5046 sessionMachine->uninit();
5047
5048 LogFlowThisFunc(("rc=%08X\n", rc));
5049 LogFlowThisFuncLeave();
5050 return rc;
5051}
5052
5053/**
5054 * @note Locks this object for writing, calls the client process
5055 * (inside the lock).
5056 */
5057HRESULT Machine::openRemoteSession(IInternalSessionControl *aControl,
5058 IN_BSTR aType,
5059 IN_BSTR aEnvironment,
5060 Progress *aProgress)
5061{
5062 LogFlowThisFuncEnter();
5063
5064 AssertReturn(aControl, E_FAIL);
5065 AssertReturn(aProgress, E_FAIL);
5066
5067 AutoCaller autoCaller(this);
5068 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5069
5070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5071
5072 if (!mData->mRegistered)
5073 return setError(E_UNEXPECTED,
5074 tr("The machine '%ls' is not registered"),
5075 mUserData->mName.raw());
5076
5077 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
5078
5079 if (mData->mSession.mState == SessionState_Open ||
5080 mData->mSession.mState == SessionState_Spawning ||
5081 mData->mSession.mState == SessionState_Closing)
5082 return setError(VBOX_E_INVALID_OBJECT_STATE,
5083 tr("A session for the machine '%ls' is currently open (or being opened or closed)"),
5084 mUserData->mName.raw());
5085
5086 /* may not be busy */
5087 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
5088
5089 /* get the path to the executable */
5090 char szPath[RTPATH_MAX];
5091 RTPathAppPrivateArch(szPath, RTPATH_MAX);
5092 size_t sz = strlen(szPath);
5093 szPath[sz++] = RTPATH_DELIMITER;
5094 szPath[sz] = 0;
5095 char *cmd = szPath + sz;
5096 sz = RTPATH_MAX - sz;
5097
5098 int vrc = VINF_SUCCESS;
5099 RTPROCESS pid = NIL_RTPROCESS;
5100
5101 RTENV env = RTENV_DEFAULT;
5102
5103 if (aEnvironment != NULL && *aEnvironment)
5104 {
5105 char *newEnvStr = NULL;
5106
5107 do
5108 {
5109 /* clone the current environment */
5110 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
5111 AssertRCBreakStmt(vrc2, vrc = vrc2);
5112
5113 newEnvStr = RTStrDup(Utf8Str(aEnvironment).c_str());
5114 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
5115
5116 /* put new variables to the environment
5117 * (ignore empty variable names here since RTEnv API
5118 * intentionally doesn't do that) */
5119 char *var = newEnvStr;
5120 for (char *p = newEnvStr; *p; ++p)
5121 {
5122 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
5123 {
5124 *p = '\0';
5125 if (*var)
5126 {
5127 char *val = strchr(var, '=');
5128 if (val)
5129 {
5130 *val++ = '\0';
5131 vrc2 = RTEnvSetEx(env, var, val);
5132 }
5133 else
5134 vrc2 = RTEnvUnsetEx(env, var);
5135 if (RT_FAILURE(vrc2))
5136 break;
5137 }
5138 var = p + 1;
5139 }
5140 }
5141 if (RT_SUCCESS(vrc2) && *var)
5142 vrc2 = RTEnvPutEx(env, var);
5143
5144 AssertRCBreakStmt(vrc2, vrc = vrc2);
5145 }
5146 while (0);
5147
5148 if (newEnvStr != NULL)
5149 RTStrFree(newEnvStr);
5150 }
5151
5152 Utf8Str strType(aType);
5153
5154 /* Qt is default */
5155#ifdef VBOX_WITH_QTGUI
5156 if (strType == "gui" || strType == "GUI/Qt")
5157 {
5158# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
5159 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
5160# else
5161 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
5162# endif
5163 Assert(sz >= sizeof(VirtualBox_exe));
5164 strcpy(cmd, VirtualBox_exe);
5165
5166 Utf8Str idStr = mData->mUuid.toString();
5167# ifdef RT_OS_WINDOWS /** @todo drop this once the RTProcCreate bug has been fixed */
5168 const char * args[] = {szPath, "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
5169# else
5170 Utf8Str strName = mUserData->mName;
5171 const char * args[] = {szPath, "--comment", strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
5172# endif
5173 vrc = RTProcCreate(szPath, args, env, 0, &pid);
5174 }
5175#else /* !VBOX_WITH_QTGUI */
5176 if (0)
5177 ;
5178#endif /* VBOX_WITH_QTGUI */
5179
5180 else
5181
5182#ifdef VBOX_WITH_VBOXSDL
5183 if (strType == "sdl" || strType == "GUI/SDL")
5184 {
5185 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
5186 Assert(sz >= sizeof(VBoxSDL_exe));
5187 strcpy(cmd, VBoxSDL_exe);
5188
5189 Utf8Str idStr = mData->mUuid.toString();
5190# ifdef RT_OS_WINDOWS
5191 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0 };
5192# else
5193 Utf8Str strName = mUserData->mName;
5194 const char * args[] = {szPath, "--comment", strName.c_str(), "--startvm", idStr.c_str(), 0 };
5195# endif
5196 vrc = RTProcCreate(szPath, args, env, 0, &pid);
5197 }
5198#else /* !VBOX_WITH_VBOXSDL */
5199 if (0)
5200 ;
5201#endif /* !VBOX_WITH_VBOXSDL */
5202
5203 else
5204
5205#ifdef VBOX_WITH_HEADLESS
5206 if ( strType == "headless"
5207 || strType == "capture"
5208#ifdef VBOX_WITH_VRDP
5209 || strType == "vrdp"
5210#endif
5211 )
5212 {
5213 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
5214 Assert(sz >= sizeof(VBoxHeadless_exe));
5215 strcpy(cmd, VBoxHeadless_exe);
5216
5217 Utf8Str idStr = mData->mUuid.toString();
5218 /* Leave space for 2 args, as "headless" needs --vrdp off on non-OSE. */
5219# ifdef RT_OS_WINDOWS
5220 const char * args[] = {szPath, "--startvm", idStr.c_str(), 0, 0, 0 };
5221# else
5222 Utf8Str strName = mUserData->mName;
5223 const char * args[] = {szPath, "--comment", strName.c_str(), "--startvm", idStr.c_str(), 0, 0, 0 };
5224# endif
5225#ifdef VBOX_WITH_VRDP
5226 if (strType == "headless")
5227 {
5228 unsigned pos = RT_ELEMENTS(args) - 3;
5229 args[pos++] = "--vrdp";
5230 args[pos] = "off";
5231 }
5232#endif
5233 if (strType == "capture")
5234 {
5235 unsigned pos = RT_ELEMENTS(args) - 3;
5236 args[pos] = "--capture";
5237 }
5238 vrc = RTProcCreate(szPath, args, env, 0, &pid);
5239 }
5240#else /* !VBOX_WITH_HEADLESS */
5241 if (0)
5242 ;
5243#endif /* !VBOX_WITH_HEADLESS */
5244 else
5245 {
5246 RTEnvDestroy(env);
5247 return setError(E_INVALIDARG,
5248 tr("Invalid session type: '%s'"),
5249 strType.c_str());
5250 }
5251
5252 RTEnvDestroy(env);
5253
5254 if (RT_FAILURE(vrc))
5255 return setError(VBOX_E_IPRT_ERROR,
5256 tr("Could not launch a process for the machine '%ls' (%Rrc)"),
5257 mUserData->mName.raw(), vrc);
5258
5259 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
5260
5261 /*
5262 * Note that we don't leave the lock here before calling the client,
5263 * because it doesn't need to call us back if called with a NULL argument.
5264 * Leaving the lock herer is dangerous because we didn't prepare the
5265 * launch data yet, but the client we've just started may happen to be
5266 * too fast and call openSession() that will fail (because of PID, etc.),
5267 * so that the Machine will never get out of the Spawning session state.
5268 */
5269
5270 /* inform the session that it will be a remote one */
5271 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
5272 HRESULT rc = aControl->AssignMachine(NULL);
5273 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
5274
5275 if (FAILED(rc))
5276 {
5277 /* restore the session state */
5278 mData->mSession.mState = SessionState_Closed;
5279 /* The failure may occur w/o any error info (from RPC), so provide one */
5280 return setError(VBOX_E_VM_ERROR,
5281 tr("Failed to assign the machine to the session (%Rrc)"), rc);
5282 }
5283
5284 /* attach launch data to the machine */
5285 Assert(mData->mSession.mPid == NIL_RTPROCESS);
5286 mData->mSession.mRemoteControls.push_back (aControl);
5287 mData->mSession.mProgress = aProgress;
5288 mData->mSession.mPid = pid;
5289 mData->mSession.mState = SessionState_Spawning;
5290 mData->mSession.mType = strType;
5291
5292 LogFlowThisFuncLeave();
5293 return S_OK;
5294}
5295
5296/**
5297 * @note Locks this object for writing, calls the client process
5298 * (outside the lock).
5299 */
5300HRESULT Machine::openExistingSession(IInternalSessionControl *aControl)
5301{
5302 LogFlowThisFuncEnter();
5303
5304 AssertReturn(aControl, E_FAIL);
5305
5306 AutoCaller autoCaller(this);
5307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5308
5309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5310
5311 if (!mData->mRegistered)
5312 return setError(E_UNEXPECTED,
5313 tr("The machine '%ls' is not registered"),
5314 mUserData->mName.raw());
5315
5316 LogFlowThisFunc(("mSession.state=%s\n", Global::stringifySessionState(mData->mSession.mState)));
5317
5318 if (mData->mSession.mState != SessionState_Open)
5319 return setError(VBOX_E_INVALID_SESSION_STATE,
5320 tr("The machine '%ls' does not have an open session"),
5321 mUserData->mName.raw());
5322
5323 ComAssertRet(!mData->mSession.mDirectControl.isNull(), E_FAIL);
5324
5325 // copy member variables before leaving lock
5326 ComPtr<IInternalSessionControl> pDirectControl = mData->mSession.mDirectControl;
5327 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
5328 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
5329
5330 /*
5331 * Leave the lock before calling the client process. It's safe here
5332 * since the only thing to do after we get the lock again is to add
5333 * the remote control to the list (which doesn't directly influence
5334 * anything).
5335 */
5336 alock.leave();
5337
5338 // get the console from the direct session (this is a remote call)
5339 ComPtr<IConsole> pConsole;
5340 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
5341 HRESULT rc = pDirectControl->GetRemoteConsole(pConsole.asOutParam());
5342 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
5343 if (FAILED (rc))
5344 /* The failure may occur w/o any error info (from RPC), so provide one */
5345 return setError (VBOX_E_VM_ERROR,
5346 tr ("Failed to get a console object from the direct session (%Rrc)"), rc);
5347
5348 ComAssertRet(!pConsole.isNull(), E_FAIL);
5349
5350 /* attach the remote session to the machine */
5351 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
5352 rc = aControl->AssignRemoteMachine(pSessionMachine, pConsole);
5353 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
5354
5355 /* The failure may occur w/o any error info (from RPC), so provide one */
5356 if (FAILED(rc))
5357 return setError(VBOX_E_VM_ERROR,
5358 tr("Failed to assign the machine to the session (%Rrc)"),
5359 rc);
5360
5361 alock.enter();
5362
5363 /* need to revalidate the state after entering the lock again */
5364 if (mData->mSession.mState != SessionState_Open)
5365 {
5366 aControl->Uninitialize();
5367
5368 return setError(VBOX_E_INVALID_SESSION_STATE,
5369 tr("The machine '%ls' does not have an open session"),
5370 mUserData->mName.raw());
5371 }
5372
5373 /* store the control in the list */
5374 mData->mSession.mRemoteControls.push_back(aControl);
5375
5376 LogFlowThisFuncLeave();
5377 return S_OK;
5378}
5379
5380/**
5381 * Returns @c true if the given machine has an open direct session and returns
5382 * the session machine instance and additional session data (on some platforms)
5383 * if so.
5384 *
5385 * Note that when the method returns @c false, the arguments remain unchanged.
5386 *
5387 * @param aMachine Session machine object.
5388 * @param aControl Direct session control object (optional).
5389 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
5390 *
5391 * @note locks this object for reading.
5392 */
5393#if defined(RT_OS_WINDOWS)
5394bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
5395 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
5396 HANDLE *aIPCSem /*= NULL*/,
5397 bool aAllowClosing /*= false*/)
5398#elif defined(RT_OS_OS2)
5399bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
5400 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
5401 HMTX *aIPCSem /*= NULL*/,
5402 bool aAllowClosing /*= false*/)
5403#else
5404bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
5405 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
5406 bool aAllowClosing /*= false*/)
5407#endif
5408{
5409 AutoLimitedCaller autoCaller(this);
5410 AssertComRCReturn(autoCaller.rc(), false);
5411
5412 /* just return false for inaccessible machines */
5413 if (autoCaller.state() != Ready)
5414 return false;
5415
5416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5417
5418 if (mData->mSession.mState == SessionState_Open ||
5419 (aAllowClosing && mData->mSession.mState == SessionState_Closing))
5420 {
5421 AssertReturn(!mData->mSession.mMachine.isNull(), false);
5422
5423 aMachine = mData->mSession.mMachine;
5424
5425 if (aControl != NULL)
5426 *aControl = mData->mSession.mDirectControl;
5427
5428#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5429 /* Additional session data */
5430 if (aIPCSem != NULL)
5431 *aIPCSem = aMachine->mIPCSem;
5432#endif
5433 return true;
5434 }
5435
5436 return false;
5437}
5438
5439/**
5440 * Returns @c true if the given machine has an spawning direct session and
5441 * returns and additional session data (on some platforms) if so.
5442 *
5443 * Note that when the method returns @c false, the arguments remain unchanged.
5444 *
5445 * @param aPID PID of the spawned direct session process.
5446 *
5447 * @note locks this object for reading.
5448 */
5449#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5450bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
5451#else
5452bool Machine::isSessionSpawning()
5453#endif
5454{
5455 AutoLimitedCaller autoCaller(this);
5456 AssertComRCReturn(autoCaller.rc(), false);
5457
5458 /* just return false for inaccessible machines */
5459 if (autoCaller.state() != Ready)
5460 return false;
5461
5462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5463
5464 if (mData->mSession.mState == SessionState_Spawning)
5465 {
5466#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5467 /* Additional session data */
5468 if (aPID != NULL)
5469 {
5470 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
5471 *aPID = mData->mSession.mPid;
5472 }
5473#endif
5474 return true;
5475 }
5476
5477 return false;
5478}
5479
5480/**
5481 * Called from the client watcher thread to check for unexpected client process
5482 * death during Session_Spawning state (e.g. before it successfully opened a
5483 * direct session).
5484 *
5485 * On Win32 and on OS/2, this method is called only when we've got the
5486 * direct client's process termination notification, so it always returns @c
5487 * true.
5488 *
5489 * On other platforms, this method returns @c true if the client process is
5490 * terminated and @c false if it's still alive.
5491 *
5492 * @note Locks this object for writing.
5493 */
5494bool Machine::checkForSpawnFailure()
5495{
5496 AutoCaller autoCaller(this);
5497 if (!autoCaller.isOk())
5498 {
5499 /* nothing to do */
5500 LogFlowThisFunc(("Already uninitialized!\n"));
5501 return true;
5502 }
5503
5504 /* VirtualBox::addProcessToReap() needs a write lock */
5505 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
5506
5507 if (mData->mSession.mState != SessionState_Spawning)
5508 {
5509 /* nothing to do */
5510 LogFlowThisFunc(("Not spawning any more!\n"));
5511 return true;
5512 }
5513
5514 HRESULT rc = S_OK;
5515
5516#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5517
5518 /* the process was already unexpectedly terminated, we just need to set an
5519 * error and finalize session spawning */
5520 rc = setError(E_FAIL,
5521 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
5522 getName().raw());
5523#else
5524
5525 /* PID not yet initialized, skip check. */
5526 if (mData->mSession.mPid == NIL_RTPROCESS)
5527 return false;
5528
5529 RTPROCSTATUS status;
5530 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
5531 &status);
5532
5533 if (vrc != VERR_PROCESS_RUNNING)
5534 rc = setError(E_FAIL,
5535 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
5536 getName().raw());
5537#endif
5538
5539 if (FAILED(rc))
5540 {
5541 /* Close the remote session, remove the remote control from the list
5542 * and reset session state to Closed (@note keep the code in sync with
5543 * the relevant part in checkForSpawnFailure()). */
5544
5545 Assert(mData->mSession.mRemoteControls.size() == 1);
5546 if (mData->mSession.mRemoteControls.size() == 1)
5547 {
5548 ErrorInfoKeeper eik;
5549 mData->mSession.mRemoteControls.front()->Uninitialize();
5550 }
5551
5552 mData->mSession.mRemoteControls.clear();
5553 mData->mSession.mState = SessionState_Closed;
5554
5555 /* finalize the progress after setting the state */
5556 if (!mData->mSession.mProgress.isNull())
5557 {
5558 mData->mSession.mProgress->notifyComplete(rc);
5559 mData->mSession.mProgress.setNull();
5560 }
5561
5562 mParent->addProcessToReap(mData->mSession.mPid);
5563 mData->mSession.mPid = NIL_RTPROCESS;
5564
5565 mParent->onSessionStateChange(mData->mUuid, SessionState_Closed);
5566 return true;
5567 }
5568
5569 return false;
5570}
5571
5572/**
5573 * Checks that the registered flag of the machine can be set according to
5574 * the argument and sets it. On success, commits and saves all settings.
5575 *
5576 * @note When this machine is inaccessible, the only valid value for \a
5577 * aRegistered is FALSE (i.e. unregister the machine) because unregistered
5578 * inaccessible machines are not currently supported. Note that unregistering
5579 * an inaccessible machine will \b uninitialize this machine object. Therefore,
5580 * the caller must make sure there are no active Machine::addCaller() calls
5581 * on the current thread because this will block Machine::uninit().
5582 *
5583 * @note Must be called from mParent's write lock. Locks this object and
5584 * children for writing.
5585 */
5586HRESULT Machine::trySetRegistered(BOOL argNewRegistered)
5587{
5588 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
5589
5590 AutoLimitedCaller autoCaller(this);
5591 AssertComRCReturnRC(autoCaller.rc());
5592
5593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5594
5595 /* wait for state dependants to drop to zero */
5596 ensureNoStateDependencies();
5597
5598 ComAssertRet(mData->mRegistered != argNewRegistered, E_FAIL);
5599
5600 if (!mData->mAccessible)
5601 {
5602 /* A special case: the machine is not accessible. */
5603
5604 /* inaccessible machines can only be unregistered */
5605 AssertReturn(!argNewRegistered, E_FAIL);
5606
5607 /* Uninitialize ourselves here because currently there may be no
5608 * unregistered that are inaccessible (this state combination is not
5609 * supported). Note releasing the caller and leaving the lock before
5610 * calling uninit() */
5611
5612 alock.leave();
5613 autoCaller.release();
5614
5615 uninit();
5616
5617 return S_OK;
5618 }
5619
5620 AssertReturn(autoCaller.state() == Ready, E_FAIL);
5621
5622 if (argNewRegistered)
5623 {
5624 if (mData->mRegistered)
5625 return setError(VBOX_E_INVALID_OBJECT_STATE,
5626 tr("The machine '%ls' with UUID {%s} is already registered"),
5627 mUserData->mName.raw(),
5628 mData->mUuid.toString().raw());
5629 }
5630 else
5631 {
5632 if (mData->mMachineState == MachineState_Saved)
5633 return setError(VBOX_E_INVALID_VM_STATE,
5634 tr("Cannot unregister the machine '%ls' because it is in the Saved state"),
5635 mUserData->mName.raw());
5636
5637 size_t snapshotCount = 0;
5638 if (mData->mFirstSnapshot)
5639 snapshotCount = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5640 if (snapshotCount)
5641 return setError(VBOX_E_INVALID_OBJECT_STATE,
5642 tr("Cannot unregister the machine '%ls' because it has %d snapshots"),
5643 mUserData->mName.raw(), snapshotCount);
5644
5645 if (mData->mSession.mState != SessionState_Closed)
5646 return setError(VBOX_E_INVALID_OBJECT_STATE,
5647 tr("Cannot unregister the machine '%ls' because it has an open session"),
5648 mUserData->mName.raw());
5649
5650 if (mMediaData->mAttachments.size() != 0)
5651 return setError(VBOX_E_INVALID_OBJECT_STATE,
5652 tr("Cannot unregister the machine '%ls' because it has %d medium attachments"),
5653 mUserData->mName.raw(),
5654 mMediaData->mAttachments.size());
5655
5656 /* Note that we do not prevent unregistration of a DVD or Floppy image
5657 * is attached: as opposed to hard disks detaching such an image
5658 * implicitly in this method (which we will do below) won't have any
5659 * side effects (like detached orphan base and diff hard disks etc).*/
5660 }
5661
5662 HRESULT rc = S_OK;
5663
5664 // Ensure the settings are saved. If we are going to be registered and
5665 // no config file exists yet, create it by calling saveSettings() too.
5666 if ( (m_flModifications)
5667 || (argNewRegistered && !mData->m_pMachineConfigFile->fileExists())
5668 )
5669 {
5670 rc = saveSettings(NULL);
5671 // no need to check whether VirtualBox.xml needs saving too since
5672 // we can't have a machine XML file rename pending
5673 if (FAILED(rc)) return rc;
5674 }
5675
5676 /* more config checking goes here */
5677
5678 if (SUCCEEDED(rc))
5679 {
5680 /* we may have had implicit modifications we want to fix on success */
5681 commit();
5682
5683 mData->mRegistered = argNewRegistered;
5684 }
5685 else
5686 {
5687 /* we may have had implicit modifications we want to cancel on failure*/
5688 rollback(false /* aNotify */);
5689 }
5690
5691 return rc;
5692}
5693
5694/**
5695 * Increases the number of objects dependent on the machine state or on the
5696 * registered state. Guarantees that these two states will not change at least
5697 * until #releaseStateDependency() is called.
5698 *
5699 * Depending on the @a aDepType value, additional state checks may be made.
5700 * These checks will set extended error info on failure. See
5701 * #checkStateDependency() for more info.
5702 *
5703 * If this method returns a failure, the dependency is not added and the caller
5704 * is not allowed to rely on any particular machine state or registration state
5705 * value and may return the failed result code to the upper level.
5706 *
5707 * @param aDepType Dependency type to add.
5708 * @param aState Current machine state (NULL if not interested).
5709 * @param aRegistered Current registered state (NULL if not interested).
5710 *
5711 * @note Locks this object for writing.
5712 */
5713HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
5714 MachineState_T *aState /* = NULL */,
5715 BOOL *aRegistered /* = NULL */)
5716{
5717 AutoCaller autoCaller(this);
5718 AssertComRCReturnRC(autoCaller.rc());
5719
5720 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5721
5722 HRESULT rc = checkStateDependency(aDepType);
5723 if (FAILED(rc)) return rc;
5724
5725 {
5726 if (mData->mMachineStateChangePending != 0)
5727 {
5728 /* ensureNoStateDependencies() is waiting for state dependencies to
5729 * drop to zero so don't add more. It may make sense to wait a bit
5730 * and retry before reporting an error (since the pending state
5731 * transition should be really quick) but let's just assert for
5732 * now to see if it ever happens on practice. */
5733
5734 AssertFailed();
5735
5736 return setError(E_ACCESSDENIED,
5737 tr("Machine state change is in progress. Please retry the operation later."));
5738 }
5739
5740 ++mData->mMachineStateDeps;
5741 Assert(mData->mMachineStateDeps != 0 /* overflow */);
5742 }
5743
5744 if (aState)
5745 *aState = mData->mMachineState;
5746 if (aRegistered)
5747 *aRegistered = mData->mRegistered;
5748
5749 return S_OK;
5750}
5751
5752/**
5753 * Decreases the number of objects dependent on the machine state.
5754 * Must always complete the #addStateDependency() call after the state
5755 * dependency is no more necessary.
5756 */
5757void Machine::releaseStateDependency()
5758{
5759 AutoCaller autoCaller(this);
5760 AssertComRCReturnVoid(autoCaller.rc());
5761
5762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5763
5764 /* releaseStateDependency() w/o addStateDependency()? */
5765 AssertReturnVoid(mData->mMachineStateDeps != 0);
5766 -- mData->mMachineStateDeps;
5767
5768 if (mData->mMachineStateDeps == 0)
5769 {
5770 /* inform ensureNoStateDependencies() that there are no more deps */
5771 if (mData->mMachineStateChangePending != 0)
5772 {
5773 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
5774 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
5775 }
5776 }
5777}
5778
5779// protected methods
5780/////////////////////////////////////////////////////////////////////////////
5781
5782/**
5783 * Performs machine state checks based on the @a aDepType value. If a check
5784 * fails, this method will set extended error info, otherwise it will return
5785 * S_OK. It is supposed, that on failure, the caller will immedieately return
5786 * the return value of this method to the upper level.
5787 *
5788 * When @a aDepType is AnyStateDep, this method always returns S_OK.
5789 *
5790 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
5791 * current state of this machine object allows to change settings of the
5792 * machine (i.e. the machine is not registered, or registered but not running
5793 * and not saved). It is useful to call this method from Machine setters
5794 * before performing any change.
5795 *
5796 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
5797 * as for MutableStateDep except that if the machine is saved, S_OK is also
5798 * returned. This is useful in setters which allow changing machine
5799 * properties when it is in the saved state.
5800 *
5801 * @param aDepType Dependency type to check.
5802 *
5803 * @note Non Machine based classes should use #addStateDependency() and
5804 * #releaseStateDependency() methods or the smart AutoStateDependency
5805 * template.
5806 *
5807 * @note This method must be called from under this object's read or write
5808 * lock.
5809 */
5810HRESULT Machine::checkStateDependency(StateDependency aDepType)
5811{
5812 switch (aDepType)
5813 {
5814 case AnyStateDep:
5815 {
5816 break;
5817 }
5818 case MutableStateDep:
5819 {
5820 if ( mData->mRegistered
5821 && ( getClassID() != clsidSessionMachine /** @todo This was just convered raw; Check if Running and Paused should actually be included here... (Live Migration) */
5822 || ( mData->mMachineState != MachineState_Paused
5823 && mData->mMachineState != MachineState_Running
5824 && mData->mMachineState != MachineState_Aborted
5825 && mData->mMachineState != MachineState_Teleported
5826 && mData->mMachineState != MachineState_PoweredOff
5827 )
5828 )
5829 )
5830 return setError(VBOX_E_INVALID_VM_STATE,
5831 tr("The machine is not mutable (state is %s)"),
5832 Global::stringifyMachineState(mData->mMachineState));
5833 break;
5834 }
5835 case MutableOrSavedStateDep:
5836 {
5837 if ( mData->mRegistered
5838 && ( getClassID() != clsidSessionMachine /** @todo This was just convered raw; Check if Running and Paused should actually be included here... (Live Migration) */
5839 || ( mData->mMachineState != MachineState_Paused
5840 && mData->mMachineState != MachineState_Running
5841 && mData->mMachineState != MachineState_Aborted
5842 && mData->mMachineState != MachineState_Teleported
5843 && mData->mMachineState != MachineState_Saved
5844 && mData->mMachineState != MachineState_PoweredOff
5845 )
5846 )
5847 )
5848 return setError(VBOX_E_INVALID_VM_STATE,
5849 tr("The machine is not mutable (state is %s)"),
5850 Global::stringifyMachineState(mData->mMachineState));
5851 break;
5852 }
5853 }
5854
5855 return S_OK;
5856}
5857
5858/**
5859 * Helper to initialize all associated child objects and allocate data
5860 * structures.
5861 *
5862 * This method must be called as a part of the object's initialization procedure
5863 * (usually done in the #init() method).
5864 *
5865 * @note Must be called only from #init() or from #registeredInit().
5866 */
5867HRESULT Machine::initDataAndChildObjects()
5868{
5869 AutoCaller autoCaller(this);
5870 AssertComRCReturnRC(autoCaller.rc());
5871 AssertComRCReturn(autoCaller.state() == InInit ||
5872 autoCaller.state() == Limited, E_FAIL);
5873
5874 AssertReturn(!mData->mAccessible, E_FAIL);
5875
5876 /* allocate data structures */
5877 mSSData.allocate();
5878 mUserData.allocate();
5879 mHWData.allocate();
5880 mMediaData.allocate();
5881 mStorageControllers.allocate();
5882
5883 /* initialize mOSTypeId */
5884 mUserData->mOSTypeId = mParent->getUnknownOSType()->id();
5885
5886 /* create associated BIOS settings object */
5887 unconst(mBIOSSettings).createObject();
5888 mBIOSSettings->init(this);
5889
5890#ifdef VBOX_WITH_VRDP
5891 /* create an associated VRDPServer object (default is disabled) */
5892 unconst(mVRDPServer).createObject();
5893 mVRDPServer->init(this);
5894#endif
5895
5896 /* create associated serial port objects */
5897 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
5898 {
5899 unconst(mSerialPorts[slot]).createObject();
5900 mSerialPorts[slot]->init(this, slot);
5901 }
5902
5903 /* create associated parallel port objects */
5904 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
5905 {
5906 unconst(mParallelPorts[slot]).createObject();
5907 mParallelPorts[slot]->init(this, slot);
5908 }
5909
5910 /* create the audio adapter object (always present, default is disabled) */
5911 unconst(mAudioAdapter).createObject();
5912 mAudioAdapter->init(this);
5913
5914 /* create the USB controller object (always present, default is disabled) */
5915 unconst(mUSBController).createObject();
5916 mUSBController->init(this);
5917
5918 /* create associated network adapter objects */
5919 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot ++)
5920 {
5921 unconst(mNetworkAdapters[slot]).createObject();
5922 mNetworkAdapters[slot]->init(this, slot);
5923 }
5924
5925 return S_OK;
5926}
5927
5928/**
5929 * Helper to uninitialize all associated child objects and to free all data
5930 * structures.
5931 *
5932 * This method must be called as a part of the object's uninitialization
5933 * procedure (usually done in the #uninit() method).
5934 *
5935 * @note Must be called only from #uninit() or from #registeredInit().
5936 */
5937void Machine::uninitDataAndChildObjects()
5938{
5939 AutoCaller autoCaller(this);
5940 AssertComRCReturnVoid(autoCaller.rc());
5941 AssertComRCReturnVoid( autoCaller.state() == InUninit
5942 || autoCaller.state() == Limited);
5943
5944 /* uninit all children using addDependentChild()/removeDependentChild()
5945 * in their init()/uninit() methods */
5946 uninitDependentChildren();
5947
5948 /* tell all our other child objects we've been uninitialized */
5949
5950 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
5951 {
5952 if (mNetworkAdapters[slot])
5953 {
5954 mNetworkAdapters[slot]->uninit();
5955 unconst(mNetworkAdapters[slot]).setNull();
5956 }
5957 }
5958
5959 if (mUSBController)
5960 {
5961 mUSBController->uninit();
5962 unconst(mUSBController).setNull();
5963 }
5964
5965 if (mAudioAdapter)
5966 {
5967 mAudioAdapter->uninit();
5968 unconst(mAudioAdapter).setNull();
5969 }
5970
5971 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
5972 {
5973 if (mParallelPorts[slot])
5974 {
5975 mParallelPorts[slot]->uninit();
5976 unconst(mParallelPorts[slot]).setNull();
5977 }
5978 }
5979
5980 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
5981 {
5982 if (mSerialPorts[slot])
5983 {
5984 mSerialPorts[slot]->uninit();
5985 unconst(mSerialPorts[slot]).setNull();
5986 }
5987 }
5988
5989#ifdef VBOX_WITH_VRDP
5990 if (mVRDPServer)
5991 {
5992 mVRDPServer->uninit();
5993 unconst(mVRDPServer).setNull();
5994 }
5995#endif
5996
5997 if (mBIOSSettings)
5998 {
5999 mBIOSSettings->uninit();
6000 unconst(mBIOSSettings).setNull();
6001 }
6002
6003 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
6004 * instance is uninitialized; SessionMachine instances refer to real
6005 * Machine hard disks). This is necessary for a clean re-initialization of
6006 * the VM after successfully re-checking the accessibility state. Note
6007 * that in case of normal Machine or SnapshotMachine uninitialization (as
6008 * a result of unregistering or discarding the snapshot), outdated hard
6009 * disk attachments will already be uninitialized and deleted, so this
6010 * code will not affect them. */
6011 VBoxClsID clsid = getClassID();
6012 if ( !!mMediaData
6013 && (clsid == clsidMachine || clsid == clsidSnapshotMachine)
6014 )
6015 {
6016 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
6017 it != mMediaData->mAttachments.end();
6018 ++it)
6019 {
6020 ComObjPtr<Medium> hd = (*it)->getMedium();
6021 if (hd.isNull())
6022 continue;
6023 HRESULT rc = hd->detachFrom(mData->mUuid, getSnapshotId());
6024 AssertComRC(rc);
6025 }
6026 }
6027
6028 if (getClassID() == clsidMachine)
6029 {
6030 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
6031 if (mData->mFirstSnapshot)
6032 {
6033 // snapshots tree is protected by media write lock; strictly
6034 // this isn't necessary here since we're deleting the entire
6035 // machine, but otherwise we assert in Snapshot::uninit()
6036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6037 mData->mFirstSnapshot->uninit();
6038 mData->mFirstSnapshot.setNull();
6039 }
6040
6041 mData->mCurrentSnapshot.setNull();
6042 }
6043
6044 /* free data structures (the essential mData structure is not freed here
6045 * since it may be still in use) */
6046 mMediaData.free();
6047 mStorageControllers.free();
6048 mHWData.free();
6049 mUserData.free();
6050 mSSData.free();
6051}
6052
6053/**
6054 * Returns a pointer to the Machine object for this machine that acts like a
6055 * parent for complex machine data objects such as shared folders, etc.
6056 *
6057 * For primary Machine objects and for SnapshotMachine objects, returns this
6058 * object's pointer itself. For SessoinMachine objects, returns the peer
6059 * (primary) machine pointer.
6060 */
6061Machine* Machine::getMachine()
6062{
6063 if (getClassID() == clsidSessionMachine)
6064 return (Machine*)mPeer;
6065 return this;
6066}
6067
6068/**
6069 * Makes sure that there are no machine state dependants. If necessary, waits
6070 * for the number of dependants to drop to zero.
6071 *
6072 * Make sure this method is called from under this object's write lock to
6073 * guarantee that no new dependants may be added when this method returns
6074 * control to the caller.
6075 *
6076 * @note Locks this object for writing. The lock will be released while waiting
6077 * (if necessary).
6078 *
6079 * @warning To be used only in methods that change the machine state!
6080 */
6081void Machine::ensureNoStateDependencies()
6082{
6083 AssertReturnVoid(isWriteLockOnCurrentThread());
6084
6085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6086
6087 /* Wait for all state dependants if necessary */
6088 if (mData->mMachineStateDeps != 0)
6089 {
6090 /* lazy semaphore creation */
6091 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
6092 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
6093
6094 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
6095 mData->mMachineStateDeps));
6096
6097 ++mData->mMachineStateChangePending;
6098
6099 /* reset the semaphore before waiting, the last dependant will signal
6100 * it */
6101 RTSemEventMultiReset(mData->mMachineStateDepsSem);
6102
6103 alock.leave();
6104
6105 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
6106
6107 alock.enter();
6108
6109 -- mData->mMachineStateChangePending;
6110 }
6111}
6112
6113/**
6114 * Changes the machine state and informs callbacks.
6115 *
6116 * This method is not intended to fail so it either returns S_OK or asserts (and
6117 * returns a failure).
6118 *
6119 * @note Locks this object for writing.
6120 */
6121HRESULT Machine::setMachineState(MachineState_T aMachineState)
6122{
6123 LogFlowThisFuncEnter();
6124 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
6125
6126 AutoCaller autoCaller(this);
6127 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6128
6129 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6130
6131 /* wait for state dependants to drop to zero */
6132 ensureNoStateDependencies();
6133
6134 if (mData->mMachineState != aMachineState)
6135 {
6136 mData->mMachineState = aMachineState;
6137
6138 RTTimeNow(&mData->mLastStateChange);
6139
6140 mParent->onMachineStateChange(mData->mUuid, aMachineState);
6141 }
6142
6143 LogFlowThisFuncLeave();
6144 return S_OK;
6145}
6146
6147/**
6148 * Searches for a shared folder with the given logical name
6149 * in the collection of shared folders.
6150 *
6151 * @param aName logical name of the shared folder
6152 * @param aSharedFolder where to return the found object
6153 * @param aSetError whether to set the error info if the folder is
6154 * not found
6155 * @return
6156 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
6157 *
6158 * @note
6159 * must be called from under the object's lock!
6160 */
6161HRESULT Machine::findSharedFolder(CBSTR aName,
6162 ComObjPtr<SharedFolder> &aSharedFolder,
6163 bool aSetError /* = false */)
6164{
6165 bool found = false;
6166 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
6167 !found && it != mHWData->mSharedFolders.end();
6168 ++it)
6169 {
6170 AutoWriteLock alock(*it COMMA_LOCKVAL_SRC_POS);
6171 found = (*it)->getName() == aName;
6172 if (found)
6173 aSharedFolder = *it;
6174 }
6175
6176 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
6177
6178 if (aSetError && !found)
6179 setError(rc, tr("Could not find a shared folder named '%ls'"), aName);
6180
6181 return rc;
6182}
6183
6184/**
6185 * Loads all the VM settings by walking down the <Machine> node.
6186 *
6187 * @param aRegistered true when the machine is being loaded on VirtualBox
6188 * startup
6189 *
6190 * @note This method is intended to be called only from init(), so it assumes
6191 * all machine data fields have appropriate default values when it is called.
6192 *
6193 * @note Doesn't lock any objects.
6194 */
6195HRESULT Machine::loadSettings(bool aRegistered)
6196{
6197 LogFlowThisFuncEnter();
6198 AssertReturn(getClassID() == clsidMachine, E_FAIL);
6199
6200 AutoCaller autoCaller(this);
6201 AssertReturn(autoCaller.state() == InInit, E_FAIL);
6202
6203 HRESULT rc = S_OK;
6204
6205 try
6206 {
6207 Assert(mData->m_pMachineConfigFile == NULL);
6208
6209 // load and parse machine XML; this will throw on XML or logic errors
6210 mData->m_pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
6211
6212 /* If the stored UUID is not empty, it means the registered machine
6213 * is being loaded. Compare the loaded UUID with the stored one taken
6214 * from the global registry. */
6215 if (!mData->mUuid.isEmpty())
6216 {
6217 if (mData->mUuid != mData->m_pMachineConfigFile->uuid)
6218 {
6219 throw setError(E_FAIL,
6220 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
6221 mData->m_pMachineConfigFile->uuid.raw(),
6222 mData->m_strConfigFileFull.raw(),
6223 mData->mUuid.toString().raw(),
6224 mParent->settingsFilePath().raw());
6225 }
6226 }
6227 else
6228 unconst(mData->mUuid) = mData->m_pMachineConfigFile->uuid;
6229
6230 /* name (required) */
6231 mUserData->mName = mData->m_pMachineConfigFile->strName;
6232
6233 /* nameSync (optional, default is true) */
6234 mUserData->mNameSync = mData->m_pMachineConfigFile->fNameSync;
6235
6236 mUserData->mDescription = mData->m_pMachineConfigFile->strDescription;
6237
6238 // guest OS type
6239 mUserData->mOSTypeId = mData->m_pMachineConfigFile->strOsType;
6240 /* look up the object by Id to check it is valid */
6241 ComPtr<IGuestOSType> guestOSType;
6242 rc = mParent->GetGuestOSType(mUserData->mOSTypeId,
6243 guestOSType.asOutParam());
6244 if (FAILED(rc)) throw rc;
6245
6246 // stateFile (optional)
6247 if (mData->m_pMachineConfigFile->strStateFile.isEmpty())
6248 mSSData->mStateFilePath.setNull();
6249 else
6250 {
6251 Utf8Str stateFilePathFull(mData->m_pMachineConfigFile->strStateFile);
6252 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
6253 if (RT_FAILURE(vrc))
6254 throw setError(E_FAIL,
6255 tr("Invalid saved state file path '%s' (%Rrc)"),
6256 mData->m_pMachineConfigFile->strStateFile.raw(),
6257 vrc);
6258 mSSData->mStateFilePath = stateFilePathFull;
6259 }
6260
6261 /* snapshotFolder (optional) */
6262 rc = COMSETTER(SnapshotFolder)(Bstr(mData->m_pMachineConfigFile->strSnapshotFolder));
6263 if (FAILED(rc)) throw rc;
6264
6265 /* currentStateModified (optional, default is true) */
6266 mData->mCurrentStateModified = mData->m_pMachineConfigFile->fCurrentStateModified;
6267
6268 mData->mLastStateChange = mData->m_pMachineConfigFile->timeLastStateChange;
6269
6270 /* teleportation */
6271 mUserData->mTeleporterEnabled = mData->m_pMachineConfigFile->fTeleporterEnabled;
6272 mUserData->mTeleporterPort = mData->m_pMachineConfigFile->uTeleporterPort;
6273 mUserData->mTeleporterAddress = mData->m_pMachineConfigFile->strTeleporterAddress;
6274 mUserData->mTeleporterPassword = mData->m_pMachineConfigFile->strTeleporterPassword;
6275
6276 /* RTC */
6277 mUserData->mRTCUseUTC = mData->m_pMachineConfigFile->fRTCUseUTC;
6278
6279 /*
6280 * note: all mUserData members must be assigned prior this point because
6281 * we need to commit changes in order to let mUserData be shared by all
6282 * snapshot machine instances.
6283 */
6284 mUserData.commitCopy();
6285
6286 /* Snapshot node (optional) */
6287 size_t cRootSnapshots;
6288 if ((cRootSnapshots = mData->m_pMachineConfigFile->llFirstSnapshot.size()))
6289 {
6290 // there must be only one root snapshot
6291 Assert(cRootSnapshots == 1);
6292
6293 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
6294
6295 rc = loadSnapshot(snap,
6296 mData->m_pMachineConfigFile->uuidCurrentSnapshot,
6297 NULL); // no parent == first snapshot
6298 if (FAILED(rc)) throw rc;
6299 }
6300
6301 /* Hardware node (required) */
6302 rc = loadHardware(mData->m_pMachineConfigFile->hardwareMachine);
6303 if (FAILED(rc)) throw rc;
6304
6305 /* Load storage controllers */
6306 rc = loadStorageControllers(mData->m_pMachineConfigFile->storageMachine, aRegistered);
6307 if (FAILED(rc)) throw rc;
6308
6309 /*
6310 * NOTE: the assignment below must be the last thing to do,
6311 * otherwise it will be not possible to change the settings
6312 * somewehere in the code above because all setters will be
6313 * blocked by checkStateDependency(MutableStateDep).
6314 */
6315
6316 /* set the machine state to Aborted or Saved when appropriate */
6317 if (mData->m_pMachineConfigFile->fAborted)
6318 {
6319 Assert(!mSSData->mStateFilePath.isEmpty());
6320 mSSData->mStateFilePath.setNull();
6321
6322 /* no need to use setMachineState() during init() */
6323 mData->mMachineState = MachineState_Aborted;
6324 }
6325 else if (!mSSData->mStateFilePath.isEmpty())
6326 {
6327 /* no need to use setMachineState() during init() */
6328 mData->mMachineState = MachineState_Saved;
6329 }
6330
6331 // after loading settings, we are no longer different from the XML on disk
6332 m_flModifications = 0;
6333 }
6334 catch (HRESULT err)
6335 {
6336 /* we assume that error info is set by the thrower */
6337 rc = err;
6338 }
6339 catch (...)
6340 {
6341 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
6342 }
6343
6344 LogFlowThisFuncLeave();
6345 return rc;
6346}
6347
6348/**
6349 * Recursively loads all snapshots starting from the given.
6350 *
6351 * @param aNode <Snapshot> node.
6352 * @param aCurSnapshotId Current snapshot ID from the settings file.
6353 * @param aParentSnapshot Parent snapshot.
6354 */
6355HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
6356 const Guid &aCurSnapshotId,
6357 Snapshot *aParentSnapshot)
6358{
6359 AssertReturn(getClassID() == clsidMachine, E_FAIL);
6360
6361 HRESULT rc = S_OK;
6362
6363 Utf8Str strStateFile;
6364 if (!data.strStateFile.isEmpty())
6365 {
6366 /* optional */
6367 strStateFile = data.strStateFile;
6368 int vrc = calculateFullPath(strStateFile, strStateFile);
6369 if (RT_FAILURE(vrc))
6370 return setError(E_FAIL,
6371 tr("Invalid saved state file path '%s' (%Rrc)"),
6372 strStateFile.raw(),
6373 vrc);
6374 }
6375
6376 /* create a snapshot machine object */
6377 ComObjPtr<SnapshotMachine> pSnapshotMachine;
6378 pSnapshotMachine.createObject();
6379 rc = pSnapshotMachine->init(this,
6380 data.hardware,
6381 data.storage,
6382 data.uuid,
6383 strStateFile);
6384 if (FAILED(rc)) return rc;
6385
6386 /* create a snapshot object */
6387 ComObjPtr<Snapshot> pSnapshot;
6388 pSnapshot.createObject();
6389 /* initialize the snapshot */
6390 rc = pSnapshot->init(mParent, // VirtualBox object
6391 data.uuid,
6392 data.strName,
6393 data.strDescription,
6394 data.timestamp,
6395 pSnapshotMachine,
6396 aParentSnapshot);
6397 if (FAILED(rc)) return rc;
6398
6399 /* memorize the first snapshot if necessary */
6400 if (!mData->mFirstSnapshot)
6401 mData->mFirstSnapshot = pSnapshot;
6402
6403 /* memorize the current snapshot when appropriate */
6404 if ( !mData->mCurrentSnapshot
6405 && pSnapshot->getId() == aCurSnapshotId
6406 )
6407 mData->mCurrentSnapshot = pSnapshot;
6408
6409 // now create the children
6410 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
6411 it != data.llChildSnapshots.end();
6412 ++it)
6413 {
6414 const settings::Snapshot &childData = *it;
6415 // recurse
6416 rc = loadSnapshot(childData,
6417 aCurSnapshotId,
6418 pSnapshot); // parent = the one we created above
6419 if (FAILED(rc)) return rc;
6420 }
6421
6422 return rc;
6423}
6424
6425/**
6426 * @param aNode <Hardware> node.
6427 */
6428HRESULT Machine::loadHardware(const settings::Hardware &data)
6429{
6430 AssertReturn(getClassID() == clsidMachine || getClassID() == clsidSnapshotMachine, E_FAIL);
6431
6432 HRESULT rc = S_OK;
6433
6434 try
6435 {
6436 /* The hardware version attribute (optional). */
6437 mHWData->mHWVersion = data.strVersion;
6438 mHWData->mHardwareUUID = data.uuid;
6439
6440 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
6441 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
6442 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
6443 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
6444 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
6445 mHWData->mPAEEnabled = data.fPAE;
6446 mHWData->mSyntheticCpu = data.fSyntheticCpu;
6447
6448 mHWData->mCPUCount = data.cCPUs;
6449 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
6450
6451 // cpu
6452 if (mHWData->mCPUHotPlugEnabled)
6453 {
6454 for (settings::CpuList::const_iterator it = data.llCpus.begin();
6455 it != data.llCpus.end();
6456 ++it)
6457 {
6458 const settings::Cpu &cpu = *it;
6459
6460 mHWData->mCPUAttached[cpu.ulId] = true;
6461 }
6462 }
6463
6464 // cpuid leafs
6465 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
6466 it != data.llCpuIdLeafs.end();
6467 ++it)
6468 {
6469 const settings::CpuIdLeaf &leaf = *it;
6470
6471 switch (leaf.ulId)
6472 {
6473 case 0x0:
6474 case 0x1:
6475 case 0x2:
6476 case 0x3:
6477 case 0x4:
6478 case 0x5:
6479 case 0x6:
6480 case 0x7:
6481 case 0x8:
6482 case 0x9:
6483 case 0xA:
6484 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
6485 break;
6486
6487 case 0x80000000:
6488 case 0x80000001:
6489 case 0x80000002:
6490 case 0x80000003:
6491 case 0x80000004:
6492 case 0x80000005:
6493 case 0x80000006:
6494 case 0x80000007:
6495 case 0x80000008:
6496 case 0x80000009:
6497 case 0x8000000A:
6498 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
6499 break;
6500
6501 default:
6502 /* just ignore */
6503 break;
6504 }
6505 }
6506
6507 mHWData->mMemorySize = data.ulMemorySizeMB;
6508
6509 // boot order
6510 for (size_t i = 0;
6511 i < RT_ELEMENTS(mHWData->mBootOrder);
6512 i++)
6513 {
6514 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
6515 if (it == data.mapBootOrder.end())
6516 mHWData->mBootOrder[i] = DeviceType_Null;
6517 else
6518 mHWData->mBootOrder[i] = it->second;
6519 }
6520
6521 mHWData->mVRAMSize = data.ulVRAMSizeMB;
6522 mHWData->mMonitorCount = data.cMonitors;
6523 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
6524 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
6525 mHWData->mFirmwareType = data.firmwareType;
6526 mHWData->mPointingHidType = data.pointingHidType;
6527 mHWData->mKeyboardHidType = data.keyboardHidType;
6528 mHWData->mHpetEnabled = data.fHpetEnabled;
6529
6530#ifdef VBOX_WITH_VRDP
6531 /* RemoteDisplay */
6532 rc = mVRDPServer->loadSettings(data.vrdpSettings);
6533 if (FAILED(rc)) return rc;
6534#endif
6535
6536 /* BIOS */
6537 rc = mBIOSSettings->loadSettings(data.biosSettings);
6538 if (FAILED(rc)) return rc;
6539
6540 /* USB Controller */
6541 rc = mUSBController->loadSettings(data.usbController);
6542 if (FAILED(rc)) return rc;
6543
6544 // network adapters
6545 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
6546 it != data.llNetworkAdapters.end();
6547 ++it)
6548 {
6549 const settings::NetworkAdapter &nic = *it;
6550
6551 /* slot unicity is guaranteed by XML Schema */
6552 AssertBreak(nic.ulSlot < RT_ELEMENTS(mNetworkAdapters));
6553 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(nic);
6554 if (FAILED(rc)) return rc;
6555 }
6556
6557 // serial ports
6558 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
6559 it != data.llSerialPorts.end();
6560 ++it)
6561 {
6562 const settings::SerialPort &s = *it;
6563
6564 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
6565 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
6566 if (FAILED(rc)) return rc;
6567 }
6568
6569 // parallel ports (optional)
6570 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
6571 it != data.llParallelPorts.end();
6572 ++it)
6573 {
6574 const settings::ParallelPort &p = *it;
6575
6576 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
6577 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
6578 if (FAILED(rc)) return rc;
6579 }
6580
6581 /* AudioAdapter */
6582 rc = mAudioAdapter->loadSettings(data.audioAdapter);
6583 if (FAILED(rc)) return rc;
6584
6585 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
6586 it != data.llSharedFolders.end();
6587 ++it)
6588 {
6589 const settings::SharedFolder &sf = *it;
6590 rc = CreateSharedFolder(Bstr(sf.strName), Bstr(sf.strHostPath), sf.fWritable);
6591 if (FAILED(rc)) return rc;
6592 }
6593
6594 // Clipboard
6595 mHWData->mClipboardMode = data.clipboardMode;
6596
6597 // guest settings
6598 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
6599 mHWData->mStatisticsUpdateInterval = data.ulStatisticsUpdateInterval;
6600
6601 // IO settings
6602 mHWData->mIoMgrType = data.ioSettings.ioMgrType;
6603 mHWData->mIoBackendType = data.ioSettings.ioBackendType;
6604 mHWData->mIoCacheEnabled = data.ioSettings.fIoCacheEnabled;
6605 mHWData->mIoCacheSize = data.ioSettings.ulIoCacheSize;
6606 mHWData->mIoBandwidthMax = data.ioSettings.ulIoBandwidthMax;
6607
6608#ifdef VBOX_WITH_GUEST_PROPS
6609 /* Guest properties (optional) */
6610 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
6611 it != data.llGuestProperties.end();
6612 ++it)
6613 {
6614 const settings::GuestProperty &prop = *it;
6615 uint32_t fFlags = guestProp::NILFLAG;
6616 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
6617 HWData::GuestProperty property = { prop.strName, prop.strValue, prop.timestamp, fFlags };
6618 mHWData->mGuestProperties.push_back(property);
6619 }
6620
6621 mHWData->mPropertyServiceActive = false;
6622 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
6623#endif /* VBOX_WITH_GUEST_PROPS defined */
6624 }
6625 catch(std::bad_alloc &)
6626 {
6627 return E_OUTOFMEMORY;
6628 }
6629
6630 AssertComRC(rc);
6631 return rc;
6632}
6633
6634 /**
6635 * @param aNode <StorageControllers> node.
6636 */
6637HRESULT Machine::loadStorageControllers(const settings::Storage &data,
6638 bool aRegistered,
6639 const Guid *aSnapshotId /* = NULL */)
6640{
6641 AssertReturn(getClassID() == clsidMachine || getClassID() == clsidSnapshotMachine, E_FAIL);
6642
6643 HRESULT rc = S_OK;
6644
6645 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
6646 it != data.llStorageControllers.end();
6647 ++it)
6648 {
6649 const settings::StorageController &ctlData = *it;
6650
6651 ComObjPtr<StorageController> pCtl;
6652 /* Try to find one with the name first. */
6653 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
6654 if (SUCCEEDED(rc))
6655 return setError(VBOX_E_OBJECT_IN_USE,
6656 tr("Storage controller named '%s' already exists"),
6657 ctlData.strName.raw());
6658
6659 pCtl.createObject();
6660 rc = pCtl->init(this,
6661 ctlData.strName,
6662 ctlData.storageBus,
6663 ctlData.ulInstance);
6664 if (FAILED(rc)) return rc;
6665
6666 mStorageControllers->push_back(pCtl);
6667
6668 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
6669 if (FAILED(rc)) return rc;
6670
6671 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
6672 if (FAILED(rc)) return rc;
6673
6674 /* Set IDE emulation settings (only for AHCI controller). */
6675 if (ctlData.controllerType == StorageControllerType_IntelAhci)
6676 {
6677 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
6678 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
6679 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
6680 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
6681 )
6682 return rc;
6683 }
6684
6685 /* Load the attached devices now. */
6686 rc = loadStorageDevices(pCtl,
6687 ctlData,
6688 aRegistered,
6689 aSnapshotId);
6690 if (FAILED(rc)) return rc;
6691 }
6692
6693 return S_OK;
6694}
6695
6696/**
6697 * @param aNode <HardDiskAttachments> node.
6698 * @param aRegistered true when the machine is being loaded on VirtualBox
6699 * startup, or when a snapshot is being loaded (which
6700 * currently can happen on startup only)
6701 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
6702 *
6703 * @note Lock mParent for reading and hard disks for writing before calling.
6704 */
6705HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
6706 const settings::StorageController &data,
6707 bool aRegistered,
6708 const Guid *aSnapshotId /*= NULL*/)
6709{
6710 AssertReturn( (getClassID() == clsidMachine && aSnapshotId == NULL)
6711 || (getClassID() == clsidSnapshotMachine && aSnapshotId != NULL),
6712 E_FAIL);
6713
6714 HRESULT rc = S_OK;
6715
6716 if (!aRegistered && data.llAttachedDevices.size() > 0)
6717 /* when the machine is being loaded (opened) from a file, it cannot
6718 * have hard disks attached (this should not happen normally,
6719 * because we don't allow to attach hard disks to an unregistered
6720 * VM at all */
6721 return setError(E_FAIL,
6722 tr("Unregistered machine '%ls' cannot have storage devices attached (found %d attachments)"),
6723 mUserData->mName.raw(),
6724 data.llAttachedDevices.size());
6725
6726 /* paranoia: detect duplicate attachments */
6727 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
6728 it != data.llAttachedDevices.end();
6729 ++it)
6730 {
6731 for (settings::AttachedDevicesList::const_iterator it2 = it;
6732 it2 != data.llAttachedDevices.end();
6733 ++it2)
6734 {
6735 if (it == it2)
6736 continue;
6737
6738 if ( (*it).lPort == (*it2).lPort
6739 && (*it).lDevice == (*it2).lDevice)
6740 {
6741 return setError(E_FAIL,
6742 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%ls'"),
6743 aStorageController->getName().raw(), (*it).lPort, (*it).lDevice, mUserData->mName.raw());
6744 }
6745 }
6746 }
6747
6748 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
6749 it != data.llAttachedDevices.end();
6750 ++it)
6751 {
6752 const settings::AttachedDevice &dev = *it;
6753 ComObjPtr<Medium> medium;
6754
6755 switch (dev.deviceType)
6756 {
6757 case DeviceType_Floppy:
6758 /* find a floppy by UUID */
6759 if (!dev.uuid.isEmpty())
6760 rc = mParent->findFloppyImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
6761 /* find a floppy by host device name */
6762 else if (!dev.strHostDriveSrc.isEmpty())
6763 {
6764 SafeIfaceArray<IMedium> drivevec;
6765 rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
6766 if (SUCCEEDED(rc))
6767 {
6768 for (size_t i = 0; i < drivevec.size(); ++i)
6769 {
6770 /// @todo eliminate this conversion
6771 ComObjPtr<Medium> med = (Medium *)drivevec[i];
6772 if ( dev.strHostDriveSrc == med->getName()
6773 || dev.strHostDriveSrc == med->getLocation())
6774 {
6775 medium = med;
6776 break;
6777 }
6778 }
6779 }
6780 }
6781 break;
6782
6783 case DeviceType_DVD:
6784 /* find a DVD by UUID */
6785 if (!dev.uuid.isEmpty())
6786 rc = mParent->findDVDImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
6787 /* find a DVD by host device name */
6788 else if (!dev.strHostDriveSrc.isEmpty())
6789 {
6790 SafeIfaceArray<IMedium> drivevec;
6791 rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
6792 if (SUCCEEDED(rc))
6793 {
6794 for (size_t i = 0; i < drivevec.size(); ++i)
6795 {
6796 Bstr hostDriveSrc(dev.strHostDriveSrc);
6797 /// @todo eliminate this conversion
6798 ComObjPtr<Medium> med = (Medium *)drivevec[i];
6799 if ( hostDriveSrc == med->getName()
6800 || hostDriveSrc == med->getLocation())
6801 {
6802 medium = med;
6803 break;
6804 }
6805 }
6806 }
6807 }
6808 break;
6809
6810 case DeviceType_HardDisk:
6811 {
6812 /* find a hard disk by UUID */
6813 rc = mParent->findHardDisk(&dev.uuid, NULL, true /* aDoSetError */, &medium);
6814 if (FAILED(rc))
6815 {
6816 VBoxClsID clsid = getClassID();
6817 if (clsid == clsidSnapshotMachine)
6818 {
6819 // wrap another error message around the "cannot find hard disk" set by findHardDisk
6820 // so the user knows that the bad disk is in a snapshot somewhere
6821 com::ErrorInfo info;
6822 return setError(E_FAIL,
6823 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
6824 aSnapshotId->raw(),
6825 info.getText().raw());
6826 }
6827 else
6828 return rc;
6829 }
6830
6831 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
6832
6833 if (medium->getType() == MediumType_Immutable)
6834 {
6835 if (getClassID() == clsidSnapshotMachine)
6836 return setError(E_FAIL,
6837 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
6838 "of the virtual machine '%ls' ('%s')"),
6839 medium->getLocationFull().raw(),
6840 dev.uuid.raw(),
6841 aSnapshotId->raw(),
6842 mUserData->mName.raw(),
6843 mData->m_strConfigFileFull.raw());
6844
6845 return setError(E_FAIL,
6846 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s')"),
6847 medium->getLocationFull().raw(),
6848 dev.uuid.raw(),
6849 mUserData->mName.raw(),
6850 mData->m_strConfigFileFull.raw());
6851 }
6852
6853 if ( getClassID() != clsidSnapshotMachine
6854 && medium->getChildren().size() != 0
6855 )
6856 return setError(E_FAIL,
6857 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s') "
6858 "because it has %d differencing child hard disks"),
6859 medium->getLocationFull().raw(),
6860 dev.uuid.raw(),
6861 mUserData->mName.raw(),
6862 mData->m_strConfigFileFull.raw(),
6863 medium->getChildren().size());
6864
6865 if (findAttachment(mMediaData->mAttachments,
6866 medium))
6867 return setError(E_FAIL,
6868 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%ls' ('%s')"),
6869 medium->getLocationFull().raw(),
6870 dev.uuid.raw(),
6871 mUserData->mName.raw(),
6872 mData->m_strConfigFileFull.raw());
6873
6874 break;
6875 }
6876
6877 default:
6878 return setError(E_FAIL,
6879 tr("Device with unknown type is attached to the virtual machine '%s' ('%s')"),
6880 medium->getLocationFull().raw(),
6881 mUserData->mName.raw(),
6882 mData->m_strConfigFileFull.raw());
6883 }
6884
6885 if (FAILED(rc))
6886 break;
6887
6888 const Bstr controllerName = aStorageController->getName();
6889 ComObjPtr<MediumAttachment> pAttachment;
6890 pAttachment.createObject();
6891 rc = pAttachment->init(this,
6892 medium,
6893 controllerName,
6894 dev.lPort,
6895 dev.lDevice,
6896 dev.deviceType,
6897 dev.fPassThrough);
6898 if (FAILED(rc)) break;
6899
6900 /* associate the medium with this machine and snapshot */
6901 if (!medium.isNull())
6902 {
6903 if (getClassID() == clsidSnapshotMachine)
6904 rc = medium->attachTo(mData->mUuid, *aSnapshotId);
6905 else
6906 rc = medium->attachTo(mData->mUuid);
6907 }
6908
6909 if (FAILED(rc))
6910 break;
6911
6912 /* back up mMediaData to let registeredInit() properly rollback on failure
6913 * (= limited accessibility) */
6914 setModified(IsModified_Storage);
6915 mMediaData.backup();
6916 mMediaData->mAttachments.push_back(pAttachment);
6917 }
6918
6919 return rc;
6920}
6921
6922/**
6923 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
6924 *
6925 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
6926 * @param aSnapshot where to return the found snapshot
6927 * @param aSetError true to set extended error info on failure
6928 */
6929HRESULT Machine::findSnapshot(const Guid &aId,
6930 ComObjPtr<Snapshot> &aSnapshot,
6931 bool aSetError /* = false */)
6932{
6933 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
6934
6935 if (!mData->mFirstSnapshot)
6936 {
6937 if (aSetError)
6938 return setError(E_FAIL,
6939 tr("This machine does not have any snapshots"));
6940 return E_FAIL;
6941 }
6942
6943 if (aId.isEmpty())
6944 aSnapshot = mData->mFirstSnapshot;
6945 else
6946 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId);
6947
6948 if (!aSnapshot)
6949 {
6950 if (aSetError)
6951 return setError(E_FAIL,
6952 tr("Could not find a snapshot with UUID {%s}"),
6953 aId.toString().raw());
6954 return E_FAIL;
6955 }
6956
6957 return S_OK;
6958}
6959
6960/**
6961 * Returns the snapshot with the given name or fails of no such snapshot.
6962 *
6963 * @param aName snapshot name to find
6964 * @param aSnapshot where to return the found snapshot
6965 * @param aSetError true to set extended error info on failure
6966 */
6967HRESULT Machine::findSnapshot(IN_BSTR aName,
6968 ComObjPtr<Snapshot> &aSnapshot,
6969 bool aSetError /* = false */)
6970{
6971 AssertReturn(aName, E_INVALIDARG);
6972
6973 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
6974
6975 if (!mData->mFirstSnapshot)
6976 {
6977 if (aSetError)
6978 return setError(VBOX_E_OBJECT_NOT_FOUND,
6979 tr("This machine does not have any snapshots"));
6980 return VBOX_E_OBJECT_NOT_FOUND;
6981 }
6982
6983 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aName);
6984
6985 if (!aSnapshot)
6986 {
6987 if (aSetError)
6988 return setError(VBOX_E_OBJECT_NOT_FOUND,
6989 tr("Could not find a snapshot named '%ls'"), aName);
6990 return VBOX_E_OBJECT_NOT_FOUND;
6991 }
6992
6993 return S_OK;
6994}
6995
6996/**
6997 * Returns a storage controller object with the given name.
6998 *
6999 * @param aName storage controller name to find
7000 * @param aStorageController where to return the found storage controller
7001 * @param aSetError true to set extended error info on failure
7002 */
7003HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
7004 ComObjPtr<StorageController> &aStorageController,
7005 bool aSetError /* = false */)
7006{
7007 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
7008
7009 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
7010 it != mStorageControllers->end();
7011 ++it)
7012 {
7013 if ((*it)->getName() == aName)
7014 {
7015 aStorageController = (*it);
7016 return S_OK;
7017 }
7018 }
7019
7020 if (aSetError)
7021 return setError(VBOX_E_OBJECT_NOT_FOUND,
7022 tr("Could not find a storage controller named '%s'"),
7023 aName.raw());
7024 return VBOX_E_OBJECT_NOT_FOUND;
7025}
7026
7027HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
7028 MediaData::AttachmentList &atts)
7029{
7030 AutoCaller autoCaller(this);
7031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7032
7033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7034
7035 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
7036 it != mMediaData->mAttachments.end();
7037 ++it)
7038 {
7039 if ((*it)->getControllerName() == aName)
7040 atts.push_back(*it);
7041 }
7042
7043 return S_OK;
7044}
7045
7046/**
7047 * Helper for #saveSettings. Cares about renaming the settings directory and
7048 * file if the machine name was changed and about creating a new settings file
7049 * if this is a new machine.
7050 *
7051 * @note Must be never called directly but only from #saveSettings().
7052 *
7053 * @param aRenamed receives |true| if the name was changed and the settings
7054 * file was renamed as a result, or |false| otherwise. The
7055 * value makes sense only on success.
7056 * @param aNew receives |true| if a virgin settings file was created.
7057 */
7058HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
7059{
7060 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7061
7062 HRESULT rc = S_OK;
7063
7064 bool fSettingsFileIsNew = !mData->m_pMachineConfigFile->fileExists();
7065
7066 /* attempt to rename the settings file if machine name is changed */
7067 if ( mUserData->mNameSync
7068 && mUserData.isBackedUp()
7069 && mUserData.backedUpData()->mName != mUserData->mName
7070 )
7071 {
7072 bool dirRenamed = false;
7073 bool fileRenamed = false;
7074
7075 Utf8Str configFile, newConfigFile;
7076 Utf8Str configDir, newConfigDir;
7077
7078 do
7079 {
7080 int vrc = VINF_SUCCESS;
7081
7082 Utf8Str name = mUserData.backedUpData()->mName;
7083 Utf8Str newName = mUserData->mName;
7084
7085 configFile = mData->m_strConfigFileFull;
7086
7087 /* first, rename the directory if it matches the machine name */
7088 configDir = configFile;
7089 configDir.stripFilename();
7090 newConfigDir = configDir;
7091 if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str()))
7092 {
7093 newConfigDir.stripFilename();
7094 newConfigDir = Utf8StrFmt("%s%c%s",
7095 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
7096 /* new dir and old dir cannot be equal here because of 'if'
7097 * above and because name != newName */
7098 Assert(configDir != newConfigDir);
7099 if (!fSettingsFileIsNew)
7100 {
7101 /* perform real rename only if the machine is not new */
7102 vrc = RTPathRename(configDir.raw(), newConfigDir.raw(), 0);
7103 if (RT_FAILURE(vrc))
7104 {
7105 rc = setError(E_FAIL,
7106 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
7107 configDir.raw(),
7108 newConfigDir.raw(),
7109 vrc);
7110 break;
7111 }
7112 dirRenamed = true;
7113 }
7114 }
7115
7116 newConfigFile = Utf8StrFmt("%s%c%s.xml",
7117 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
7118
7119 /* then try to rename the settings file itself */
7120 if (newConfigFile != configFile)
7121 {
7122 /* get the path to old settings file in renamed directory */
7123 configFile = Utf8StrFmt("%s%c%s",
7124 newConfigDir.raw(),
7125 RTPATH_DELIMITER,
7126 RTPathFilename(configFile.c_str()));
7127 if (!fSettingsFileIsNew)
7128 {
7129 /* perform real rename only if the machine is not new */
7130 vrc = RTFileRename(configFile.raw(), newConfigFile.raw(), 0);
7131 if (RT_FAILURE(vrc))
7132 {
7133 rc = setError(E_FAIL,
7134 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
7135 configFile.raw(),
7136 newConfigFile.raw(),
7137 vrc);
7138 break;
7139 }
7140 fileRenamed = true;
7141 }
7142 }
7143
7144 /* update m_strConfigFileFull amd mConfigFile */
7145 mData->m_strConfigFileFull = newConfigFile;
7146
7147 // compute the relative path too
7148 Utf8Str path = newConfigFile;
7149 mParent->calculateRelativePath(path, path);
7150 mData->m_strConfigFile = path;
7151
7152 // store the old and new so that VirtualBox::saveSettings() can update
7153 // the media registry
7154 if ( mData->mRegistered
7155 && configDir != newConfigDir)
7156 {
7157 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
7158
7159 if (pfNeedsGlobalSaveSettings)
7160 *pfNeedsGlobalSaveSettings = true;
7161 }
7162
7163 /* update the snapshot folder */
7164 path = mUserData->mSnapshotFolderFull;
7165 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
7166 {
7167 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
7168 path.raw() + configDir.length());
7169 mUserData->mSnapshotFolderFull = path;
7170 calculateRelativePath(path, path);
7171 mUserData->mSnapshotFolder = path;
7172 }
7173
7174 /* update the saved state file path */
7175 path = mSSData->mStateFilePath;
7176 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
7177 {
7178 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
7179 path.raw() + configDir.length());
7180 mSSData->mStateFilePath = path;
7181 }
7182
7183 /* Update saved state file paths of all online snapshots.
7184 * Note that saveSettings() will recognize name change
7185 * and will save all snapshots in this case. */
7186 if (mData->mFirstSnapshot)
7187 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
7188 newConfigDir.c_str());
7189 }
7190 while (0);
7191
7192 if (FAILED(rc))
7193 {
7194 /* silently try to rename everything back */
7195 if (fileRenamed)
7196 RTFileRename(newConfigFile.raw(), configFile.raw(), 0);
7197 if (dirRenamed)
7198 RTPathRename(newConfigDir.raw(), configDir.raw(), 0);
7199 }
7200
7201 if (FAILED(rc)) return rc;
7202 }
7203
7204 if (fSettingsFileIsNew)
7205 {
7206 /* create a virgin config file */
7207 int vrc = VINF_SUCCESS;
7208
7209 /* ensure the settings directory exists */
7210 Utf8Str path(mData->m_strConfigFileFull);
7211 path.stripFilename();
7212 if (!RTDirExists(path.c_str()))
7213 {
7214 vrc = RTDirCreateFullPath(path.c_str(), 0777);
7215 if (RT_FAILURE(vrc))
7216 {
7217 return setError(E_FAIL,
7218 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
7219 path.raw(),
7220 vrc);
7221 }
7222 }
7223
7224 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
7225 path = Utf8Str(mData->m_strConfigFileFull);
7226 vrc = RTFileOpen(&mData->mHandleCfgFile, path.c_str(),
7227 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
7228 if (RT_FAILURE(vrc))
7229 {
7230 mData->mHandleCfgFile = NIL_RTFILE;
7231 return setError(E_FAIL,
7232 tr("Could not create the settings file '%s' (%Rrc)"),
7233 path.raw(),
7234 vrc);
7235 }
7236 RTFileClose(mData->mHandleCfgFile);
7237 }
7238
7239 return rc;
7240}
7241
7242/**
7243 * Saves and commits machine data, user data and hardware data.
7244 *
7245 * Note that on failure, the data remains uncommitted.
7246 *
7247 * @a aFlags may combine the following flags:
7248 *
7249 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
7250 * Used when saving settings after an operation that makes them 100%
7251 * correspond to the settings from the current snapshot.
7252 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
7253 * #isReallyModified() returns false. This is necessary for cases when we
7254 * change machine data directly, not through the backup()/commit() mechanism.
7255 * - SaveS_Force: settings will be saved without doing a deep compare of the
7256 * settings structures. This is used when this is called because snapshots
7257 * have changed to avoid the overhead of the deep compare.
7258 *
7259 * @note Must be called from under this object's write lock. Locks children for
7260 * writing.
7261 *
7262 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
7263 * initialized to false and that will be set to true by this function if
7264 * the caller must invoke VirtualBox::saveSettings() because the global
7265 * settings have changed. This will happen if a machine rename has been
7266 * saved and the global machine and media registries will therefore need
7267 * updating.
7268 */
7269HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
7270 int aFlags /*= 0*/)
7271{
7272 LogFlowThisFuncEnter();
7273
7274 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7275
7276 /* make sure child objects are unable to modify the settings while we are
7277 * saving them */
7278 ensureNoStateDependencies();
7279
7280 AssertReturn( getClassID() == clsidMachine
7281 || getClassID() == clsidSessionMachine,
7282 E_FAIL);
7283
7284 HRESULT rc = S_OK;
7285 bool fNeedsWrite = false;
7286
7287 /* First, prepare to save settings. It will care about renaming the
7288 * settings directory and file if the machine name was changed and about
7289 * creating a new settings file if this is a new machine. */
7290 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
7291 if (FAILED(rc)) return rc;
7292
7293 // keep a pointer to the current settings structures
7294 settings::MachineConfigFile *pOldConfig = mData->m_pMachineConfigFile;
7295
7296 try
7297 {
7298 // make a fresh one to have everyone write stuff into
7299 mData->m_pMachineConfigFile = new settings::MachineConfigFile(NULL);
7300 mData->m_pMachineConfigFile->copyBaseFrom(*pOldConfig);
7301
7302 // deep copy extradata
7303 mData->m_pMachineConfigFile->mapExtraDataItems = pOldConfig->mapExtraDataItems;
7304
7305 mData->m_pMachineConfigFile->uuid = mData->mUuid;
7306 mData->m_pMachineConfigFile->strName = mUserData->mName;
7307 mData->m_pMachineConfigFile->fNameSync = !!mUserData->mNameSync;
7308 mData->m_pMachineConfigFile->strDescription = mUserData->mDescription;
7309 mData->m_pMachineConfigFile->strOsType = mUserData->mOSTypeId;
7310
7311 if ( mData->mMachineState == MachineState_Saved
7312 || mData->mMachineState == MachineState_Restoring
7313 // when deleting a snapshot we may or may not have a saved state in the current state,
7314 // so let's not assert here please
7315 || ( (mData->mMachineState == MachineState_DeletingSnapshot)
7316 && (!mSSData->mStateFilePath.isEmpty())
7317 )
7318 )
7319 {
7320 Assert(!mSSData->mStateFilePath.isEmpty());
7321 /* try to make the file name relative to the settings file dir */
7322 calculateRelativePath(mSSData->mStateFilePath, mData->m_pMachineConfigFile->strStateFile);
7323 }
7324 else
7325 {
7326 Assert(mSSData->mStateFilePath.isEmpty());
7327 mData->m_pMachineConfigFile->strStateFile.setNull();
7328 }
7329
7330 if (mData->mCurrentSnapshot)
7331 mData->m_pMachineConfigFile->uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
7332 else
7333 mData->m_pMachineConfigFile->uuidCurrentSnapshot.clear();
7334
7335 mData->m_pMachineConfigFile->strSnapshotFolder = mUserData->mSnapshotFolder;
7336 // mData->m_pMachineConfigFile->fCurrentStateModified is special, see below
7337 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
7338 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
7339/// @todo Live Migration: mData->m_pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
7340
7341 mData->m_pMachineConfigFile->fTeleporterEnabled = !!mUserData->mTeleporterEnabled;
7342 mData->m_pMachineConfigFile->uTeleporterPort = mUserData->mTeleporterPort;
7343 mData->m_pMachineConfigFile->strTeleporterAddress = mUserData->mTeleporterAddress;
7344 mData->m_pMachineConfigFile->strTeleporterPassword = mUserData->mTeleporterPassword;
7345
7346 mData->m_pMachineConfigFile->fRTCUseUTC = !!mUserData->mRTCUseUTC;
7347
7348 rc = saveHardware(mData->m_pMachineConfigFile->hardwareMachine);
7349 if (FAILED(rc)) throw rc;
7350
7351 rc = saveStorageControllers(mData->m_pMachineConfigFile->storageMachine);
7352 if (FAILED(rc)) throw rc;
7353
7354 // save snapshots
7355 rc = saveAllSnapshots();
7356 if (FAILED(rc)) throw rc;
7357
7358 if (aFlags & SaveS_ResetCurStateModified)
7359 {
7360 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
7361 mData->mCurrentStateModified = FALSE;
7362 fNeedsWrite = true; // always, no need to compare
7363 }
7364 else if (aFlags & SaveS_Force)
7365 {
7366 fNeedsWrite = true; // always, no need to compare
7367 }
7368 else
7369 {
7370 if (!mData->mCurrentStateModified)
7371 {
7372 // do a deep compare of the settings that we just saved with the settings
7373 // previously stored in the config file; this invokes MachineConfigFile::operator==
7374 // which does a deep compare of all the settings, which is expensive but less expensive
7375 // than writing out XML in vain
7376 bool fAnySettingsChanged = (*mData->m_pMachineConfigFile == *pOldConfig);
7377
7378 // could still be modified if any settings changed
7379 mData->mCurrentStateModified = fAnySettingsChanged;
7380
7381 fNeedsWrite = fAnySettingsChanged;
7382 }
7383 else
7384 fNeedsWrite = true;
7385 }
7386
7387 mData->m_pMachineConfigFile->fCurrentStateModified = !!mData->mCurrentStateModified;
7388
7389 if (fNeedsWrite)
7390 // now spit it all out!
7391 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
7392
7393 // after saving settings, we are no longer different from the XML on disk
7394 m_flModifications = 0;
7395 }
7396 catch (HRESULT err)
7397 {
7398 // we assume that error info is set by the thrower
7399 rc = err;
7400
7401 // restore old config
7402 delete mData->m_pMachineConfigFile;
7403 mData->m_pMachineConfigFile = pOldConfig;
7404 }
7405 catch (...)
7406 {
7407 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
7408 }
7409
7410 if (SUCCEEDED(rc))
7411 {
7412 commit();
7413 delete pOldConfig;
7414 }
7415
7416 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
7417 {
7418 /* Fire the data change event, even on failure (since we've already
7419 * committed all data). This is done only for SessionMachines because
7420 * mutable Machine instances are always not registered (i.e. private
7421 * to the client process that creates them) and thus don't need to
7422 * inform callbacks. */
7423 if (getClassID() == clsidSessionMachine)
7424 mParent->onMachineDataChange(mData->mUuid);
7425 }
7426
7427 LogFlowThisFunc(("rc=%08X\n", rc));
7428 LogFlowThisFuncLeave();
7429 return rc;
7430}
7431
7432HRESULT Machine::saveAllSnapshots()
7433{
7434 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7435
7436 HRESULT rc = S_OK;
7437
7438 try
7439 {
7440 mData->m_pMachineConfigFile->llFirstSnapshot.clear();
7441
7442 if (mData->mFirstSnapshot)
7443 {
7444 settings::Snapshot snapNew;
7445 mData->m_pMachineConfigFile->llFirstSnapshot.push_back(snapNew);
7446
7447 // get reference to the fresh copy of the snapshot on the list and
7448 // work on that copy directly to avoid excessive copying later
7449 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
7450
7451 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
7452 if (FAILED(rc)) throw rc;
7453 }
7454
7455// if (mType == IsSessionMachine)
7456// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
7457
7458 }
7459 catch (HRESULT err)
7460 {
7461 /* we assume that error info is set by the thrower */
7462 rc = err;
7463 }
7464 catch (...)
7465 {
7466 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
7467 }
7468
7469 return rc;
7470}
7471
7472/**
7473 * Saves the VM hardware configuration. It is assumed that the
7474 * given node is empty.
7475 *
7476 * @param aNode <Hardware> node to save the VM hardware confguration to.
7477 */
7478HRESULT Machine::saveHardware(settings::Hardware &data)
7479{
7480 HRESULT rc = S_OK;
7481
7482 try
7483 {
7484 /* The hardware version attribute (optional).
7485 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
7486 if ( mHWData->mHWVersion == "1"
7487 && mSSData->mStateFilePath.isEmpty()
7488 )
7489 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. */
7490
7491 data.strVersion = mHWData->mHWVersion;
7492 data.uuid = mHWData->mHardwareUUID;
7493
7494 // CPU
7495 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
7496 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
7497 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
7498 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
7499 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
7500 data.fPAE = !!mHWData->mPAEEnabled;
7501 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
7502
7503 /* Standard and Extended CPUID leafs. */
7504 data.llCpuIdLeafs.clear();
7505 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
7506 {
7507 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
7508 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
7509 }
7510 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
7511 {
7512 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
7513 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
7514 }
7515
7516 data.cCPUs = mHWData->mCPUCount;
7517 data.fCpuHotPlug = mHWData->mCPUHotPlugEnabled;
7518
7519 data.llCpus.clear();
7520 if (data.fCpuHotPlug)
7521 {
7522 for (unsigned idx = 0; idx < data.cCPUs; idx++)
7523 {
7524 if (mHWData->mCPUAttached[idx])
7525 {
7526 settings::Cpu cpu;
7527 cpu.ulId = idx;
7528 data.llCpus.push_back(cpu);
7529 }
7530 }
7531 }
7532
7533 // memory
7534 data.ulMemorySizeMB = mHWData->mMemorySize;
7535
7536 // firmware
7537 data.firmwareType = mHWData->mFirmwareType;
7538
7539 // HID
7540 data.pointingHidType = mHWData->mPointingHidType;
7541 data.keyboardHidType = mHWData->mKeyboardHidType;
7542
7543 // HPET
7544 data.fHpetEnabled = mHWData->mHpetEnabled;
7545
7546 // boot order
7547 data.mapBootOrder.clear();
7548 for (size_t i = 0;
7549 i < RT_ELEMENTS(mHWData->mBootOrder);
7550 ++i)
7551 data.mapBootOrder[i] = mHWData->mBootOrder[i];
7552
7553 // display
7554 data.ulVRAMSizeMB = mHWData->mVRAMSize;
7555 data.cMonitors = mHWData->mMonitorCount;
7556 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
7557 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
7558
7559#ifdef VBOX_WITH_VRDP
7560 /* VRDP settings (optional) */
7561 rc = mVRDPServer->saveSettings(data.vrdpSettings);
7562 if (FAILED(rc)) throw rc;
7563#endif
7564
7565 /* BIOS (required) */
7566 rc = mBIOSSettings->saveSettings(data.biosSettings);
7567 if (FAILED(rc)) throw rc;
7568
7569 /* USB Controller (required) */
7570 rc = mUSBController->saveSettings(data.usbController);
7571 if (FAILED(rc)) throw rc;
7572
7573 /* Network adapters (required) */
7574 data.llNetworkAdapters.clear();
7575 for (ULONG slot = 0;
7576 slot < RT_ELEMENTS(mNetworkAdapters);
7577 ++slot)
7578 {
7579 settings::NetworkAdapter nic;
7580 nic.ulSlot = slot;
7581 rc = mNetworkAdapters[slot]->saveSettings(nic);
7582 if (FAILED(rc)) throw rc;
7583
7584 data.llNetworkAdapters.push_back(nic);
7585 }
7586
7587 /* Serial ports */
7588 data.llSerialPorts.clear();
7589 for (ULONG slot = 0;
7590 slot < RT_ELEMENTS(mSerialPorts);
7591 ++slot)
7592 {
7593 settings::SerialPort s;
7594 s.ulSlot = slot;
7595 rc = mSerialPorts[slot]->saveSettings(s);
7596 if (FAILED(rc)) return rc;
7597
7598 data.llSerialPorts.push_back(s);
7599 }
7600
7601 /* Parallel ports */
7602 data.llParallelPorts.clear();
7603 for (ULONG slot = 0;
7604 slot < RT_ELEMENTS(mParallelPorts);
7605 ++slot)
7606 {
7607 settings::ParallelPort p;
7608 p.ulSlot = slot;
7609 rc = mParallelPorts[slot]->saveSettings(p);
7610 if (FAILED(rc)) return rc;
7611
7612 data.llParallelPorts.push_back(p);
7613 }
7614
7615 /* Audio adapter */
7616 rc = mAudioAdapter->saveSettings(data.audioAdapter);
7617 if (FAILED(rc)) return rc;
7618
7619 /* Shared folders */
7620 data.llSharedFolders.clear();
7621 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
7622 it != mHWData->mSharedFolders.end();
7623 ++it)
7624 {
7625 ComObjPtr<SharedFolder> pFolder = *it;
7626 settings::SharedFolder sf;
7627 sf.strName = pFolder->getName();
7628 sf.strHostPath = pFolder->getHostPath();
7629 sf.fWritable = !!pFolder->isWritable();
7630
7631 data.llSharedFolders.push_back(sf);
7632 }
7633
7634 // clipboard
7635 data.clipboardMode = mHWData->mClipboardMode;
7636
7637 /* Guest */
7638 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
7639 data.ulStatisticsUpdateInterval = mHWData->mStatisticsUpdateInterval;
7640
7641 // IO settings
7642 data.ioSettings.ioMgrType = mHWData->mIoMgrType;
7643 data.ioSettings.ioBackendType = mHWData->mIoBackendType;
7644 data.ioSettings.fIoCacheEnabled = mHWData->mIoCacheEnabled;
7645 data.ioSettings.ulIoCacheSize = mHWData->mIoCacheSize;
7646 data.ioSettings.ulIoBandwidthMax = mHWData->mIoBandwidthMax;
7647
7648 // guest properties
7649 data.llGuestProperties.clear();
7650#ifdef VBOX_WITH_GUEST_PROPS
7651 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
7652 it != mHWData->mGuestProperties.end();
7653 ++it)
7654 {
7655 HWData::GuestProperty property = *it;
7656
7657 settings::GuestProperty prop;
7658 prop.strName = property.strName;
7659 prop.strValue = property.strValue;
7660 prop.timestamp = property.mTimestamp;
7661 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
7662 guestProp::writeFlags(property.mFlags, szFlags);
7663 prop.strFlags = szFlags;
7664
7665 data.llGuestProperties.push_back(prop);
7666 }
7667
7668 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
7669#endif /* VBOX_WITH_GUEST_PROPS defined */
7670 }
7671 catch(std::bad_alloc &)
7672 {
7673 return E_OUTOFMEMORY;
7674 }
7675
7676 AssertComRC(rc);
7677 return rc;
7678}
7679
7680/**
7681 * Saves the storage controller configuration.
7682 *
7683 * @param aNode <StorageControllers> node to save the VM hardware confguration to.
7684 */
7685HRESULT Machine::saveStorageControllers(settings::Storage &data)
7686{
7687 data.llStorageControllers.clear();
7688
7689 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
7690 it != mStorageControllers->end();
7691 ++it)
7692 {
7693 HRESULT rc;
7694 ComObjPtr<StorageController> pCtl = *it;
7695
7696 settings::StorageController ctl;
7697 ctl.strName = pCtl->getName();
7698 ctl.controllerType = pCtl->getControllerType();
7699 ctl.storageBus = pCtl->getStorageBus();
7700 ctl.ulInstance = pCtl->getInstance();
7701
7702 /* Save the port count. */
7703 ULONG portCount;
7704 rc = pCtl->COMGETTER(PortCount)(&portCount);
7705 ComAssertComRCRet(rc, rc);
7706 ctl.ulPortCount = portCount;
7707
7708 /* Save IDE emulation settings. */
7709 if (ctl.controllerType == StorageControllerType_IntelAhci)
7710 {
7711 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
7712 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
7713 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
7714 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
7715 )
7716 ComAssertComRCRet(rc, rc);
7717 }
7718
7719 /* save the devices now. */
7720 rc = saveStorageDevices(pCtl, ctl);
7721 ComAssertComRCRet(rc, rc);
7722
7723 data.llStorageControllers.push_back(ctl);
7724 }
7725
7726 return S_OK;
7727}
7728
7729/**
7730 * Saves the hard disk confguration.
7731 */
7732HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
7733 settings::StorageController &data)
7734{
7735 MediaData::AttachmentList atts;
7736
7737 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()), atts);
7738 if (FAILED(rc)) return rc;
7739
7740 data.llAttachedDevices.clear();
7741 for (MediaData::AttachmentList::const_iterator it = atts.begin();
7742 it != atts.end();
7743 ++it)
7744 {
7745 settings::AttachedDevice dev;
7746
7747 MediumAttachment *pAttach = *it;
7748 Medium *pMedium = pAttach->getMedium();
7749
7750 dev.deviceType = pAttach->getType();
7751 dev.lPort = pAttach->getPort();
7752 dev.lDevice = pAttach->getDevice();
7753 if (pMedium)
7754 {
7755 BOOL fHostDrive = FALSE;
7756 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
7757 if (FAILED(rc))
7758 return rc;
7759 if (fHostDrive)
7760 dev.strHostDriveSrc = pMedium->getLocation();
7761 else
7762 dev.uuid = pMedium->getId();
7763 dev.fPassThrough = pAttach->getPassthrough();
7764 }
7765
7766 data.llAttachedDevices.push_back(dev);
7767 }
7768
7769 return S_OK;
7770}
7771
7772/**
7773 * Saves machine state settings as defined by aFlags
7774 * (SaveSTS_* values).
7775 *
7776 * @param aFlags Combination of SaveSTS_* flags.
7777 *
7778 * @note Locks objects for writing.
7779 */
7780HRESULT Machine::saveStateSettings(int aFlags)
7781{
7782 if (aFlags == 0)
7783 return S_OK;
7784
7785 AutoCaller autoCaller(this);
7786 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7787
7788 /* This object's write lock is also necessary to serialize file access
7789 * (prevent concurrent reads and writes) */
7790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7791
7792 HRESULT rc = S_OK;
7793
7794 Assert(mData->m_pMachineConfigFile);
7795
7796 try
7797 {
7798 if (aFlags & SaveSTS_CurStateModified)
7799 mData->m_pMachineConfigFile->fCurrentStateModified = true;
7800
7801 if (aFlags & SaveSTS_StateFilePath)
7802 {
7803 if (!mSSData->mStateFilePath.isEmpty())
7804 /* try to make the file name relative to the settings file dir */
7805 calculateRelativePath(mSSData->mStateFilePath, mData->m_pMachineConfigFile->strStateFile);
7806 else
7807 mData->m_pMachineConfigFile->strStateFile.setNull();
7808 }
7809
7810 if (aFlags & SaveSTS_StateTimeStamp)
7811 {
7812 Assert( mData->mMachineState != MachineState_Aborted
7813 || mSSData->mStateFilePath.isEmpty());
7814
7815 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
7816
7817 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
7818//@todo live migration mData->m_pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
7819 }
7820
7821 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
7822 }
7823 catch (...)
7824 {
7825 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
7826 }
7827
7828 return rc;
7829}
7830
7831/**
7832 * Creates differencing hard disks for all normal hard disks attached to this
7833 * machine and a new set of attachments to refer to created disks.
7834 *
7835 * Used when taking a snapshot or when discarding the current state.
7836 *
7837 * This method assumes that mMediaData contains the original hard disk attachments
7838 * it needs to create diffs for. On success, these attachments will be replaced
7839 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
7840 * called to delete created diffs which will also rollback mMediaData and restore
7841 * whatever was backed up before calling this method.
7842 *
7843 * Attachments with non-normal hard disks are left as is.
7844 *
7845 * If @a aOnline is @c false then the original hard disks that require implicit
7846 * diffs will be locked for reading. Otherwise it is assumed that they are
7847 * already locked for writing (when the VM was started). Note that in the latter
7848 * case it is responsibility of the caller to lock the newly created diffs for
7849 * writing if this method succeeds.
7850 *
7851 * @param aFolder Folder where to create diff hard disks.
7852 * @param aProgress Progress object to run (must contain at least as
7853 * many operations left as the number of hard disks
7854 * attached).
7855 * @param aOnline Whether the VM was online prior to this operation.
7856 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
7857 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
7858 *
7859 * @note The progress object is not marked as completed, neither on success nor
7860 * on failure. This is a responsibility of the caller.
7861 *
7862 * @note Locks this object for writing.
7863 */
7864HRESULT Machine::createImplicitDiffs(const Bstr &aFolder,
7865 IProgress *aProgress,
7866 ULONG aWeight,
7867 bool aOnline,
7868 bool *pfNeedsSaveSettings)
7869{
7870 AssertReturn(!aFolder.isEmpty(), E_FAIL);
7871
7872 LogFlowThisFunc(("aFolder='%ls', aOnline=%d\n", aFolder.raw(), aOnline));
7873
7874 AutoCaller autoCaller(this);
7875 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7876
7877 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7878
7879 /* must be in a protective state because we leave the lock below */
7880 AssertReturn( mData->mMachineState == MachineState_Saving
7881 || mData->mMachineState == MachineState_LiveSnapshotting
7882 || mData->mMachineState == MachineState_RestoringSnapshot
7883 || mData->mMachineState == MachineState_DeletingSnapshot
7884 , E_FAIL);
7885
7886 HRESULT rc = S_OK;
7887
7888 MediaList lockedMedia;
7889
7890 try
7891 {
7892 if (!aOnline)
7893 {
7894 /* lock all attached hard disks early to detect "in use"
7895 * situations before creating actual diffs */
7896 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7897 it != mMediaData->mAttachments.end();
7898 ++it)
7899 {
7900 MediumAttachment* pAtt = *it;
7901 if (pAtt->getType() == DeviceType_HardDisk)
7902 {
7903 Medium* pHD = pAtt->getMedium();
7904 Assert(pHD);
7905 rc = pHD->LockRead(NULL);
7906 if (FAILED(rc)) throw rc;
7907 lockedMedia.push_back(pHD);
7908 }
7909 }
7910 }
7911
7912 /* remember the current list (note that we don't use backup() since
7913 * mMediaData may be already backed up) */
7914 MediaData::AttachmentList atts = mMediaData->mAttachments;
7915
7916 /* start from scratch */
7917 mMediaData->mAttachments.clear();
7918
7919 /* go through remembered attachments and create diffs for normal hard
7920 * disks and attach them */
7921 for (MediaData::AttachmentList::const_iterator it = atts.begin();
7922 it != atts.end();
7923 ++it)
7924 {
7925 MediumAttachment* pAtt = *it;
7926
7927 DeviceType_T devType = pAtt->getType();
7928 Medium* medium = pAtt->getMedium();
7929
7930 if ( devType != DeviceType_HardDisk
7931 || medium == NULL
7932 || medium->getType() != MediumType_Normal)
7933 {
7934 /* copy the attachment as is */
7935
7936 /** @todo the progress object created in Console::TakeSnaphot
7937 * only expects operations for hard disks. Later other
7938 * device types need to show up in the progress as well. */
7939 if (devType == DeviceType_HardDisk)
7940 {
7941 if (medium == NULL)
7942 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")),
7943 aWeight); // weight
7944 else
7945 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
7946 medium->getBase()->getName().raw()),
7947 aWeight); // weight
7948 }
7949
7950 mMediaData->mAttachments.push_back(pAtt);
7951 continue;
7952 }
7953
7954 /* need a diff */
7955 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
7956 medium->getBase()->getName().raw()),
7957 aWeight); // weight
7958
7959 ComObjPtr<Medium> diff;
7960 diff.createObject();
7961 rc = diff->init(mParent,
7962 medium->preferredDiffFormat().raw(),
7963 BstrFmt("%ls"RTPATH_SLASH_STR,
7964 mUserData->mSnapshotFolderFull.raw()).raw(),
7965 pfNeedsSaveSettings);
7966 if (FAILED(rc)) throw rc;
7967
7968 /* leave the lock before the potentially lengthy operation */
7969 alock.leave();
7970
7971 rc = medium->createDiffStorageAndWait(diff,
7972 MediumVariant_Standard,
7973 pfNeedsSaveSettings);
7974
7975 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
7976 * the push_back? Looks like we're going to leave medium with the
7977 * wrong kind of lock (general issue with if we fail anywhere at all)
7978 * and an orphaned VDI in the snapshots folder. */
7979 // at this point, the old image is still locked for writing, but instead
7980 // we need the new diff image locked for writing and lock the previously
7981 // current one for reading only
7982 if (aOnline)
7983 {
7984 diff->LockWrite(NULL);
7985 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(diff), true));
7986 medium->UnlockWrite(NULL);
7987 medium->LockRead(NULL);
7988 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(medium), false));
7989 }
7990
7991 if (FAILED(rc)) throw rc;
7992
7993 alock.enter();
7994
7995 rc = diff->attachTo(mData->mUuid);
7996 AssertComRCThrowRC(rc);
7997
7998 /* add a new attachment */
7999 ComObjPtr<MediumAttachment> attachment;
8000 attachment.createObject();
8001 rc = attachment->init(this,
8002 diff,
8003 pAtt->getControllerName(),
8004 pAtt->getPort(),
8005 pAtt->getDevice(),
8006 DeviceType_HardDisk,
8007 true /* aImplicit */);
8008 if (FAILED(rc)) throw rc;
8009
8010 mMediaData->mAttachments.push_back(attachment);
8011 }
8012 }
8013 catch (HRESULT aRC) { rc = aRC; }
8014
8015 /* unlock all hard disks we locked */
8016 if (!aOnline)
8017 {
8018 ErrorInfoKeeper eik;
8019
8020 for (MediaList::const_iterator it = lockedMedia.begin();
8021 it != lockedMedia.end();
8022 ++it)
8023 {
8024 HRESULT rc2 = (*it)->UnlockRead(NULL);
8025 AssertComRC(rc2);
8026 }
8027 }
8028
8029 if (FAILED(rc))
8030 {
8031 MultiResultRef mrc(rc);
8032
8033 mrc = deleteImplicitDiffs(pfNeedsSaveSettings);
8034 }
8035
8036 return rc;
8037}
8038
8039/**
8040 * Deletes implicit differencing hard disks created either by
8041 * #createImplicitDiffs() or by #AttachMedium() and rolls back mMediaData.
8042 *
8043 * Note that to delete hard disks created by #AttachMedium() this method is
8044 * called from #fixupMedia() when the changes are rolled back.
8045 *
8046 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
8047 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
8048 *
8049 * @note Locks this object for writing.
8050 */
8051HRESULT Machine::deleteImplicitDiffs(bool *pfNeedsSaveSettings)
8052{
8053 AutoCaller autoCaller(this);
8054 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8055
8056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8057 LogFlowThisFuncEnter();
8058
8059 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
8060
8061 HRESULT rc = S_OK;
8062
8063 MediaData::AttachmentList implicitAtts;
8064
8065 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
8066
8067 /* enumerate new attachments */
8068 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8069 it != mMediaData->mAttachments.end();
8070 ++it)
8071 {
8072 ComObjPtr<Medium> hd = (*it)->getMedium();
8073 if (hd.isNull())
8074 continue;
8075
8076 if ((*it)->isImplicit())
8077 {
8078 /* deassociate and mark for deletion */
8079 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
8080 rc = hd->detachFrom(mData->mUuid);
8081 AssertComRC(rc);
8082 implicitAtts.push_back(*it);
8083 continue;
8084 }
8085
8086 /* was this hard disk attached before? */
8087 if (!findAttachment(oldAtts, hd))
8088 {
8089 /* no: de-associate */
8090 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
8091 rc = hd->detachFrom(mData->mUuid);
8092 AssertComRC(rc);
8093 continue;
8094 }
8095 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
8096 }
8097
8098 /* rollback hard disk changes */
8099 mMediaData.rollback();
8100
8101 MultiResult mrc(S_OK);
8102
8103 /* delete unused implicit diffs */
8104 if (implicitAtts.size() != 0)
8105 {
8106 /* will leave the lock before the potentially lengthy
8107 * operation, so protect with the special state (unless already
8108 * protected) */
8109 MachineState_T oldState = mData->mMachineState;
8110 if ( oldState != MachineState_Saving
8111 && oldState != MachineState_LiveSnapshotting
8112 && oldState != MachineState_RestoringSnapshot
8113 && oldState != MachineState_DeletingSnapshot
8114 )
8115 setMachineState(MachineState_SettingUp);
8116
8117 alock.leave();
8118
8119 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
8120 it != implicitAtts.end();
8121 ++it)
8122 {
8123 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
8124 ComObjPtr<Medium> hd = (*it)->getMedium();
8125
8126 rc = hd->deleteStorageAndWait(NULL /*aProgress*/, pfNeedsSaveSettings);
8127#if 1 /* HACK ALERT: Just make it kind of work */ /** @todo Fix this hack properly. The LockWrite / UnlockWrite / LockRead changes aren't undone! */
8128 if (rc == VBOX_E_INVALID_OBJECT_STATE)
8129 {
8130 LogFlowFunc(("Applying unlock hack on '%s'! FIXME!\n", (*it)->getLogName()));
8131 hd->UnlockWrite(NULL);
8132 rc = hd->deleteStorageAndWait(NULL /*aProgress*/, pfNeedsSaveSettings);
8133 }
8134#endif
8135 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
8136 mrc = rc;
8137 }
8138
8139 alock.enter();
8140
8141 if (mData->mMachineState == MachineState_SettingUp)
8142 {
8143 setMachineState(oldState);
8144 }
8145 }
8146
8147 return mrc;
8148}
8149
8150/**
8151 * Looks through the given list of media attachments for one with the given parameters
8152 * and returns it, or NULL if not found. The list is a parameter so that backup lists
8153 * can be searched as well if needed.
8154 *
8155 * @param list
8156 * @param aControllerName
8157 * @param aControllerPort
8158 * @param aDevice
8159 * @return
8160 */
8161MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
8162 IN_BSTR aControllerName,
8163 LONG aControllerPort,
8164 LONG aDevice)
8165{
8166 for (MediaData::AttachmentList::const_iterator it = ll.begin();
8167 it != ll.end();
8168 ++it)
8169 {
8170 MediumAttachment *pAttach = *it;
8171 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
8172 return pAttach;
8173 }
8174
8175 return NULL;
8176}
8177
8178/**
8179 * Looks through the given list of media attachments for one with the given parameters
8180 * and returns it, or NULL if not found. The list is a parameter so that backup lists
8181 * can be searched as well if needed.
8182 *
8183 * @param list
8184 * @param aControllerName
8185 * @param aControllerPort
8186 * @param aDevice
8187 * @return
8188 */
8189MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
8190 ComObjPtr<Medium> pMedium)
8191{
8192 for (MediaData::AttachmentList::const_iterator it = ll.begin();
8193 it != ll.end();
8194 ++it)
8195 {
8196 MediumAttachment *pAttach = *it;
8197 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
8198 if (pMediumThis.equalsTo(pMedium))
8199 return pAttach;
8200 }
8201
8202 return NULL;
8203}
8204
8205/**
8206 * Looks through the given list of media attachments for one with the given parameters
8207 * and returns it, or NULL if not found. The list is a parameter so that backup lists
8208 * can be searched as well if needed.
8209 *
8210 * @param list
8211 * @param aControllerName
8212 * @param aControllerPort
8213 * @param aDevice
8214 * @return
8215 */
8216MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
8217 Guid &id)
8218{
8219 for (MediaData::AttachmentList::const_iterator it = ll.begin();
8220 it != ll.end();
8221 ++it)
8222 {
8223 MediumAttachment *pAttach = *it;
8224 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
8225 if (pMediumThis->getId() == id)
8226 return pAttach;
8227 }
8228
8229 return NULL;
8230}
8231
8232/**
8233 * Perform deferred hard disk detachments.
8234 *
8235 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
8236 * backed up).
8237 *
8238 * If @a aOnline is @c true then this method will also unlock the old hard disks
8239 * for which the new implicit diffs were created and will lock these new diffs for
8240 * writing.
8241 *
8242 * @param aOnline Whether the VM was online prior to this operation.
8243 *
8244 * @note Locks this object for writing!
8245 */
8246void Machine::commitMedia(bool aOnline /*= false*/)
8247{
8248 AutoCaller autoCaller(this);
8249 AssertComRCReturnVoid(autoCaller.rc());
8250
8251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8252
8253 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
8254
8255 HRESULT rc = S_OK;
8256
8257 /* no attach/detach operations -- nothing to do */
8258 if (!mMediaData.isBackedUp())
8259 return;
8260
8261 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
8262
8263 /* enumerate new attachments */
8264 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8265 it != mMediaData->mAttachments.end();
8266 ++it)
8267 {
8268 MediumAttachment *pAttach = *it;
8269
8270 pAttach->commit();
8271
8272 Medium* pMedium = pAttach->getMedium();
8273 bool fImplicit = pAttach->isImplicit();
8274
8275 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
8276 (pMedium) ? pMedium->getName().raw() : "NULL",
8277 fImplicit));
8278
8279 /** @todo convert all this Machine-based voodoo to MediumAttachment
8280 * based commit logic. */
8281 if (fImplicit)
8282 {
8283 /* convert implicit attachment to normal */
8284 pAttach->setImplicit(false);
8285
8286 if ( aOnline
8287 && pMedium
8288 && pAttach->getType() == DeviceType_HardDisk
8289 )
8290 {
8291 rc = pMedium->LockWrite(NULL);
8292 AssertComRC(rc);
8293
8294 mData->mSession.mLockedMedia.push_back(
8295 Data::Session::LockedMedia::value_type(
8296 ComPtr<IMedium>(pMedium), true));
8297
8298 /* also, relock the old hard disk which is a base for the
8299 * new diff for reading if the VM is online */
8300
8301 ComObjPtr<Medium> parent = pMedium->getParent();
8302 /* make the relock atomic */
8303 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
8304 rc = parent->UnlockWrite(NULL);
8305 AssertComRC(rc);
8306 rc = parent->LockRead(NULL);
8307 AssertComRC(rc);
8308
8309 /* XXX actually we should replace the old entry in that
8310 * vector (write lock => read lock) but this would take
8311 * some effort. So lets just ignore the error code in
8312 * SessionMachine::unlockMedia(). */
8313 mData->mSession.mLockedMedia.push_back(
8314 Data::Session::LockedMedia::value_type (
8315 ComPtr<IMedium>(parent), false));
8316 }
8317
8318 continue;
8319 }
8320
8321 if (pMedium)
8322 {
8323 /* was this medium attached before? */
8324 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
8325 oldIt != oldAtts.end();
8326 ++oldIt)
8327 {
8328 MediumAttachment *pOldAttach = *oldIt;
8329 if (pOldAttach->getMedium().equalsTo(pMedium))
8330 {
8331 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().raw()));
8332
8333 /* yes: remove from old to avoid de-association */
8334 oldAtts.erase(oldIt);
8335 break;
8336 }
8337 }
8338 }
8339 }
8340
8341 /* enumerate remaining old attachments and de-associate from the
8342 * current machine state */
8343 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
8344 it != oldAtts.end();
8345 ++it)
8346 {
8347 MediumAttachment *pAttach = *it;
8348 Medium* pMedium = pAttach->getMedium();
8349
8350 /* Detach only hard disks, since DVD/floppy media is detached
8351 * instantly in MountMedium. */
8352 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
8353 {
8354 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().raw()));
8355
8356 /* now de-associate from the current machine state */
8357 rc = pMedium->detachFrom(mData->mUuid);
8358 AssertComRC(rc);
8359
8360 if ( aOnline
8361 && pAttach->getType() == DeviceType_HardDisk)
8362 {
8363 /* unlock since not used anymore */
8364 MediumState_T state;
8365 rc = pMedium->UnlockWrite(&state);
8366 /* the disk may be alredy relocked for reading above */
8367 Assert(SUCCEEDED(rc) || state == MediumState_LockedRead);
8368 }
8369 }
8370 }
8371
8372 /* commit the hard disk changes */
8373 mMediaData.commit();
8374
8375 if (getClassID() == clsidSessionMachine)
8376 {
8377 /* attach new data to the primary machine and reshare it */
8378 mPeer->mMediaData.attach(mMediaData);
8379 }
8380
8381 return;
8382}
8383
8384/**
8385 * Perform deferred deletion of implicitly created diffs.
8386 *
8387 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
8388 * backed up).
8389 *
8390 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
8391 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
8392 *
8393 * @note Locks this object for writing!
8394 */
8395void Machine::rollbackMedia()
8396{
8397 AutoCaller autoCaller(this);
8398 AssertComRCReturnVoid (autoCaller.rc());
8399
8400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8401
8402 LogFlowThisFunc(("Entering\n"));
8403
8404 HRESULT rc = S_OK;
8405
8406 /* no attach/detach operations -- nothing to do */
8407 if (!mMediaData.isBackedUp())
8408 return;
8409
8410 /* enumerate new attachments */
8411 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8412 it != mMediaData->mAttachments.end();
8413 ++it)
8414 {
8415 MediumAttachment *pAttach = *it;
8416 /* Fix up the backrefs for DVD/floppy media. */
8417 if (pAttach->getType() != DeviceType_HardDisk)
8418 {
8419 Medium* pMedium = pAttach->getMedium();
8420 if (pMedium)
8421 {
8422 rc = pMedium->detachFrom(mData->mUuid);
8423 AssertComRC(rc);
8424 }
8425 }
8426
8427 (*it)->rollback();
8428
8429 pAttach = *it;
8430 /* Fix up the backrefs for DVD/floppy media. */
8431 if (pAttach->getType() != DeviceType_HardDisk)
8432 {
8433 Medium* pMedium = pAttach->getMedium();
8434 if (pMedium)
8435 {
8436 rc = pMedium->attachTo(mData->mUuid);
8437 AssertComRC(rc);
8438 }
8439 }
8440 }
8441
8442 /** @todo convert all this Machine-based voodoo to MediumAttachment
8443 * based rollback logic. */
8444 // @todo r=dj the below totally fails if this gets called from Machine::rollback(),
8445 // which gets called if Machine::registeredInit() fails...
8446 deleteImplicitDiffs(NULL /*pfNeedsSaveSettings*/);
8447
8448 return;
8449}
8450
8451/**
8452 * Returns true if the settings file is located in the directory named exactly
8453 * as the machine. This will be true if the machine settings structure was
8454 * created by default in #openConfigLoader().
8455 *
8456 * @param aSettingsDir if not NULL, the full machine settings file directory
8457 * name will be assigned there.
8458 *
8459 * @note Doesn't lock anything.
8460 * @note Not thread safe (must be called from this object's lock).
8461 */
8462bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */)
8463{
8464 Utf8Str settingsDir = mData->m_strConfigFileFull;
8465 settingsDir.stripFilename();
8466 char *dirName = RTPathFilename(settingsDir.c_str());
8467
8468 AssertReturn(dirName, false);
8469
8470 /* if we don't rename anything on name change, return false shorlty */
8471 if (!mUserData->mNameSync)
8472 return false;
8473
8474 if (aSettingsDir)
8475 *aSettingsDir = settingsDir;
8476
8477 return Bstr(dirName) == mUserData->mName;
8478}
8479
8480/**
8481 * Discards all changes to machine settings.
8482 *
8483 * @param aNotify Whether to notify the direct session about changes or not.
8484 *
8485 * @note Locks objects for writing!
8486 */
8487void Machine::rollback(bool aNotify)
8488{
8489 AutoCaller autoCaller(this);
8490 AssertComRCReturn(autoCaller.rc(), (void)0);
8491
8492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8493
8494 if (!mStorageControllers.isNull())
8495 {
8496 if (mStorageControllers.isBackedUp())
8497 {
8498 /* unitialize all new devices (absent in the backed up list). */
8499 StorageControllerList::const_iterator it = mStorageControllers->begin();
8500 StorageControllerList *backedList = mStorageControllers.backedUpData();
8501 while (it != mStorageControllers->end())
8502 {
8503 if ( std::find(backedList->begin(), backedList->end(), *it)
8504 == backedList->end()
8505 )
8506 {
8507 (*it)->uninit();
8508 }
8509 ++it;
8510 }
8511
8512 /* restore the list */
8513 mStorageControllers.rollback();
8514 }
8515
8516 /* rollback any changes to devices after restoring the list */
8517 if (m_flModifications & IsModified_Storage)
8518 {
8519 StorageControllerList::const_iterator it = mStorageControllers->begin();
8520 while (it != mStorageControllers->end())
8521 {
8522 (*it)->rollback();
8523 ++it;
8524 }
8525 }
8526 }
8527
8528 mUserData.rollback();
8529
8530 mHWData.rollback();
8531
8532 if (m_flModifications & IsModified_Storage)
8533 rollbackMedia();
8534
8535 if (mBIOSSettings)
8536 mBIOSSettings->rollback();
8537
8538#ifdef VBOX_WITH_VRDP
8539 if (mVRDPServer && (m_flModifications & IsModified_VRDPServer))
8540 mVRDPServer->rollback();
8541#endif
8542
8543 if (mAudioAdapter)
8544 mAudioAdapter->rollback();
8545
8546 if (mUSBController && (m_flModifications & IsModified_USB))
8547 mUSBController->rollback();
8548
8549 ComPtr<INetworkAdapter> networkAdapters[RT_ELEMENTS(mNetworkAdapters)];
8550 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
8551 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
8552
8553 if (m_flModifications & IsModified_NetworkAdapters)
8554 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
8555 if ( mNetworkAdapters[slot]
8556 && mNetworkAdapters[slot]->isModified())
8557 {
8558 mNetworkAdapters[slot]->rollback();
8559 networkAdapters[slot] = mNetworkAdapters[slot];
8560 }
8561
8562 if (m_flModifications & IsModified_SerialPorts)
8563 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8564 if ( mSerialPorts[slot]
8565 && mSerialPorts[slot]->isModified())
8566 {
8567 mSerialPorts[slot]->rollback();
8568 serialPorts[slot] = mSerialPorts[slot];
8569 }
8570
8571 if (m_flModifications & IsModified_ParallelPorts)
8572 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8573 if ( mParallelPorts[slot]
8574 && mParallelPorts[slot]->isModified())
8575 {
8576 mParallelPorts[slot]->rollback();
8577 parallelPorts[slot] = mParallelPorts[slot];
8578 }
8579
8580 if (aNotify)
8581 {
8582 /* inform the direct session about changes */
8583
8584 ComObjPtr<Machine> that = this;
8585 uint32_t flModifications = m_flModifications;
8586 alock.leave();
8587
8588 if (flModifications & IsModified_SharedFolders)
8589 that->onSharedFolderChange();
8590
8591 if (flModifications & IsModified_VRDPServer)
8592 that->onVRDPServerChange();
8593 if (flModifications & IsModified_USB)
8594 that->onUSBControllerChange();
8595
8596 for (ULONG slot = 0; slot < RT_ELEMENTS(networkAdapters); slot ++)
8597 if (networkAdapters[slot])
8598 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
8599 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot ++)
8600 if (serialPorts[slot])
8601 that->onSerialPortChange(serialPorts[slot]);
8602 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot ++)
8603 if (parallelPorts[slot])
8604 that->onParallelPortChange(parallelPorts[slot]);
8605
8606 if (flModifications & IsModified_Storage)
8607 that->onStorageControllerChange();
8608 }
8609}
8610
8611/**
8612 * Commits all the changes to machine settings.
8613 *
8614 * Note that this operation is supposed to never fail.
8615 *
8616 * @note Locks this object and children for writing.
8617 */
8618void Machine::commit()
8619{
8620 AutoCaller autoCaller(this);
8621 AssertComRCReturnVoid(autoCaller.rc());
8622
8623 AutoCaller peerCaller(mPeer);
8624 AssertComRCReturnVoid(peerCaller.rc());
8625
8626 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
8627
8628 /*
8629 * use safe commit to ensure Snapshot machines (that share mUserData)
8630 * will still refer to a valid memory location
8631 */
8632 mUserData.commitCopy();
8633
8634 mHWData.commit();
8635
8636 if (mMediaData.isBackedUp())
8637 commitMedia();
8638
8639 mBIOSSettings->commit();
8640#ifdef VBOX_WITH_VRDP
8641 mVRDPServer->commit();
8642#endif
8643 mAudioAdapter->commit();
8644 mUSBController->commit();
8645
8646 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
8647 mNetworkAdapters[slot]->commit();
8648 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8649 mSerialPorts[slot]->commit();
8650 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8651 mParallelPorts[slot]->commit();
8652
8653 bool commitStorageControllers = false;
8654
8655 if (mStorageControllers.isBackedUp())
8656 {
8657 mStorageControllers.commit();
8658
8659 if (mPeer)
8660 {
8661 AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);
8662
8663 /* Commit all changes to new controllers (this will reshare data with
8664 * peers for thos who have peers) */
8665 StorageControllerList *newList = new StorageControllerList();
8666 StorageControllerList::const_iterator it = mStorageControllers->begin();
8667 while (it != mStorageControllers->end())
8668 {
8669 (*it)->commit();
8670
8671 /* look if this controller has a peer device */
8672 ComObjPtr<StorageController> peer = (*it)->getPeer();
8673 if (!peer)
8674 {
8675 /* no peer means the device is a newly created one;
8676 * create a peer owning data this device share it with */
8677 peer.createObject();
8678 peer->init(mPeer, *it, true /* aReshare */);
8679 }
8680 else
8681 {
8682 /* remove peer from the old list */
8683 mPeer->mStorageControllers->remove(peer);
8684 }
8685 /* and add it to the new list */
8686 newList->push_back(peer);
8687
8688 ++it;
8689 }
8690
8691 /* uninit old peer's controllers that are left */
8692 it = mPeer->mStorageControllers->begin();
8693 while (it != mPeer->mStorageControllers->end())
8694 {
8695 (*it)->uninit();
8696 ++it;
8697 }
8698
8699 /* attach new list of controllers to our peer */
8700 mPeer->mStorageControllers.attach(newList);
8701 }
8702 else
8703 {
8704 /* we have no peer (our parent is the newly created machine);
8705 * just commit changes to devices */
8706 commitStorageControllers = true;
8707 }
8708 }
8709 else
8710 {
8711 /* the list of controllers itself is not changed,
8712 * just commit changes to controllers themselves */
8713 commitStorageControllers = true;
8714 }
8715
8716 if (commitStorageControllers)
8717 {
8718 StorageControllerList::const_iterator it = mStorageControllers->begin();
8719 while (it != mStorageControllers->end())
8720 {
8721 (*it)->commit();
8722 ++it;
8723 }
8724 }
8725
8726 if (getClassID() == clsidSessionMachine)
8727 {
8728 /* attach new data to the primary machine and reshare it */
8729 mPeer->mUserData.attach(mUserData);
8730 mPeer->mHWData.attach(mHWData);
8731 /* mMediaData is reshared by fixupMedia */
8732 // mPeer->mMediaData.attach(mMediaData);
8733 Assert(mPeer->mMediaData.data() == mMediaData.data());
8734 }
8735}
8736
8737/**
8738 * Copies all the hardware data from the given machine.
8739 *
8740 * Currently, only called when the VM is being restored from a snapshot. In
8741 * particular, this implies that the VM is not running during this method's
8742 * call.
8743 *
8744 * @note This method must be called from under this object's lock.
8745 *
8746 * @note This method doesn't call #commit(), so all data remains backed up and
8747 * unsaved.
8748 */
8749void Machine::copyFrom(Machine *aThat)
8750{
8751 AssertReturnVoid(getClassID() == clsidMachine || getClassID() == clsidSessionMachine);
8752 AssertReturnVoid(aThat->getClassID() == clsidSnapshotMachine);
8753
8754 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
8755
8756 mHWData.assignCopy(aThat->mHWData);
8757
8758 // create copies of all shared folders (mHWData after attiching a copy
8759 // contains just references to original objects)
8760 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
8761 it != mHWData->mSharedFolders.end();
8762 ++it)
8763 {
8764 ComObjPtr<SharedFolder> folder;
8765 folder.createObject();
8766 HRESULT rc = folder->initCopy(getMachine(), *it);
8767 AssertComRC(rc);
8768 *it = folder;
8769 }
8770
8771 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
8772#ifdef VBOX_WITH_VRDP
8773 mVRDPServer->copyFrom(aThat->mVRDPServer);
8774#endif
8775 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
8776 mUSBController->copyFrom(aThat->mUSBController);
8777
8778 /* create private copies of all controllers */
8779 mStorageControllers.backup();
8780 mStorageControllers->clear();
8781 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
8782 it != aThat->mStorageControllers->end();
8783 ++it)
8784 {
8785 ComObjPtr<StorageController> ctrl;
8786 ctrl.createObject();
8787 ctrl->initCopy(this, *it);
8788 mStorageControllers->push_back(ctrl);
8789 }
8790
8791 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
8792 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
8793 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8794 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
8795 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8796 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
8797}
8798
8799#ifdef VBOX_WITH_RESOURCE_USAGE_API
8800void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
8801{
8802 pm::CollectorHAL *hal = aCollector->getHAL();
8803 /* Create sub metrics */
8804 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
8805 "Percentage of processor time spent in user mode by VM process.");
8806 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
8807 "Percentage of processor time spent in kernel mode by VM process.");
8808 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
8809 "Size of resident portion of VM process in memory.");
8810 /* Create and register base metrics */
8811 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
8812 cpuLoadUser, cpuLoadKernel);
8813 aCollector->registerBaseMetric(cpuLoad);
8814 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
8815 ramUsageUsed);
8816 aCollector->registerBaseMetric(ramUsage);
8817
8818 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
8819 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
8820 new pm::AggregateAvg()));
8821 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
8822 new pm::AggregateMin()));
8823 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
8824 new pm::AggregateMax()));
8825 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
8826 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
8827 new pm::AggregateAvg()));
8828 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
8829 new pm::AggregateMin()));
8830 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
8831 new pm::AggregateMax()));
8832
8833 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
8834 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
8835 new pm::AggregateAvg()));
8836 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
8837 new pm::AggregateMin()));
8838 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
8839 new pm::AggregateMax()));
8840};
8841
8842void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
8843{
8844 aCollector->unregisterMetricsFor(aMachine);
8845 aCollector->unregisterBaseMetricsFor(aMachine);
8846};
8847#endif /* VBOX_WITH_RESOURCE_USAGE_API */
8848
8849
8850////////////////////////////////////////////////////////////////////////////////
8851
8852DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
8853
8854HRESULT SessionMachine::FinalConstruct()
8855{
8856 LogFlowThisFunc(("\n"));
8857
8858#if defined(RT_OS_WINDOWS)
8859 mIPCSem = NULL;
8860#elif defined(RT_OS_OS2)
8861 mIPCSem = NULLHANDLE;
8862#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8863 mIPCSem = -1;
8864#else
8865# error "Port me!"
8866#endif
8867
8868 return S_OK;
8869}
8870
8871void SessionMachine::FinalRelease()
8872{
8873 LogFlowThisFunc(("\n"));
8874
8875 uninit(Uninit::Unexpected);
8876}
8877
8878/**
8879 * @note Must be called only by Machine::openSession() from its own write lock.
8880 */
8881HRESULT SessionMachine::init(Machine *aMachine)
8882{
8883 LogFlowThisFuncEnter();
8884 LogFlowThisFunc(("mName={%ls}\n", aMachine->mUserData->mName.raw()));
8885
8886 AssertReturn(aMachine, E_INVALIDARG);
8887
8888 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
8889
8890 /* Enclose the state transition NotReady->InInit->Ready */
8891 AutoInitSpan autoInitSpan(this);
8892 AssertReturn(autoInitSpan.isOk(), E_FAIL);
8893
8894 /* create the interprocess semaphore */
8895#if defined(RT_OS_WINDOWS)
8896 mIPCSemName = aMachine->mData->m_strConfigFileFull;
8897 for (size_t i = 0; i < mIPCSemName.length(); i++)
8898 if (mIPCSemName[i] == '\\')
8899 mIPCSemName[i] = '/';
8900 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName);
8901 ComAssertMsgRet(mIPCSem,
8902 ("Cannot create IPC mutex '%ls', err=%d",
8903 mIPCSemName.raw(), ::GetLastError()),
8904 E_FAIL);
8905#elif defined(RT_OS_OS2)
8906 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
8907 aMachine->mData->mUuid.raw());
8908 mIPCSemName = ipcSem;
8909 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.raw(), &mIPCSem, 0, FALSE);
8910 ComAssertMsgRet(arc == NO_ERROR,
8911 ("Cannot create IPC mutex '%s', arc=%ld",
8912 ipcSem.raw(), arc),
8913 E_FAIL);
8914#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8915# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8916# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
8917 /** @todo Check that this still works correctly. */
8918 AssertCompileSize(key_t, 8);
8919# else
8920 AssertCompileSize(key_t, 4);
8921# endif
8922 key_t key;
8923 mIPCSem = -1;
8924 mIPCKey = "0";
8925 for (uint32_t i = 0; i < 1 << 24; i++)
8926 {
8927 key = ((uint32_t)'V' << 24) | i;
8928 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
8929 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
8930 {
8931 mIPCSem = sem;
8932 if (sem >= 0)
8933 mIPCKey = BstrFmt("%u", key);
8934 break;
8935 }
8936 }
8937# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8938 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
8939 char *pszSemName = NULL;
8940 RTStrUtf8ToCurrentCP(&pszSemName, semName);
8941 key_t key = ::ftok(pszSemName, 'V');
8942 RTStrFree(pszSemName);
8943
8944 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
8945# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8946
8947 int errnoSave = errno;
8948 if (mIPCSem < 0 && errnoSave == ENOSYS)
8949 {
8950 setError(E_FAIL,
8951 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
8952 "support for SysV IPC. Check the host kernel configuration for "
8953 "CONFIG_SYSVIPC=y"));
8954 return E_FAIL;
8955 }
8956 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
8957 * the IPC semaphores */
8958 if (mIPCSem < 0 && errnoSave == ENOSPC)
8959 {
8960#ifdef RT_OS_LINUX
8961 setError(E_FAIL,
8962 tr("Cannot create IPC semaphore because the system limit for the "
8963 "maximum number of semaphore sets (SEMMNI), or the system wide "
8964 "maximum number of sempahores (SEMMNS) would be exceeded. The "
8965 "current set of SysV IPC semaphores can be determined from "
8966 "the file /proc/sysvipc/sem"));
8967#else
8968 setError(E_FAIL,
8969 tr("Cannot create IPC semaphore because the system-imposed limit "
8970 "on the maximum number of allowed semaphores or semaphore "
8971 "identifiers system-wide would be exceeded"));
8972#endif
8973 return E_FAIL;
8974 }
8975 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
8976 E_FAIL);
8977 /* set the initial value to 1 */
8978 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
8979 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
8980 E_FAIL);
8981#else
8982# error "Port me!"
8983#endif
8984
8985 /* memorize the peer Machine */
8986 unconst(mPeer) = aMachine;
8987 /* share the parent pointer */
8988 unconst(mParent) = aMachine->mParent;
8989
8990 /* take the pointers to data to share */
8991 mData.share(aMachine->mData);
8992 mSSData.share(aMachine->mSSData);
8993
8994 mUserData.share(aMachine->mUserData);
8995 mHWData.share(aMachine->mHWData);
8996 mMediaData.share(aMachine->mMediaData);
8997
8998 mStorageControllers.allocate();
8999 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
9000 it != aMachine->mStorageControllers->end();
9001 ++it)
9002 {
9003 ComObjPtr<StorageController> ctl;
9004 ctl.createObject();
9005 ctl->init(this, *it);
9006 mStorageControllers->push_back(ctl);
9007 }
9008
9009 unconst(mBIOSSettings).createObject();
9010 mBIOSSettings->init(this, aMachine->mBIOSSettings);
9011#ifdef VBOX_WITH_VRDP
9012 /* create another VRDPServer object that will be mutable */
9013 unconst(mVRDPServer).createObject();
9014 mVRDPServer->init(this, aMachine->mVRDPServer);
9015#endif
9016 /* create another audio adapter object that will be mutable */
9017 unconst(mAudioAdapter).createObject();
9018 mAudioAdapter->init(this, aMachine->mAudioAdapter);
9019 /* create a list of serial ports that will be mutable */
9020 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
9021 {
9022 unconst(mSerialPorts[slot]).createObject();
9023 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
9024 }
9025 /* create a list of parallel ports that will be mutable */
9026 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
9027 {
9028 unconst(mParallelPorts[slot]).createObject();
9029 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
9030 }
9031 /* create another USB controller object that will be mutable */
9032 unconst(mUSBController).createObject();
9033 mUSBController->init(this, aMachine->mUSBController);
9034
9035 /* create a list of network adapters that will be mutable */
9036 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
9037 {
9038 unconst(mNetworkAdapters[slot]).createObject();
9039 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
9040 }
9041
9042 /* default is to delete saved state on Saved -> PoweredOff transition */
9043 mRemoveSavedState = true;
9044
9045 /* Confirm a successful initialization when it's the case */
9046 autoInitSpan.setSucceeded();
9047
9048 LogFlowThisFuncLeave();
9049 return S_OK;
9050}
9051
9052/**
9053 * Uninitializes this session object. If the reason is other than
9054 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
9055 *
9056 * @param aReason uninitialization reason
9057 *
9058 * @note Locks mParent + this object for writing.
9059 */
9060void SessionMachine::uninit(Uninit::Reason aReason)
9061{
9062 LogFlowThisFuncEnter();
9063 LogFlowThisFunc(("reason=%d\n", aReason));
9064
9065 /*
9066 * Strongly reference ourselves to prevent this object deletion after
9067 * mData->mSession.mMachine.setNull() below (which can release the last
9068 * reference and call the destructor). Important: this must be done before
9069 * accessing any members (and before AutoUninitSpan that does it as well).
9070 * This self reference will be released as the very last step on return.
9071 */
9072 ComObjPtr<SessionMachine> selfRef = this;
9073
9074 /* Enclose the state transition Ready->InUninit->NotReady */
9075 AutoUninitSpan autoUninitSpan(this);
9076 if (autoUninitSpan.uninitDone())
9077 {
9078 LogFlowThisFunc(("Already uninitialized\n"));
9079 LogFlowThisFuncLeave();
9080 return;
9081 }
9082
9083 if (autoUninitSpan.initFailed())
9084 {
9085 /* We've been called by init() because it's failed. It's not really
9086 * necessary (nor it's safe) to perform the regular uninit sequense
9087 * below, the following is enough.
9088 */
9089 LogFlowThisFunc(("Initialization failed.\n"));
9090#if defined(RT_OS_WINDOWS)
9091 if (mIPCSem)
9092 ::CloseHandle(mIPCSem);
9093 mIPCSem = NULL;
9094#elif defined(RT_OS_OS2)
9095 if (mIPCSem != NULLHANDLE)
9096 ::DosCloseMutexSem(mIPCSem);
9097 mIPCSem = NULLHANDLE;
9098#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9099 if (mIPCSem >= 0)
9100 ::semctl(mIPCSem, 0, IPC_RMID);
9101 mIPCSem = -1;
9102# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9103 mIPCKey = "0";
9104# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
9105#else
9106# error "Port me!"
9107#endif
9108 uninitDataAndChildObjects();
9109 mData.free();
9110 unconst(mParent) = NULL;
9111 unconst(mPeer) = NULL;
9112 LogFlowThisFuncLeave();
9113 return;
9114 }
9115
9116 /* We need to lock this object in uninit() because the lock is shared
9117 * with mPeer (as well as data we modify below). mParent->addProcessToReap()
9118 * and others need mParent lock, and USB needs host lock. */
9119 AutoMultiWriteLock3 alock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
9120
9121#ifdef VBOX_WITH_RESOURCE_USAGE_API
9122 unregisterMetrics(mParent->performanceCollector(), mPeer);
9123#endif /* VBOX_WITH_RESOURCE_USAGE_API */
9124
9125 MachineState_T lastState = mData->mMachineState;
9126 NOREF(lastState);
9127
9128 if (aReason == Uninit::Abnormal)
9129 {
9130 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
9131 Global::IsOnlineOrTransient(lastState)));
9132
9133 /* reset the state to Aborted */
9134 if (mData->mMachineState != MachineState_Aborted)
9135 setMachineState(MachineState_Aborted);
9136 }
9137
9138 // any machine settings modified?
9139 if (m_flModifications)
9140 {
9141 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
9142 rollback(false /* aNotify */);
9143 }
9144
9145 Assert(mSnapshotData.mStateFilePath.isEmpty() || !mSnapshotData.mSnapshot);
9146 if (!mSnapshotData.mStateFilePath.isEmpty())
9147 {
9148 LogWarningThisFunc(("canceling failed save state request!\n"));
9149 endSavingState(FALSE /* aSuccess */);
9150 }
9151 else if (!mSnapshotData.mSnapshot.isNull())
9152 {
9153 LogWarningThisFunc(("canceling untaken snapshot!\n"));
9154
9155 /* delete all differencing hard disks created (this will also attach
9156 * their parents back by rolling back mMediaData) */
9157 rollbackMedia();
9158 /* delete the saved state file (it might have been already created) */
9159 if (mSnapshotData.mSnapshot->stateFilePath().length())
9160 RTFileDelete(mSnapshotData.mSnapshot->stateFilePath().c_str());
9161
9162 mSnapshotData.mSnapshot->uninit();
9163 }
9164
9165#ifdef VBOX_WITH_USB
9166 /* release all captured USB devices */
9167 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
9168 {
9169 /* Console::captureUSBDevices() is called in the VM process only after
9170 * setting the machine state to Starting or Restoring.
9171 * Console::detachAllUSBDevices() will be called upon successful
9172 * termination. So, we need to release USB devices only if there was
9173 * an abnormal termination of a running VM.
9174 *
9175 * This is identical to SessionMachine::DetachAllUSBDevices except
9176 * for the aAbnormal argument. */
9177 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
9178 AssertComRC(rc);
9179 NOREF(rc);
9180
9181 USBProxyService *service = mParent->host()->usbProxyService();
9182 if (service)
9183 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
9184 }
9185#endif /* VBOX_WITH_USB */
9186
9187 if (!mData->mSession.mType.isEmpty())
9188 {
9189 /* mType is not null when this machine's process has been started by
9190 * VirtualBox::OpenRemoteSession(), therefore it is our child. We
9191 * need to queue the PID to reap the process (and avoid zombies on
9192 * Linux). */
9193 Assert(mData->mSession.mPid != NIL_RTPROCESS);
9194 mParent->addProcessToReap(mData->mSession.mPid);
9195 }
9196
9197 mData->mSession.mPid = NIL_RTPROCESS;
9198
9199 if (aReason == Uninit::Unexpected)
9200 {
9201 /* Uninitialization didn't come from #checkForDeath(), so tell the
9202 * client watcher thread to update the set of machines that have open
9203 * sessions. */
9204 mParent->updateClientWatcher();
9205 }
9206
9207 /* uninitialize all remote controls */
9208 if (mData->mSession.mRemoteControls.size())
9209 {
9210 LogFlowThisFunc(("Closing remote sessions (%d):\n",
9211 mData->mSession.mRemoteControls.size()));
9212
9213 Data::Session::RemoteControlList::iterator it =
9214 mData->mSession.mRemoteControls.begin();
9215 while (it != mData->mSession.mRemoteControls.end())
9216 {
9217 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
9218 HRESULT rc = (*it)->Uninitialize();
9219 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
9220 if (FAILED(rc))
9221 LogWarningThisFunc(("Forgot to close the remote session?\n"));
9222 ++it;
9223 }
9224 mData->mSession.mRemoteControls.clear();
9225 }
9226
9227 /*
9228 * An expected uninitialization can come only from #checkForDeath().
9229 * Otherwise it means that something's got really wrong (for examlple,
9230 * the Session implementation has released the VirtualBox reference
9231 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
9232 * etc). However, it's also possible, that the client releases the IPC
9233 * semaphore correctly (i.e. before it releases the VirtualBox reference),
9234 * but the VirtualBox release event comes first to the server process.
9235 * This case is practically possible, so we should not assert on an
9236 * unexpected uninit, just log a warning.
9237 */
9238
9239 if ((aReason == Uninit::Unexpected))
9240 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
9241
9242 if (aReason != Uninit::Normal)
9243 {
9244 mData->mSession.mDirectControl.setNull();
9245 }
9246 else
9247 {
9248 /* this must be null here (see #OnSessionEnd()) */
9249 Assert(mData->mSession.mDirectControl.isNull());
9250 Assert(mData->mSession.mState == SessionState_Closing);
9251 Assert(!mData->mSession.mProgress.isNull());
9252 }
9253 if (mData->mSession.mProgress)
9254 {
9255 if (aReason == Uninit::Normal)
9256 mData->mSession.mProgress->notifyComplete(S_OK);
9257 else
9258 mData->mSession.mProgress->notifyComplete(E_FAIL,
9259 COM_IIDOF(ISession),
9260 getComponentName(),
9261 tr("The VM session was aborted"));
9262 mData->mSession.mProgress.setNull();
9263 }
9264
9265 /* remove the association between the peer machine and this session machine */
9266 Assert(mData->mSession.mMachine == this ||
9267 aReason == Uninit::Unexpected);
9268
9269 /* reset the rest of session data */
9270 mData->mSession.mMachine.setNull();
9271 mData->mSession.mState = SessionState_Closed;
9272 mData->mSession.mType.setNull();
9273
9274 /* close the interprocess semaphore before leaving the exclusive lock */
9275#if defined(RT_OS_WINDOWS)
9276 if (mIPCSem)
9277 ::CloseHandle(mIPCSem);
9278 mIPCSem = NULL;
9279#elif defined(RT_OS_OS2)
9280 if (mIPCSem != NULLHANDLE)
9281 ::DosCloseMutexSem(mIPCSem);
9282 mIPCSem = NULLHANDLE;
9283#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9284 if (mIPCSem >= 0)
9285 ::semctl(mIPCSem, 0, IPC_RMID);
9286 mIPCSem = -1;
9287# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9288 mIPCKey = "0";
9289# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
9290#else
9291# error "Port me!"
9292#endif
9293
9294 /* fire an event */
9295 mParent->onSessionStateChange(mData->mUuid, SessionState_Closed);
9296
9297 uninitDataAndChildObjects();
9298
9299 /* free the essential data structure last */
9300 mData.free();
9301
9302 /* leave the exclusive lock before setting the below two to NULL */
9303 alock.leave();
9304
9305 unconst(mParent) = NULL;
9306 unconst(mPeer) = NULL;
9307
9308 LogFlowThisFuncLeave();
9309}
9310
9311// util::Lockable interface
9312////////////////////////////////////////////////////////////////////////////////
9313
9314/**
9315 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
9316 * with the primary Machine instance (mPeer).
9317 */
9318RWLockHandle *SessionMachine::lockHandle() const
9319{
9320 AssertReturn(mPeer != NULL, NULL);
9321 return mPeer->lockHandle();
9322}
9323
9324// IInternalMachineControl methods
9325////////////////////////////////////////////////////////////////////////////////
9326
9327/**
9328 * @note Locks this object for writing.
9329 */
9330STDMETHODIMP SessionMachine::SetRemoveSavedState(BOOL aRemove)
9331{
9332 AutoCaller autoCaller(this);
9333 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9334
9335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9336
9337 mRemoveSavedState = aRemove;
9338
9339 return S_OK;
9340}
9341
9342/**
9343 * @note Locks the same as #setMachineState() does.
9344 */
9345STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
9346{
9347 return setMachineState(aMachineState);
9348}
9349
9350/**
9351 * @note Locks this object for reading.
9352 */
9353STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
9354{
9355 AutoCaller autoCaller(this);
9356 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9357
9358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9359
9360#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
9361 mIPCSemName.cloneTo(aId);
9362 return S_OK;
9363#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9364# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9365 mIPCKey.cloneTo(aId);
9366# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
9367 mData->m_strConfigFileFull.cloneTo(aId);
9368# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
9369 return S_OK;
9370#else
9371# error "Port me!"
9372#endif
9373}
9374
9375/**
9376 * @note Locks this object for writing.
9377 */
9378STDMETHODIMP SessionMachine::SetPowerUpInfo(IVirtualBoxErrorInfo *aError)
9379{
9380 AutoCaller autoCaller(this);
9381 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9382
9383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9384
9385 if ( mData->mSession.mState == SessionState_Open
9386 && mData->mSession.mProgress)
9387 {
9388 /* Finalize the progress, since the remote session has completed
9389 * power on (successful or not). */
9390 if (aError)
9391 {
9392 /* Transfer error information immediately, as the
9393 * IVirtualBoxErrorInfo object is most likely transient. */
9394 HRESULT rc;
9395 LONG rRc = S_OK;
9396 rc = aError->COMGETTER(ResultCode)(&rRc);
9397 AssertComRCReturnRC(rc);
9398 Bstr rIID;
9399 rc = aError->COMGETTER(InterfaceID)(rIID.asOutParam());
9400 AssertComRCReturnRC(rc);
9401 Bstr rComponent;
9402 rc = aError->COMGETTER(Component)(rComponent.asOutParam());
9403 AssertComRCReturnRC(rc);
9404 Bstr rText;
9405 rc = aError->COMGETTER(Text)(rText.asOutParam());
9406 AssertComRCReturnRC(rc);
9407 mData->mSession.mProgress->notifyComplete(rRc, Guid(rIID), rComponent, Utf8Str(rText).raw());
9408 }
9409 else
9410 mData->mSession.mProgress->notifyComplete(S_OK);
9411 mData->mSession.mProgress.setNull();
9412
9413 return S_OK;
9414 }
9415 else
9416 return VBOX_E_INVALID_OBJECT_STATE;
9417}
9418
9419/**
9420 * Goes through the USB filters of the given machine to see if the given
9421 * device matches any filter or not.
9422 *
9423 * @note Locks the same as USBController::hasMatchingFilter() does.
9424 */
9425STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
9426 BOOL *aMatched,
9427 ULONG *aMaskedIfs)
9428{
9429 LogFlowThisFunc(("\n"));
9430
9431 CheckComArgNotNull(aUSBDevice);
9432 CheckComArgOutPointerValid(aMatched);
9433
9434 AutoCaller autoCaller(this);
9435 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9436
9437#ifdef VBOX_WITH_USB
9438 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
9439#else
9440 NOREF(aUSBDevice);
9441 NOREF(aMaskedIfs);
9442 *aMatched = FALSE;
9443#endif
9444
9445 return S_OK;
9446}
9447
9448/**
9449 * @note Locks the same as Host::captureUSBDevice() does.
9450 */
9451STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
9452{
9453 LogFlowThisFunc(("\n"));
9454
9455 AutoCaller autoCaller(this);
9456 AssertComRCReturnRC(autoCaller.rc());
9457
9458#ifdef VBOX_WITH_USB
9459 /* if captureDeviceForVM() fails, it must have set extended error info */
9460 MultiResult rc = mParent->host()->checkUSBProxyService();
9461 if (FAILED(rc)) return rc;
9462
9463 USBProxyService *service = mParent->host()->usbProxyService();
9464 AssertReturn(service, E_FAIL);
9465 return service->captureDeviceForVM(this, Guid(aId));
9466#else
9467 NOREF(aId);
9468 return E_NOTIMPL;
9469#endif
9470}
9471
9472/**
9473 * @note Locks the same as Host::detachUSBDevice() does.
9474 */
9475STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
9476{
9477 LogFlowThisFunc(("\n"));
9478
9479 AutoCaller autoCaller(this);
9480 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9481
9482#ifdef VBOX_WITH_USB
9483 USBProxyService *service = mParent->host()->usbProxyService();
9484 AssertReturn(service, E_FAIL);
9485 return service->detachDeviceFromVM(this, Guid(aId), !!aDone);
9486#else
9487 NOREF(aId);
9488 NOREF(aDone);
9489 return E_NOTIMPL;
9490#endif
9491}
9492
9493/**
9494 * Inserts all machine filters to the USB proxy service and then calls
9495 * Host::autoCaptureUSBDevices().
9496 *
9497 * Called by Console from the VM process upon VM startup.
9498 *
9499 * @note Locks what called methods lock.
9500 */
9501STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
9502{
9503 LogFlowThisFunc(("\n"));
9504
9505 AutoCaller autoCaller(this);
9506 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9507
9508#ifdef VBOX_WITH_USB
9509 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
9510 AssertComRC(rc);
9511 NOREF(rc);
9512
9513 USBProxyService *service = mParent->host()->usbProxyService();
9514 AssertReturn(service, E_FAIL);
9515 return service->autoCaptureDevicesForVM(this);
9516#else
9517 return S_OK;
9518#endif
9519}
9520
9521/**
9522 * Removes all machine filters from the USB proxy service and then calls
9523 * Host::detachAllUSBDevices().
9524 *
9525 * Called by Console from the VM process upon normal VM termination or by
9526 * SessionMachine::uninit() upon abnormal VM termination (from under the
9527 * Machine/SessionMachine lock).
9528 *
9529 * @note Locks what called methods lock.
9530 */
9531STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
9532{
9533 LogFlowThisFunc(("\n"));
9534
9535 AutoCaller autoCaller(this);
9536 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9537
9538#ifdef VBOX_WITH_USB
9539 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
9540 AssertComRC(rc);
9541 NOREF(rc);
9542
9543 USBProxyService *service = mParent->host()->usbProxyService();
9544 AssertReturn(service, E_FAIL);
9545 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
9546#else
9547 NOREF(aDone);
9548 return S_OK;
9549#endif
9550}
9551
9552/**
9553 * @note Locks this object for writing.
9554 */
9555STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
9556 IProgress **aProgress)
9557{
9558 LogFlowThisFuncEnter();
9559
9560 AssertReturn(aSession, E_INVALIDARG);
9561 AssertReturn(aProgress, E_INVALIDARG);
9562
9563 AutoCaller autoCaller(this);
9564
9565 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
9566 /*
9567 * We don't assert below because it might happen that a non-direct session
9568 * informs us it is closed right after we've been uninitialized -- it's ok.
9569 */
9570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9571
9572 /* get IInternalSessionControl interface */
9573 ComPtr<IInternalSessionControl> control(aSession);
9574
9575 ComAssertRet(!control.isNull(), E_INVALIDARG);
9576
9577 /* Creating a Progress object requires the VirtualBox lock, and
9578 * thus locking it here is required by the lock order rules. */
9579 AutoMultiWriteLock2 alock(mParent->lockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
9580
9581 if (control.equalsTo(mData->mSession.mDirectControl))
9582 {
9583 ComAssertRet(aProgress, E_POINTER);
9584
9585 /* The direct session is being normally closed by the client process
9586 * ----------------------------------------------------------------- */
9587
9588 /* go to the closing state (essential for all open*Session() calls and
9589 * for #checkForDeath()) */
9590 Assert(mData->mSession.mState == SessionState_Open);
9591 mData->mSession.mState = SessionState_Closing;
9592
9593 /* set direct control to NULL to release the remote instance */
9594 mData->mSession.mDirectControl.setNull();
9595 LogFlowThisFunc(("Direct control is set to NULL\n"));
9596
9597 if (mData->mSession.mProgress)
9598 {
9599 /* finalize the progress, someone might wait if a frontend
9600 * closes the session before powering on the VM. */
9601 mData->mSession.mProgress->notifyComplete(E_FAIL,
9602 COM_IIDOF(ISession),
9603 getComponentName(),
9604 tr("The VM session was closed before any attempt to power it on"));
9605 mData->mSession.mProgress.setNull();
9606 }
9607
9608 /* Create the progress object the client will use to wait until
9609 * #checkForDeath() is called to uninitialize this session object after
9610 * it releases the IPC semaphore. */
9611 Assert(mData->mSession.mProgress.isNull());
9612 ComObjPtr<Progress> progress;
9613 progress.createObject();
9614 ComPtr<IUnknown> pPeer(mPeer);
9615 progress->init(mParent, pPeer,
9616 Bstr(tr("Closing session")), FALSE /* aCancelable */);
9617 progress.queryInterfaceTo(aProgress);
9618 mData->mSession.mProgress = progress;
9619 }
9620 else
9621 {
9622 /* the remote session is being normally closed */
9623 Data::Session::RemoteControlList::iterator it =
9624 mData->mSession.mRemoteControls.begin();
9625 while (it != mData->mSession.mRemoteControls.end())
9626 {
9627 if (control.equalsTo(*it))
9628 break;
9629 ++it;
9630 }
9631 BOOL found = it != mData->mSession.mRemoteControls.end();
9632 ComAssertMsgRet(found, ("The session is not found in the session list!"),
9633 E_INVALIDARG);
9634 mData->mSession.mRemoteControls.remove(*it);
9635 }
9636
9637 LogFlowThisFuncLeave();
9638 return S_OK;
9639}
9640
9641/**
9642 * @note Locks this object for writing.
9643 */
9644STDMETHODIMP SessionMachine::BeginSavingState(IProgress *aProgress, BSTR *aStateFilePath)
9645{
9646 LogFlowThisFuncEnter();
9647
9648 AssertReturn(aProgress, E_INVALIDARG);
9649 AssertReturn(aStateFilePath, E_POINTER);
9650
9651 AutoCaller autoCaller(this);
9652 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9653
9654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9655
9656 AssertReturn( mData->mMachineState == MachineState_Paused
9657 && mSnapshotData.mLastState == MachineState_Null
9658 && mSnapshotData.mProgressId.isEmpty()
9659 && mSnapshotData.mStateFilePath.isEmpty(),
9660 E_FAIL);
9661
9662 /* memorize the progress ID and add it to the global collection */
9663 Bstr progressId;
9664 HRESULT rc = aProgress->COMGETTER(Id)(progressId.asOutParam());
9665 AssertComRCReturn(rc, rc);
9666 rc = mParent->addProgress(aProgress);
9667 AssertComRCReturn(rc, rc);
9668
9669 Bstr stateFilePath;
9670 /* stateFilePath is null when the machine is not running */
9671 if (mData->mMachineState == MachineState_Paused)
9672 {
9673 stateFilePath = Utf8StrFmt("%ls%c{%RTuuid}.sav",
9674 mUserData->mSnapshotFolderFull.raw(),
9675 RTPATH_DELIMITER, mData->mUuid.raw());
9676 }
9677
9678 /* fill in the snapshot data */
9679 mSnapshotData.mLastState = mData->mMachineState;
9680 mSnapshotData.mProgressId = Guid(progressId);
9681 mSnapshotData.mStateFilePath = stateFilePath;
9682
9683 /* set the state to Saving (this is expected by Console::SaveState()) */
9684 setMachineState(MachineState_Saving);
9685
9686 stateFilePath.cloneTo(aStateFilePath);
9687
9688 return S_OK;
9689}
9690
9691/**
9692 * @note Locks mParent + this object for writing.
9693 */
9694STDMETHODIMP SessionMachine::EndSavingState(BOOL aSuccess)
9695{
9696 LogFlowThisFunc(("\n"));
9697
9698 AutoCaller autoCaller(this);
9699 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9700
9701 /* endSavingState() need mParent lock */
9702 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
9703
9704 AssertReturn( mData->mMachineState == MachineState_Saving
9705 && mSnapshotData.mLastState != MachineState_Null
9706 && !mSnapshotData.mProgressId.isEmpty()
9707 && !mSnapshotData.mStateFilePath.isEmpty(),
9708 E_FAIL);
9709
9710 /*
9711 * on success, set the state to Saved;
9712 * on failure, set the state to the state we had when BeginSavingState() was
9713 * called (this is expected by Console::SaveState() and
9714 * Console::saveStateThread())
9715 */
9716 if (aSuccess)
9717 setMachineState(MachineState_Saved);
9718 else
9719 setMachineState(mSnapshotData.mLastState);
9720
9721 return endSavingState(aSuccess);
9722}
9723
9724/**
9725 * @note Locks this object for writing.
9726 */
9727STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
9728{
9729 LogFlowThisFunc(("\n"));
9730
9731 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
9732
9733 AutoCaller autoCaller(this);
9734 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9735
9736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9737
9738 AssertReturn( mData->mMachineState == MachineState_PoweredOff
9739 || mData->mMachineState == MachineState_Teleported
9740 || mData->mMachineState == MachineState_Aborted
9741 , E_FAIL); /** @todo setError. */
9742
9743 Utf8Str stateFilePathFull = aSavedStateFile;
9744 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
9745 if (RT_FAILURE(vrc))
9746 return setError(VBOX_E_FILE_ERROR,
9747 tr("Invalid saved state file path '%ls' (%Rrc)"),
9748 aSavedStateFile,
9749 vrc);
9750
9751 mSSData->mStateFilePath = stateFilePathFull;
9752
9753 /* The below setMachineState() will detect the state transition and will
9754 * update the settings file */
9755
9756 return setMachineState(MachineState_Saved);
9757}
9758
9759STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
9760 ComSafeArrayOut(BSTR, aValues),
9761 ComSafeArrayOut(ULONG64, aTimestamps),
9762 ComSafeArrayOut(BSTR, aFlags))
9763{
9764 LogFlowThisFunc(("\n"));
9765
9766#ifdef VBOX_WITH_GUEST_PROPS
9767 using namespace guestProp;
9768
9769 AutoCaller autoCaller(this);
9770 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9771
9772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9773
9774 AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
9775 AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
9776 AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
9777 AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
9778
9779 size_t cEntries = mHWData->mGuestProperties.size();
9780 com::SafeArray<BSTR> names(cEntries);
9781 com::SafeArray<BSTR> values(cEntries);
9782 com::SafeArray<ULONG64> timestamps(cEntries);
9783 com::SafeArray<BSTR> flags(cEntries);
9784 unsigned i = 0;
9785 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
9786 it != mHWData->mGuestProperties.end();
9787 ++it)
9788 {
9789 char szFlags[MAX_FLAGS_LEN + 1];
9790 it->strName.cloneTo(&names[i]);
9791 it->strValue.cloneTo(&values[i]);
9792 timestamps[i] = it->mTimestamp;
9793 /* If it is NULL, keep it NULL. */
9794 if (it->mFlags)
9795 {
9796 writeFlags(it->mFlags, szFlags);
9797 Bstr(szFlags).cloneTo(&flags[i]);
9798 }
9799 else
9800 flags[i] = NULL;
9801 ++i;
9802 }
9803 names.detachTo(ComSafeArrayOutArg(aNames));
9804 values.detachTo(ComSafeArrayOutArg(aValues));
9805 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
9806 flags.detachTo(ComSafeArrayOutArg(aFlags));
9807 mHWData->mPropertyServiceActive = true;
9808 return S_OK;
9809#else
9810 ReturnComNotImplemented();
9811#endif
9812}
9813
9814STDMETHODIMP SessionMachine::PushGuestProperties(ComSafeArrayIn(IN_BSTR, aNames),
9815 ComSafeArrayIn(IN_BSTR, aValues),
9816 ComSafeArrayIn(ULONG64, aTimestamps),
9817 ComSafeArrayIn(IN_BSTR, aFlags))
9818{
9819 LogFlowThisFunc(("\n"));
9820
9821#ifdef VBOX_WITH_GUEST_PROPS
9822 using namespace guestProp;
9823
9824 AssertReturn(!ComSafeArrayInIsNull(aNames), E_POINTER);
9825 AssertReturn(!ComSafeArrayInIsNull(aValues), E_POINTER);
9826 AssertReturn(!ComSafeArrayInIsNull(aTimestamps), E_POINTER);
9827 AssertReturn(!ComSafeArrayInIsNull(aFlags), E_POINTER);
9828
9829 AutoCaller autoCaller(this);
9830 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9831
9832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9833
9834 /*
9835 * Temporarily reset the registered flag, so that our machine state
9836 * changes (i.e. mHWData.backup()) succeed. (isMutable() used in all
9837 * setters will return FALSE for a Machine instance if mRegistered is TRUE).
9838 *
9839 * This is copied from registeredInit(), and may or may not be the right
9840 * way to handle this.
9841 *
9842 * @todo r=dj review this, this gets called during machine power-down when
9843 * we have already saved the machine settings, there's no need to do this
9844 * twice.
9845 */
9846 Assert(mData->mRegistered);
9847 mData->mRegistered = FALSE;
9848
9849 HRESULT rc = checkStateDependency(MutableStateDep);
9850 AssertLogRelMsgReturn(SUCCEEDED(rc), ("%Rhrc\n", rc), rc);
9851
9852 com::SafeArray<IN_BSTR> names( ComSafeArrayInArg(aNames));
9853 com::SafeArray<IN_BSTR> values( ComSafeArrayInArg(aValues));
9854 com::SafeArray<ULONG64> timestamps(ComSafeArrayInArg(aTimestamps));
9855 com::SafeArray<IN_BSTR> flags( ComSafeArrayInArg(aFlags));
9856
9857 DiscardSettings();
9858 setModified(IsModified_MachineData);
9859 mHWData.backup();
9860
9861 mHWData->mGuestProperties.erase(mHWData->mGuestProperties.begin(),
9862 mHWData->mGuestProperties.end());
9863 for (unsigned i = 0; i < names.size(); ++i)
9864 {
9865 uint32_t fFlags = NILFLAG;
9866 validateFlags(Utf8Str(flags[i]).raw(), &fFlags);
9867 HWData::GuestProperty property = { names[i], values[i], timestamps[i], fFlags };
9868 mHWData->mGuestProperties.push_back(property);
9869 }
9870
9871 mHWData->mPropertyServiceActive = false;
9872
9873 alock.release();
9874 SaveSettings();
9875
9876 /* Restore the mRegistered flag. */
9877 alock.acquire();
9878 mData->mRegistered = TRUE;
9879
9880 return S_OK;
9881#else
9882 ReturnComNotImplemented();
9883#endif
9884}
9885
9886STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
9887 IN_BSTR aValue,
9888 ULONG64 aTimestamp,
9889 IN_BSTR aFlags)
9890{
9891 LogFlowThisFunc(("\n"));
9892
9893#ifdef VBOX_WITH_GUEST_PROPS
9894 using namespace guestProp;
9895
9896 CheckComArgStrNotEmptyOrNull(aName);
9897 if (aValue != NULL && (!VALID_PTR(aValue) || !VALID_PTR(aFlags)))
9898 return E_POINTER; /* aValue can be NULL to indicate deletion */
9899
9900 try
9901 {
9902 /*
9903 * Convert input up front.
9904 */
9905 Utf8Str utf8Name(aName);
9906 uint32_t fFlags = NILFLAG;
9907 if (aFlags)
9908 {
9909 Utf8Str utf8Flags(aFlags);
9910 int vrc = validateFlags(utf8Flags.raw(), &fFlags);
9911 AssertRCReturn(vrc, E_INVALIDARG);
9912 }
9913
9914 /*
9915 * Now grab the object lock, validate the state and do the update.
9916 */
9917 AutoCaller autoCaller(this);
9918 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9919
9920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9921
9922 AssertReturn(mHWData->mPropertyServiceActive, VBOX_E_INVALID_OBJECT_STATE);
9923 switch (mData->mMachineState)
9924 {
9925 case MachineState_Paused:
9926 case MachineState_Running:
9927 case MachineState_Teleporting:
9928 case MachineState_TeleportingPausedVM:
9929 case MachineState_LiveSnapshotting:
9930 case MachineState_Saving:
9931 break;
9932
9933 default:
9934 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
9935 VBOX_E_INVALID_VM_STATE);
9936 }
9937
9938 setModified(IsModified_MachineData);
9939 mHWData.backup();
9940
9941 /** @todo r=bird: The careful memory handling doesn't work out here because
9942 * the catch block won't undo any damange we've done. So, if push_back throws
9943 * bad_alloc then you've lost the value.
9944 *
9945 * Another thing. Doing a linear search here isn't extremely efficient, esp.
9946 * since values that changes actually bubbles to the end of the list. Using
9947 * something that has an efficient lookup and can tollerate a bit of updates
9948 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
9949 * combination of RTStrCache (for sharing names and getting uniqueness into
9950 * the bargain) and hash/tree is another. */
9951 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
9952 iter != mHWData->mGuestProperties.end();
9953 ++iter)
9954 if (utf8Name == iter->strName)
9955 {
9956 mHWData->mGuestProperties.erase(iter);
9957 break;
9958 }
9959 if (aValue != NULL)
9960 {
9961 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
9962 mHWData->mGuestProperties.push_back(property);
9963 }
9964
9965 /*
9966 * Send a callback notification if appropriate
9967 */
9968 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
9969 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.raw(),
9970 RTSTR_MAX,
9971 utf8Name.raw(),
9972 RTSTR_MAX, NULL)
9973 )
9974 {
9975 alock.leave();
9976
9977 mParent->onGuestPropertyChange(mData->mUuid,
9978 aName,
9979 aValue,
9980 aFlags);
9981 }
9982 }
9983 catch (...)
9984 {
9985 return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
9986 }
9987 return S_OK;
9988#else
9989 ReturnComNotImplemented();
9990#endif
9991}
9992
9993// public methods only for internal purposes
9994/////////////////////////////////////////////////////////////////////////////
9995
9996/**
9997 * Called from the client watcher thread to check for expected or unexpected
9998 * death of the client process that has a direct session to this machine.
9999 *
10000 * On Win32 and on OS/2, this method is called only when we've got the
10001 * mutex (i.e. the client has either died or terminated normally) so it always
10002 * returns @c true (the client is terminated, the session machine is
10003 * uninitialized).
10004 *
10005 * On other platforms, the method returns @c true if the client process has
10006 * terminated normally or abnormally and the session machine was uninitialized,
10007 * and @c false if the client process is still alive.
10008 *
10009 * @note Locks this object for writing.
10010 */
10011bool SessionMachine::checkForDeath()
10012{
10013 Uninit::Reason reason;
10014 bool terminated = false;
10015
10016 /* Enclose autoCaller with a block because calling uninit() from under it
10017 * will deadlock. */
10018 {
10019 AutoCaller autoCaller(this);
10020 if (!autoCaller.isOk())
10021 {
10022 /* return true if not ready, to cause the client watcher to exclude
10023 * the corresponding session from watching */
10024 LogFlowThisFunc(("Already uninitialized!\n"));
10025 return true;
10026 }
10027
10028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10029
10030 /* Determine the reason of death: if the session state is Closing here,
10031 * everything is fine. Otherwise it means that the client did not call
10032 * OnSessionEnd() before it released the IPC semaphore. This may happen
10033 * either because the client process has abnormally terminated, or
10034 * because it simply forgot to call ISession::Close() before exiting. We
10035 * threat the latter also as an abnormal termination (see
10036 * Session::uninit() for details). */
10037 reason = mData->mSession.mState == SessionState_Closing ?
10038 Uninit::Normal :
10039 Uninit::Abnormal;
10040
10041#if defined(RT_OS_WINDOWS)
10042
10043 AssertMsg(mIPCSem, ("semaphore must be created"));
10044
10045 /* release the IPC mutex */
10046 ::ReleaseMutex(mIPCSem);
10047
10048 terminated = true;
10049
10050#elif defined(RT_OS_OS2)
10051
10052 AssertMsg(mIPCSem, ("semaphore must be created"));
10053
10054 /* release the IPC mutex */
10055 ::DosReleaseMutexSem(mIPCSem);
10056
10057 terminated = true;
10058
10059#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
10060
10061 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
10062
10063 int val = ::semctl(mIPCSem, 0, GETVAL);
10064 if (val > 0)
10065 {
10066 /* the semaphore is signaled, meaning the session is terminated */
10067 terminated = true;
10068 }
10069
10070#else
10071# error "Port me!"
10072#endif
10073
10074 } /* AutoCaller block */
10075
10076 if (terminated)
10077 uninit(reason);
10078
10079 return terminated;
10080}
10081
10082/**
10083 * @note Locks this object for reading.
10084 */
10085HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
10086{
10087 LogFlowThisFunc(("\n"));
10088
10089 AutoCaller autoCaller(this);
10090 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10091
10092 ComPtr<IInternalSessionControl> directControl;
10093 {
10094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10095 directControl = mData->mSession.mDirectControl;
10096 }
10097
10098 /* ignore notifications sent after #OnSessionEnd() is called */
10099 if (!directControl)
10100 return S_OK;
10101
10102 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
10103}
10104
10105/**
10106 * @note Locks this object for reading.
10107 */
10108HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
10109{
10110 LogFlowThisFunc(("\n"));
10111
10112 AutoCaller autoCaller(this);
10113 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10114
10115 ComPtr<IInternalSessionControl> directControl;
10116 {
10117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10118 directControl = mData->mSession.mDirectControl;
10119 }
10120
10121 /* ignore notifications sent after #OnSessionEnd() is called */
10122 if (!directControl)
10123 return S_OK;
10124
10125 return directControl->OnSerialPortChange(serialPort);
10126}
10127
10128/**
10129 * @note Locks this object for reading.
10130 */
10131HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
10132{
10133 LogFlowThisFunc(("\n"));
10134
10135 AutoCaller autoCaller(this);
10136 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10137
10138 ComPtr<IInternalSessionControl> directControl;
10139 {
10140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10141 directControl = mData->mSession.mDirectControl;
10142 }
10143
10144 /* ignore notifications sent after #OnSessionEnd() is called */
10145 if (!directControl)
10146 return S_OK;
10147
10148 return directControl->OnParallelPortChange(parallelPort);
10149}
10150
10151/**
10152 * @note Locks this object for reading.
10153 */
10154HRESULT SessionMachine::onStorageControllerChange()
10155{
10156 LogFlowThisFunc(("\n"));
10157
10158 AutoCaller autoCaller(this);
10159 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10160
10161 ComPtr<IInternalSessionControl> directControl;
10162 {
10163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10164 directControl = mData->mSession.mDirectControl;
10165 }
10166
10167 /* ignore notifications sent after #OnSessionEnd() is called */
10168 if (!directControl)
10169 return S_OK;
10170
10171 return directControl->OnStorageControllerChange();
10172}
10173
10174/**
10175 * @note Locks this object for reading.
10176 */
10177HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
10178{
10179 LogFlowThisFunc(("\n"));
10180
10181 AutoCaller autoCaller(this);
10182 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10183
10184 ComPtr<IInternalSessionControl> directControl;
10185 {
10186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10187 directControl = mData->mSession.mDirectControl;
10188 }
10189
10190 /* ignore notifications sent after #OnSessionEnd() is called */
10191 if (!directControl)
10192 return S_OK;
10193
10194 return directControl->OnMediumChange(aAttachment, aForce);
10195}
10196
10197/**
10198 * @note Locks this object for reading.
10199 */
10200HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
10201{
10202 LogFlowThisFunc(("\n"));
10203
10204 AutoCaller autoCaller(this);
10205 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10206
10207 ComPtr<IInternalSessionControl> directControl;
10208 {
10209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10210 directControl = mData->mSession.mDirectControl;
10211 }
10212
10213 /* ignore notifications sent after #OnSessionEnd() is called */
10214 if (!directControl)
10215 return S_OK;
10216
10217 return directControl->OnCPUChange(aCPU, aRemove);
10218}
10219
10220/**
10221 * @note Locks this object for reading.
10222 */
10223HRESULT SessionMachine::onVRDPServerChange()
10224{
10225 LogFlowThisFunc(("\n"));
10226
10227 AutoCaller autoCaller(this);
10228 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10229
10230 ComPtr<IInternalSessionControl> directControl;
10231 {
10232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10233 directControl = mData->mSession.mDirectControl;
10234 }
10235
10236 /* ignore notifications sent after #OnSessionEnd() is called */
10237 if (!directControl)
10238 return S_OK;
10239
10240 return directControl->OnVRDPServerChange();
10241}
10242
10243/**
10244 * @note Locks this object for reading.
10245 */
10246HRESULT SessionMachine::onUSBControllerChange()
10247{
10248 LogFlowThisFunc(("\n"));
10249
10250 AutoCaller autoCaller(this);
10251 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10252
10253 ComPtr<IInternalSessionControl> directControl;
10254 {
10255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10256 directControl = mData->mSession.mDirectControl;
10257 }
10258
10259 /* ignore notifications sent after #OnSessionEnd() is called */
10260 if (!directControl)
10261 return S_OK;
10262
10263 return directControl->OnUSBControllerChange();
10264}
10265
10266/**
10267 * @note Locks this object for reading.
10268 */
10269HRESULT SessionMachine::onSharedFolderChange()
10270{
10271 LogFlowThisFunc(("\n"));
10272
10273 AutoCaller autoCaller(this);
10274 AssertComRCReturnRC(autoCaller.rc());
10275
10276 ComPtr<IInternalSessionControl> directControl;
10277 {
10278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10279 directControl = mData->mSession.mDirectControl;
10280 }
10281
10282 /* ignore notifications sent after #OnSessionEnd() is called */
10283 if (!directControl)
10284 return S_OK;
10285
10286 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
10287}
10288
10289/**
10290 * Returns @c true if this machine's USB controller reports it has a matching
10291 * filter for the given USB device and @c false otherwise.
10292 *
10293 * @note Caller must have requested machine read lock.
10294 */
10295bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
10296{
10297 AutoCaller autoCaller(this);
10298 /* silently return if not ready -- this method may be called after the
10299 * direct machine session has been called */
10300 if (!autoCaller.isOk())
10301 return false;
10302
10303
10304#ifdef VBOX_WITH_USB
10305 switch (mData->mMachineState)
10306 {
10307 case MachineState_Starting:
10308 case MachineState_Restoring:
10309 case MachineState_TeleportingIn:
10310 case MachineState_Paused:
10311 case MachineState_Running:
10312 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
10313 * elsewhere... */
10314 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
10315 default: break;
10316 }
10317#else
10318 NOREF(aDevice);
10319 NOREF(aMaskedIfs);
10320#endif
10321 return false;
10322}
10323
10324/**
10325 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
10326 */
10327HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
10328 IVirtualBoxErrorInfo *aError,
10329 ULONG aMaskedIfs)
10330{
10331 LogFlowThisFunc(("\n"));
10332
10333 AutoCaller autoCaller(this);
10334
10335 /* This notification may happen after the machine object has been
10336 * uninitialized (the session was closed), so don't assert. */
10337 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10338
10339 ComPtr<IInternalSessionControl> directControl;
10340 {
10341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10342 directControl = mData->mSession.mDirectControl;
10343 }
10344
10345 /* fail on notifications sent after #OnSessionEnd() is called, it is
10346 * expected by the caller */
10347 if (!directControl)
10348 return E_FAIL;
10349
10350 /* No locks should be held at this point. */
10351 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
10352 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
10353
10354 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
10355}
10356
10357/**
10358 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
10359 */
10360HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
10361 IVirtualBoxErrorInfo *aError)
10362{
10363 LogFlowThisFunc(("\n"));
10364
10365 AutoCaller autoCaller(this);
10366
10367 /* This notification may happen after the machine object has been
10368 * uninitialized (the session was closed), so don't assert. */
10369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10370
10371 ComPtr<IInternalSessionControl> directControl;
10372 {
10373 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10374 directControl = mData->mSession.mDirectControl;
10375 }
10376
10377 /* fail on notifications sent after #OnSessionEnd() is called, it is
10378 * expected by the caller */
10379 if (!directControl)
10380 return E_FAIL;
10381
10382 /* No locks should be held at this point. */
10383 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
10384 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
10385
10386 return directControl->OnUSBDeviceDetach(aId, aError);
10387}
10388
10389// protected methods
10390/////////////////////////////////////////////////////////////////////////////
10391
10392/**
10393 * Helper method to finalize saving the state.
10394 *
10395 * @note Must be called from under this object's lock.
10396 *
10397 * @param aSuccess TRUE if the snapshot has been taken successfully
10398 *
10399 * @note Locks mParent + this objects for writing.
10400 */
10401HRESULT SessionMachine::endSavingState(BOOL aSuccess)
10402{
10403 LogFlowThisFuncEnter();
10404
10405 AutoCaller autoCaller(this);
10406 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10407
10408 /* saveSettings() needs mParent lock */
10409 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
10410
10411 HRESULT rc = S_OK;
10412
10413 if (aSuccess)
10414 {
10415 mSSData->mStateFilePath = mSnapshotData.mStateFilePath;
10416
10417 /* save all VM settings */
10418 rc = saveSettings(NULL);
10419 // no need to check whether VirtualBox.xml needs saving also since
10420 // we can't have a name change pending at this point
10421 }
10422 else
10423 {
10424 /* delete the saved state file (it might have been already created) */
10425 RTFileDelete(mSnapshotData.mStateFilePath.c_str());
10426 }
10427
10428 /* remove the completed progress object */
10429 mParent->removeProgress(mSnapshotData.mProgressId);
10430
10431 /* clear out the temporary saved state data */
10432 mSnapshotData.mLastState = MachineState_Null;
10433 mSnapshotData.mProgressId.clear();
10434 mSnapshotData.mStateFilePath.setNull();
10435
10436 LogFlowThisFuncLeave();
10437 return rc;
10438}
10439
10440/**
10441 * Locks the attached media.
10442 *
10443 * All attached hard disks are locked for writing and DVD/floppy are locked for
10444 * reading. Parents of attached hard disks (if any) are locked for reading.
10445 *
10446 * This method also performs accessibility check of all media it locks: if some
10447 * media is inaccessible, the method will return a failure and a bunch of
10448 * extended error info objects per each inaccessible medium.
10449 *
10450 * Note that this method is atomic: if it returns a success, all media are
10451 * locked as described above; on failure no media is locked at all (all
10452 * succeeded individual locks will be undone).
10453 *
10454 * This method is intended to be called when the machine is in Starting or
10455 * Restoring state and asserts otherwise.
10456 *
10457 * The locks made by this method must be undone by calling #unlockMedia() when
10458 * no more needed.
10459 */
10460HRESULT SessionMachine::lockMedia()
10461{
10462 AutoCaller autoCaller(this);
10463 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10464
10465 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10466
10467 AssertReturn( mData->mMachineState == MachineState_Starting
10468 || mData->mMachineState == MachineState_Restoring
10469 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
10470
10471 try
10472 {
10473 HRESULT rc = S_OK;
10474
10475 ErrorInfoKeeper eik(true /* aIsNull */);
10476 MultiResult mrc(S_OK);
10477
10478 /* Lock all medium objects attached to the VM.
10479 * Get status for inaccessible media as well. */
10480 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10481 it != mMediaData->mAttachments.end();
10482 ++it)
10483 {
10484 DeviceType_T devType = (*it)->getType();
10485 ComObjPtr<Medium> medium = (*it)->getMedium();
10486
10487 bool first = true;
10488
10489 /** @todo split out the media locking, and put it into
10490 * MediumImpl.cpp, as it needs this functionality too. */
10491 while (!medium.isNull())
10492 {
10493 MediumState_T mediumState = medium->getState();
10494
10495 /* accessibility check must be first, otherwise locking
10496 * interferes with getting the medium state. */
10497 if (mediumState == MediumState_Inaccessible)
10498 {
10499 rc = medium->RefreshState(&mediumState);
10500 if (FAILED(rc)) throw rc;
10501
10502 if (mediumState == MediumState_Inaccessible)
10503 {
10504 Bstr error;
10505 rc = medium->COMGETTER(LastAccessError)(error.asOutParam());
10506 if (FAILED(rc)) throw rc;
10507
10508 Bstr loc;
10509 rc = medium->COMGETTER(Location)(loc.asOutParam());
10510 if (FAILED(rc)) throw rc;
10511
10512 /* collect multiple errors */
10513 eik.restore();
10514
10515 /* be in sync with MediumBase::setStateError() */
10516 Assert(!error.isEmpty());
10517 mrc = setError(E_FAIL,
10518 tr("Medium '%ls' is not accessible. %ls"),
10519 loc.raw(),
10520 error.raw());
10521
10522 eik.fetch();
10523 }
10524 }
10525
10526 if (first)
10527 {
10528 if (devType != DeviceType_DVD)
10529 {
10530 /* HardDisk and Floppy medium must be locked for writing */
10531 rc = medium->LockWrite(NULL);
10532 if (FAILED(rc)) throw rc;
10533 }
10534 else
10535 {
10536 /* DVD medium must be locked for reading */
10537 rc = medium->LockRead(NULL);
10538 if (FAILED(rc)) throw rc;
10539 }
10540
10541 mData->mSession.mLockedMedia.push_back(
10542 Data::Session::LockedMedia::value_type(
10543 ComPtr<IMedium>(medium), true));
10544
10545 first = false;
10546 }
10547 else
10548 {
10549 rc = medium->LockRead(NULL);
10550 if (FAILED(rc)) throw rc;
10551
10552 mData->mSession.mLockedMedia.push_back(
10553 Data::Session::LockedMedia::value_type(
10554 ComPtr<IMedium>(medium), false));
10555 }
10556
10557
10558 /* no locks or callers here since there should be no way to
10559 * change the hard disk parent at this point (as it is still
10560 * attached to the machine) */
10561 medium = medium->getParent();
10562 }
10563 }
10564
10565 /* @todo r=dj is this correct? first restoring the eik and then throwing? */
10566 eik.restore();
10567 HRESULT rc2 = (HRESULT)mrc;
10568 if (FAILED(rc2)) throw rc2;
10569 }
10570 catch (HRESULT aRC)
10571 {
10572 /* Unlock all locked media on failure */
10573 unlockMedia();
10574 return aRC;
10575 }
10576
10577 return S_OK;
10578}
10579
10580/**
10581 * Undoes the locks made by by #lockMedia().
10582 */
10583void SessionMachine::unlockMedia()
10584{
10585 AutoCaller autoCaller(this);
10586 AssertComRCReturnVoid(autoCaller.rc());
10587
10588 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10589
10590 /* we may be holding important error info on the current thread;
10591 * preserve it */
10592 ErrorInfoKeeper eik;
10593
10594 HRESULT rc = S_OK;
10595
10596 for (Data::Session::LockedMedia::const_iterator
10597 it = mData->mSession.mLockedMedia.begin();
10598 it != mData->mSession.mLockedMedia.end(); ++it)
10599 {
10600 MediumState_T state;
10601 if (it->second)
10602 rc = it->first->UnlockWrite(&state);
10603 else
10604 rc = it->first->UnlockRead(&state);
10605
10606 /* The second can happen if an object was re-locked in
10607 * Machine::fixupMedia(). The last can happen when e.g a DVD/Floppy
10608 * image was unmounted at runtime. */
10609 Assert(SUCCEEDED(rc) || state == MediumState_LockedRead || state == MediumState_Created);
10610 }
10611
10612 mData->mSession.mLockedMedia.clear();
10613}
10614
10615/**
10616 * Helper to change the machine state (reimplementation).
10617 *
10618 * @note Locks this object for writing.
10619 */
10620HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
10621{
10622 LogFlowThisFuncEnter();
10623 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
10624
10625 AutoCaller autoCaller(this);
10626 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10627
10628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10629
10630 MachineState_T oldMachineState = mData->mMachineState;
10631
10632 AssertMsgReturn(oldMachineState != aMachineState,
10633 ("oldMachineState=%s, aMachineState=%s\n",
10634 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
10635 E_FAIL);
10636
10637 HRESULT rc = S_OK;
10638
10639 int stsFlags = 0;
10640 bool deleteSavedState = false;
10641
10642 /* detect some state transitions */
10643
10644 if ( ( oldMachineState == MachineState_Saved
10645 && aMachineState == MachineState_Restoring)
10646 || ( ( oldMachineState == MachineState_PoweredOff
10647 || oldMachineState == MachineState_Teleported
10648 || oldMachineState == MachineState_Aborted
10649 )
10650 && ( aMachineState == MachineState_TeleportingIn
10651 || aMachineState == MachineState_Starting
10652 )
10653 )
10654 )
10655 {
10656 /* The EMT thread is about to start */
10657
10658 /* Nothing to do here for now... */
10659
10660 /// @todo NEWMEDIA don't let mDVDDrive and other children
10661 /// change anything when in the Starting/Restoring state
10662 }
10663 else if ( ( oldMachineState == MachineState_Running
10664 || oldMachineState == MachineState_Paused
10665 || oldMachineState == MachineState_Teleporting
10666 || oldMachineState == MachineState_LiveSnapshotting
10667 || oldMachineState == MachineState_Stuck
10668 || oldMachineState == MachineState_Starting
10669 || oldMachineState == MachineState_Stopping
10670 || oldMachineState == MachineState_Saving
10671 || oldMachineState == MachineState_Restoring
10672 || oldMachineState == MachineState_TeleportingPausedVM
10673 || oldMachineState == MachineState_TeleportingIn
10674 )
10675 && ( aMachineState == MachineState_PoweredOff
10676 || aMachineState == MachineState_Saved
10677 || aMachineState == MachineState_Teleported
10678 || aMachineState == MachineState_Aborted
10679 )
10680 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
10681 * snapshot */
10682 && ( mSnapshotData.mSnapshot.isNull()
10683 || mSnapshotData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
10684 )
10685 )
10686 {
10687 /* The EMT thread has just stopped, unlock attached media. Note that as
10688 * opposed to locking that is done from Console, we do unlocking here
10689 * because the VM process may have aborted before having a chance to
10690 * properly unlock all media it locked. */
10691
10692 unlockMedia();
10693 }
10694
10695 if (oldMachineState == MachineState_Restoring)
10696 {
10697 if (aMachineState != MachineState_Saved)
10698 {
10699 /*
10700 * delete the saved state file once the machine has finished
10701 * restoring from it (note that Console sets the state from
10702 * Restoring to Saved if the VM couldn't restore successfully,
10703 * to give the user an ability to fix an error and retry --
10704 * we keep the saved state file in this case)
10705 */
10706 deleteSavedState = true;
10707 }
10708 }
10709 else if ( oldMachineState == MachineState_Saved
10710 && ( aMachineState == MachineState_PoweredOff
10711 || aMachineState == MachineState_Aborted
10712 || aMachineState == MachineState_Teleported
10713 )
10714 )
10715 {
10716 /*
10717 * delete the saved state after Console::DiscardSavedState() is called
10718 * or if the VM process (owning a direct VM session) crashed while the
10719 * VM was Saved
10720 */
10721
10722 /// @todo (dmik)
10723 // Not sure that deleting the saved state file just because of the
10724 // client death before it attempted to restore the VM is a good
10725 // thing. But when it crashes we need to go to the Aborted state
10726 // which cannot have the saved state file associated... The only
10727 // way to fix this is to make the Aborted condition not a VM state
10728 // but a bool flag: i.e., when a crash occurs, set it to true and
10729 // change the state to PoweredOff or Saved depending on the
10730 // saved state presence.
10731
10732 deleteSavedState = true;
10733 mData->mCurrentStateModified = TRUE;
10734 stsFlags |= SaveSTS_CurStateModified;
10735 }
10736
10737 if ( aMachineState == MachineState_Starting
10738 || aMachineState == MachineState_Restoring
10739 || aMachineState == MachineState_TeleportingIn
10740 )
10741 {
10742 /* set the current state modified flag to indicate that the current
10743 * state is no more identical to the state in the
10744 * current snapshot */
10745 if (!mData->mCurrentSnapshot.isNull())
10746 {
10747 mData->mCurrentStateModified = TRUE;
10748 stsFlags |= SaveSTS_CurStateModified;
10749 }
10750 }
10751
10752 if (deleteSavedState)
10753 {
10754 if (mRemoveSavedState)
10755 {
10756 Assert(!mSSData->mStateFilePath.isEmpty());
10757 RTFileDelete(mSSData->mStateFilePath.c_str());
10758 }
10759 mSSData->mStateFilePath.setNull();
10760 stsFlags |= SaveSTS_StateFilePath;
10761 }
10762
10763 /* redirect to the underlying peer machine */
10764 mPeer->setMachineState(aMachineState);
10765
10766 if ( aMachineState == MachineState_PoweredOff
10767 || aMachineState == MachineState_Teleported
10768 || aMachineState == MachineState_Aborted
10769 || aMachineState == MachineState_Saved)
10770 {
10771 /* the machine has stopped execution
10772 * (or the saved state file was adopted) */
10773 stsFlags |= SaveSTS_StateTimeStamp;
10774 }
10775
10776 if ( ( oldMachineState == MachineState_PoweredOff
10777 || oldMachineState == MachineState_Aborted
10778 || oldMachineState == MachineState_Teleported
10779 )
10780 && aMachineState == MachineState_Saved)
10781 {
10782 /* the saved state file was adopted */
10783 Assert(!mSSData->mStateFilePath.isEmpty());
10784 stsFlags |= SaveSTS_StateFilePath;
10785 }
10786
10787 rc = saveStateSettings(stsFlags);
10788
10789 if ( ( oldMachineState != MachineState_PoweredOff
10790 && oldMachineState != MachineState_Aborted
10791 && oldMachineState != MachineState_Teleported
10792 )
10793 && ( aMachineState == MachineState_PoweredOff
10794 || aMachineState == MachineState_Aborted
10795 || aMachineState == MachineState_Teleported
10796 )
10797 )
10798 {
10799 /* we've been shut down for any reason */
10800 /* no special action so far */
10801 }
10802
10803 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
10804 LogFlowThisFuncLeave();
10805 return rc;
10806}
10807
10808/**
10809 * Sends the current machine state value to the VM process.
10810 *
10811 * @note Locks this object for reading, then calls a client process.
10812 */
10813HRESULT SessionMachine::updateMachineStateOnClient()
10814{
10815 AutoCaller autoCaller(this);
10816 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10817
10818 ComPtr<IInternalSessionControl> directControl;
10819 {
10820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10821 AssertReturn(!!mData, E_FAIL);
10822 directControl = mData->mSession.mDirectControl;
10823
10824 /* directControl may be already set to NULL here in #OnSessionEnd()
10825 * called too early by the direct session process while there is still
10826 * some operation (like discarding the snapshot) in progress. The client
10827 * process in this case is waiting inside Session::close() for the
10828 * "end session" process object to complete, while #uninit() called by
10829 * #checkForDeath() on the Watcher thread is waiting for the pending
10830 * operation to complete. For now, we accept this inconsitent behavior
10831 * and simply do nothing here. */
10832
10833 if (mData->mSession.mState == SessionState_Closing)
10834 return S_OK;
10835
10836 AssertReturn(!directControl.isNull(), E_FAIL);
10837 }
10838
10839 return directControl->UpdateMachineState(mData->mMachineState);
10840}
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