VirtualBox

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

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

Added large page property.

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