VirtualBox

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

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

More cleanup

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

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