VirtualBox

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

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

Main: don't export snapshots in OVF, separate OVF import from vbox:Machine import

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

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