VirtualBox

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

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

Main: remove templates for 'weak' com pointers which do nothing anyway

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 351.9 KB
Line 
1/* $Id: MachineImpl.cpp 27607 2010-03-22 18:13:07Z 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 *
7256 * @note Must be called from under this object's write lock. Locks children for
7257 * writing.
7258 *
7259 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
7260 * initialized to false and that will be set to true by this function if
7261 * the caller must invoke VirtualBox::saveSettings() because the global
7262 * settings have changed. This will happen if a machine rename has been
7263 * saved and the global machine and media registries will therefore need
7264 * updating.
7265 */
7266HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
7267 int aFlags /*= 0*/)
7268{
7269 LogFlowThisFuncEnter();
7270
7271 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7272
7273 /* make sure child objects are unable to modify the settings while we are
7274 * saving them */
7275 ensureNoStateDependencies();
7276
7277 AssertReturn( getClassID() == clsidMachine
7278 || getClassID() == clsidSessionMachine,
7279 E_FAIL);
7280
7281 HRESULT rc = S_OK;
7282 bool fNeedsWrite = false;
7283
7284 /* First, prepare to save settings. It will care about renaming the
7285 * settings directory and file if the machine name was changed and about
7286 * creating a new settings file if this is a new machine. */
7287 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
7288 if (FAILED(rc)) return rc;
7289
7290 // keep a pointer to the current settings structures
7291 settings::MachineConfigFile *pOldConfig = mData->m_pMachineConfigFile;
7292
7293 try
7294 {
7295 // make a fresh one to have everyone write stuff into
7296 mData->m_pMachineConfigFile = new settings::MachineConfigFile(NULL);
7297 mData->m_pMachineConfigFile->copyBaseFrom(*pOldConfig);
7298
7299 // deep copy extradata
7300 mData->m_pMachineConfigFile->mapExtraDataItems = pOldConfig->mapExtraDataItems;
7301
7302 mData->m_pMachineConfigFile->uuid = mData->mUuid;
7303 mData->m_pMachineConfigFile->strName = mUserData->mName;
7304 mData->m_pMachineConfigFile->fNameSync = !!mUserData->mNameSync;
7305 mData->m_pMachineConfigFile->strDescription = mUserData->mDescription;
7306 mData->m_pMachineConfigFile->strOsType = mUserData->mOSTypeId;
7307
7308 if ( mData->mMachineState == MachineState_Saved
7309 || mData->mMachineState == MachineState_Restoring
7310 // when deleting a snapshot we may or may not have a saved state in the current state,
7311 // so let's not assert here please
7312 || ( (mData->mMachineState == MachineState_DeletingSnapshot)
7313 && (!mSSData->mStateFilePath.isEmpty())
7314 )
7315 )
7316 {
7317 Assert(!mSSData->mStateFilePath.isEmpty());
7318 /* try to make the file name relative to the settings file dir */
7319 calculateRelativePath(mSSData->mStateFilePath, mData->m_pMachineConfigFile->strStateFile);
7320 }
7321 else
7322 {
7323 Assert(mSSData->mStateFilePath.isEmpty());
7324 mData->m_pMachineConfigFile->strStateFile.setNull();
7325 }
7326
7327 if (mData->mCurrentSnapshot)
7328 mData->m_pMachineConfigFile->uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
7329 else
7330 mData->m_pMachineConfigFile->uuidCurrentSnapshot.clear();
7331
7332 mData->m_pMachineConfigFile->strSnapshotFolder = mUserData->mSnapshotFolder;
7333 // mData->m_pMachineConfigFile->fCurrentStateModified is special, see below
7334 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
7335 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
7336/// @todo Live Migration: mData->m_pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
7337
7338 mData->m_pMachineConfigFile->fTeleporterEnabled = !!mUserData->mTeleporterEnabled;
7339 mData->m_pMachineConfigFile->uTeleporterPort = mUserData->mTeleporterPort;
7340 mData->m_pMachineConfigFile->strTeleporterAddress = mUserData->mTeleporterAddress;
7341 mData->m_pMachineConfigFile->strTeleporterPassword = mUserData->mTeleporterPassword;
7342
7343 mData->m_pMachineConfigFile->fRTCUseUTC = !!mUserData->mRTCUseUTC;
7344
7345 rc = saveHardware(mData->m_pMachineConfigFile->hardwareMachine);
7346 if (FAILED(rc)) throw rc;
7347
7348 rc = saveStorageControllers(mData->m_pMachineConfigFile->storageMachine);
7349 if (FAILED(rc)) throw rc;
7350
7351 // save snapshots
7352 rc = saveAllSnapshots();
7353 if (FAILED(rc)) throw rc;
7354
7355 if (aFlags & SaveS_ResetCurStateModified)
7356 {
7357 // this gets set by restoreSnapshot()
7358 mData->mCurrentStateModified = FALSE;
7359 fNeedsWrite = true; // always, no need to compare
7360 }
7361 else
7362 {
7363 if (!mData->mCurrentStateModified)
7364 {
7365 // do a deep compare of the settings that we just saved with the settings
7366 // previously stored in the config file; this invokes MachineConfigFile::operator==
7367 // which does a deep compare of all the settings, which is expensive but less expensive
7368 // than writing out XML in vain
7369 bool fAnySettingsChanged = (*mData->m_pMachineConfigFile == *pOldConfig);
7370
7371 // could still be modified if any settings changed
7372 mData->mCurrentStateModified = fAnySettingsChanged;
7373
7374 fNeedsWrite = fAnySettingsChanged;
7375 }
7376 else
7377 fNeedsWrite = true;
7378 }
7379
7380 mData->m_pMachineConfigFile->fCurrentStateModified = !!mData->mCurrentStateModified;
7381
7382 if (fNeedsWrite)
7383 // now spit it all out!
7384 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
7385
7386 // after saving settings, we are no longer different from the XML on disk
7387 m_flModifications = 0;
7388 }
7389 catch (HRESULT err)
7390 {
7391 // we assume that error info is set by the thrower
7392 rc = err;
7393
7394 // restore old config
7395 delete mData->m_pMachineConfigFile;
7396 mData->m_pMachineConfigFile = pOldConfig;
7397 }
7398 catch (...)
7399 {
7400 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
7401 }
7402
7403 if (SUCCEEDED(rc))
7404 {
7405 commit();
7406 delete pOldConfig;
7407 }
7408
7409 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
7410 {
7411 /* Fire the data change event, even on failure (since we've already
7412 * committed all data). This is done only for SessionMachines because
7413 * mutable Machine instances are always not registered (i.e. private
7414 * to the client process that creates them) and thus don't need to
7415 * inform callbacks. */
7416 if (getClassID() == clsidSessionMachine)
7417 mParent->onMachineDataChange(mData->mUuid);
7418 }
7419
7420 LogFlowThisFunc(("rc=%08X\n", rc));
7421 LogFlowThisFuncLeave();
7422 return rc;
7423}
7424
7425HRESULT Machine::saveAllSnapshots()
7426{
7427 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7428
7429 HRESULT rc = S_OK;
7430
7431 try
7432 {
7433 mData->m_pMachineConfigFile->llFirstSnapshot.clear();
7434
7435 if (mData->mFirstSnapshot)
7436 {
7437 settings::Snapshot snapNew;
7438 mData->m_pMachineConfigFile->llFirstSnapshot.push_back(snapNew);
7439
7440 // get reference to the fresh copy of the snapshot on the list and
7441 // work on that copy directly to avoid excessive copying later
7442 settings::Snapshot &snap = mData->m_pMachineConfigFile->llFirstSnapshot.front();
7443
7444 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
7445 if (FAILED(rc)) throw rc;
7446 }
7447
7448// if (mType == IsSessionMachine)
7449// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
7450
7451 }
7452 catch (HRESULT err)
7453 {
7454 /* we assume that error info is set by the thrower */
7455 rc = err;
7456 }
7457 catch (...)
7458 {
7459 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
7460 }
7461
7462 return rc;
7463}
7464
7465/**
7466 * Saves the VM hardware configuration. It is assumed that the
7467 * given node is empty.
7468 *
7469 * @param aNode <Hardware> node to save the VM hardware confguration to.
7470 */
7471HRESULT Machine::saveHardware(settings::Hardware &data)
7472{
7473 HRESULT rc = S_OK;
7474
7475 try
7476 {
7477 /* The hardware version attribute (optional).
7478 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
7479 if ( mHWData->mHWVersion == "1"
7480 && mSSData->mStateFilePath.isEmpty()
7481 )
7482 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. */
7483
7484 data.strVersion = mHWData->mHWVersion;
7485 data.uuid = mHWData->mHardwareUUID;
7486
7487 // CPU
7488 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
7489 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
7490 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
7491 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
7492 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
7493 data.fPAE = !!mHWData->mPAEEnabled;
7494 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
7495
7496 /* Standard and Extended CPUID leafs. */
7497 data.llCpuIdLeafs.clear();
7498 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
7499 {
7500 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
7501 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
7502 }
7503 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
7504 {
7505 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
7506 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
7507 }
7508
7509 data.cCPUs = mHWData->mCPUCount;
7510 data.fCpuHotPlug = mHWData->mCPUHotPlugEnabled;
7511
7512 data.llCpus.clear();
7513 if (data.fCpuHotPlug)
7514 {
7515 for (unsigned idx = 0; idx < data.cCPUs; idx++)
7516 {
7517 if (mHWData->mCPUAttached[idx])
7518 {
7519 settings::Cpu cpu;
7520 cpu.ulId = idx;
7521 data.llCpus.push_back(cpu);
7522 }
7523 }
7524 }
7525
7526 // memory
7527 data.ulMemorySizeMB = mHWData->mMemorySize;
7528
7529 // firmware
7530 data.firmwareType = mHWData->mFirmwareType;
7531
7532 // HID
7533 data.pointingHidType = mHWData->mPointingHidType;
7534 data.keyboardHidType = mHWData->mKeyboardHidType;
7535
7536 // HPET
7537 data.fHpetEnabled = mHWData->mHpetEnabled;
7538
7539 // boot order
7540 data.mapBootOrder.clear();
7541 for (size_t i = 0;
7542 i < RT_ELEMENTS(mHWData->mBootOrder);
7543 ++i)
7544 data.mapBootOrder[i] = mHWData->mBootOrder[i];
7545
7546 // display
7547 data.ulVRAMSizeMB = mHWData->mVRAMSize;
7548 data.cMonitors = mHWData->mMonitorCount;
7549 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
7550 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
7551
7552#ifdef VBOX_WITH_VRDP
7553 /* VRDP settings (optional) */
7554 rc = mVRDPServer->saveSettings(data.vrdpSettings);
7555 if (FAILED(rc)) throw rc;
7556#endif
7557
7558 /* BIOS (required) */
7559 rc = mBIOSSettings->saveSettings(data.biosSettings);
7560 if (FAILED(rc)) throw rc;
7561
7562 /* USB Controller (required) */
7563 rc = mUSBController->saveSettings(data.usbController);
7564 if (FAILED(rc)) throw rc;
7565
7566 /* Network adapters (required) */
7567 data.llNetworkAdapters.clear();
7568 for (ULONG slot = 0;
7569 slot < RT_ELEMENTS(mNetworkAdapters);
7570 ++slot)
7571 {
7572 settings::NetworkAdapter nic;
7573 nic.ulSlot = slot;
7574 rc = mNetworkAdapters[slot]->saveSettings(nic);
7575 if (FAILED(rc)) throw rc;
7576
7577 data.llNetworkAdapters.push_back(nic);
7578 }
7579
7580 /* Serial ports */
7581 data.llSerialPorts.clear();
7582 for (ULONG slot = 0;
7583 slot < RT_ELEMENTS(mSerialPorts);
7584 ++slot)
7585 {
7586 settings::SerialPort s;
7587 s.ulSlot = slot;
7588 rc = mSerialPorts[slot]->saveSettings(s);
7589 if (FAILED(rc)) return rc;
7590
7591 data.llSerialPorts.push_back(s);
7592 }
7593
7594 /* Parallel ports */
7595 data.llParallelPorts.clear();
7596 for (ULONG slot = 0;
7597 slot < RT_ELEMENTS(mParallelPorts);
7598 ++slot)
7599 {
7600 settings::ParallelPort p;
7601 p.ulSlot = slot;
7602 rc = mParallelPorts[slot]->saveSettings(p);
7603 if (FAILED(rc)) return rc;
7604
7605 data.llParallelPorts.push_back(p);
7606 }
7607
7608 /* Audio adapter */
7609 rc = mAudioAdapter->saveSettings(data.audioAdapter);
7610 if (FAILED(rc)) return rc;
7611
7612 /* Shared folders */
7613 data.llSharedFolders.clear();
7614 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
7615 it != mHWData->mSharedFolders.end();
7616 ++it)
7617 {
7618 ComObjPtr<SharedFolder> pFolder = *it;
7619 settings::SharedFolder sf;
7620 sf.strName = pFolder->getName();
7621 sf.strHostPath = pFolder->getHostPath();
7622 sf.fWritable = !!pFolder->isWritable();
7623
7624 data.llSharedFolders.push_back(sf);
7625 }
7626
7627 // clipboard
7628 data.clipboardMode = mHWData->mClipboardMode;
7629
7630 /* Guest */
7631 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
7632 data.ulStatisticsUpdateInterval = mHWData->mStatisticsUpdateInterval;
7633
7634 // IO settings
7635 data.ioSettings.ioMgrType = mHWData->mIoMgrType;
7636 data.ioSettings.ioBackendType = mHWData->mIoBackendType;
7637 data.ioSettings.fIoCacheEnabled = mHWData->mIoCacheEnabled;
7638 data.ioSettings.ulIoCacheSize = mHWData->mIoCacheSize;
7639 data.ioSettings.ulIoBandwidthMax = mHWData->mIoBandwidthMax;
7640
7641 // guest properties
7642 data.llGuestProperties.clear();
7643#ifdef VBOX_WITH_GUEST_PROPS
7644 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
7645 it != mHWData->mGuestProperties.end();
7646 ++it)
7647 {
7648 HWData::GuestProperty property = *it;
7649
7650 settings::GuestProperty prop;
7651 prop.strName = property.strName;
7652 prop.strValue = property.strValue;
7653 prop.timestamp = property.mTimestamp;
7654 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
7655 guestProp::writeFlags(property.mFlags, szFlags);
7656 prop.strFlags = szFlags;
7657
7658 data.llGuestProperties.push_back(prop);
7659 }
7660
7661 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
7662#endif /* VBOX_WITH_GUEST_PROPS defined */
7663 }
7664 catch(std::bad_alloc &)
7665 {
7666 return E_OUTOFMEMORY;
7667 }
7668
7669 AssertComRC(rc);
7670 return rc;
7671}
7672
7673/**
7674 * Saves the storage controller configuration.
7675 *
7676 * @param aNode <StorageControllers> node to save the VM hardware confguration to.
7677 */
7678HRESULT Machine::saveStorageControllers(settings::Storage &data)
7679{
7680 data.llStorageControllers.clear();
7681
7682 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
7683 it != mStorageControllers->end();
7684 ++it)
7685 {
7686 HRESULT rc;
7687 ComObjPtr<StorageController> pCtl = *it;
7688
7689 settings::StorageController ctl;
7690 ctl.strName = pCtl->getName();
7691 ctl.controllerType = pCtl->getControllerType();
7692 ctl.storageBus = pCtl->getStorageBus();
7693 ctl.ulInstance = pCtl->getInstance();
7694
7695 /* Save the port count. */
7696 ULONG portCount;
7697 rc = pCtl->COMGETTER(PortCount)(&portCount);
7698 ComAssertComRCRet(rc, rc);
7699 ctl.ulPortCount = portCount;
7700
7701 /* Save IDE emulation settings. */
7702 if (ctl.controllerType == StorageControllerType_IntelAhci)
7703 {
7704 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
7705 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
7706 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
7707 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
7708 )
7709 ComAssertComRCRet(rc, rc);
7710 }
7711
7712 /* save the devices now. */
7713 rc = saveStorageDevices(pCtl, ctl);
7714 ComAssertComRCRet(rc, rc);
7715
7716 data.llStorageControllers.push_back(ctl);
7717 }
7718
7719 return S_OK;
7720}
7721
7722/**
7723 * Saves the hard disk confguration.
7724 */
7725HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
7726 settings::StorageController &data)
7727{
7728 MediaData::AttachmentList atts;
7729
7730 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()), atts);
7731 if (FAILED(rc)) return rc;
7732
7733 data.llAttachedDevices.clear();
7734 for (MediaData::AttachmentList::const_iterator it = atts.begin();
7735 it != atts.end();
7736 ++it)
7737 {
7738 settings::AttachedDevice dev;
7739
7740 MediumAttachment *pAttach = *it;
7741 Medium *pMedium = pAttach->getMedium();
7742
7743 dev.deviceType = pAttach->getType();
7744 dev.lPort = pAttach->getPort();
7745 dev.lDevice = pAttach->getDevice();
7746 if (pMedium)
7747 {
7748 BOOL fHostDrive = FALSE;
7749 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
7750 if (FAILED(rc))
7751 return rc;
7752 if (fHostDrive)
7753 dev.strHostDriveSrc = pMedium->getLocation();
7754 else
7755 dev.uuid = pMedium->getId();
7756 dev.fPassThrough = pAttach->getPassthrough();
7757 }
7758
7759 data.llAttachedDevices.push_back(dev);
7760 }
7761
7762 return S_OK;
7763}
7764
7765/**
7766 * Saves machine state settings as defined by aFlags
7767 * (SaveSTS_* values).
7768 *
7769 * @param aFlags Combination of SaveSTS_* flags.
7770 *
7771 * @note Locks objects for writing.
7772 */
7773HRESULT Machine::saveStateSettings(int aFlags)
7774{
7775 if (aFlags == 0)
7776 return S_OK;
7777
7778 AutoCaller autoCaller(this);
7779 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7780
7781 /* This object's write lock is also necessary to serialize file access
7782 * (prevent concurrent reads and writes) */
7783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7784
7785 HRESULT rc = S_OK;
7786
7787 Assert(mData->m_pMachineConfigFile);
7788
7789 try
7790 {
7791 if (aFlags & SaveSTS_CurStateModified)
7792 mData->m_pMachineConfigFile->fCurrentStateModified = true;
7793
7794 if (aFlags & SaveSTS_StateFilePath)
7795 {
7796 if (!mSSData->mStateFilePath.isEmpty())
7797 /* try to make the file name relative to the settings file dir */
7798 calculateRelativePath(mSSData->mStateFilePath, mData->m_pMachineConfigFile->strStateFile);
7799 else
7800 mData->m_pMachineConfigFile->strStateFile.setNull();
7801 }
7802
7803 if (aFlags & SaveSTS_StateTimeStamp)
7804 {
7805 Assert( mData->mMachineState != MachineState_Aborted
7806 || mSSData->mStateFilePath.isEmpty());
7807
7808 mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
7809
7810 mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
7811//@todo live migration mData->m_pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
7812 }
7813
7814 mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
7815 }
7816 catch (...)
7817 {
7818 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
7819 }
7820
7821 return rc;
7822}
7823
7824/**
7825 * Creates differencing hard disks for all normal hard disks attached to this
7826 * machine and a new set of attachments to refer to created disks.
7827 *
7828 * Used when taking a snapshot or when discarding the current state.
7829 *
7830 * This method assumes that mMediaData contains the original hard disk attachments
7831 * it needs to create diffs for. On success, these attachments will be replaced
7832 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
7833 * called to delete created diffs which will also rollback mMediaData and restore
7834 * whatever was backed up before calling this method.
7835 *
7836 * Attachments with non-normal hard disks are left as is.
7837 *
7838 * If @a aOnline is @c false then the original hard disks that require implicit
7839 * diffs will be locked for reading. Otherwise it is assumed that they are
7840 * already locked for writing (when the VM was started). Note that in the latter
7841 * case it is responsibility of the caller to lock the newly created diffs for
7842 * writing if this method succeeds.
7843 *
7844 * @param aFolder Folder where to create diff hard disks.
7845 * @param aProgress Progress object to run (must contain at least as
7846 * many operations left as the number of hard disks
7847 * attached).
7848 * @param aOnline Whether the VM was online prior to this operation.
7849 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
7850 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
7851 *
7852 * @note The progress object is not marked as completed, neither on success nor
7853 * on failure. This is a responsibility of the caller.
7854 *
7855 * @note Locks this object for writing.
7856 */
7857HRESULT Machine::createImplicitDiffs(const Bstr &aFolder,
7858 IProgress *aProgress,
7859 ULONG aWeight,
7860 bool aOnline,
7861 bool *pfNeedsSaveSettings)
7862{
7863 AssertReturn(!aFolder.isEmpty(), E_FAIL);
7864
7865 LogFlowThisFunc(("aFolder='%ls', aOnline=%d\n", aFolder.raw(), aOnline));
7866
7867 AutoCaller autoCaller(this);
7868 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7869
7870 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7871
7872 /* must be in a protective state because we leave the lock below */
7873 AssertReturn( mData->mMachineState == MachineState_Saving
7874 || mData->mMachineState == MachineState_LiveSnapshotting
7875 || mData->mMachineState == MachineState_RestoringSnapshot
7876 || mData->mMachineState == MachineState_DeletingSnapshot
7877 , E_FAIL);
7878
7879 HRESULT rc = S_OK;
7880
7881 MediaList lockedMedia;
7882
7883 try
7884 {
7885 if (!aOnline)
7886 {
7887 /* lock all attached hard disks early to detect "in use"
7888 * situations before creating actual diffs */
7889 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7890 it != mMediaData->mAttachments.end();
7891 ++it)
7892 {
7893 MediumAttachment* pAtt = *it;
7894 if (pAtt->getType() == DeviceType_HardDisk)
7895 {
7896 Medium* pHD = pAtt->getMedium();
7897 Assert(pHD);
7898 rc = pHD->LockRead(NULL);
7899 if (FAILED(rc)) throw rc;
7900 lockedMedia.push_back(pHD);
7901 }
7902 }
7903 }
7904
7905 /* remember the current list (note that we don't use backup() since
7906 * mMediaData may be already backed up) */
7907 MediaData::AttachmentList atts = mMediaData->mAttachments;
7908
7909 /* start from scratch */
7910 mMediaData->mAttachments.clear();
7911
7912 /* go through remembered attachments and create diffs for normal hard
7913 * disks and attach them */
7914 for (MediaData::AttachmentList::const_iterator it = atts.begin();
7915 it != atts.end();
7916 ++it)
7917 {
7918 MediumAttachment* pAtt = *it;
7919
7920 DeviceType_T devType = pAtt->getType();
7921 Medium* medium = pAtt->getMedium();
7922
7923 if ( devType != DeviceType_HardDisk
7924 || medium == NULL
7925 || medium->getType() != MediumType_Normal)
7926 {
7927 /* copy the attachment as is */
7928
7929 /** @todo the progress object created in Console::TakeSnaphot
7930 * only expects operations for hard disks. Later other
7931 * device types need to show up in the progress as well. */
7932 if (devType == DeviceType_HardDisk)
7933 {
7934 if (medium == NULL)
7935 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")),
7936 aWeight); // weight
7937 else
7938 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
7939 medium->getBase()->getName().raw()),
7940 aWeight); // weight
7941 }
7942
7943 mMediaData->mAttachments.push_back(pAtt);
7944 continue;
7945 }
7946
7947 /* need a diff */
7948 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
7949 medium->getBase()->getName().raw()),
7950 aWeight); // weight
7951
7952 ComObjPtr<Medium> diff;
7953 diff.createObject();
7954 rc = diff->init(mParent,
7955 medium->preferredDiffFormat().raw(),
7956 BstrFmt("%ls"RTPATH_SLASH_STR,
7957 mUserData->mSnapshotFolderFull.raw()).raw(),
7958 pfNeedsSaveSettings);
7959 if (FAILED(rc)) throw rc;
7960
7961 /* leave the lock before the potentially lengthy operation */
7962 alock.leave();
7963
7964 rc = medium->createDiffStorageAndWait(diff,
7965 MediumVariant_Standard,
7966 pfNeedsSaveSettings);
7967
7968 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
7969 * the push_back? Looks like we're going to leave medium with the
7970 * wrong kind of lock (general issue with if we fail anywhere at all)
7971 * and an orphaned VDI in the snapshots folder. */
7972 // at this point, the old image is still locked for writing, but instead
7973 // we need the new diff image locked for writing and lock the previously
7974 // current one for reading only
7975 if (aOnline)
7976 {
7977 diff->LockWrite(NULL);
7978 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(diff), true));
7979 medium->UnlockWrite(NULL);
7980 medium->LockRead(NULL);
7981 mData->mSession.mLockedMedia.push_back(Data::Session::LockedMedia::value_type(ComPtr<IMedium>(medium), false));
7982 }
7983
7984 if (FAILED(rc)) throw rc;
7985
7986 alock.enter();
7987
7988 rc = diff->attachTo(mData->mUuid);
7989 AssertComRCThrowRC(rc);
7990
7991 /* add a new attachment */
7992 ComObjPtr<MediumAttachment> attachment;
7993 attachment.createObject();
7994 rc = attachment->init(this,
7995 diff,
7996 pAtt->getControllerName(),
7997 pAtt->getPort(),
7998 pAtt->getDevice(),
7999 DeviceType_HardDisk,
8000 true /* aImplicit */);
8001 if (FAILED(rc)) throw rc;
8002
8003 mMediaData->mAttachments.push_back(attachment);
8004 }
8005 }
8006 catch (HRESULT aRC) { rc = aRC; }
8007
8008 /* unlock all hard disks we locked */
8009 if (!aOnline)
8010 {
8011 ErrorInfoKeeper eik;
8012
8013 for (MediaList::const_iterator it = lockedMedia.begin();
8014 it != lockedMedia.end();
8015 ++it)
8016 {
8017 HRESULT rc2 = (*it)->UnlockRead(NULL);
8018 AssertComRC(rc2);
8019 }
8020 }
8021
8022 if (FAILED(rc))
8023 {
8024 MultiResultRef mrc(rc);
8025
8026 mrc = deleteImplicitDiffs(pfNeedsSaveSettings);
8027 }
8028
8029 return rc;
8030}
8031
8032/**
8033 * Deletes implicit differencing hard disks created either by
8034 * #createImplicitDiffs() or by #AttachMedium() and rolls back mMediaData.
8035 *
8036 * Note that to delete hard disks created by #AttachMedium() this method is
8037 * called from #fixupMedia() when the changes are rolled back.
8038 *
8039 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
8040 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
8041 *
8042 * @note Locks this object for writing.
8043 */
8044HRESULT Machine::deleteImplicitDiffs(bool *pfNeedsSaveSettings)
8045{
8046 AutoCaller autoCaller(this);
8047 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8048
8049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8050 LogFlowThisFuncEnter();
8051
8052 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
8053
8054 HRESULT rc = S_OK;
8055
8056 MediaData::AttachmentList implicitAtts;
8057
8058 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
8059
8060 /* enumerate new attachments */
8061 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8062 it != mMediaData->mAttachments.end();
8063 ++it)
8064 {
8065 ComObjPtr<Medium> hd = (*it)->getMedium();
8066 if (hd.isNull())
8067 continue;
8068
8069 if ((*it)->isImplicit())
8070 {
8071 /* deassociate and mark for deletion */
8072 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
8073 rc = hd->detachFrom(mData->mUuid);
8074 AssertComRC(rc);
8075 implicitAtts.push_back(*it);
8076 continue;
8077 }
8078
8079 /* was this hard disk attached before? */
8080 if (!findAttachment(oldAtts, hd))
8081 {
8082 /* no: de-associate */
8083 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
8084 rc = hd->detachFrom(mData->mUuid);
8085 AssertComRC(rc);
8086 continue;
8087 }
8088 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
8089 }
8090
8091 /* rollback hard disk changes */
8092 mMediaData.rollback();
8093
8094 MultiResult mrc(S_OK);
8095
8096 /* delete unused implicit diffs */
8097 if (implicitAtts.size() != 0)
8098 {
8099 /* will leave the lock before the potentially lengthy
8100 * operation, so protect with the special state (unless already
8101 * protected) */
8102 MachineState_T oldState = mData->mMachineState;
8103 if ( oldState != MachineState_Saving
8104 && oldState != MachineState_LiveSnapshotting
8105 && oldState != MachineState_RestoringSnapshot
8106 && oldState != MachineState_DeletingSnapshot
8107 )
8108 setMachineState(MachineState_SettingUp);
8109
8110 alock.leave();
8111
8112 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
8113 it != implicitAtts.end();
8114 ++it)
8115 {
8116 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
8117 ComObjPtr<Medium> hd = (*it)->getMedium();
8118
8119 rc = hd->deleteStorageAndWait(NULL /*aProgress*/, pfNeedsSaveSettings);
8120#if 1 /* HACK ALERT: Just make it kind of work */ /** @todo Fix this hack properly. The LockWrite / UnlockWrite / LockRead changes aren't undone! */
8121 if (rc == VBOX_E_INVALID_OBJECT_STATE)
8122 {
8123 LogFlowFunc(("Applying unlock hack on '%s'! FIXME!\n", (*it)->getLogName()));
8124 hd->UnlockWrite(NULL);
8125 rc = hd->deleteStorageAndWait(NULL /*aProgress*/, pfNeedsSaveSettings);
8126 }
8127#endif
8128 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
8129 mrc = rc;
8130 }
8131
8132 alock.enter();
8133
8134 if (mData->mMachineState == MachineState_SettingUp)
8135 {
8136 setMachineState(oldState);
8137 }
8138 }
8139
8140 return mrc;
8141}
8142
8143/**
8144 * Looks through the given list of media attachments for one with the given parameters
8145 * and returns it, or NULL if not found. The list is a parameter so that backup lists
8146 * can be searched as well if needed.
8147 *
8148 * @param list
8149 * @param aControllerName
8150 * @param aControllerPort
8151 * @param aDevice
8152 * @return
8153 */
8154MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
8155 IN_BSTR aControllerName,
8156 LONG aControllerPort,
8157 LONG aDevice)
8158{
8159 for (MediaData::AttachmentList::const_iterator it = ll.begin();
8160 it != ll.end();
8161 ++it)
8162 {
8163 MediumAttachment *pAttach = *it;
8164 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
8165 return pAttach;
8166 }
8167
8168 return NULL;
8169}
8170
8171/**
8172 * Looks through the given list of media attachments for one with the given parameters
8173 * and returns it, or NULL if not found. The list is a parameter so that backup lists
8174 * can be searched as well if needed.
8175 *
8176 * @param list
8177 * @param aControllerName
8178 * @param aControllerPort
8179 * @param aDevice
8180 * @return
8181 */
8182MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
8183 ComObjPtr<Medium> pMedium)
8184{
8185 for (MediaData::AttachmentList::const_iterator it = ll.begin();
8186 it != ll.end();
8187 ++it)
8188 {
8189 MediumAttachment *pAttach = *it;
8190 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
8191 if (pMediumThis.equalsTo(pMedium))
8192 return pAttach;
8193 }
8194
8195 return NULL;
8196}
8197
8198/**
8199 * Looks through the given list of media attachments for one with the given parameters
8200 * and returns it, or NULL if not found. The list is a parameter so that backup lists
8201 * can be searched as well if needed.
8202 *
8203 * @param list
8204 * @param aControllerName
8205 * @param aControllerPort
8206 * @param aDevice
8207 * @return
8208 */
8209MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
8210 Guid &id)
8211{
8212 for (MediaData::AttachmentList::const_iterator it = ll.begin();
8213 it != ll.end();
8214 ++it)
8215 {
8216 MediumAttachment *pAttach = *it;
8217 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
8218 if (pMediumThis->getId() == id)
8219 return pAttach;
8220 }
8221
8222 return NULL;
8223}
8224
8225/**
8226 * Perform deferred hard disk detachments.
8227 *
8228 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
8229 * backed up).
8230 *
8231 * If @a aOnline is @c true then this method will also unlock the old hard disks
8232 * for which the new implicit diffs were created and will lock these new diffs for
8233 * writing.
8234 *
8235 * @param aOnline Whether the VM was online prior to this operation.
8236 *
8237 * @note Locks this object for writing!
8238 */
8239void Machine::commitMedia(bool aOnline /*= false*/)
8240{
8241 AutoCaller autoCaller(this);
8242 AssertComRCReturnVoid(autoCaller.rc());
8243
8244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8245
8246 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
8247
8248 HRESULT rc = S_OK;
8249
8250 /* no attach/detach operations -- nothing to do */
8251 if (!mMediaData.isBackedUp())
8252 return;
8253
8254 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
8255
8256 /* enumerate new attachments */
8257 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8258 it != mMediaData->mAttachments.end();
8259 ++it)
8260 {
8261 MediumAttachment *pAttach = *it;
8262
8263 pAttach->commit();
8264
8265 Medium* pMedium = pAttach->getMedium();
8266 bool fImplicit = pAttach->isImplicit();
8267
8268 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
8269 (pMedium) ? pMedium->getName().raw() : "NULL",
8270 fImplicit));
8271
8272 /** @todo convert all this Machine-based voodoo to MediumAttachment
8273 * based commit logic. */
8274 if (fImplicit)
8275 {
8276 /* convert implicit attachment to normal */
8277 pAttach->setImplicit(false);
8278
8279 if ( aOnline
8280 && pMedium
8281 && pAttach->getType() == DeviceType_HardDisk
8282 )
8283 {
8284 rc = pMedium->LockWrite(NULL);
8285 AssertComRC(rc);
8286
8287 mData->mSession.mLockedMedia.push_back(
8288 Data::Session::LockedMedia::value_type(
8289 ComPtr<IMedium>(pMedium), true));
8290
8291 /* also, relock the old hard disk which is a base for the
8292 * new diff for reading if the VM is online */
8293
8294 ComObjPtr<Medium> parent = pMedium->getParent();
8295 /* make the relock atomic */
8296 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
8297 rc = parent->UnlockWrite(NULL);
8298 AssertComRC(rc);
8299 rc = parent->LockRead(NULL);
8300 AssertComRC(rc);
8301
8302 /* XXX actually we should replace the old entry in that
8303 * vector (write lock => read lock) but this would take
8304 * some effort. So lets just ignore the error code in
8305 * SessionMachine::unlockMedia(). */
8306 mData->mSession.mLockedMedia.push_back(
8307 Data::Session::LockedMedia::value_type (
8308 ComPtr<IMedium>(parent), false));
8309 }
8310
8311 continue;
8312 }
8313
8314 if (pMedium)
8315 {
8316 /* was this medium attached before? */
8317 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
8318 oldIt != oldAtts.end();
8319 ++oldIt)
8320 {
8321 MediumAttachment *pOldAttach = *oldIt;
8322 if (pOldAttach->getMedium().equalsTo(pMedium))
8323 {
8324 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().raw()));
8325
8326 /* yes: remove from old to avoid de-association */
8327 oldAtts.erase(oldIt);
8328 break;
8329 }
8330 }
8331 }
8332 }
8333
8334 /* enumerate remaining old attachments and de-associate from the
8335 * current machine state */
8336 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
8337 it != oldAtts.end();
8338 ++it)
8339 {
8340 MediumAttachment *pAttach = *it;
8341 Medium* pMedium = pAttach->getMedium();
8342
8343 /* Detach only hard disks, since DVD/floppy media is detached
8344 * instantly in MountMedium. */
8345 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
8346 {
8347 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().raw()));
8348
8349 /* now de-associate from the current machine state */
8350 rc = pMedium->detachFrom(mData->mUuid);
8351 AssertComRC(rc);
8352
8353 if ( aOnline
8354 && pAttach->getType() == DeviceType_HardDisk)
8355 {
8356 /* unlock since not used anymore */
8357 MediumState_T state;
8358 rc = pMedium->UnlockWrite(&state);
8359 /* the disk may be alredy relocked for reading above */
8360 Assert(SUCCEEDED(rc) || state == MediumState_LockedRead);
8361 }
8362 }
8363 }
8364
8365 /* commit the hard disk changes */
8366 mMediaData.commit();
8367
8368 if (getClassID() == clsidSessionMachine)
8369 {
8370 /* attach new data to the primary machine and reshare it */
8371 mPeer->mMediaData.attach(mMediaData);
8372 }
8373
8374 return;
8375}
8376
8377/**
8378 * Perform deferred deletion of implicitly created diffs.
8379 *
8380 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
8381 * backed up).
8382 *
8383 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
8384 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
8385 *
8386 * @note Locks this object for writing!
8387 */
8388void Machine::rollbackMedia()
8389{
8390 AutoCaller autoCaller(this);
8391 AssertComRCReturnVoid (autoCaller.rc());
8392
8393 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8394
8395 LogFlowThisFunc(("Entering\n"));
8396
8397 HRESULT rc = S_OK;
8398
8399 /* no attach/detach operations -- nothing to do */
8400 if (!mMediaData.isBackedUp())
8401 return;
8402
8403 /* enumerate new attachments */
8404 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8405 it != mMediaData->mAttachments.end();
8406 ++it)
8407 {
8408 MediumAttachment *pAttach = *it;
8409 /* Fix up the backrefs for DVD/floppy media. */
8410 if (pAttach->getType() != DeviceType_HardDisk)
8411 {
8412 Medium* pMedium = pAttach->getMedium();
8413 if (pMedium)
8414 {
8415 rc = pMedium->detachFrom(mData->mUuid);
8416 AssertComRC(rc);
8417 }
8418 }
8419
8420 (*it)->rollback();
8421
8422 pAttach = *it;
8423 /* Fix up the backrefs for DVD/floppy media. */
8424 if (pAttach->getType() != DeviceType_HardDisk)
8425 {
8426 Medium* pMedium = pAttach->getMedium();
8427 if (pMedium)
8428 {
8429 rc = pMedium->attachTo(mData->mUuid);
8430 AssertComRC(rc);
8431 }
8432 }
8433 }
8434
8435 /** @todo convert all this Machine-based voodoo to MediumAttachment
8436 * based rollback logic. */
8437 // @todo r=dj the below totally fails if this gets called from Machine::rollback(),
8438 // which gets called if Machine::registeredInit() fails...
8439 deleteImplicitDiffs(NULL /*pfNeedsSaveSettings*/);
8440
8441 return;
8442}
8443
8444/**
8445 * Returns true if the settings file is located in the directory named exactly
8446 * as the machine. This will be true if the machine settings structure was
8447 * created by default in #openConfigLoader().
8448 *
8449 * @param aSettingsDir if not NULL, the full machine settings file directory
8450 * name will be assigned there.
8451 *
8452 * @note Doesn't lock anything.
8453 * @note Not thread safe (must be called from this object's lock).
8454 */
8455bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */)
8456{
8457 Utf8Str settingsDir = mData->m_strConfigFileFull;
8458 settingsDir.stripFilename();
8459 char *dirName = RTPathFilename(settingsDir.c_str());
8460
8461 AssertReturn(dirName, false);
8462
8463 /* if we don't rename anything on name change, return false shorlty */
8464 if (!mUserData->mNameSync)
8465 return false;
8466
8467 if (aSettingsDir)
8468 *aSettingsDir = settingsDir;
8469
8470 return Bstr(dirName) == mUserData->mName;
8471}
8472
8473/**
8474 * Discards all changes to machine settings.
8475 *
8476 * @param aNotify Whether to notify the direct session about changes or not.
8477 *
8478 * @note Locks objects for writing!
8479 */
8480void Machine::rollback(bool aNotify)
8481{
8482 AutoCaller autoCaller(this);
8483 AssertComRCReturn(autoCaller.rc(), (void)0);
8484
8485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8486
8487 if (!mStorageControllers.isNull())
8488 {
8489 if (mStorageControllers.isBackedUp())
8490 {
8491 /* unitialize all new devices (absent in the backed up list). */
8492 StorageControllerList::const_iterator it = mStorageControllers->begin();
8493 StorageControllerList *backedList = mStorageControllers.backedUpData();
8494 while (it != mStorageControllers->end())
8495 {
8496 if ( std::find(backedList->begin(), backedList->end(), *it)
8497 == backedList->end()
8498 )
8499 {
8500 (*it)->uninit();
8501 }
8502 ++it;
8503 }
8504
8505 /* restore the list */
8506 mStorageControllers.rollback();
8507 }
8508
8509 /* rollback any changes to devices after restoring the list */
8510 if (m_flModifications & IsModified_Storage)
8511 {
8512 StorageControllerList::const_iterator it = mStorageControllers->begin();
8513 while (it != mStorageControllers->end())
8514 {
8515 (*it)->rollback();
8516 ++it;
8517 }
8518 }
8519 }
8520
8521 mUserData.rollback();
8522
8523 mHWData.rollback();
8524
8525 if (m_flModifications & IsModified_Storage)
8526 rollbackMedia();
8527
8528 if (mBIOSSettings)
8529 mBIOSSettings->rollback();
8530
8531#ifdef VBOX_WITH_VRDP
8532 if (mVRDPServer && (m_flModifications & IsModified_VRDPServer))
8533 mVRDPServer->rollback();
8534#endif
8535
8536 if (mAudioAdapter)
8537 mAudioAdapter->rollback();
8538
8539 if (mUSBController && (m_flModifications & IsModified_USB))
8540 mUSBController->rollback();
8541
8542 ComPtr<INetworkAdapter> networkAdapters[RT_ELEMENTS(mNetworkAdapters)];
8543 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
8544 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
8545
8546 if (m_flModifications & IsModified_NetworkAdapters)
8547 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
8548 if ( mNetworkAdapters[slot]
8549 && mNetworkAdapters[slot]->isModified())
8550 {
8551 mNetworkAdapters[slot]->rollback();
8552 networkAdapters[slot] = mNetworkAdapters[slot];
8553 }
8554
8555 if (m_flModifications & IsModified_SerialPorts)
8556 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8557 if ( mSerialPorts[slot]
8558 && mSerialPorts[slot]->isModified())
8559 {
8560 mSerialPorts[slot]->rollback();
8561 serialPorts[slot] = mSerialPorts[slot];
8562 }
8563
8564 if (m_flModifications & IsModified_ParallelPorts)
8565 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8566 if ( mParallelPorts[slot]
8567 && mParallelPorts[slot]->isModified())
8568 {
8569 mParallelPorts[slot]->rollback();
8570 parallelPorts[slot] = mParallelPorts[slot];
8571 }
8572
8573 if (aNotify)
8574 {
8575 /* inform the direct session about changes */
8576
8577 ComObjPtr<Machine> that = this;
8578 uint32_t flModifications = m_flModifications;
8579 alock.leave();
8580
8581 if (flModifications & IsModified_SharedFolders)
8582 that->onSharedFolderChange();
8583
8584 if (flModifications & IsModified_VRDPServer)
8585 that->onVRDPServerChange();
8586 if (flModifications & IsModified_USB)
8587 that->onUSBControllerChange();
8588
8589 for (ULONG slot = 0; slot < RT_ELEMENTS(networkAdapters); slot ++)
8590 if (networkAdapters[slot])
8591 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
8592 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot ++)
8593 if (serialPorts[slot])
8594 that->onSerialPortChange(serialPorts[slot]);
8595 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot ++)
8596 if (parallelPorts[slot])
8597 that->onParallelPortChange(parallelPorts[slot]);
8598
8599 if (flModifications & IsModified_Storage)
8600 that->onStorageControllerChange();
8601 }
8602}
8603
8604/**
8605 * Commits all the changes to machine settings.
8606 *
8607 * Note that this operation is supposed to never fail.
8608 *
8609 * @note Locks this object and children for writing.
8610 */
8611void Machine::commit()
8612{
8613 AutoCaller autoCaller(this);
8614 AssertComRCReturnVoid(autoCaller.rc());
8615
8616 AutoCaller peerCaller(mPeer);
8617 AssertComRCReturnVoid(peerCaller.rc());
8618
8619 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
8620
8621 /*
8622 * use safe commit to ensure Snapshot machines (that share mUserData)
8623 * will still refer to a valid memory location
8624 */
8625 mUserData.commitCopy();
8626
8627 mHWData.commit();
8628
8629 if (mMediaData.isBackedUp())
8630 commitMedia();
8631
8632 mBIOSSettings->commit();
8633#ifdef VBOX_WITH_VRDP
8634 mVRDPServer->commit();
8635#endif
8636 mAudioAdapter->commit();
8637 mUSBController->commit();
8638
8639 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
8640 mNetworkAdapters[slot]->commit();
8641 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8642 mSerialPorts[slot]->commit();
8643 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8644 mParallelPorts[slot]->commit();
8645
8646 bool commitStorageControllers = false;
8647
8648 if (mStorageControllers.isBackedUp())
8649 {
8650 mStorageControllers.commit();
8651
8652 if (mPeer)
8653 {
8654 AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);
8655
8656 /* Commit all changes to new controllers (this will reshare data with
8657 * peers for thos who have peers) */
8658 StorageControllerList *newList = new StorageControllerList();
8659 StorageControllerList::const_iterator it = mStorageControllers->begin();
8660 while (it != mStorageControllers->end())
8661 {
8662 (*it)->commit();
8663
8664 /* look if this controller has a peer device */
8665 ComObjPtr<StorageController> peer = (*it)->getPeer();
8666 if (!peer)
8667 {
8668 /* no peer means the device is a newly created one;
8669 * create a peer owning data this device share it with */
8670 peer.createObject();
8671 peer->init(mPeer, *it, true /* aReshare */);
8672 }
8673 else
8674 {
8675 /* remove peer from the old list */
8676 mPeer->mStorageControllers->remove(peer);
8677 }
8678 /* and add it to the new list */
8679 newList->push_back(peer);
8680
8681 ++it;
8682 }
8683
8684 /* uninit old peer's controllers that are left */
8685 it = mPeer->mStorageControllers->begin();
8686 while (it != mPeer->mStorageControllers->end())
8687 {
8688 (*it)->uninit();
8689 ++it;
8690 }
8691
8692 /* attach new list of controllers to our peer */
8693 mPeer->mStorageControllers.attach(newList);
8694 }
8695 else
8696 {
8697 /* we have no peer (our parent is the newly created machine);
8698 * just commit changes to devices */
8699 commitStorageControllers = true;
8700 }
8701 }
8702 else
8703 {
8704 /* the list of controllers itself is not changed,
8705 * just commit changes to controllers themselves */
8706 commitStorageControllers = true;
8707 }
8708
8709 if (commitStorageControllers)
8710 {
8711 StorageControllerList::const_iterator it = mStorageControllers->begin();
8712 while (it != mStorageControllers->end())
8713 {
8714 (*it)->commit();
8715 ++it;
8716 }
8717 }
8718
8719 if (getClassID() == clsidSessionMachine)
8720 {
8721 /* attach new data to the primary machine and reshare it */
8722 mPeer->mUserData.attach(mUserData);
8723 mPeer->mHWData.attach(mHWData);
8724 /* mMediaData is reshared by fixupMedia */
8725 // mPeer->mMediaData.attach(mMediaData);
8726 Assert(mPeer->mMediaData.data() == mMediaData.data());
8727 }
8728}
8729
8730/**
8731 * Copies all the hardware data from the given machine.
8732 *
8733 * Currently, only called when the VM is being restored from a snapshot. In
8734 * particular, this implies that the VM is not running during this method's
8735 * call.
8736 *
8737 * @note This method must be called from under this object's lock.
8738 *
8739 * @note This method doesn't call #commit(), so all data remains backed up and
8740 * unsaved.
8741 */
8742void Machine::copyFrom(Machine *aThat)
8743{
8744 AssertReturnVoid(getClassID() == clsidMachine || getClassID() == clsidSessionMachine);
8745 AssertReturnVoid(aThat->getClassID() == clsidSnapshotMachine);
8746
8747 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
8748
8749 mHWData.assignCopy(aThat->mHWData);
8750
8751 // create copies of all shared folders (mHWData after attiching a copy
8752 // contains just references to original objects)
8753 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
8754 it != mHWData->mSharedFolders.end();
8755 ++it)
8756 {
8757 ComObjPtr<SharedFolder> folder;
8758 folder.createObject();
8759 HRESULT rc = folder->initCopy(getMachine(), *it);
8760 AssertComRC(rc);
8761 *it = folder;
8762 }
8763
8764 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
8765#ifdef VBOX_WITH_VRDP
8766 mVRDPServer->copyFrom(aThat->mVRDPServer);
8767#endif
8768 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
8769 mUSBController->copyFrom(aThat->mUSBController);
8770
8771 /* create private copies of all controllers */
8772 mStorageControllers.backup();
8773 mStorageControllers->clear();
8774 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
8775 it != aThat->mStorageControllers->end();
8776 ++it)
8777 {
8778 ComObjPtr<StorageController> ctrl;
8779 ctrl.createObject();
8780 ctrl->initCopy(this, *it);
8781 mStorageControllers->push_back(ctrl);
8782 }
8783
8784 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
8785 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
8786 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8787 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
8788 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8789 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
8790}
8791
8792#ifdef VBOX_WITH_RESOURCE_USAGE_API
8793void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
8794{
8795 pm::CollectorHAL *hal = aCollector->getHAL();
8796 /* Create sub metrics */
8797 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
8798 "Percentage of processor time spent in user mode by VM process.");
8799 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
8800 "Percentage of processor time spent in kernel mode by VM process.");
8801 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
8802 "Size of resident portion of VM process in memory.");
8803 /* Create and register base metrics */
8804 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
8805 cpuLoadUser, cpuLoadKernel);
8806 aCollector->registerBaseMetric(cpuLoad);
8807 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
8808 ramUsageUsed);
8809 aCollector->registerBaseMetric(ramUsage);
8810
8811 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
8812 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
8813 new pm::AggregateAvg()));
8814 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
8815 new pm::AggregateMin()));
8816 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
8817 new pm::AggregateMax()));
8818 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
8819 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
8820 new pm::AggregateAvg()));
8821 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
8822 new pm::AggregateMin()));
8823 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
8824 new pm::AggregateMax()));
8825
8826 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
8827 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
8828 new pm::AggregateAvg()));
8829 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
8830 new pm::AggregateMin()));
8831 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
8832 new pm::AggregateMax()));
8833};
8834
8835void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
8836{
8837 aCollector->unregisterMetricsFor(aMachine);
8838 aCollector->unregisterBaseMetricsFor(aMachine);
8839};
8840#endif /* VBOX_WITH_RESOURCE_USAGE_API */
8841
8842
8843////////////////////////////////////////////////////////////////////////////////
8844
8845DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
8846
8847HRESULT SessionMachine::FinalConstruct()
8848{
8849 LogFlowThisFunc(("\n"));
8850
8851#if defined(RT_OS_WINDOWS)
8852 mIPCSem = NULL;
8853#elif defined(RT_OS_OS2)
8854 mIPCSem = NULLHANDLE;
8855#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8856 mIPCSem = -1;
8857#else
8858# error "Port me!"
8859#endif
8860
8861 return S_OK;
8862}
8863
8864void SessionMachine::FinalRelease()
8865{
8866 LogFlowThisFunc(("\n"));
8867
8868 uninit(Uninit::Unexpected);
8869}
8870
8871/**
8872 * @note Must be called only by Machine::openSession() from its own write lock.
8873 */
8874HRESULT SessionMachine::init(Machine *aMachine)
8875{
8876 LogFlowThisFuncEnter();
8877 LogFlowThisFunc(("mName={%ls}\n", aMachine->mUserData->mName.raw()));
8878
8879 AssertReturn(aMachine, E_INVALIDARG);
8880
8881 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
8882
8883 /* Enclose the state transition NotReady->InInit->Ready */
8884 AutoInitSpan autoInitSpan(this);
8885 AssertReturn(autoInitSpan.isOk(), E_FAIL);
8886
8887 /* create the interprocess semaphore */
8888#if defined(RT_OS_WINDOWS)
8889 mIPCSemName = aMachine->mData->m_strConfigFileFull;
8890 for (size_t i = 0; i < mIPCSemName.length(); i++)
8891 if (mIPCSemName[i] == '\\')
8892 mIPCSemName[i] = '/';
8893 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName);
8894 ComAssertMsgRet(mIPCSem,
8895 ("Cannot create IPC mutex '%ls', err=%d",
8896 mIPCSemName.raw(), ::GetLastError()),
8897 E_FAIL);
8898#elif defined(RT_OS_OS2)
8899 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
8900 aMachine->mData->mUuid.raw());
8901 mIPCSemName = ipcSem;
8902 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.raw(), &mIPCSem, 0, FALSE);
8903 ComAssertMsgRet(arc == NO_ERROR,
8904 ("Cannot create IPC mutex '%s', arc=%ld",
8905 ipcSem.raw(), arc),
8906 E_FAIL);
8907#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
8908# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
8909# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
8910 /** @todo Check that this still works correctly. */
8911 AssertCompileSize(key_t, 8);
8912# else
8913 AssertCompileSize(key_t, 4);
8914# endif
8915 key_t key;
8916 mIPCSem = -1;
8917 mIPCKey = "0";
8918 for (uint32_t i = 0; i < 1 << 24; i++)
8919 {
8920 key = ((uint32_t)'V' << 24) | i;
8921 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
8922 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
8923 {
8924 mIPCSem = sem;
8925 if (sem >= 0)
8926 mIPCKey = BstrFmt("%u", key);
8927 break;
8928 }
8929 }
8930# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8931 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
8932 char *pszSemName = NULL;
8933 RTStrUtf8ToCurrentCP(&pszSemName, semName);
8934 key_t key = ::ftok(pszSemName, 'V');
8935 RTStrFree(pszSemName);
8936
8937 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
8938# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
8939
8940 int errnoSave = errno;
8941 if (mIPCSem < 0 && errnoSave == ENOSYS)
8942 {
8943 setError(E_FAIL,
8944 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
8945 "support for SysV IPC. Check the host kernel configuration for "
8946 "CONFIG_SYSVIPC=y"));
8947 return E_FAIL;
8948 }
8949 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
8950 * the IPC semaphores */
8951 if (mIPCSem < 0 && errnoSave == ENOSPC)
8952 {
8953#ifdef RT_OS_LINUX
8954 setError(E_FAIL,
8955 tr("Cannot create IPC semaphore because the system limit for the "
8956 "maximum number of semaphore sets (SEMMNI), or the system wide "
8957 "maximum number of sempahores (SEMMNS) would be exceeded. The "
8958 "current set of SysV IPC semaphores can be determined from "
8959 "the file /proc/sysvipc/sem"));
8960#else
8961 setError(E_FAIL,
8962 tr("Cannot create IPC semaphore because the system-imposed limit "
8963 "on the maximum number of allowed semaphores or semaphore "
8964 "identifiers system-wide would be exceeded"));
8965#endif
8966 return E_FAIL;
8967 }
8968 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
8969 E_FAIL);
8970 /* set the initial value to 1 */
8971 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
8972 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
8973 E_FAIL);
8974#else
8975# error "Port me!"
8976#endif
8977
8978 /* memorize the peer Machine */
8979 unconst(mPeer) = aMachine;
8980 /* share the parent pointer */
8981 unconst(mParent) = aMachine->mParent;
8982
8983 /* take the pointers to data to share */
8984 mData.share(aMachine->mData);
8985 mSSData.share(aMachine->mSSData);
8986
8987 mUserData.share(aMachine->mUserData);
8988 mHWData.share(aMachine->mHWData);
8989 mMediaData.share(aMachine->mMediaData);
8990
8991 mStorageControllers.allocate();
8992 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
8993 it != aMachine->mStorageControllers->end();
8994 ++it)
8995 {
8996 ComObjPtr<StorageController> ctl;
8997 ctl.createObject();
8998 ctl->init(this, *it);
8999 mStorageControllers->push_back(ctl);
9000 }
9001
9002 unconst(mBIOSSettings).createObject();
9003 mBIOSSettings->init(this, aMachine->mBIOSSettings);
9004#ifdef VBOX_WITH_VRDP
9005 /* create another VRDPServer object that will be mutable */
9006 unconst(mVRDPServer).createObject();
9007 mVRDPServer->init(this, aMachine->mVRDPServer);
9008#endif
9009 /* create another audio adapter object that will be mutable */
9010 unconst(mAudioAdapter).createObject();
9011 mAudioAdapter->init(this, aMachine->mAudioAdapter);
9012 /* create a list of serial ports that will be mutable */
9013 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
9014 {
9015 unconst(mSerialPorts[slot]).createObject();
9016 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
9017 }
9018 /* create a list of parallel ports that will be mutable */
9019 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
9020 {
9021 unconst(mParallelPorts[slot]).createObject();
9022 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
9023 }
9024 /* create another USB controller object that will be mutable */
9025 unconst(mUSBController).createObject();
9026 mUSBController->init(this, aMachine->mUSBController);
9027
9028 /* create a list of network adapters that will be mutable */
9029 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
9030 {
9031 unconst(mNetworkAdapters[slot]).createObject();
9032 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
9033 }
9034
9035 /* default is to delete saved state on Saved -> PoweredOff transition */
9036 mRemoveSavedState = true;
9037
9038 /* Confirm a successful initialization when it's the case */
9039 autoInitSpan.setSucceeded();
9040
9041 LogFlowThisFuncLeave();
9042 return S_OK;
9043}
9044
9045/**
9046 * Uninitializes this session object. If the reason is other than
9047 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
9048 *
9049 * @param aReason uninitialization reason
9050 *
9051 * @note Locks mParent + this object for writing.
9052 */
9053void SessionMachine::uninit(Uninit::Reason aReason)
9054{
9055 LogFlowThisFuncEnter();
9056 LogFlowThisFunc(("reason=%d\n", aReason));
9057
9058 /*
9059 * Strongly reference ourselves to prevent this object deletion after
9060 * mData->mSession.mMachine.setNull() below (which can release the last
9061 * reference and call the destructor). Important: this must be done before
9062 * accessing any members (and before AutoUninitSpan that does it as well).
9063 * This self reference will be released as the very last step on return.
9064 */
9065 ComObjPtr<SessionMachine> selfRef = this;
9066
9067 /* Enclose the state transition Ready->InUninit->NotReady */
9068 AutoUninitSpan autoUninitSpan(this);
9069 if (autoUninitSpan.uninitDone())
9070 {
9071 LogFlowThisFunc(("Already uninitialized\n"));
9072 LogFlowThisFuncLeave();
9073 return;
9074 }
9075
9076 if (autoUninitSpan.initFailed())
9077 {
9078 /* We've been called by init() because it's failed. It's not really
9079 * necessary (nor it's safe) to perform the regular uninit sequense
9080 * below, the following is enough.
9081 */
9082 LogFlowThisFunc(("Initialization failed.\n"));
9083#if defined(RT_OS_WINDOWS)
9084 if (mIPCSem)
9085 ::CloseHandle(mIPCSem);
9086 mIPCSem = NULL;
9087#elif defined(RT_OS_OS2)
9088 if (mIPCSem != NULLHANDLE)
9089 ::DosCloseMutexSem(mIPCSem);
9090 mIPCSem = NULLHANDLE;
9091#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9092 if (mIPCSem >= 0)
9093 ::semctl(mIPCSem, 0, IPC_RMID);
9094 mIPCSem = -1;
9095# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9096 mIPCKey = "0";
9097# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
9098#else
9099# error "Port me!"
9100#endif
9101 uninitDataAndChildObjects();
9102 mData.free();
9103 unconst(mParent) = NULL;
9104 unconst(mPeer) = NULL;
9105 LogFlowThisFuncLeave();
9106 return;
9107 }
9108
9109 /* We need to lock this object in uninit() because the lock is shared
9110 * with mPeer (as well as data we modify below). mParent->addProcessToReap()
9111 * and others need mParent lock, and USB needs host lock. */
9112 AutoMultiWriteLock3 alock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
9113
9114#ifdef VBOX_WITH_RESOURCE_USAGE_API
9115 unregisterMetrics(mParent->performanceCollector(), mPeer);
9116#endif /* VBOX_WITH_RESOURCE_USAGE_API */
9117
9118 MachineState_T lastState = mData->mMachineState;
9119 NOREF(lastState);
9120
9121 if (aReason == Uninit::Abnormal)
9122 {
9123 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
9124 Global::IsOnlineOrTransient(lastState)));
9125
9126 /* reset the state to Aborted */
9127 if (mData->mMachineState != MachineState_Aborted)
9128 setMachineState(MachineState_Aborted);
9129 }
9130
9131 // any machine settings modified?
9132 if (m_flModifications)
9133 {
9134 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
9135 rollback(false /* aNotify */);
9136 }
9137
9138 Assert(mSnapshotData.mStateFilePath.isEmpty() || !mSnapshotData.mSnapshot);
9139 if (!mSnapshotData.mStateFilePath.isEmpty())
9140 {
9141 LogWarningThisFunc(("canceling failed save state request!\n"));
9142 endSavingState(FALSE /* aSuccess */);
9143 }
9144 else if (!mSnapshotData.mSnapshot.isNull())
9145 {
9146 LogWarningThisFunc(("canceling untaken snapshot!\n"));
9147
9148 /* delete all differencing hard disks created (this will also attach
9149 * their parents back by rolling back mMediaData) */
9150 rollbackMedia();
9151 /* delete the saved state file (it might have been already created) */
9152 if (mSnapshotData.mSnapshot->stateFilePath().length())
9153 RTFileDelete(mSnapshotData.mSnapshot->stateFilePath().c_str());
9154
9155 mSnapshotData.mSnapshot->uninit();
9156 }
9157
9158#ifdef VBOX_WITH_USB
9159 /* release all captured USB devices */
9160 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
9161 {
9162 /* Console::captureUSBDevices() is called in the VM process only after
9163 * setting the machine state to Starting or Restoring.
9164 * Console::detachAllUSBDevices() will be called upon successful
9165 * termination. So, we need to release USB devices only if there was
9166 * an abnormal termination of a running VM.
9167 *
9168 * This is identical to SessionMachine::DetachAllUSBDevices except
9169 * for the aAbnormal argument. */
9170 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
9171 AssertComRC(rc);
9172 NOREF(rc);
9173
9174 USBProxyService *service = mParent->host()->usbProxyService();
9175 if (service)
9176 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
9177 }
9178#endif /* VBOX_WITH_USB */
9179
9180 if (!mData->mSession.mType.isEmpty())
9181 {
9182 /* mType is not null when this machine's process has been started by
9183 * VirtualBox::OpenRemoteSession(), therefore it is our child. We
9184 * need to queue the PID to reap the process (and avoid zombies on
9185 * Linux). */
9186 Assert(mData->mSession.mPid != NIL_RTPROCESS);
9187 mParent->addProcessToReap(mData->mSession.mPid);
9188 }
9189
9190 mData->mSession.mPid = NIL_RTPROCESS;
9191
9192 if (aReason == Uninit::Unexpected)
9193 {
9194 /* Uninitialization didn't come from #checkForDeath(), so tell the
9195 * client watcher thread to update the set of machines that have open
9196 * sessions. */
9197 mParent->updateClientWatcher();
9198 }
9199
9200 /* uninitialize all remote controls */
9201 if (mData->mSession.mRemoteControls.size())
9202 {
9203 LogFlowThisFunc(("Closing remote sessions (%d):\n",
9204 mData->mSession.mRemoteControls.size()));
9205
9206 Data::Session::RemoteControlList::iterator it =
9207 mData->mSession.mRemoteControls.begin();
9208 while (it != mData->mSession.mRemoteControls.end())
9209 {
9210 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
9211 HRESULT rc = (*it)->Uninitialize();
9212 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
9213 if (FAILED(rc))
9214 LogWarningThisFunc(("Forgot to close the remote session?\n"));
9215 ++it;
9216 }
9217 mData->mSession.mRemoteControls.clear();
9218 }
9219
9220 /*
9221 * An expected uninitialization can come only from #checkForDeath().
9222 * Otherwise it means that something's got really wrong (for examlple,
9223 * the Session implementation has released the VirtualBox reference
9224 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
9225 * etc). However, it's also possible, that the client releases the IPC
9226 * semaphore correctly (i.e. before it releases the VirtualBox reference),
9227 * but the VirtualBox release event comes first to the server process.
9228 * This case is practically possible, so we should not assert on an
9229 * unexpected uninit, just log a warning.
9230 */
9231
9232 if ((aReason == Uninit::Unexpected))
9233 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
9234
9235 if (aReason != Uninit::Normal)
9236 {
9237 mData->mSession.mDirectControl.setNull();
9238 }
9239 else
9240 {
9241 /* this must be null here (see #OnSessionEnd()) */
9242 Assert(mData->mSession.mDirectControl.isNull());
9243 Assert(mData->mSession.mState == SessionState_Closing);
9244 Assert(!mData->mSession.mProgress.isNull());
9245 }
9246 if (mData->mSession.mProgress)
9247 {
9248 if (aReason == Uninit::Normal)
9249 mData->mSession.mProgress->notifyComplete(S_OK);
9250 else
9251 mData->mSession.mProgress->notifyComplete(E_FAIL,
9252 COM_IIDOF(ISession),
9253 getComponentName(),
9254 tr("The VM session was aborted"));
9255 mData->mSession.mProgress.setNull();
9256 }
9257
9258 /* remove the association between the peer machine and this session machine */
9259 Assert(mData->mSession.mMachine == this ||
9260 aReason == Uninit::Unexpected);
9261
9262 /* reset the rest of session data */
9263 mData->mSession.mMachine.setNull();
9264 mData->mSession.mState = SessionState_Closed;
9265 mData->mSession.mType.setNull();
9266
9267 /* close the interprocess semaphore before leaving the exclusive lock */
9268#if defined(RT_OS_WINDOWS)
9269 if (mIPCSem)
9270 ::CloseHandle(mIPCSem);
9271 mIPCSem = NULL;
9272#elif defined(RT_OS_OS2)
9273 if (mIPCSem != NULLHANDLE)
9274 ::DosCloseMutexSem(mIPCSem);
9275 mIPCSem = NULLHANDLE;
9276#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9277 if (mIPCSem >= 0)
9278 ::semctl(mIPCSem, 0, IPC_RMID);
9279 mIPCSem = -1;
9280# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9281 mIPCKey = "0";
9282# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
9283#else
9284# error "Port me!"
9285#endif
9286
9287 /* fire an event */
9288 mParent->onSessionStateChange(mData->mUuid, SessionState_Closed);
9289
9290 uninitDataAndChildObjects();
9291
9292 /* free the essential data structure last */
9293 mData.free();
9294
9295 /* leave the exclusive lock before setting the below two to NULL */
9296 alock.leave();
9297
9298 unconst(mParent) = NULL;
9299 unconst(mPeer) = NULL;
9300
9301 LogFlowThisFuncLeave();
9302}
9303
9304// util::Lockable interface
9305////////////////////////////////////////////////////////////////////////////////
9306
9307/**
9308 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
9309 * with the primary Machine instance (mPeer).
9310 */
9311RWLockHandle *SessionMachine::lockHandle() const
9312{
9313 AssertReturn(mPeer != NULL, NULL);
9314 return mPeer->lockHandle();
9315}
9316
9317// IInternalMachineControl methods
9318////////////////////////////////////////////////////////////////////////////////
9319
9320/**
9321 * @note Locks this object for writing.
9322 */
9323STDMETHODIMP SessionMachine::SetRemoveSavedState(BOOL aRemove)
9324{
9325 AutoCaller autoCaller(this);
9326 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9327
9328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9329
9330 mRemoveSavedState = aRemove;
9331
9332 return S_OK;
9333}
9334
9335/**
9336 * @note Locks the same as #setMachineState() does.
9337 */
9338STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
9339{
9340 return setMachineState(aMachineState);
9341}
9342
9343/**
9344 * @note Locks this object for reading.
9345 */
9346STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
9347{
9348 AutoCaller autoCaller(this);
9349 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9350
9351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9352
9353#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
9354 mIPCSemName.cloneTo(aId);
9355 return S_OK;
9356#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9357# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9358 mIPCKey.cloneTo(aId);
9359# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
9360 mData->m_strConfigFileFull.cloneTo(aId);
9361# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
9362 return S_OK;
9363#else
9364# error "Port me!"
9365#endif
9366}
9367
9368/**
9369 * @note Locks this object for writing.
9370 */
9371STDMETHODIMP SessionMachine::SetPowerUpInfo(IVirtualBoxErrorInfo *aError)
9372{
9373 AutoCaller autoCaller(this);
9374 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9375
9376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9377
9378 if ( mData->mSession.mState == SessionState_Open
9379 && mData->mSession.mProgress)
9380 {
9381 /* Finalize the progress, since the remote session has completed
9382 * power on (successful or not). */
9383 if (aError)
9384 {
9385 /* Transfer error information immediately, as the
9386 * IVirtualBoxErrorInfo object is most likely transient. */
9387 HRESULT rc;
9388 LONG rRc = S_OK;
9389 rc = aError->COMGETTER(ResultCode)(&rRc);
9390 AssertComRCReturnRC(rc);
9391 Bstr rIID;
9392 rc = aError->COMGETTER(InterfaceID)(rIID.asOutParam());
9393 AssertComRCReturnRC(rc);
9394 Bstr rComponent;
9395 rc = aError->COMGETTER(Component)(rComponent.asOutParam());
9396 AssertComRCReturnRC(rc);
9397 Bstr rText;
9398 rc = aError->COMGETTER(Text)(rText.asOutParam());
9399 AssertComRCReturnRC(rc);
9400 mData->mSession.mProgress->notifyComplete(rRc, Guid(rIID), rComponent, Utf8Str(rText).raw());
9401 }
9402 else
9403 mData->mSession.mProgress->notifyComplete(S_OK);
9404 mData->mSession.mProgress.setNull();
9405
9406 return S_OK;
9407 }
9408 else
9409 return VBOX_E_INVALID_OBJECT_STATE;
9410}
9411
9412/**
9413 * Goes through the USB filters of the given machine to see if the given
9414 * device matches any filter or not.
9415 *
9416 * @note Locks the same as USBController::hasMatchingFilter() does.
9417 */
9418STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
9419 BOOL *aMatched,
9420 ULONG *aMaskedIfs)
9421{
9422 LogFlowThisFunc(("\n"));
9423
9424 CheckComArgNotNull(aUSBDevice);
9425 CheckComArgOutPointerValid(aMatched);
9426
9427 AutoCaller autoCaller(this);
9428 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9429
9430#ifdef VBOX_WITH_USB
9431 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
9432#else
9433 NOREF(aUSBDevice);
9434 NOREF(aMaskedIfs);
9435 *aMatched = FALSE;
9436#endif
9437
9438 return S_OK;
9439}
9440
9441/**
9442 * @note Locks the same as Host::captureUSBDevice() does.
9443 */
9444STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
9445{
9446 LogFlowThisFunc(("\n"));
9447
9448 AutoCaller autoCaller(this);
9449 AssertComRCReturnRC(autoCaller.rc());
9450
9451#ifdef VBOX_WITH_USB
9452 /* if captureDeviceForVM() fails, it must have set extended error info */
9453 MultiResult rc = mParent->host()->checkUSBProxyService();
9454 if (FAILED(rc)) return rc;
9455
9456 USBProxyService *service = mParent->host()->usbProxyService();
9457 AssertReturn(service, E_FAIL);
9458 return service->captureDeviceForVM(this, Guid(aId));
9459#else
9460 NOREF(aId);
9461 return E_NOTIMPL;
9462#endif
9463}
9464
9465/**
9466 * @note Locks the same as Host::detachUSBDevice() does.
9467 */
9468STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
9469{
9470 LogFlowThisFunc(("\n"));
9471
9472 AutoCaller autoCaller(this);
9473 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9474
9475#ifdef VBOX_WITH_USB
9476 USBProxyService *service = mParent->host()->usbProxyService();
9477 AssertReturn(service, E_FAIL);
9478 return service->detachDeviceFromVM(this, Guid(aId), !!aDone);
9479#else
9480 NOREF(aId);
9481 NOREF(aDone);
9482 return E_NOTIMPL;
9483#endif
9484}
9485
9486/**
9487 * Inserts all machine filters to the USB proxy service and then calls
9488 * Host::autoCaptureUSBDevices().
9489 *
9490 * Called by Console from the VM process upon VM startup.
9491 *
9492 * @note Locks what called methods lock.
9493 */
9494STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
9495{
9496 LogFlowThisFunc(("\n"));
9497
9498 AutoCaller autoCaller(this);
9499 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9500
9501#ifdef VBOX_WITH_USB
9502 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
9503 AssertComRC(rc);
9504 NOREF(rc);
9505
9506 USBProxyService *service = mParent->host()->usbProxyService();
9507 AssertReturn(service, E_FAIL);
9508 return service->autoCaptureDevicesForVM(this);
9509#else
9510 return S_OK;
9511#endif
9512}
9513
9514/**
9515 * Removes all machine filters from the USB proxy service and then calls
9516 * Host::detachAllUSBDevices().
9517 *
9518 * Called by Console from the VM process upon normal VM termination or by
9519 * SessionMachine::uninit() upon abnormal VM termination (from under the
9520 * Machine/SessionMachine lock).
9521 *
9522 * @note Locks what called methods lock.
9523 */
9524STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
9525{
9526 LogFlowThisFunc(("\n"));
9527
9528 AutoCaller autoCaller(this);
9529 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9530
9531#ifdef VBOX_WITH_USB
9532 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
9533 AssertComRC(rc);
9534 NOREF(rc);
9535
9536 USBProxyService *service = mParent->host()->usbProxyService();
9537 AssertReturn(service, E_FAIL);
9538 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
9539#else
9540 NOREF(aDone);
9541 return S_OK;
9542#endif
9543}
9544
9545/**
9546 * @note Locks this object for writing.
9547 */
9548STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
9549 IProgress **aProgress)
9550{
9551 LogFlowThisFuncEnter();
9552
9553 AssertReturn(aSession, E_INVALIDARG);
9554 AssertReturn(aProgress, E_INVALIDARG);
9555
9556 AutoCaller autoCaller(this);
9557
9558 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
9559 /*
9560 * We don't assert below because it might happen that a non-direct session
9561 * informs us it is closed right after we've been uninitialized -- it's ok.
9562 */
9563 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9564
9565 /* get IInternalSessionControl interface */
9566 ComPtr<IInternalSessionControl> control(aSession);
9567
9568 ComAssertRet(!control.isNull(), E_INVALIDARG);
9569
9570 /* Creating a Progress object requires the VirtualBox lock, and
9571 * thus locking it here is required by the lock order rules. */
9572 AutoMultiWriteLock2 alock(mParent->lockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
9573
9574 if (control.equalsTo(mData->mSession.mDirectControl))
9575 {
9576 ComAssertRet(aProgress, E_POINTER);
9577
9578 /* The direct session is being normally closed by the client process
9579 * ----------------------------------------------------------------- */
9580
9581 /* go to the closing state (essential for all open*Session() calls and
9582 * for #checkForDeath()) */
9583 Assert(mData->mSession.mState == SessionState_Open);
9584 mData->mSession.mState = SessionState_Closing;
9585
9586 /* set direct control to NULL to release the remote instance */
9587 mData->mSession.mDirectControl.setNull();
9588 LogFlowThisFunc(("Direct control is set to NULL\n"));
9589
9590 if (mData->mSession.mProgress)
9591 {
9592 /* finalize the progress, someone might wait if a frontend
9593 * closes the session before powering on the VM. */
9594 mData->mSession.mProgress->notifyComplete(E_FAIL,
9595 COM_IIDOF(ISession),
9596 getComponentName(),
9597 tr("The VM session was closed before any attempt to power it on"));
9598 mData->mSession.mProgress.setNull();
9599 }
9600
9601 /* Create the progress object the client will use to wait until
9602 * #checkForDeath() is called to uninitialize this session object after
9603 * it releases the IPC semaphore. */
9604 Assert(mData->mSession.mProgress.isNull());
9605 ComObjPtr<Progress> progress;
9606 progress.createObject();
9607 progress->init(mParent, mPeer,
9608 Bstr(tr("Closing session")), FALSE /* aCancelable */);
9609 progress.queryInterfaceTo(aProgress);
9610 mData->mSession.mProgress = progress;
9611 }
9612 else
9613 {
9614 /* the remote session is being normally closed */
9615 Data::Session::RemoteControlList::iterator it =
9616 mData->mSession.mRemoteControls.begin();
9617 while (it != mData->mSession.mRemoteControls.end())
9618 {
9619 if (control.equalsTo(*it))
9620 break;
9621 ++it;
9622 }
9623 BOOL found = it != mData->mSession.mRemoteControls.end();
9624 ComAssertMsgRet(found, ("The session is not found in the session list!"),
9625 E_INVALIDARG);
9626 mData->mSession.mRemoteControls.remove(*it);
9627 }
9628
9629 LogFlowThisFuncLeave();
9630 return S_OK;
9631}
9632
9633/**
9634 * @note Locks this object for writing.
9635 */
9636STDMETHODIMP SessionMachine::BeginSavingState(IProgress *aProgress, BSTR *aStateFilePath)
9637{
9638 LogFlowThisFuncEnter();
9639
9640 AssertReturn(aProgress, E_INVALIDARG);
9641 AssertReturn(aStateFilePath, E_POINTER);
9642
9643 AutoCaller autoCaller(this);
9644 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9645
9646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9647
9648 AssertReturn( mData->mMachineState == MachineState_Paused
9649 && mSnapshotData.mLastState == MachineState_Null
9650 && mSnapshotData.mProgressId.isEmpty()
9651 && mSnapshotData.mStateFilePath.isEmpty(),
9652 E_FAIL);
9653
9654 /* memorize the progress ID and add it to the global collection */
9655 Bstr progressId;
9656 HRESULT rc = aProgress->COMGETTER(Id)(progressId.asOutParam());
9657 AssertComRCReturn(rc, rc);
9658 rc = mParent->addProgress(aProgress);
9659 AssertComRCReturn(rc, rc);
9660
9661 Bstr stateFilePath;
9662 /* stateFilePath is null when the machine is not running */
9663 if (mData->mMachineState == MachineState_Paused)
9664 {
9665 stateFilePath = Utf8StrFmt("%ls%c{%RTuuid}.sav",
9666 mUserData->mSnapshotFolderFull.raw(),
9667 RTPATH_DELIMITER, mData->mUuid.raw());
9668 }
9669
9670 /* fill in the snapshot data */
9671 mSnapshotData.mLastState = mData->mMachineState;
9672 mSnapshotData.mProgressId = Guid(progressId);
9673 mSnapshotData.mStateFilePath = stateFilePath;
9674
9675 /* set the state to Saving (this is expected by Console::SaveState()) */
9676 setMachineState(MachineState_Saving);
9677
9678 stateFilePath.cloneTo(aStateFilePath);
9679
9680 return S_OK;
9681}
9682
9683/**
9684 * @note Locks mParent + this object for writing.
9685 */
9686STDMETHODIMP SessionMachine::EndSavingState(BOOL aSuccess)
9687{
9688 LogFlowThisFunc(("\n"));
9689
9690 AutoCaller autoCaller(this);
9691 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9692
9693 /* endSavingState() need mParent lock */
9694 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
9695
9696 AssertReturn( mData->mMachineState == MachineState_Saving
9697 && mSnapshotData.mLastState != MachineState_Null
9698 && !mSnapshotData.mProgressId.isEmpty()
9699 && !mSnapshotData.mStateFilePath.isEmpty(),
9700 E_FAIL);
9701
9702 /*
9703 * on success, set the state to Saved;
9704 * on failure, set the state to the state we had when BeginSavingState() was
9705 * called (this is expected by Console::SaveState() and
9706 * Console::saveStateThread())
9707 */
9708 if (aSuccess)
9709 setMachineState(MachineState_Saved);
9710 else
9711 setMachineState(mSnapshotData.mLastState);
9712
9713 return endSavingState(aSuccess);
9714}
9715
9716/**
9717 * @note Locks this object for writing.
9718 */
9719STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
9720{
9721 LogFlowThisFunc(("\n"));
9722
9723 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
9724
9725 AutoCaller autoCaller(this);
9726 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9727
9728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9729
9730 AssertReturn( mData->mMachineState == MachineState_PoweredOff
9731 || mData->mMachineState == MachineState_Teleported
9732 || mData->mMachineState == MachineState_Aborted
9733 , E_FAIL); /** @todo setError. */
9734
9735 Utf8Str stateFilePathFull = aSavedStateFile;
9736 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
9737 if (RT_FAILURE(vrc))
9738 return setError(VBOX_E_FILE_ERROR,
9739 tr("Invalid saved state file path '%ls' (%Rrc)"),
9740 aSavedStateFile,
9741 vrc);
9742
9743 mSSData->mStateFilePath = stateFilePathFull;
9744
9745 /* The below setMachineState() will detect the state transition and will
9746 * update the settings file */
9747
9748 return setMachineState(MachineState_Saved);
9749}
9750
9751STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
9752 ComSafeArrayOut(BSTR, aValues),
9753 ComSafeArrayOut(ULONG64, aTimestamps),
9754 ComSafeArrayOut(BSTR, aFlags))
9755{
9756 LogFlowThisFunc(("\n"));
9757
9758#ifdef VBOX_WITH_GUEST_PROPS
9759 using namespace guestProp;
9760
9761 AutoCaller autoCaller(this);
9762 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9763
9764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9765
9766 AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
9767 AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
9768 AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
9769 AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
9770
9771 size_t cEntries = mHWData->mGuestProperties.size();
9772 com::SafeArray<BSTR> names(cEntries);
9773 com::SafeArray<BSTR> values(cEntries);
9774 com::SafeArray<ULONG64> timestamps(cEntries);
9775 com::SafeArray<BSTR> flags(cEntries);
9776 unsigned i = 0;
9777 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
9778 it != mHWData->mGuestProperties.end();
9779 ++it)
9780 {
9781 char szFlags[MAX_FLAGS_LEN + 1];
9782 it->strName.cloneTo(&names[i]);
9783 it->strValue.cloneTo(&values[i]);
9784 timestamps[i] = it->mTimestamp;
9785 /* If it is NULL, keep it NULL. */
9786 if (it->mFlags)
9787 {
9788 writeFlags(it->mFlags, szFlags);
9789 Bstr(szFlags).cloneTo(&flags[i]);
9790 }
9791 else
9792 flags[i] = NULL;
9793 ++i;
9794 }
9795 names.detachTo(ComSafeArrayOutArg(aNames));
9796 values.detachTo(ComSafeArrayOutArg(aValues));
9797 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
9798 flags.detachTo(ComSafeArrayOutArg(aFlags));
9799 mHWData->mPropertyServiceActive = true;
9800 return S_OK;
9801#else
9802 ReturnComNotImplemented();
9803#endif
9804}
9805
9806STDMETHODIMP SessionMachine::PushGuestProperties(ComSafeArrayIn(IN_BSTR, aNames),
9807 ComSafeArrayIn(IN_BSTR, aValues),
9808 ComSafeArrayIn(ULONG64, aTimestamps),
9809 ComSafeArrayIn(IN_BSTR, aFlags))
9810{
9811 LogFlowThisFunc(("\n"));
9812
9813#ifdef VBOX_WITH_GUEST_PROPS
9814 using namespace guestProp;
9815
9816 AssertReturn(!ComSafeArrayInIsNull(aNames), E_POINTER);
9817 AssertReturn(!ComSafeArrayInIsNull(aValues), E_POINTER);
9818 AssertReturn(!ComSafeArrayInIsNull(aTimestamps), E_POINTER);
9819 AssertReturn(!ComSafeArrayInIsNull(aFlags), E_POINTER);
9820
9821 AutoCaller autoCaller(this);
9822 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9823
9824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9825
9826 /*
9827 * Temporarily reset the registered flag, so that our machine state
9828 * changes (i.e. mHWData.backup()) succeed. (isMutable() used in all
9829 * setters will return FALSE for a Machine instance if mRegistered is TRUE).
9830 *
9831 * This is copied from registeredInit(), and may or may not be the right
9832 * way to handle this.
9833 *
9834 * @todo r=dj review this, this gets called during machine power-down when
9835 * we have already saved the machine settings, there's no need to do this
9836 * twice.
9837 */
9838 Assert(mData->mRegistered);
9839 mData->mRegistered = FALSE;
9840
9841 HRESULT rc = checkStateDependency(MutableStateDep);
9842 AssertLogRelMsgReturn(SUCCEEDED(rc), ("%Rhrc\n", rc), rc);
9843
9844 com::SafeArray<IN_BSTR> names( ComSafeArrayInArg(aNames));
9845 com::SafeArray<IN_BSTR> values( ComSafeArrayInArg(aValues));
9846 com::SafeArray<ULONG64> timestamps(ComSafeArrayInArg(aTimestamps));
9847 com::SafeArray<IN_BSTR> flags( ComSafeArrayInArg(aFlags));
9848
9849 DiscardSettings();
9850 setModified(IsModified_MachineData);
9851 mHWData.backup();
9852
9853 mHWData->mGuestProperties.erase(mHWData->mGuestProperties.begin(),
9854 mHWData->mGuestProperties.end());
9855 for (unsigned i = 0; i < names.size(); ++i)
9856 {
9857 uint32_t fFlags = NILFLAG;
9858 validateFlags(Utf8Str(flags[i]).raw(), &fFlags);
9859 HWData::GuestProperty property = { names[i], values[i], timestamps[i], fFlags };
9860 mHWData->mGuestProperties.push_back(property);
9861 }
9862
9863 mHWData->mPropertyServiceActive = false;
9864
9865 alock.release();
9866 SaveSettings();
9867
9868 /* Restore the mRegistered flag. */
9869 alock.acquire();
9870 mData->mRegistered = TRUE;
9871
9872 return S_OK;
9873#else
9874 ReturnComNotImplemented();
9875#endif
9876}
9877
9878STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
9879 IN_BSTR aValue,
9880 ULONG64 aTimestamp,
9881 IN_BSTR aFlags)
9882{
9883 LogFlowThisFunc(("\n"));
9884
9885#ifdef VBOX_WITH_GUEST_PROPS
9886 using namespace guestProp;
9887
9888 CheckComArgStrNotEmptyOrNull(aName);
9889 if (aValue != NULL && (!VALID_PTR(aValue) || !VALID_PTR(aFlags)))
9890 return E_POINTER; /* aValue can be NULL to indicate deletion */
9891
9892 try
9893 {
9894 /*
9895 * Convert input up front.
9896 */
9897 Utf8Str utf8Name(aName);
9898 uint32_t fFlags = NILFLAG;
9899 if (aFlags)
9900 {
9901 Utf8Str utf8Flags(aFlags);
9902 int vrc = validateFlags(utf8Flags.raw(), &fFlags);
9903 AssertRCReturn(vrc, E_INVALIDARG);
9904 }
9905
9906 /*
9907 * Now grab the object lock, validate the state and do the update.
9908 */
9909 AutoCaller autoCaller(this);
9910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9911
9912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9913
9914 AssertReturn(mHWData->mPropertyServiceActive, VBOX_E_INVALID_OBJECT_STATE);
9915 switch (mData->mMachineState)
9916 {
9917 case MachineState_Paused:
9918 case MachineState_Running:
9919 case MachineState_Teleporting:
9920 case MachineState_TeleportingPausedVM:
9921 case MachineState_LiveSnapshotting:
9922 case MachineState_Saving:
9923 break;
9924
9925 default:
9926 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
9927 VBOX_E_INVALID_VM_STATE);
9928 }
9929
9930 setModified(IsModified_MachineData);
9931 mHWData.backup();
9932
9933 /** @todo r=bird: The careful memory handling doesn't work out here because
9934 * the catch block won't undo any damange we've done. So, if push_back throws
9935 * bad_alloc then you've lost the value.
9936 *
9937 * Another thing. Doing a linear search here isn't extremely efficient, esp.
9938 * since values that changes actually bubbles to the end of the list. Using
9939 * something that has an efficient lookup and can tollerate a bit of updates
9940 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
9941 * combination of RTStrCache (for sharing names and getting uniqueness into
9942 * the bargain) and hash/tree is another. */
9943 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
9944 iter != mHWData->mGuestProperties.end();
9945 ++iter)
9946 if (utf8Name == iter->strName)
9947 {
9948 mHWData->mGuestProperties.erase(iter);
9949 break;
9950 }
9951 if (aValue != NULL)
9952 {
9953 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
9954 mHWData->mGuestProperties.push_back(property);
9955 }
9956
9957 /*
9958 * Send a callback notification if appropriate
9959 */
9960 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
9961 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.raw(),
9962 RTSTR_MAX,
9963 utf8Name.raw(),
9964 RTSTR_MAX, NULL)
9965 )
9966 {
9967 alock.leave();
9968
9969 mParent->onGuestPropertyChange(mData->mUuid,
9970 aName,
9971 aValue,
9972 aFlags);
9973 }
9974 }
9975 catch (...)
9976 {
9977 return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
9978 }
9979 return S_OK;
9980#else
9981 ReturnComNotImplemented();
9982#endif
9983}
9984
9985// public methods only for internal purposes
9986/////////////////////////////////////////////////////////////////////////////
9987
9988/**
9989 * Called from the client watcher thread to check for expected or unexpected
9990 * death of the client process that has a direct session to this machine.
9991 *
9992 * On Win32 and on OS/2, this method is called only when we've got the
9993 * mutex (i.e. the client has either died or terminated normally) so it always
9994 * returns @c true (the client is terminated, the session machine is
9995 * uninitialized).
9996 *
9997 * On other platforms, the method returns @c true if the client process has
9998 * terminated normally or abnormally and the session machine was uninitialized,
9999 * and @c false if the client process is still alive.
10000 *
10001 * @note Locks this object for writing.
10002 */
10003bool SessionMachine::checkForDeath()
10004{
10005 Uninit::Reason reason;
10006 bool terminated = false;
10007
10008 /* Enclose autoCaller with a block because calling uninit() from under it
10009 * will deadlock. */
10010 {
10011 AutoCaller autoCaller(this);
10012 if (!autoCaller.isOk())
10013 {
10014 /* return true if not ready, to cause the client watcher to exclude
10015 * the corresponding session from watching */
10016 LogFlowThisFunc(("Already uninitialized!\n"));
10017 return true;
10018 }
10019
10020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10021
10022 /* Determine the reason of death: if the session state is Closing here,
10023 * everything is fine. Otherwise it means that the client did not call
10024 * OnSessionEnd() before it released the IPC semaphore. This may happen
10025 * either because the client process has abnormally terminated, or
10026 * because it simply forgot to call ISession::Close() before exiting. We
10027 * threat the latter also as an abnormal termination (see
10028 * Session::uninit() for details). */
10029 reason = mData->mSession.mState == SessionState_Closing ?
10030 Uninit::Normal :
10031 Uninit::Abnormal;
10032
10033#if defined(RT_OS_WINDOWS)
10034
10035 AssertMsg(mIPCSem, ("semaphore must be created"));
10036
10037 /* release the IPC mutex */
10038 ::ReleaseMutex(mIPCSem);
10039
10040 terminated = true;
10041
10042#elif defined(RT_OS_OS2)
10043
10044 AssertMsg(mIPCSem, ("semaphore must be created"));
10045
10046 /* release the IPC mutex */
10047 ::DosReleaseMutexSem(mIPCSem);
10048
10049 terminated = true;
10050
10051#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
10052
10053 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
10054
10055 int val = ::semctl(mIPCSem, 0, GETVAL);
10056 if (val > 0)
10057 {
10058 /* the semaphore is signaled, meaning the session is terminated */
10059 terminated = true;
10060 }
10061
10062#else
10063# error "Port me!"
10064#endif
10065
10066 } /* AutoCaller block */
10067
10068 if (terminated)
10069 uninit(reason);
10070
10071 return terminated;
10072}
10073
10074/**
10075 * @note Locks this object for reading.
10076 */
10077HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
10078{
10079 LogFlowThisFunc(("\n"));
10080
10081 AutoCaller autoCaller(this);
10082 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10083
10084 ComPtr<IInternalSessionControl> directControl;
10085 {
10086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10087 directControl = mData->mSession.mDirectControl;
10088 }
10089
10090 /* ignore notifications sent after #OnSessionEnd() is called */
10091 if (!directControl)
10092 return S_OK;
10093
10094 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
10095}
10096
10097/**
10098 * @note Locks this object for reading.
10099 */
10100HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
10101{
10102 LogFlowThisFunc(("\n"));
10103
10104 AutoCaller autoCaller(this);
10105 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10106
10107 ComPtr<IInternalSessionControl> directControl;
10108 {
10109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10110 directControl = mData->mSession.mDirectControl;
10111 }
10112
10113 /* ignore notifications sent after #OnSessionEnd() is called */
10114 if (!directControl)
10115 return S_OK;
10116
10117 return directControl->OnSerialPortChange(serialPort);
10118}
10119
10120/**
10121 * @note Locks this object for reading.
10122 */
10123HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
10124{
10125 LogFlowThisFunc(("\n"));
10126
10127 AutoCaller autoCaller(this);
10128 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10129
10130 ComPtr<IInternalSessionControl> directControl;
10131 {
10132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10133 directControl = mData->mSession.mDirectControl;
10134 }
10135
10136 /* ignore notifications sent after #OnSessionEnd() is called */
10137 if (!directControl)
10138 return S_OK;
10139
10140 return directControl->OnParallelPortChange(parallelPort);
10141}
10142
10143/**
10144 * @note Locks this object for reading.
10145 */
10146HRESULT SessionMachine::onStorageControllerChange()
10147{
10148 LogFlowThisFunc(("\n"));
10149
10150 AutoCaller autoCaller(this);
10151 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10152
10153 ComPtr<IInternalSessionControl> directControl;
10154 {
10155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10156 directControl = mData->mSession.mDirectControl;
10157 }
10158
10159 /* ignore notifications sent after #OnSessionEnd() is called */
10160 if (!directControl)
10161 return S_OK;
10162
10163 return directControl->OnStorageControllerChange();
10164}
10165
10166/**
10167 * @note Locks this object for reading.
10168 */
10169HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
10170{
10171 LogFlowThisFunc(("\n"));
10172
10173 AutoCaller autoCaller(this);
10174 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10175
10176 ComPtr<IInternalSessionControl> directControl;
10177 {
10178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10179 directControl = mData->mSession.mDirectControl;
10180 }
10181
10182 /* ignore notifications sent after #OnSessionEnd() is called */
10183 if (!directControl)
10184 return S_OK;
10185
10186 return directControl->OnMediumChange(aAttachment, aForce);
10187}
10188
10189/**
10190 * @note Locks this object for reading.
10191 */
10192HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
10193{
10194 LogFlowThisFunc(("\n"));
10195
10196 AutoCaller autoCaller(this);
10197 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10198
10199 ComPtr<IInternalSessionControl> directControl;
10200 {
10201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10202 directControl = mData->mSession.mDirectControl;
10203 }
10204
10205 /* ignore notifications sent after #OnSessionEnd() is called */
10206 if (!directControl)
10207 return S_OK;
10208
10209 return directControl->OnCPUChange(aCPU, aRemove);
10210}
10211
10212/**
10213 * @note Locks this object for reading.
10214 */
10215HRESULT SessionMachine::onVRDPServerChange()
10216{
10217 LogFlowThisFunc(("\n"));
10218
10219 AutoCaller autoCaller(this);
10220 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10221
10222 ComPtr<IInternalSessionControl> directControl;
10223 {
10224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10225 directControl = mData->mSession.mDirectControl;
10226 }
10227
10228 /* ignore notifications sent after #OnSessionEnd() is called */
10229 if (!directControl)
10230 return S_OK;
10231
10232 return directControl->OnVRDPServerChange();
10233}
10234
10235/**
10236 * @note Locks this object for reading.
10237 */
10238HRESULT SessionMachine::onUSBControllerChange()
10239{
10240 LogFlowThisFunc(("\n"));
10241
10242 AutoCaller autoCaller(this);
10243 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10244
10245 ComPtr<IInternalSessionControl> directControl;
10246 {
10247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10248 directControl = mData->mSession.mDirectControl;
10249 }
10250
10251 /* ignore notifications sent after #OnSessionEnd() is called */
10252 if (!directControl)
10253 return S_OK;
10254
10255 return directControl->OnUSBControllerChange();
10256}
10257
10258/**
10259 * @note Locks this object for reading.
10260 */
10261HRESULT SessionMachine::onSharedFolderChange()
10262{
10263 LogFlowThisFunc(("\n"));
10264
10265 AutoCaller autoCaller(this);
10266 AssertComRCReturnRC(autoCaller.rc());
10267
10268 ComPtr<IInternalSessionControl> directControl;
10269 {
10270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10271 directControl = mData->mSession.mDirectControl;
10272 }
10273
10274 /* ignore notifications sent after #OnSessionEnd() is called */
10275 if (!directControl)
10276 return S_OK;
10277
10278 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
10279}
10280
10281/**
10282 * Returns @c true if this machine's USB controller reports it has a matching
10283 * filter for the given USB device and @c false otherwise.
10284 *
10285 * @note Caller must have requested machine read lock.
10286 */
10287bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
10288{
10289 AutoCaller autoCaller(this);
10290 /* silently return if not ready -- this method may be called after the
10291 * direct machine session has been called */
10292 if (!autoCaller.isOk())
10293 return false;
10294
10295
10296#ifdef VBOX_WITH_USB
10297 switch (mData->mMachineState)
10298 {
10299 case MachineState_Starting:
10300 case MachineState_Restoring:
10301 case MachineState_TeleportingIn:
10302 case MachineState_Paused:
10303 case MachineState_Running:
10304 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
10305 * elsewhere... */
10306 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
10307 default: break;
10308 }
10309#else
10310 NOREF(aDevice);
10311 NOREF(aMaskedIfs);
10312#endif
10313 return false;
10314}
10315
10316/**
10317 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
10318 */
10319HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
10320 IVirtualBoxErrorInfo *aError,
10321 ULONG aMaskedIfs)
10322{
10323 LogFlowThisFunc(("\n"));
10324
10325 AutoCaller autoCaller(this);
10326
10327 /* This notification may happen after the machine object has been
10328 * uninitialized (the session was closed), so don't assert. */
10329 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10330
10331 ComPtr<IInternalSessionControl> directControl;
10332 {
10333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10334 directControl = mData->mSession.mDirectControl;
10335 }
10336
10337 /* fail on notifications sent after #OnSessionEnd() is called, it is
10338 * expected by the caller */
10339 if (!directControl)
10340 return E_FAIL;
10341
10342 /* No locks should be held at this point. */
10343 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
10344 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
10345
10346 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
10347}
10348
10349/**
10350 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
10351 */
10352HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
10353 IVirtualBoxErrorInfo *aError)
10354{
10355 LogFlowThisFunc(("\n"));
10356
10357 AutoCaller autoCaller(this);
10358
10359 /* This notification may happen after the machine object has been
10360 * uninitialized (the session was closed), so don't assert. */
10361 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10362
10363 ComPtr<IInternalSessionControl> directControl;
10364 {
10365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10366 directControl = mData->mSession.mDirectControl;
10367 }
10368
10369 /* fail on notifications sent after #OnSessionEnd() is called, it is
10370 * expected by the caller */
10371 if (!directControl)
10372 return E_FAIL;
10373
10374 /* No locks should be held at this point. */
10375 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
10376 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
10377
10378 return directControl->OnUSBDeviceDetach(aId, aError);
10379}
10380
10381// protected methods
10382/////////////////////////////////////////////////////////////////////////////
10383
10384/**
10385 * Helper method to finalize saving the state.
10386 *
10387 * @note Must be called from under this object's lock.
10388 *
10389 * @param aSuccess TRUE if the snapshot has been taken successfully
10390 *
10391 * @note Locks mParent + this objects for writing.
10392 */
10393HRESULT SessionMachine::endSavingState(BOOL aSuccess)
10394{
10395 LogFlowThisFuncEnter();
10396
10397 AutoCaller autoCaller(this);
10398 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10399
10400 /* saveSettings() needs mParent lock */
10401 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
10402
10403 HRESULT rc = S_OK;
10404
10405 if (aSuccess)
10406 {
10407 mSSData->mStateFilePath = mSnapshotData.mStateFilePath;
10408
10409 /* save all VM settings */
10410 rc = saveSettings(NULL);
10411 // no need to check whether VirtualBox.xml needs saving also since
10412 // we can't have a name change pending at this point
10413 }
10414 else
10415 {
10416 /* delete the saved state file (it might have been already created) */
10417 RTFileDelete(mSnapshotData.mStateFilePath.c_str());
10418 }
10419
10420 /* remove the completed progress object */
10421 mParent->removeProgress(mSnapshotData.mProgressId);
10422
10423 /* clear out the temporary saved state data */
10424 mSnapshotData.mLastState = MachineState_Null;
10425 mSnapshotData.mProgressId.clear();
10426 mSnapshotData.mStateFilePath.setNull();
10427
10428 LogFlowThisFuncLeave();
10429 return rc;
10430}
10431
10432/**
10433 * Locks the attached media.
10434 *
10435 * All attached hard disks are locked for writing and DVD/floppy are locked for
10436 * reading. Parents of attached hard disks (if any) are locked for reading.
10437 *
10438 * This method also performs accessibility check of all media it locks: if some
10439 * media is inaccessible, the method will return a failure and a bunch of
10440 * extended error info objects per each inaccessible medium.
10441 *
10442 * Note that this method is atomic: if it returns a success, all media are
10443 * locked as described above; on failure no media is locked at all (all
10444 * succeeded individual locks will be undone).
10445 *
10446 * This method is intended to be called when the machine is in Starting or
10447 * Restoring state and asserts otherwise.
10448 *
10449 * The locks made by this method must be undone by calling #unlockMedia() when
10450 * no more needed.
10451 */
10452HRESULT SessionMachine::lockMedia()
10453{
10454 AutoCaller autoCaller(this);
10455 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10456
10457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10458
10459 AssertReturn( mData->mMachineState == MachineState_Starting
10460 || mData->mMachineState == MachineState_Restoring
10461 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
10462
10463 try
10464 {
10465 HRESULT rc = S_OK;
10466
10467 ErrorInfoKeeper eik(true /* aIsNull */);
10468 MultiResult mrc(S_OK);
10469
10470 /* Lock all medium objects attached to the VM.
10471 * Get status for inaccessible media as well. */
10472 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10473 it != mMediaData->mAttachments.end();
10474 ++it)
10475 {
10476 DeviceType_T devType = (*it)->getType();
10477 ComObjPtr<Medium> medium = (*it)->getMedium();
10478
10479 bool first = true;
10480
10481 /** @todo split out the media locking, and put it into
10482 * MediumImpl.cpp, as it needs this functionality too. */
10483 while (!medium.isNull())
10484 {
10485 MediumState_T mediumState = medium->getState();
10486
10487 /* accessibility check must be first, otherwise locking
10488 * interferes with getting the medium state. */
10489 if (mediumState == MediumState_Inaccessible)
10490 {
10491 rc = medium->RefreshState(&mediumState);
10492 if (FAILED(rc)) throw rc;
10493
10494 if (mediumState == MediumState_Inaccessible)
10495 {
10496 Bstr error;
10497 rc = medium->COMGETTER(LastAccessError)(error.asOutParam());
10498 if (FAILED(rc)) throw rc;
10499
10500 Bstr loc;
10501 rc = medium->COMGETTER(Location)(loc.asOutParam());
10502 if (FAILED(rc)) throw rc;
10503
10504 /* collect multiple errors */
10505 eik.restore();
10506
10507 /* be in sync with MediumBase::setStateError() */
10508 Assert(!error.isEmpty());
10509 mrc = setError(E_FAIL,
10510 tr("Medium '%ls' is not accessible. %ls"),
10511 loc.raw(),
10512 error.raw());
10513
10514 eik.fetch();
10515 }
10516 }
10517
10518 if (first)
10519 {
10520 if (devType != DeviceType_DVD)
10521 {
10522 /* HardDisk and Floppy medium must be locked for writing */
10523 rc = medium->LockWrite(NULL);
10524 if (FAILED(rc)) throw rc;
10525 }
10526 else
10527 {
10528 /* DVD medium must be locked for reading */
10529 rc = medium->LockRead(NULL);
10530 if (FAILED(rc)) throw rc;
10531 }
10532
10533 mData->mSession.mLockedMedia.push_back(
10534 Data::Session::LockedMedia::value_type(
10535 ComPtr<IMedium>(medium), true));
10536
10537 first = false;
10538 }
10539 else
10540 {
10541 rc = medium->LockRead(NULL);
10542 if (FAILED(rc)) throw rc;
10543
10544 mData->mSession.mLockedMedia.push_back(
10545 Data::Session::LockedMedia::value_type(
10546 ComPtr<IMedium>(medium), false));
10547 }
10548
10549
10550 /* no locks or callers here since there should be no way to
10551 * change the hard disk parent at this point (as it is still
10552 * attached to the machine) */
10553 medium = medium->getParent();
10554 }
10555 }
10556
10557 /* @todo r=dj is this correct? first restoring the eik and then throwing? */
10558 eik.restore();
10559 HRESULT rc2 = (HRESULT)mrc;
10560 if (FAILED(rc2)) throw rc2;
10561 }
10562 catch (HRESULT aRC)
10563 {
10564 /* Unlock all locked media on failure */
10565 unlockMedia();
10566 return aRC;
10567 }
10568
10569 return S_OK;
10570}
10571
10572/**
10573 * Undoes the locks made by by #lockMedia().
10574 */
10575void SessionMachine::unlockMedia()
10576{
10577 AutoCaller autoCaller(this);
10578 AssertComRCReturnVoid(autoCaller.rc());
10579
10580 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10581
10582 /* we may be holding important error info on the current thread;
10583 * preserve it */
10584 ErrorInfoKeeper eik;
10585
10586 HRESULT rc = S_OK;
10587
10588 for (Data::Session::LockedMedia::const_iterator
10589 it = mData->mSession.mLockedMedia.begin();
10590 it != mData->mSession.mLockedMedia.end(); ++it)
10591 {
10592 MediumState_T state;
10593 if (it->second)
10594 rc = it->first->UnlockWrite(&state);
10595 else
10596 rc = it->first->UnlockRead(&state);
10597
10598 /* The second can happen if an object was re-locked in
10599 * Machine::fixupMedia(). The last can happen when e.g a DVD/Floppy
10600 * image was unmounted at runtime. */
10601 Assert(SUCCEEDED(rc) || state == MediumState_LockedRead || state == MediumState_Created);
10602 }
10603
10604 mData->mSession.mLockedMedia.clear();
10605}
10606
10607/**
10608 * Helper to change the machine state (reimplementation).
10609 *
10610 * @note Locks this object for writing.
10611 */
10612HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
10613{
10614 LogFlowThisFuncEnter();
10615 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
10616
10617 AutoCaller autoCaller(this);
10618 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10619
10620 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10621
10622 MachineState_T oldMachineState = mData->mMachineState;
10623
10624 AssertMsgReturn(oldMachineState != aMachineState,
10625 ("oldMachineState=%s, aMachineState=%s\n",
10626 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
10627 E_FAIL);
10628
10629 HRESULT rc = S_OK;
10630
10631 int stsFlags = 0;
10632 bool deleteSavedState = false;
10633
10634 /* detect some state transitions */
10635
10636 if ( ( oldMachineState == MachineState_Saved
10637 && aMachineState == MachineState_Restoring)
10638 || ( ( oldMachineState == MachineState_PoweredOff
10639 || oldMachineState == MachineState_Teleported
10640 || oldMachineState == MachineState_Aborted
10641 )
10642 && ( aMachineState == MachineState_TeleportingIn
10643 || aMachineState == MachineState_Starting
10644 )
10645 )
10646 )
10647 {
10648 /* The EMT thread is about to start */
10649
10650 /* Nothing to do here for now... */
10651
10652 /// @todo NEWMEDIA don't let mDVDDrive and other children
10653 /// change anything when in the Starting/Restoring state
10654 }
10655 else if ( ( oldMachineState == MachineState_Running
10656 || oldMachineState == MachineState_Paused
10657 || oldMachineState == MachineState_Teleporting
10658 || oldMachineState == MachineState_LiveSnapshotting
10659 || oldMachineState == MachineState_Stuck
10660 || oldMachineState == MachineState_Starting
10661 || oldMachineState == MachineState_Stopping
10662 || oldMachineState == MachineState_Saving
10663 || oldMachineState == MachineState_Restoring
10664 || oldMachineState == MachineState_TeleportingPausedVM
10665 || oldMachineState == MachineState_TeleportingIn
10666 )
10667 && ( aMachineState == MachineState_PoweredOff
10668 || aMachineState == MachineState_Saved
10669 || aMachineState == MachineState_Teleported
10670 || aMachineState == MachineState_Aborted
10671 )
10672 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
10673 * snapshot */
10674 && ( mSnapshotData.mSnapshot.isNull()
10675 || mSnapshotData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
10676 )
10677 )
10678 {
10679 /* The EMT thread has just stopped, unlock attached media. Note that as
10680 * opposed to locking that is done from Console, we do unlocking here
10681 * because the VM process may have aborted before having a chance to
10682 * properly unlock all media it locked. */
10683
10684 unlockMedia();
10685 }
10686
10687 if (oldMachineState == MachineState_Restoring)
10688 {
10689 if (aMachineState != MachineState_Saved)
10690 {
10691 /*
10692 * delete the saved state file once the machine has finished
10693 * restoring from it (note that Console sets the state from
10694 * Restoring to Saved if the VM couldn't restore successfully,
10695 * to give the user an ability to fix an error and retry --
10696 * we keep the saved state file in this case)
10697 */
10698 deleteSavedState = true;
10699 }
10700 }
10701 else if ( oldMachineState == MachineState_Saved
10702 && ( aMachineState == MachineState_PoweredOff
10703 || aMachineState == MachineState_Aborted
10704 || aMachineState == MachineState_Teleported
10705 )
10706 )
10707 {
10708 /*
10709 * delete the saved state after Console::DiscardSavedState() is called
10710 * or if the VM process (owning a direct VM session) crashed while the
10711 * VM was Saved
10712 */
10713
10714 /// @todo (dmik)
10715 // Not sure that deleting the saved state file just because of the
10716 // client death before it attempted to restore the VM is a good
10717 // thing. But when it crashes we need to go to the Aborted state
10718 // which cannot have the saved state file associated... The only
10719 // way to fix this is to make the Aborted condition not a VM state
10720 // but a bool flag: i.e., when a crash occurs, set it to true and
10721 // change the state to PoweredOff or Saved depending on the
10722 // saved state presence.
10723
10724 deleteSavedState = true;
10725 mData->mCurrentStateModified = TRUE;
10726 stsFlags |= SaveSTS_CurStateModified;
10727 }
10728
10729 if ( aMachineState == MachineState_Starting
10730 || aMachineState == MachineState_Restoring
10731 || aMachineState == MachineState_TeleportingIn
10732 )
10733 {
10734 /* set the current state modified flag to indicate that the current
10735 * state is no more identical to the state in the
10736 * current snapshot */
10737 if (!mData->mCurrentSnapshot.isNull())
10738 {
10739 mData->mCurrentStateModified = TRUE;
10740 stsFlags |= SaveSTS_CurStateModified;
10741 }
10742 }
10743
10744 if (deleteSavedState)
10745 {
10746 if (mRemoveSavedState)
10747 {
10748 Assert(!mSSData->mStateFilePath.isEmpty());
10749 RTFileDelete(mSSData->mStateFilePath.c_str());
10750 }
10751 mSSData->mStateFilePath.setNull();
10752 stsFlags |= SaveSTS_StateFilePath;
10753 }
10754
10755 /* redirect to the underlying peer machine */
10756 mPeer->setMachineState(aMachineState);
10757
10758 if ( aMachineState == MachineState_PoweredOff
10759 || aMachineState == MachineState_Teleported
10760 || aMachineState == MachineState_Aborted
10761 || aMachineState == MachineState_Saved)
10762 {
10763 /* the machine has stopped execution
10764 * (or the saved state file was adopted) */
10765 stsFlags |= SaveSTS_StateTimeStamp;
10766 }
10767
10768 if ( ( oldMachineState == MachineState_PoweredOff
10769 || oldMachineState == MachineState_Aborted
10770 || oldMachineState == MachineState_Teleported
10771 )
10772 && aMachineState == MachineState_Saved)
10773 {
10774 /* the saved state file was adopted */
10775 Assert(!mSSData->mStateFilePath.isEmpty());
10776 stsFlags |= SaveSTS_StateFilePath;
10777 }
10778
10779 rc = saveStateSettings(stsFlags);
10780
10781 if ( ( oldMachineState != MachineState_PoweredOff
10782 && oldMachineState != MachineState_Aborted
10783 && oldMachineState != MachineState_Teleported
10784 )
10785 && ( aMachineState == MachineState_PoweredOff
10786 || aMachineState == MachineState_Aborted
10787 || aMachineState == MachineState_Teleported
10788 )
10789 )
10790 {
10791 /* we've been shut down for any reason */
10792 /* no special action so far */
10793 }
10794
10795 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
10796 LogFlowThisFuncLeave();
10797 return rc;
10798}
10799
10800/**
10801 * Sends the current machine state value to the VM process.
10802 *
10803 * @note Locks this object for reading, then calls a client process.
10804 */
10805HRESULT SessionMachine::updateMachineStateOnClient()
10806{
10807 AutoCaller autoCaller(this);
10808 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10809
10810 ComPtr<IInternalSessionControl> directControl;
10811 {
10812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10813 AssertReturn(!!mData, E_FAIL);
10814 directControl = mData->mSession.mDirectControl;
10815
10816 /* directControl may be already set to NULL here in #OnSessionEnd()
10817 * called too early by the direct session process while there is still
10818 * some operation (like discarding the snapshot) in progress. The client
10819 * process in this case is waiting inside Session::close() for the
10820 * "end session" process object to complete, while #uninit() called by
10821 * #checkForDeath() on the Watcher thread is waiting for the pending
10822 * operation to complete. For now, we accept this inconsitent behavior
10823 * and simply do nothing here. */
10824
10825 if (mData->mSession.mState == SessionState_Closing)
10826 return S_OK;
10827
10828 AssertReturn(!directControl.isNull(), E_FAIL);
10829 }
10830
10831 return directControl->UpdateMachineState(mData->mMachineState);
10832}
Note: See TracBrowser for help on using the repository browser.

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