VirtualBox

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

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

Main: coding style

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

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