VirtualBox

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

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

Main/Machine+Console: Extend the scope of the power up progress object, and collect error information from the frontend. Much better error signalling when the VM is started via openRemoteSession. Adjusted documentation accordingly, and finally fixed the incomprehensible messages emitted by VBoxManage startvm.

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