VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/MachineImpl.cpp@ 73740

Last change on this file since 73740 was 73740, checked in by vboxsync, 7 years ago

Main: bugref:8612: add implementaion of Machine::applyDefault() in case of new vm creation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 538.5 KB
Line 
1/* $Id: MachineImpl.cpp 73740 2018-08-17 16:52:08Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47#include "MachineImplMoveVM.h"
48#include "ExtPackManagerImpl.h"
49
50// generated header
51#include "VBoxEvents.h"
52
53#ifdef VBOX_WITH_USB
54# include "USBProxyService.h"
55#endif
56
57#include "AutoCaller.h"
58#include "HashedPw.h"
59#include "Performance.h"
60
61#include <iprt/asm.h>
62#include <iprt/path.h>
63#include <iprt/dir.h>
64#include <iprt/env.h>
65#include <iprt/lockvalidator.h>
66#include <iprt/process.h>
67#include <iprt/cpp/utils.h>
68#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
69#include <iprt/sha.h>
70#include <iprt/string.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#ifdef VBOX_WITH_DTRACE_R3_MAIN
90# include "dtrace/VBoxAPI.h"
91#endif
92
93#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
94# define HOSTSUFF_EXE ".exe"
95#else /* !RT_OS_WINDOWS */
96# define HOSTSUFF_EXE ""
97#endif /* !RT_OS_WINDOWS */
98
99// defines / prototypes
100/////////////////////////////////////////////////////////////////////////////
101
102/////////////////////////////////////////////////////////////////////////////
103// Machine::Data structure
104/////////////////////////////////////////////////////////////////////////////
105
106Machine::Data::Data()
107{
108 mRegistered = FALSE;
109 pMachineConfigFile = NULL;
110 /* Contains hints on what has changed when the user is using the VM (config
111 * changes, running the VM, ...). This is used to decide if a config needs
112 * to be written to disk. */
113 flModifications = 0;
114 /* VM modification usually also trigger setting the current state to
115 * "Modified". Although this is not always the case. An e.g. is the VM
116 * initialization phase or when snapshot related data is changed. The
117 * actually behavior is controlled by the following flag. */
118 m_fAllowStateModification = false;
119 mAccessible = FALSE;
120 /* mUuid is initialized in Machine::init() */
121
122 mMachineState = MachineState_PoweredOff;
123 RTTimeNow(&mLastStateChange);
124
125 mMachineStateDeps = 0;
126 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
127 mMachineStateChangePending = 0;
128
129 mCurrentStateModified = TRUE;
130 mGuestPropertiesModified = FALSE;
131
132 mSession.mPID = NIL_RTPROCESS;
133 mSession.mLockType = LockType_Null;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
165 mVRAMSize = 8;
166 mAccelerate3DEnabled = false;
167 mAccelerate2DVideoEnabled = false;
168 mMonitorCount = 1;
169 mVideoCaptureWidth = 1024;
170 mVideoCaptureHeight = 768;
171 mVideoCaptureRate = 512;
172 mVideoCaptureFPS = 25;
173 mVideoCaptureMaxTime = 0;
174 mVideoCaptureMaxFileSize = 0;
175 mVideoCaptureEnabled = false;
176 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
177 maVideoCaptureScreens[i] = true;
178
179 mHWVirtExEnabled = true;
180 mHWVirtExNestedPagingEnabled = true;
181#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
182 mHWVirtExLargePagesEnabled = true;
183#else
184 /* Not supported on 32 bits hosts. */
185 mHWVirtExLargePagesEnabled = false;
186#endif
187 mHWVirtExVPIDEnabled = true;
188 mHWVirtExUXEnabled = true;
189 mHWVirtExForceEnabled = false;
190 mHWVirtExUseNativeApi = false;
191#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
192 mPAEEnabled = true;
193#else
194 mPAEEnabled = false;
195#endif
196 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
197 mTripleFaultReset = false;
198 mAPIC = true;
199 mX2APIC = false;
200 mIBPBOnVMExit = false;
201 mIBPBOnVMEntry = false;
202 mSpecCtrl = false;
203 mSpecCtrlByHost = false;
204 mNestedHWVirt = false;
205 mHPETEnabled = false;
206 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
207 mCpuIdPortabilityLevel = 0;
208 mCpuProfile = "host";
209
210 /* default boot order: floppy - DVD - HDD */
211 mBootOrder[0] = DeviceType_Floppy;
212 mBootOrder[1] = DeviceType_DVD;
213 mBootOrder[2] = DeviceType_HardDisk;
214 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
215 mBootOrder[i] = DeviceType_Null;
216
217 mClipboardMode = ClipboardMode_Disabled;
218 mDnDMode = DnDMode_Disabled;
219
220 mFirmwareType = FirmwareType_BIOS;
221 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
222 mPointingHIDType = PointingHIDType_PS2Mouse;
223 mChipsetType = ChipsetType_PIIX3;
224 mParavirtProvider = ParavirtProvider_Default;
225 mEmulatedUSBCardReaderEnabled = FALSE;
226
227 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
228 mCPUAttached[i] = false;
229
230 mIOCacheEnabled = true;
231 mIOCacheSize = 5; /* 5MB */
232}
233
234Machine::HWData::~HWData()
235{
236}
237
238/////////////////////////////////////////////////////////////////////////////
239// Machine class
240/////////////////////////////////////////////////////////////////////////////
241
242// constructor / destructor
243/////////////////////////////////////////////////////////////////////////////
244
245Machine::Machine() :
246#ifdef VBOX_WITH_RESOURCE_USAGE_API
247 mCollectorGuest(NULL),
248#endif
249 mPeer(NULL),
250 mParent(NULL),
251 mSerialPorts(),
252 mParallelPorts(),
253 uRegistryNeedsSaving(0)
254{}
255
256Machine::~Machine()
257{}
258
259HRESULT Machine::FinalConstruct()
260{
261 LogFlowThisFunc(("\n"));
262 return BaseFinalConstruct();
263}
264
265void Machine::FinalRelease()
266{
267 LogFlowThisFunc(("\n"));
268 uninit();
269 BaseFinalRelease();
270}
271
272/**
273 * Initializes a new machine instance; this init() variant creates a new, empty machine.
274 * This gets called from VirtualBox::CreateMachine().
275 *
276 * @param aParent Associated parent object
277 * @param strConfigFile Local file system path to the VM settings file (can
278 * be relative to the VirtualBox config directory).
279 * @param strName name for the machine
280 * @param llGroups list of groups for the machine
281 * @param strOsType OS Type string (stored as is if aOsType is NULL).
282 * @param aOsType OS Type of this machine or NULL.
283 * @param aId UUID for the new machine.
284 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
285 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
286 * scheme (includes the UUID).
287 *
288 * @return Success indicator. if not S_OK, the machine object is invalid
289 */
290HRESULT Machine::init(VirtualBox *aParent,
291 const Utf8Str &strConfigFile,
292 const Utf8Str &strName,
293 const StringsList &llGroups,
294 const Utf8Str &strOsType,
295 GuestOSType *aOsType,
296 const Guid &aId,
297 bool fForceOverwrite,
298 bool fDirectoryIncludesUUID)
299{
300 LogFlowThisFuncEnter();
301 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
302
303 /* Enclose the state transition NotReady->InInit->Ready */
304 AutoInitSpan autoInitSpan(this);
305 AssertReturn(autoInitSpan.isOk(), E_FAIL);
306
307 HRESULT rc = initImpl(aParent, strConfigFile);
308 if (FAILED(rc)) return rc;
309
310 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
311 if (FAILED(rc)) return rc;
312
313 if (SUCCEEDED(rc))
314 {
315 // create an empty machine config
316 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
317
318 rc = initDataAndChildObjects();
319 }
320
321 if (SUCCEEDED(rc))
322 {
323 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
324 mData->mAccessible = TRUE;
325
326 unconst(mData->mUuid) = aId;
327
328 mUserData->s.strName = strName;
329
330 if (llGroups.size())
331 mUserData->s.llGroups = llGroups;
332
333 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
334 // the "name sync" flag determines whether the machine directory gets renamed along
335 // with the machine file; say so if the settings file name is the same as the
336 // settings file parent directory (machine directory)
337 mUserData->s.fNameSync = i_isInOwnDir();
338
339 // initialize the default snapshots folder
340 rc = COMSETTER(SnapshotFolder)(NULL);
341 AssertComRC(rc);
342
343 if (aOsType)
344 {
345 /* Store OS type */
346 mUserData->s.strOsType = aOsType->i_id();
347
348 /* Let the OS type select 64-bit ness. */
349 mHWData->mLongMode = aOsType->i_is64Bit()
350 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
351
352 /* Let the OS type enable the X2APIC */
353 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
354 }
355 else if (!strOsType.isEmpty())
356 {
357 /* Store OS type */
358 mUserData->s.strOsType = strOsType;
359
360 /* No guest OS type object. Pick some plausible defaults which the
361 * host can handle. There's no way to know or validate anything. */
362 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
363 mHWData->mX2APIC = false;
364 }
365
366 /* Apply BIOS defaults */
367 mBIOSSettings->i_applyDefaults(aOsType);
368
369 /* Apply network adapters defaults */
370 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
371 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
372
373 /* Apply serial port defaults */
374 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
375 mSerialPorts[slot]->i_applyDefaults(aOsType);
376
377 /* Apply parallel port defaults */
378 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
379 mParallelPorts[slot]->i_applyDefaults();
380
381 /* At this point the changing of the current state modification
382 * flag is allowed. */
383 i_allowStateModification();
384
385 /* commit all changes made during the initialization */
386 i_commit();
387 }
388
389 /* Confirm a successful initialization when it's the case */
390 if (SUCCEEDED(rc))
391 {
392 if (mData->mAccessible)
393 autoInitSpan.setSucceeded();
394 else
395 autoInitSpan.setLimited();
396 }
397
398 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
399 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
400 mData->mRegistered,
401 mData->mAccessible,
402 rc));
403
404 LogFlowThisFuncLeave();
405
406 return rc;
407}
408
409/**
410 * Initializes a new instance with data from machine XML (formerly Init_Registered).
411 * Gets called in two modes:
412 *
413 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
414 * UUID is specified and we mark the machine as "registered";
415 *
416 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
417 * and the machine remains unregistered until RegisterMachine() is called.
418 *
419 * @param aParent Associated parent object
420 * @param strConfigFile Local file system path to the VM settings file (can
421 * be relative to the VirtualBox config directory).
422 * @param aId UUID of the machine or NULL (see above).
423 *
424 * @return Success indicator. if not S_OK, the machine object is invalid
425 */
426HRESULT Machine::initFromSettings(VirtualBox *aParent,
427 const Utf8Str &strConfigFile,
428 const Guid *aId)
429{
430 LogFlowThisFuncEnter();
431 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
432
433 /* Enclose the state transition NotReady->InInit->Ready */
434 AutoInitSpan autoInitSpan(this);
435 AssertReturn(autoInitSpan.isOk(), E_FAIL);
436
437 HRESULT rc = initImpl(aParent, strConfigFile);
438 if (FAILED(rc)) return rc;
439
440 if (aId)
441 {
442 // loading a registered VM:
443 unconst(mData->mUuid) = *aId;
444 mData->mRegistered = TRUE;
445 // now load the settings from XML:
446 rc = i_registeredInit();
447 // this calls initDataAndChildObjects() and loadSettings()
448 }
449 else
450 {
451 // opening an unregistered VM (VirtualBox::OpenMachine()):
452 rc = initDataAndChildObjects();
453
454 if (SUCCEEDED(rc))
455 {
456 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
457 mData->mAccessible = TRUE;
458
459 try
460 {
461 // load and parse machine XML; this will throw on XML or logic errors
462 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
463
464 // reject VM UUID duplicates, they can happen if someone
465 // tries to register an already known VM config again
466 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
467 true /* fPermitInaccessible */,
468 false /* aDoSetError */,
469 NULL) != VBOX_E_OBJECT_NOT_FOUND)
470 {
471 throw setError(E_FAIL,
472 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
473 mData->m_strConfigFile.c_str());
474 }
475
476 // use UUID from machine config
477 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
478
479 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
480 NULL /* puuidRegistry */);
481 if (FAILED(rc)) throw rc;
482
483 /* At this point the changing of the current state modification
484 * flag is allowed. */
485 i_allowStateModification();
486
487 i_commit();
488 }
489 catch (HRESULT err)
490 {
491 /* we assume that error info is set by the thrower */
492 rc = err;
493 }
494 catch (...)
495 {
496 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
497 }
498 }
499 }
500
501 /* Confirm a successful initialization when it's the case */
502 if (SUCCEEDED(rc))
503 {
504 if (mData->mAccessible)
505 autoInitSpan.setSucceeded();
506 else
507 {
508 autoInitSpan.setLimited();
509
510 // uninit media from this machine's media registry, or else
511 // reloading the settings will fail
512 mParent->i_unregisterMachineMedia(i_getId());
513 }
514 }
515
516 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
517 "rc=%08X\n",
518 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
519 mData->mRegistered, mData->mAccessible, rc));
520
521 LogFlowThisFuncLeave();
522
523 return rc;
524}
525
526/**
527 * Initializes a new instance from a machine config that is already in memory
528 * (import OVF case). Since we are importing, the UUID in the machine
529 * config is ignored and we always generate a fresh one.
530 *
531 * @param aParent Associated parent object.
532 * @param strName Name for the new machine; this overrides what is specified in config.
533 * @param strSettingsFilename File name of .vbox file.
534 * @param config Machine configuration loaded and parsed from XML.
535 *
536 * @return Success indicator. if not S_OK, the machine object is invalid
537 */
538HRESULT Machine::init(VirtualBox *aParent,
539 const Utf8Str &strName,
540 const Utf8Str &strSettingsFilename,
541 const settings::MachineConfigFile &config)
542{
543 LogFlowThisFuncEnter();
544
545 /* Enclose the state transition NotReady->InInit->Ready */
546 AutoInitSpan autoInitSpan(this);
547 AssertReturn(autoInitSpan.isOk(), E_FAIL);
548
549 HRESULT rc = initImpl(aParent, strSettingsFilename);
550 if (FAILED(rc)) return rc;
551
552 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
553 if (FAILED(rc)) return rc;
554
555 rc = initDataAndChildObjects();
556
557 if (SUCCEEDED(rc))
558 {
559 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
560 mData->mAccessible = TRUE;
561
562 // create empty machine config for instance data
563 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
564
565 // generate fresh UUID, ignore machine config
566 unconst(mData->mUuid).create();
567
568 rc = i_loadMachineDataFromSettings(config,
569 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
570
571 // override VM name as well, it may be different
572 mUserData->s.strName = strName;
573
574 if (SUCCEEDED(rc))
575 {
576 /* At this point the changing of the current state modification
577 * flag is allowed. */
578 i_allowStateModification();
579
580 /* commit all changes made during the initialization */
581 i_commit();
582 }
583 }
584
585 /* Confirm a successful initialization when it's the case */
586 if (SUCCEEDED(rc))
587 {
588 if (mData->mAccessible)
589 autoInitSpan.setSucceeded();
590 else
591 {
592 /* Ignore all errors from unregistering, they would destroy
593- * the more interesting error information we already have,
594- * pinpointing the issue with the VM config. */
595 ErrorInfoKeeper eik;
596
597 autoInitSpan.setLimited();
598
599 // uninit media from this machine's media registry, or else
600 // reloading the settings will fail
601 mParent->i_unregisterMachineMedia(i_getId());
602 }
603 }
604
605 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
606 "rc=%08X\n",
607 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
608 mData->mRegistered, mData->mAccessible, rc));
609
610 LogFlowThisFuncLeave();
611
612 return rc;
613}
614
615/**
616 * Shared code between the various init() implementations.
617 * @param aParent The VirtualBox object.
618 * @param strConfigFile Settings file.
619 * @return
620 */
621HRESULT Machine::initImpl(VirtualBox *aParent,
622 const Utf8Str &strConfigFile)
623{
624 LogFlowThisFuncEnter();
625
626 AssertReturn(aParent, E_INVALIDARG);
627 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
628
629 HRESULT rc = S_OK;
630
631 /* share the parent weakly */
632 unconst(mParent) = aParent;
633
634 /* allocate the essential machine data structure (the rest will be
635 * allocated later by initDataAndChildObjects() */
636 mData.allocate();
637
638 /* memorize the config file name (as provided) */
639 mData->m_strConfigFile = strConfigFile;
640
641 /* get the full file name */
642 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
643 if (RT_FAILURE(vrc1))
644 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
645 tr("Invalid machine settings file name '%s' (%Rrc)"),
646 strConfigFile.c_str(),
647 vrc1);
648
649 LogFlowThisFuncLeave();
650
651 return rc;
652}
653
654/**
655 * Tries to create a machine settings file in the path stored in the machine
656 * instance data. Used when a new machine is created to fail gracefully if
657 * the settings file could not be written (e.g. because machine dir is read-only).
658 * @return
659 */
660HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
661{
662 HRESULT rc = S_OK;
663
664 // when we create a new machine, we must be able to create the settings file
665 RTFILE f = NIL_RTFILE;
666 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
667 if ( RT_SUCCESS(vrc)
668 || vrc == VERR_SHARING_VIOLATION
669 )
670 {
671 if (RT_SUCCESS(vrc))
672 RTFileClose(f);
673 if (!fForceOverwrite)
674 rc = setError(VBOX_E_FILE_ERROR,
675 tr("Machine settings file '%s' already exists"),
676 mData->m_strConfigFileFull.c_str());
677 else
678 {
679 /* try to delete the config file, as otherwise the creation
680 * of a new settings file will fail. */
681 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
682 if (RT_FAILURE(vrc2))
683 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
684 tr("Could not delete the existing settings file '%s' (%Rrc)"),
685 mData->m_strConfigFileFull.c_str(), vrc2);
686 }
687 }
688 else if ( vrc != VERR_FILE_NOT_FOUND
689 && vrc != VERR_PATH_NOT_FOUND
690 )
691 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
692 tr("Invalid machine settings file name '%s' (%Rrc)"),
693 mData->m_strConfigFileFull.c_str(),
694 vrc);
695 return rc;
696}
697
698/**
699 * Initializes the registered machine by loading the settings file.
700 * This method is separated from #init() in order to make it possible to
701 * retry the operation after VirtualBox startup instead of refusing to
702 * startup the whole VirtualBox server in case if the settings file of some
703 * registered VM is invalid or inaccessible.
704 *
705 * @note Must be always called from this object's write lock
706 * (unless called from #init() that doesn't need any locking).
707 * @note Locks the mUSBController method for writing.
708 * @note Subclasses must not call this method.
709 */
710HRESULT Machine::i_registeredInit()
711{
712 AssertReturn(!i_isSessionMachine(), E_FAIL);
713 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
714 AssertReturn(mData->mUuid.isValid(), E_FAIL);
715 AssertReturn(!mData->mAccessible, E_FAIL);
716
717 HRESULT rc = initDataAndChildObjects();
718
719 if (SUCCEEDED(rc))
720 {
721 /* Temporarily reset the registered flag in order to let setters
722 * potentially called from loadSettings() succeed (isMutable() used in
723 * all setters will return FALSE for a Machine instance if mRegistered
724 * is TRUE). */
725 mData->mRegistered = FALSE;
726
727 try
728 {
729 // load and parse machine XML; this will throw on XML or logic errors
730 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
731
732 if (mData->mUuid != mData->pMachineConfigFile->uuid)
733 throw setError(E_FAIL,
734 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
735 mData->pMachineConfigFile->uuid.raw(),
736 mData->m_strConfigFileFull.c_str(),
737 mData->mUuid.toString().c_str(),
738 mParent->i_settingsFilePath().c_str());
739
740 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
741 NULL /* const Guid *puuidRegistry */);
742 if (FAILED(rc)) throw rc;
743 }
744 catch (HRESULT err)
745 {
746 /* we assume that error info is set by the thrower */
747 rc = err;
748 }
749 catch (...)
750 {
751 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
752 }
753
754 /* Restore the registered flag (even on failure) */
755 mData->mRegistered = TRUE;
756 }
757
758 if (SUCCEEDED(rc))
759 {
760 /* Set mAccessible to TRUE only if we successfully locked and loaded
761 * the settings file */
762 mData->mAccessible = TRUE;
763
764 /* commit all changes made during loading the settings file */
765 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
766 /// @todo r=klaus for some reason the settings loading logic backs up
767 // the settings, and therefore a commit is needed. Should probably be changed.
768 }
769 else
770 {
771 /* If the machine is registered, then, instead of returning a
772 * failure, we mark it as inaccessible and set the result to
773 * success to give it a try later */
774
775 /* fetch the current error info */
776 mData->mAccessError = com::ErrorInfo();
777 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
778
779 /* rollback all changes */
780 i_rollback(false /* aNotify */);
781
782 // uninit media from this machine's media registry, or else
783 // reloading the settings will fail
784 mParent->i_unregisterMachineMedia(i_getId());
785
786 /* uninitialize the common part to make sure all data is reset to
787 * default (null) values */
788 uninitDataAndChildObjects();
789
790 rc = S_OK;
791 }
792
793 return rc;
794}
795
796/**
797 * Uninitializes the instance.
798 * Called either from FinalRelease() or by the parent when it gets destroyed.
799 *
800 * @note The caller of this method must make sure that this object
801 * a) doesn't have active callers on the current thread and b) is not locked
802 * by the current thread; otherwise uninit() will hang either a) due to
803 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
804 * a dead-lock caused by this thread waiting for all callers on the other
805 * threads are done but preventing them from doing so by holding a lock.
806 */
807void Machine::uninit()
808{
809 LogFlowThisFuncEnter();
810
811 Assert(!isWriteLockOnCurrentThread());
812
813 Assert(!uRegistryNeedsSaving);
814 if (uRegistryNeedsSaving)
815 {
816 AutoCaller autoCaller(this);
817 if (SUCCEEDED(autoCaller.rc()))
818 {
819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
820 i_saveSettings(NULL, Machine::SaveS_Force);
821 }
822 }
823
824 /* Enclose the state transition Ready->InUninit->NotReady */
825 AutoUninitSpan autoUninitSpan(this);
826 if (autoUninitSpan.uninitDone())
827 return;
828
829 Assert(!i_isSnapshotMachine());
830 Assert(!i_isSessionMachine());
831 Assert(!!mData);
832
833 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
834 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
835
836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
837
838 if (!mData->mSession.mMachine.isNull())
839 {
840 /* Theoretically, this can only happen if the VirtualBox server has been
841 * terminated while there were clients running that owned open direct
842 * sessions. Since in this case we are definitely called by
843 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
844 * won't happen on the client watcher thread (because it has a
845 * VirtualBox caller for the duration of the
846 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
847 * cannot happen until the VirtualBox caller is released). This is
848 * important, because SessionMachine::uninit() cannot correctly operate
849 * after we return from this method (it expects the Machine instance is
850 * still valid). We'll call it ourselves below.
851 */
852 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
853 (SessionMachine*)mData->mSession.mMachine));
854
855 if (Global::IsOnlineOrTransient(mData->mMachineState))
856 {
857 Log1WarningThisFunc(("Setting state to Aborted!\n"));
858 /* set machine state using SessionMachine reimplementation */
859 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
860 }
861
862 /*
863 * Uninitialize SessionMachine using public uninit() to indicate
864 * an unexpected uninitialization.
865 */
866 mData->mSession.mMachine->uninit();
867 /* SessionMachine::uninit() must set mSession.mMachine to null */
868 Assert(mData->mSession.mMachine.isNull());
869 }
870
871 // uninit media from this machine's media registry, if they're still there
872 Guid uuidMachine(i_getId());
873
874 /* the lock is no more necessary (SessionMachine is uninitialized) */
875 alock.release();
876
877 /* XXX This will fail with
878 * "cannot be closed because it is still attached to 1 virtual machines"
879 * because at this point we did not call uninitDataAndChildObjects() yet
880 * and therefore also removeBackReference() for all these mediums was not called! */
881
882 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
883 mParent->i_unregisterMachineMedia(uuidMachine);
884
885 // has machine been modified?
886 if (mData->flModifications)
887 {
888 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
889 i_rollback(false /* aNotify */);
890 }
891
892 if (mData->mAccessible)
893 uninitDataAndChildObjects();
894
895 /* free the essential data structure last */
896 mData.free();
897
898 LogFlowThisFuncLeave();
899}
900
901// Wrapped IMachine properties
902/////////////////////////////////////////////////////////////////////////////
903HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
904{
905 /* mParent is constant during life time, no need to lock */
906 ComObjPtr<VirtualBox> pVirtualBox(mParent);
907 aParent = pVirtualBox;
908
909 return S_OK;
910}
911
912
913HRESULT Machine::getAccessible(BOOL *aAccessible)
914{
915 /* In some cases (medium registry related), it is necessary to be able to
916 * go through the list of all machines. Happens when an inaccessible VM
917 * has a sensible medium registry. */
918 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
919 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
920
921 HRESULT rc = S_OK;
922
923 if (!mData->mAccessible)
924 {
925 /* try to initialize the VM once more if not accessible */
926
927 AutoReinitSpan autoReinitSpan(this);
928 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
929
930#ifdef DEBUG
931 LogFlowThisFunc(("Dumping media backreferences\n"));
932 mParent->i_dumpAllBackRefs();
933#endif
934
935 if (mData->pMachineConfigFile)
936 {
937 // reset the XML file to force loadSettings() (called from i_registeredInit())
938 // to parse it again; the file might have changed
939 delete mData->pMachineConfigFile;
940 mData->pMachineConfigFile = NULL;
941 }
942
943 rc = i_registeredInit();
944
945 if (SUCCEEDED(rc) && mData->mAccessible)
946 {
947 autoReinitSpan.setSucceeded();
948
949 /* make sure interesting parties will notice the accessibility
950 * state change */
951 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
952 mParent->i_onMachineDataChange(mData->mUuid);
953 }
954 }
955
956 if (SUCCEEDED(rc))
957 *aAccessible = mData->mAccessible;
958
959 LogFlowThisFuncLeave();
960
961 return rc;
962}
963
964HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
965{
966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
967
968 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
969 {
970 /* return shortly */
971 aAccessError = NULL;
972 return S_OK;
973 }
974
975 HRESULT rc = S_OK;
976
977 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
978 rc = errorInfo.createObject();
979 if (SUCCEEDED(rc))
980 {
981 errorInfo->init(mData->mAccessError.getResultCode(),
982 mData->mAccessError.getInterfaceID().ref(),
983 Utf8Str(mData->mAccessError.getComponent()).c_str(),
984 Utf8Str(mData->mAccessError.getText()));
985 aAccessError = errorInfo;
986 }
987
988 return rc;
989}
990
991HRESULT Machine::getName(com::Utf8Str &aName)
992{
993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
994
995 aName = mUserData->s.strName;
996
997 return S_OK;
998}
999
1000HRESULT Machine::setName(const com::Utf8Str &aName)
1001{
1002 // prohibit setting a UUID only as the machine name, or else it can
1003 // never be found by findMachine()
1004 Guid test(aName);
1005
1006 if (test.isValid())
1007 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1008
1009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1010
1011 HRESULT rc = i_checkStateDependency(MutableStateDep);
1012 if (FAILED(rc)) return rc;
1013
1014 i_setModified(IsModified_MachineData);
1015 mUserData.backup();
1016 mUserData->s.strName = aName;
1017
1018 return S_OK;
1019}
1020
1021HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1022{
1023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1024
1025 aDescription = mUserData->s.strDescription;
1026
1027 return S_OK;
1028}
1029
1030HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1031{
1032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1033
1034 // this can be done in principle in any state as it doesn't affect the VM
1035 // significantly, but play safe by not messing around while complex
1036 // activities are going on
1037 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1038 if (FAILED(rc)) return rc;
1039
1040 i_setModified(IsModified_MachineData);
1041 mUserData.backup();
1042 mUserData->s.strDescription = aDescription;
1043
1044 return S_OK;
1045}
1046
1047HRESULT Machine::getId(com::Guid &aId)
1048{
1049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1050
1051 aId = mData->mUuid;
1052
1053 return S_OK;
1054}
1055
1056HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1057{
1058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1059 aGroups.resize(mUserData->s.llGroups.size());
1060 size_t i = 0;
1061 for (StringsList::const_iterator
1062 it = mUserData->s.llGroups.begin();
1063 it != mUserData->s.llGroups.end();
1064 ++it, ++i)
1065 aGroups[i] = (*it);
1066
1067 return S_OK;
1068}
1069
1070HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1071{
1072 StringsList llGroups;
1073 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1074 if (FAILED(rc))
1075 return rc;
1076
1077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1078
1079 rc = i_checkStateDependency(MutableOrSavedStateDep);
1080 if (FAILED(rc)) return rc;
1081
1082 i_setModified(IsModified_MachineData);
1083 mUserData.backup();
1084 mUserData->s.llGroups = llGroups;
1085
1086 return S_OK;
1087}
1088
1089HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1090{
1091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1092
1093 aOSTypeId = mUserData->s.strOsType;
1094
1095 return S_OK;
1096}
1097
1098HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1099{
1100 /* look up the object by Id to check it is valid */
1101 ComObjPtr<GuestOSType> pGuestOSType;
1102 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1103
1104 /* when setting, always use the "etalon" value for consistency -- lookup
1105 * by ID is case-insensitive and the input value may have different case */
1106 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1107
1108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1109
1110 HRESULT rc = i_checkStateDependency(MutableStateDep);
1111 if (FAILED(rc)) return rc;
1112
1113 i_setModified(IsModified_MachineData);
1114 mUserData.backup();
1115 mUserData->s.strOsType = osTypeId;
1116
1117 return S_OK;
1118}
1119
1120HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1121{
1122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1123
1124 *aFirmwareType = mHWData->mFirmwareType;
1125
1126 return S_OK;
1127}
1128
1129HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1130{
1131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1132
1133 HRESULT rc = i_checkStateDependency(MutableStateDep);
1134 if (FAILED(rc)) return rc;
1135
1136 i_setModified(IsModified_MachineData);
1137 mHWData.backup();
1138 mHWData->mFirmwareType = aFirmwareType;
1139
1140 return S_OK;
1141}
1142
1143HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1144{
1145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1146
1147 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1148
1149 return S_OK;
1150}
1151
1152HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1153{
1154 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1155
1156 HRESULT rc = i_checkStateDependency(MutableStateDep);
1157 if (FAILED(rc)) return rc;
1158
1159 i_setModified(IsModified_MachineData);
1160 mHWData.backup();
1161 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1162
1163 return S_OK;
1164}
1165
1166HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1167{
1168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1169
1170 *aPointingHIDType = mHWData->mPointingHIDType;
1171
1172 return S_OK;
1173}
1174
1175HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1176{
1177 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1178
1179 HRESULT rc = i_checkStateDependency(MutableStateDep);
1180 if (FAILED(rc)) return rc;
1181
1182 i_setModified(IsModified_MachineData);
1183 mHWData.backup();
1184 mHWData->mPointingHIDType = aPointingHIDType;
1185
1186 return S_OK;
1187}
1188
1189HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1190{
1191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1192
1193 *aChipsetType = mHWData->mChipsetType;
1194
1195 return S_OK;
1196}
1197
1198HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1199{
1200 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1201
1202 HRESULT rc = i_checkStateDependency(MutableStateDep);
1203 if (FAILED(rc)) return rc;
1204
1205 if (aChipsetType != mHWData->mChipsetType)
1206 {
1207 i_setModified(IsModified_MachineData);
1208 mHWData.backup();
1209 mHWData->mChipsetType = aChipsetType;
1210
1211 // Resize network adapter array, to be finalized on commit/rollback.
1212 // We must not throw away entries yet, otherwise settings are lost
1213 // without a way to roll back.
1214 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1215 size_t oldCount = mNetworkAdapters.size();
1216 if (newCount > oldCount)
1217 {
1218 mNetworkAdapters.resize(newCount);
1219 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1220 {
1221 unconst(mNetworkAdapters[slot]).createObject();
1222 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1223 }
1224 }
1225 }
1226
1227 return S_OK;
1228}
1229
1230HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1231{
1232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1233
1234 aParavirtDebug = mHWData->mParavirtDebug;
1235 return S_OK;
1236}
1237
1238HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1239{
1240 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1241
1242 HRESULT rc = i_checkStateDependency(MutableStateDep);
1243 if (FAILED(rc)) return rc;
1244
1245 /** @todo Parse/validate options? */
1246 if (aParavirtDebug != mHWData->mParavirtDebug)
1247 {
1248 i_setModified(IsModified_MachineData);
1249 mHWData.backup();
1250 mHWData->mParavirtDebug = aParavirtDebug;
1251 }
1252
1253 return S_OK;
1254}
1255
1256HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1257{
1258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1259
1260 *aParavirtProvider = mHWData->mParavirtProvider;
1261
1262 return S_OK;
1263}
1264
1265HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1266{
1267 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1268
1269 HRESULT rc = i_checkStateDependency(MutableStateDep);
1270 if (FAILED(rc)) return rc;
1271
1272 if (aParavirtProvider != mHWData->mParavirtProvider)
1273 {
1274 i_setModified(IsModified_MachineData);
1275 mHWData.backup();
1276 mHWData->mParavirtProvider = aParavirtProvider;
1277 }
1278
1279 return S_OK;
1280}
1281
1282HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1283{
1284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1285
1286 *aParavirtProvider = mHWData->mParavirtProvider;
1287 switch (mHWData->mParavirtProvider)
1288 {
1289 case ParavirtProvider_None:
1290 case ParavirtProvider_HyperV:
1291 case ParavirtProvider_KVM:
1292 case ParavirtProvider_Minimal:
1293 break;
1294
1295 /* Resolve dynamic provider types to the effective types. */
1296 default:
1297 {
1298 ComObjPtr<GuestOSType> pGuestOSType;
1299 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1300 pGuestOSType);
1301 if (FAILED(hrc2) || pGuestOSType.isNull())
1302 {
1303 *aParavirtProvider = ParavirtProvider_None;
1304 break;
1305 }
1306
1307 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1308 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1309
1310 switch (mHWData->mParavirtProvider)
1311 {
1312 case ParavirtProvider_Legacy:
1313 {
1314 if (fOsXGuest)
1315 *aParavirtProvider = ParavirtProvider_Minimal;
1316 else
1317 *aParavirtProvider = ParavirtProvider_None;
1318 break;
1319 }
1320
1321 case ParavirtProvider_Default:
1322 {
1323 if (fOsXGuest)
1324 *aParavirtProvider = ParavirtProvider_Minimal;
1325 else if ( mUserData->s.strOsType == "Windows10"
1326 || mUserData->s.strOsType == "Windows10_64"
1327 || mUserData->s.strOsType == "Windows81"
1328 || mUserData->s.strOsType == "Windows81_64"
1329 || mUserData->s.strOsType == "Windows8"
1330 || mUserData->s.strOsType == "Windows8_64"
1331 || mUserData->s.strOsType == "Windows7"
1332 || mUserData->s.strOsType == "Windows7_64"
1333 || mUserData->s.strOsType == "WindowsVista"
1334 || mUserData->s.strOsType == "WindowsVista_64"
1335 || mUserData->s.strOsType == "Windows2012"
1336 || mUserData->s.strOsType == "Windows2012_64"
1337 || mUserData->s.strOsType == "Windows2008"
1338 || mUserData->s.strOsType == "Windows2008_64")
1339 {
1340 *aParavirtProvider = ParavirtProvider_HyperV;
1341 }
1342 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1343 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1344 || mUserData->s.strOsType == "Linux"
1345 || mUserData->s.strOsType == "Linux_64"
1346 || mUserData->s.strOsType == "ArchLinux"
1347 || mUserData->s.strOsType == "ArchLinux_64"
1348 || mUserData->s.strOsType == "Debian"
1349 || mUserData->s.strOsType == "Debian_64"
1350 || mUserData->s.strOsType == "Fedora"
1351 || mUserData->s.strOsType == "Fedora_64"
1352 || mUserData->s.strOsType == "Gentoo"
1353 || mUserData->s.strOsType == "Gentoo_64"
1354 || mUserData->s.strOsType == "Mandriva"
1355 || mUserData->s.strOsType == "Mandriva_64"
1356 || mUserData->s.strOsType == "OpenSUSE"
1357 || mUserData->s.strOsType == "OpenSUSE_64"
1358 || mUserData->s.strOsType == "Oracle"
1359 || mUserData->s.strOsType == "Oracle_64"
1360 || mUserData->s.strOsType == "RedHat"
1361 || mUserData->s.strOsType == "RedHat_64"
1362 || mUserData->s.strOsType == "Turbolinux"
1363 || mUserData->s.strOsType == "Turbolinux_64"
1364 || mUserData->s.strOsType == "Ubuntu"
1365 || mUserData->s.strOsType == "Ubuntu_64"
1366 || mUserData->s.strOsType == "Xandros"
1367 || mUserData->s.strOsType == "Xandros_64")
1368 {
1369 *aParavirtProvider = ParavirtProvider_KVM;
1370 }
1371 else
1372 *aParavirtProvider = ParavirtProvider_None;
1373 break;
1374 }
1375
1376 default: AssertFailedBreak(); /* Shut up MSC. */
1377 }
1378 break;
1379 }
1380 }
1381
1382 Assert( *aParavirtProvider == ParavirtProvider_None
1383 || *aParavirtProvider == ParavirtProvider_Minimal
1384 || *aParavirtProvider == ParavirtProvider_HyperV
1385 || *aParavirtProvider == ParavirtProvider_KVM);
1386 return S_OK;
1387}
1388
1389HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1390{
1391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1392
1393 aHardwareVersion = mHWData->mHWVersion;
1394
1395 return S_OK;
1396}
1397
1398HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1399{
1400 /* check known version */
1401 Utf8Str hwVersion = aHardwareVersion;
1402 if ( hwVersion.compare("1") != 0
1403 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1404 return setError(E_INVALIDARG,
1405 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1406
1407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1408
1409 HRESULT rc = i_checkStateDependency(MutableStateDep);
1410 if (FAILED(rc)) return rc;
1411
1412 i_setModified(IsModified_MachineData);
1413 mHWData.backup();
1414 mHWData->mHWVersion = aHardwareVersion;
1415
1416 return S_OK;
1417}
1418
1419HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1420{
1421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1422
1423 if (!mHWData->mHardwareUUID.isZero())
1424 aHardwareUUID = mHWData->mHardwareUUID;
1425 else
1426 aHardwareUUID = mData->mUuid;
1427
1428 return S_OK;
1429}
1430
1431HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1432{
1433 if (!aHardwareUUID.isValid())
1434 return E_INVALIDARG;
1435
1436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1437
1438 HRESULT rc = i_checkStateDependency(MutableStateDep);
1439 if (FAILED(rc)) return rc;
1440
1441 i_setModified(IsModified_MachineData);
1442 mHWData.backup();
1443 if (aHardwareUUID == mData->mUuid)
1444 mHWData->mHardwareUUID.clear();
1445 else
1446 mHWData->mHardwareUUID = aHardwareUUID;
1447
1448 return S_OK;
1449}
1450
1451HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1452{
1453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1454
1455 *aMemorySize = mHWData->mMemorySize;
1456
1457 return S_OK;
1458}
1459
1460HRESULT Machine::setMemorySize(ULONG aMemorySize)
1461{
1462 /* check RAM limits */
1463 if ( aMemorySize < MM_RAM_MIN_IN_MB
1464 || aMemorySize > MM_RAM_MAX_IN_MB
1465 )
1466 return setError(E_INVALIDARG,
1467 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1468 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1469
1470 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1471
1472 HRESULT rc = i_checkStateDependency(MutableStateDep);
1473 if (FAILED(rc)) return rc;
1474
1475 i_setModified(IsModified_MachineData);
1476 mHWData.backup();
1477 mHWData->mMemorySize = aMemorySize;
1478
1479 return S_OK;
1480}
1481
1482HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1483{
1484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1485
1486 *aCPUCount = mHWData->mCPUCount;
1487
1488 return S_OK;
1489}
1490
1491HRESULT Machine::setCPUCount(ULONG aCPUCount)
1492{
1493 /* check CPU limits */
1494 if ( aCPUCount < SchemaDefs::MinCPUCount
1495 || aCPUCount > SchemaDefs::MaxCPUCount
1496 )
1497 return setError(E_INVALIDARG,
1498 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1499 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1500
1501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1502
1503 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1504 if (mHWData->mCPUHotPlugEnabled)
1505 {
1506 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1507 {
1508 if (mHWData->mCPUAttached[idx])
1509 return setError(E_INVALIDARG,
1510 tr("There is still a CPU attached to socket %lu."
1511 "Detach the CPU before removing the socket"),
1512 aCPUCount, idx+1);
1513 }
1514 }
1515
1516 HRESULT rc = i_checkStateDependency(MutableStateDep);
1517 if (FAILED(rc)) return rc;
1518
1519 i_setModified(IsModified_MachineData);
1520 mHWData.backup();
1521 mHWData->mCPUCount = aCPUCount;
1522
1523 return S_OK;
1524}
1525
1526HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1527{
1528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1529
1530 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1531
1532 return S_OK;
1533}
1534
1535HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1536{
1537 HRESULT rc = S_OK;
1538
1539 /* check throttle limits */
1540 if ( aCPUExecutionCap < 1
1541 || aCPUExecutionCap > 100
1542 )
1543 return setError(E_INVALIDARG,
1544 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1545 aCPUExecutionCap, 1, 100);
1546
1547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1548
1549 alock.release();
1550 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1551 alock.acquire();
1552 if (FAILED(rc)) return rc;
1553
1554 i_setModified(IsModified_MachineData);
1555 mHWData.backup();
1556 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1557
1558 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1559 if (Global::IsOnline(mData->mMachineState))
1560 i_saveSettings(NULL);
1561
1562 return S_OK;
1563}
1564
1565HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1566{
1567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1568
1569 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1570
1571 return S_OK;
1572}
1573
1574HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1575{
1576 HRESULT rc = S_OK;
1577
1578 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1579
1580 rc = i_checkStateDependency(MutableStateDep);
1581 if (FAILED(rc)) return rc;
1582
1583 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1584 {
1585 if (aCPUHotPlugEnabled)
1586 {
1587 i_setModified(IsModified_MachineData);
1588 mHWData.backup();
1589
1590 /* Add the amount of CPUs currently attached */
1591 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1592 mHWData->mCPUAttached[i] = true;
1593 }
1594 else
1595 {
1596 /*
1597 * We can disable hotplug only if the amount of maximum CPUs is equal
1598 * to the amount of attached CPUs
1599 */
1600 unsigned cCpusAttached = 0;
1601 unsigned iHighestId = 0;
1602
1603 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1604 {
1605 if (mHWData->mCPUAttached[i])
1606 {
1607 cCpusAttached++;
1608 iHighestId = i;
1609 }
1610 }
1611
1612 if ( (cCpusAttached != mHWData->mCPUCount)
1613 || (iHighestId >= mHWData->mCPUCount))
1614 return setError(E_INVALIDARG,
1615 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1616
1617 i_setModified(IsModified_MachineData);
1618 mHWData.backup();
1619 }
1620 }
1621
1622 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1623
1624 return rc;
1625}
1626
1627HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1628{
1629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1630
1631 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1632
1633 return S_OK;
1634}
1635
1636HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1637{
1638 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1639
1640 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1641 if (SUCCEEDED(hrc))
1642 {
1643 i_setModified(IsModified_MachineData);
1644 mHWData.backup();
1645 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1646 }
1647 return hrc;
1648}
1649
1650HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1651{
1652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1653 aCPUProfile = mHWData->mCpuProfile;
1654 return S_OK;
1655}
1656
1657HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1658{
1659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1660 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1661 if (SUCCEEDED(hrc))
1662 {
1663 i_setModified(IsModified_MachineData);
1664 mHWData.backup();
1665 /* Empty equals 'host'. */
1666 if (aCPUProfile.isNotEmpty())
1667 mHWData->mCpuProfile = aCPUProfile;
1668 else
1669 mHWData->mCpuProfile = "host";
1670 }
1671 return hrc;
1672}
1673
1674HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1675{
1676#ifdef VBOX_WITH_USB_CARDREADER
1677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1678
1679 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1680
1681 return S_OK;
1682#else
1683 NOREF(aEmulatedUSBCardReaderEnabled);
1684 return E_NOTIMPL;
1685#endif
1686}
1687
1688HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1689{
1690#ifdef VBOX_WITH_USB_CARDREADER
1691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1692
1693 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1694 if (FAILED(rc)) return rc;
1695
1696 i_setModified(IsModified_MachineData);
1697 mHWData.backup();
1698 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1699
1700 return S_OK;
1701#else
1702 NOREF(aEmulatedUSBCardReaderEnabled);
1703 return E_NOTIMPL;
1704#endif
1705}
1706
1707HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1708{
1709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1710
1711 *aHPETEnabled = mHWData->mHPETEnabled;
1712
1713 return S_OK;
1714}
1715
1716HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1717{
1718 HRESULT rc = S_OK;
1719
1720 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1721
1722 rc = i_checkStateDependency(MutableStateDep);
1723 if (FAILED(rc)) return rc;
1724
1725 i_setModified(IsModified_MachineData);
1726 mHWData.backup();
1727
1728 mHWData->mHPETEnabled = aHPETEnabled;
1729
1730 return rc;
1731}
1732
1733HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1734{
1735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1736
1737 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1738 return S_OK;
1739}
1740
1741HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1742{
1743 HRESULT rc = S_OK;
1744
1745 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1746
1747 i_setModified(IsModified_MachineData);
1748 mHWData.backup();
1749 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1750
1751 alock.release();
1752 rc = i_onVideoCaptureChange();
1753 alock.acquire();
1754 if (FAILED(rc))
1755 {
1756 /*
1757 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1758 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1759 * determine if it should start or stop capturing. Therefore we need to manually
1760 * undo change.
1761 */
1762 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1763 return rc;
1764 }
1765
1766 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1767 if (Global::IsOnline(mData->mMachineState))
1768 i_saveSettings(NULL);
1769
1770 return rc;
1771}
1772
1773HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1774{
1775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1776 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1777 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1778 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1779 return S_OK;
1780}
1781
1782HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1783{
1784 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1785 bool fChanged = false;
1786
1787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1788
1789 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1790 {
1791 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1792 {
1793 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1794 fChanged = true;
1795 }
1796 }
1797 if (fChanged)
1798 {
1799 alock.release();
1800 HRESULT rc = i_onVideoCaptureChange();
1801 alock.acquire();
1802 if (FAILED(rc)) return rc;
1803 i_setModified(IsModified_MachineData);
1804
1805 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1806 if (Global::IsOnline(mData->mMachineState))
1807 i_saveSettings(NULL);
1808 }
1809
1810 return S_OK;
1811}
1812
1813HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1814{
1815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1816 if (mHWData->mVideoCaptureFile.isEmpty())
1817 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1818 else
1819 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1820 return S_OK;
1821}
1822
1823HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1824{
1825 Utf8Str strFile(aVideoCaptureFile);
1826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1827
1828 if ( Global::IsOnline(mData->mMachineState)
1829 && mHWData->mVideoCaptureEnabled)
1830 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1831
1832 if (!RTPathStartsWithRoot(strFile.c_str()))
1833 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1834
1835 if (!strFile.isEmpty())
1836 {
1837 Utf8Str defaultFile;
1838 i_getDefaultVideoCaptureFile(defaultFile);
1839 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1840 strFile.setNull();
1841 }
1842
1843 i_setModified(IsModified_MachineData);
1844 mHWData.backup();
1845 mHWData->mVideoCaptureFile = strFile;
1846
1847 return S_OK;
1848}
1849
1850HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1851{
1852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1853 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1854 return S_OK;
1855}
1856
1857HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1858{
1859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1860
1861 if ( Global::IsOnline(mData->mMachineState)
1862 && mHWData->mVideoCaptureEnabled)
1863 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1864
1865 i_setModified(IsModified_MachineData);
1866 mHWData.backup();
1867 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1868
1869 return S_OK;
1870}
1871
1872HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1873{
1874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1875 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1876 return S_OK;
1877}
1878
1879HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1880{
1881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1882
1883 if ( Global::IsOnline(mData->mMachineState)
1884 && mHWData->mVideoCaptureEnabled)
1885 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1886
1887 i_setModified(IsModified_MachineData);
1888 mHWData.backup();
1889 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1890
1891 return S_OK;
1892}
1893
1894HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1895{
1896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1897 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1898 return S_OK;
1899}
1900
1901HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1902{
1903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1904
1905 if ( Global::IsOnline(mData->mMachineState)
1906 && mHWData->mVideoCaptureEnabled)
1907 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1908
1909 i_setModified(IsModified_MachineData);
1910 mHWData.backup();
1911 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1912
1913 return S_OK;
1914}
1915
1916HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1917{
1918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1919 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1920 return S_OK;
1921}
1922
1923HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1924{
1925 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1926
1927 if ( Global::IsOnline(mData->mMachineState)
1928 && mHWData->mVideoCaptureEnabled)
1929 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1930
1931 i_setModified(IsModified_MachineData);
1932 mHWData.backup();
1933 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1934
1935 return S_OK;
1936}
1937
1938HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1939{
1940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1941 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1942 return S_OK;
1943}
1944
1945HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1946{
1947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1948
1949 if ( Global::IsOnline(mData->mMachineState)
1950 && mHWData->mVideoCaptureEnabled)
1951 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1952
1953 i_setModified(IsModified_MachineData);
1954 mHWData.backup();
1955 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1956
1957 return S_OK;
1958}
1959
1960HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1961{
1962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1963 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1964 return S_OK;
1965}
1966
1967HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1968{
1969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1970
1971 if ( Global::IsOnline(mData->mMachineState)
1972 && mHWData->mVideoCaptureEnabled)
1973 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1974
1975 i_setModified(IsModified_MachineData);
1976 mHWData.backup();
1977 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1978
1979 return S_OK;
1980}
1981
1982HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1983{
1984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1985
1986 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1987 return S_OK;
1988}
1989
1990HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1991{
1992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1993
1994 if ( Global::IsOnline(mData->mMachineState)
1995 && mHWData->mVideoCaptureEnabled)
1996 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1997
1998 i_setModified(IsModified_MachineData);
1999 mHWData.backup();
2000 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
2001
2002 return S_OK;
2003}
2004
2005HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
2006{
2007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2008
2009 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
2010
2011 return S_OK;
2012}
2013
2014HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2015{
2016 switch (aGraphicsControllerType)
2017 {
2018 case GraphicsControllerType_Null:
2019 case GraphicsControllerType_VBoxVGA:
2020#ifdef VBOX_WITH_VMSVGA
2021 case GraphicsControllerType_VMSVGA:
2022#endif
2023 break;
2024 default:
2025 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2026 }
2027
2028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2029
2030 HRESULT rc = i_checkStateDependency(MutableStateDep);
2031 if (FAILED(rc)) return rc;
2032
2033 i_setModified(IsModified_MachineData);
2034 mHWData.backup();
2035 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2036
2037 return S_OK;
2038}
2039
2040HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2041{
2042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2043
2044 *aVRAMSize = mHWData->mVRAMSize;
2045
2046 return S_OK;
2047}
2048
2049HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2050{
2051 /* check VRAM limits */
2052 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2053 return setError(E_INVALIDARG,
2054 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2055 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2056
2057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 HRESULT rc = i_checkStateDependency(MutableStateDep);
2060 if (FAILED(rc)) return rc;
2061
2062 i_setModified(IsModified_MachineData);
2063 mHWData.backup();
2064 mHWData->mVRAMSize = aVRAMSize;
2065
2066 return S_OK;
2067}
2068
2069/** @todo this method should not be public */
2070HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2071{
2072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2073
2074 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2075
2076 return S_OK;
2077}
2078
2079/**
2080 * Set the memory balloon size.
2081 *
2082 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2083 * we have to make sure that we never call IGuest from here.
2084 */
2085HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2086{
2087 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2088#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2089 /* check limits */
2090 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2091 return setError(E_INVALIDARG,
2092 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2093 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2094
2095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2096
2097 i_setModified(IsModified_MachineData);
2098 mHWData.backup();
2099 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2100
2101 return S_OK;
2102#else
2103 NOREF(aMemoryBalloonSize);
2104 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2105#endif
2106}
2107
2108HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2109{
2110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2111
2112 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2113 return S_OK;
2114}
2115
2116HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2117{
2118#ifdef VBOX_WITH_PAGE_SHARING
2119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2120
2121 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2122 i_setModified(IsModified_MachineData);
2123 mHWData.backup();
2124 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2125 return S_OK;
2126#else
2127 NOREF(aPageFusionEnabled);
2128 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2129#endif
2130}
2131
2132HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2133{
2134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2135
2136 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2137
2138 return S_OK;
2139}
2140
2141HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2142{
2143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2144
2145 HRESULT rc = i_checkStateDependency(MutableStateDep);
2146 if (FAILED(rc)) return rc;
2147
2148 /** @todo check validity! */
2149
2150 i_setModified(IsModified_MachineData);
2151 mHWData.backup();
2152 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2153
2154 return S_OK;
2155}
2156
2157
2158HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2159{
2160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2161
2162 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2163
2164 return S_OK;
2165}
2166
2167HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2168{
2169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2170
2171 HRESULT rc = i_checkStateDependency(MutableStateDep);
2172 if (FAILED(rc)) return rc;
2173
2174 /** @todo check validity! */
2175 i_setModified(IsModified_MachineData);
2176 mHWData.backup();
2177 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2178
2179 return S_OK;
2180}
2181
2182HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2183{
2184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2185
2186 *aMonitorCount = mHWData->mMonitorCount;
2187
2188 return S_OK;
2189}
2190
2191HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2192{
2193 /* make sure monitor count is a sensible number */
2194 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2195 return setError(E_INVALIDARG,
2196 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2197 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2198
2199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2200
2201 HRESULT rc = i_checkStateDependency(MutableStateDep);
2202 if (FAILED(rc)) return rc;
2203
2204 i_setModified(IsModified_MachineData);
2205 mHWData.backup();
2206 mHWData->mMonitorCount = aMonitorCount;
2207
2208 return S_OK;
2209}
2210
2211HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2212{
2213 /* mBIOSSettings is constant during life time, no need to lock */
2214 aBIOSSettings = mBIOSSettings;
2215
2216 return S_OK;
2217}
2218
2219HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2220{
2221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2222
2223 switch (aProperty)
2224 {
2225 case CPUPropertyType_PAE:
2226 *aValue = mHWData->mPAEEnabled;
2227 break;
2228
2229 case CPUPropertyType_LongMode:
2230 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2231 *aValue = TRUE;
2232 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2233 *aValue = FALSE;
2234#if HC_ARCH_BITS == 64
2235 else
2236 *aValue = TRUE;
2237#else
2238 else
2239 {
2240 *aValue = FALSE;
2241
2242 ComObjPtr<GuestOSType> pGuestOSType;
2243 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2244 pGuestOSType);
2245 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2246 {
2247 if (pGuestOSType->i_is64Bit())
2248 {
2249 ComObjPtr<Host> pHost = mParent->i_host();
2250 alock.release();
2251
2252 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2253 if (FAILED(hrc2))
2254 *aValue = FALSE;
2255 }
2256 }
2257 }
2258#endif
2259 break;
2260
2261 case CPUPropertyType_TripleFaultReset:
2262 *aValue = mHWData->mTripleFaultReset;
2263 break;
2264
2265 case CPUPropertyType_APIC:
2266 *aValue = mHWData->mAPIC;
2267 break;
2268
2269 case CPUPropertyType_X2APIC:
2270 *aValue = mHWData->mX2APIC;
2271 break;
2272
2273 case CPUPropertyType_IBPBOnVMExit:
2274 *aValue = mHWData->mIBPBOnVMExit;
2275 break;
2276
2277 case CPUPropertyType_IBPBOnVMEntry:
2278 *aValue = mHWData->mIBPBOnVMEntry;
2279 break;
2280
2281 case CPUPropertyType_SpecCtrl:
2282 *aValue = mHWData->mSpecCtrl;
2283 break;
2284
2285 case CPUPropertyType_SpecCtrlByHost:
2286 *aValue = mHWData->mSpecCtrlByHost;
2287 break;
2288
2289 case CPUPropertyType_HWVirt:
2290 *aValue = mHWData->mNestedHWVirt;
2291 break;
2292
2293 default:
2294 return E_INVALIDARG;
2295 }
2296 return S_OK;
2297}
2298
2299HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2300{
2301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2302
2303 HRESULT rc = i_checkStateDependency(MutableStateDep);
2304 if (FAILED(rc)) return rc;
2305
2306 switch (aProperty)
2307 {
2308 case CPUPropertyType_PAE:
2309 i_setModified(IsModified_MachineData);
2310 mHWData.backup();
2311 mHWData->mPAEEnabled = !!aValue;
2312 break;
2313
2314 case CPUPropertyType_LongMode:
2315 i_setModified(IsModified_MachineData);
2316 mHWData.backup();
2317 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2318 break;
2319
2320 case CPUPropertyType_TripleFaultReset:
2321 i_setModified(IsModified_MachineData);
2322 mHWData.backup();
2323 mHWData->mTripleFaultReset = !!aValue;
2324 break;
2325
2326 case CPUPropertyType_APIC:
2327 if (mHWData->mX2APIC)
2328 aValue = TRUE;
2329 i_setModified(IsModified_MachineData);
2330 mHWData.backup();
2331 mHWData->mAPIC = !!aValue;
2332 break;
2333
2334 case CPUPropertyType_X2APIC:
2335 i_setModified(IsModified_MachineData);
2336 mHWData.backup();
2337 mHWData->mX2APIC = !!aValue;
2338 if (aValue)
2339 mHWData->mAPIC = !!aValue;
2340 break;
2341
2342 case CPUPropertyType_IBPBOnVMExit:
2343 i_setModified(IsModified_MachineData);
2344 mHWData.backup();
2345 mHWData->mIBPBOnVMExit = !!aValue;
2346 break;
2347
2348 case CPUPropertyType_IBPBOnVMEntry:
2349 i_setModified(IsModified_MachineData);
2350 mHWData.backup();
2351 mHWData->mIBPBOnVMEntry = !!aValue;
2352 break;
2353
2354 case CPUPropertyType_SpecCtrl:
2355 i_setModified(IsModified_MachineData);
2356 mHWData.backup();
2357 mHWData->mSpecCtrl = !!aValue;
2358 break;
2359
2360 case CPUPropertyType_SpecCtrlByHost:
2361 i_setModified(IsModified_MachineData);
2362 mHWData.backup();
2363 mHWData->mSpecCtrlByHost = !!aValue;
2364 break;
2365
2366 case CPUPropertyType_HWVirt:
2367 i_setModified(IsModified_MachineData);
2368 mHWData.backup();
2369 mHWData->mNestedHWVirt = !!aValue;
2370 break;
2371
2372 default:
2373 return E_INVALIDARG;
2374 }
2375 return S_OK;
2376}
2377
2378HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2379 ULONG *aValEcx, ULONG *aValEdx)
2380{
2381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2382 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2383 {
2384 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2385 it != mHWData->mCpuIdLeafList.end();
2386 ++it)
2387 {
2388 if (aOrdinal == 0)
2389 {
2390 const settings::CpuIdLeaf &rLeaf= *it;
2391 *aIdx = rLeaf.idx;
2392 *aSubIdx = rLeaf.idxSub;
2393 *aValEax = rLeaf.uEax;
2394 *aValEbx = rLeaf.uEbx;
2395 *aValEcx = rLeaf.uEcx;
2396 *aValEdx = rLeaf.uEdx;
2397 return S_OK;
2398 }
2399 aOrdinal--;
2400 }
2401 }
2402 return E_INVALIDARG;
2403}
2404
2405HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2406{
2407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2408
2409 /*
2410 * Search the list.
2411 */
2412 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2413 {
2414 const settings::CpuIdLeaf &rLeaf= *it;
2415 if ( rLeaf.idx == aIdx
2416 && ( aSubIdx == UINT32_MAX
2417 || rLeaf.idxSub == aSubIdx) )
2418 {
2419 *aValEax = rLeaf.uEax;
2420 *aValEbx = rLeaf.uEbx;
2421 *aValEcx = rLeaf.uEcx;
2422 *aValEdx = rLeaf.uEdx;
2423 return S_OK;
2424 }
2425 }
2426
2427 return E_INVALIDARG;
2428}
2429
2430
2431HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2432{
2433 /*
2434 * Validate input before taking locks and checking state.
2435 */
2436 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2437 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2438 if ( aIdx >= UINT32_C(0x20)
2439 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2440 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2441 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2442
2443 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2444 HRESULT rc = i_checkStateDependency(MutableStateDep);
2445 if (FAILED(rc)) return rc;
2446
2447 /*
2448 * Impose a maximum number of leaves.
2449 */
2450 if (mHWData->mCpuIdLeafList.size() > 256)
2451 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2452
2453 /*
2454 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2455 */
2456 i_setModified(IsModified_MachineData);
2457 mHWData.backup();
2458
2459 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2460 {
2461 settings::CpuIdLeaf &rLeaf= *it;
2462 if ( rLeaf.idx == aIdx
2463 && ( aSubIdx == UINT32_MAX
2464 || rLeaf.idxSub == aSubIdx) )
2465 it = mHWData->mCpuIdLeafList.erase(it);
2466 else
2467 ++it;
2468 }
2469
2470 settings::CpuIdLeaf NewLeaf;
2471 NewLeaf.idx = aIdx;
2472 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2473 NewLeaf.uEax = aValEax;
2474 NewLeaf.uEbx = aValEbx;
2475 NewLeaf.uEcx = aValEcx;
2476 NewLeaf.uEdx = aValEdx;
2477 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2478 return S_OK;
2479}
2480
2481HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2482{
2483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2484
2485 HRESULT rc = i_checkStateDependency(MutableStateDep);
2486 if (FAILED(rc)) return rc;
2487
2488 /*
2489 * Do the removal.
2490 */
2491 bool fModified = false;
2492 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2493 {
2494 settings::CpuIdLeaf &rLeaf= *it;
2495 if ( rLeaf.idx == aIdx
2496 && ( aSubIdx == UINT32_MAX
2497 || rLeaf.idxSub == aSubIdx) )
2498 {
2499 if (!fModified)
2500 {
2501 fModified = true;
2502 i_setModified(IsModified_MachineData);
2503 mHWData.backup();
2504 }
2505 it = mHWData->mCpuIdLeafList.erase(it);
2506 }
2507 else
2508 ++it;
2509 }
2510
2511 return S_OK;
2512}
2513
2514HRESULT Machine::removeAllCPUIDLeaves()
2515{
2516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2517
2518 HRESULT rc = i_checkStateDependency(MutableStateDep);
2519 if (FAILED(rc)) return rc;
2520
2521 if (mHWData->mCpuIdLeafList.size() > 0)
2522 {
2523 i_setModified(IsModified_MachineData);
2524 mHWData.backup();
2525
2526 mHWData->mCpuIdLeafList.clear();
2527 }
2528
2529 return S_OK;
2530}
2531HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2532{
2533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2534
2535 switch(aProperty)
2536 {
2537 case HWVirtExPropertyType_Enabled:
2538 *aValue = mHWData->mHWVirtExEnabled;
2539 break;
2540
2541 case HWVirtExPropertyType_VPID:
2542 *aValue = mHWData->mHWVirtExVPIDEnabled;
2543 break;
2544
2545 case HWVirtExPropertyType_NestedPaging:
2546 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2547 break;
2548
2549 case HWVirtExPropertyType_UnrestrictedExecution:
2550 *aValue = mHWData->mHWVirtExUXEnabled;
2551 break;
2552
2553 case HWVirtExPropertyType_LargePages:
2554 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2555#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2556 *aValue = FALSE;
2557#endif
2558 break;
2559
2560 case HWVirtExPropertyType_Force:
2561 *aValue = mHWData->mHWVirtExForceEnabled;
2562 break;
2563
2564 case HWVirtExPropertyType_UseNativeApi:
2565 *aValue = mHWData->mHWVirtExUseNativeApi;
2566 break;
2567
2568 default:
2569 return E_INVALIDARG;
2570 }
2571 return S_OK;
2572}
2573
2574HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2575{
2576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2577
2578 HRESULT rc = i_checkStateDependency(MutableStateDep);
2579 if (FAILED(rc)) return rc;
2580
2581 switch (aProperty)
2582 {
2583 case HWVirtExPropertyType_Enabled:
2584 i_setModified(IsModified_MachineData);
2585 mHWData.backup();
2586 mHWData->mHWVirtExEnabled = !!aValue;
2587 break;
2588
2589 case HWVirtExPropertyType_VPID:
2590 i_setModified(IsModified_MachineData);
2591 mHWData.backup();
2592 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2593 break;
2594
2595 case HWVirtExPropertyType_NestedPaging:
2596 i_setModified(IsModified_MachineData);
2597 mHWData.backup();
2598 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2599 break;
2600
2601 case HWVirtExPropertyType_UnrestrictedExecution:
2602 i_setModified(IsModified_MachineData);
2603 mHWData.backup();
2604 mHWData->mHWVirtExUXEnabled = !!aValue;
2605 break;
2606
2607 case HWVirtExPropertyType_LargePages:
2608 i_setModified(IsModified_MachineData);
2609 mHWData.backup();
2610 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2611 break;
2612
2613 case HWVirtExPropertyType_Force:
2614 i_setModified(IsModified_MachineData);
2615 mHWData.backup();
2616 mHWData->mHWVirtExForceEnabled = !!aValue;
2617 break;
2618
2619 case HWVirtExPropertyType_UseNativeApi:
2620 i_setModified(IsModified_MachineData);
2621 mHWData.backup();
2622 mHWData->mHWVirtExUseNativeApi = !!aValue;
2623 break;
2624
2625 default:
2626 return E_INVALIDARG;
2627 }
2628
2629 return S_OK;
2630}
2631
2632HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2633{
2634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2635
2636 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2637
2638 return S_OK;
2639}
2640
2641HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2642{
2643 /** @todo (r=dmik):
2644 * 1. Allow to change the name of the snapshot folder containing snapshots
2645 * 2. Rename the folder on disk instead of just changing the property
2646 * value (to be smart and not to leave garbage). Note that it cannot be
2647 * done here because the change may be rolled back. Thus, the right
2648 * place is #saveSettings().
2649 */
2650
2651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2652
2653 HRESULT rc = i_checkStateDependency(MutableStateDep);
2654 if (FAILED(rc)) return rc;
2655
2656 if (!mData->mCurrentSnapshot.isNull())
2657 return setError(E_FAIL,
2658 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2659
2660 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2661
2662 if (strSnapshotFolder.isEmpty())
2663 strSnapshotFolder = "Snapshots";
2664 int vrc = i_calculateFullPath(strSnapshotFolder,
2665 strSnapshotFolder);
2666 if (RT_FAILURE(vrc))
2667 return setErrorBoth(E_FAIL, vrc,
2668 tr("Invalid snapshot folder '%s' (%Rrc)"),
2669 strSnapshotFolder.c_str(), vrc);
2670
2671 i_setModified(IsModified_MachineData);
2672 mUserData.backup();
2673
2674 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2675
2676 return S_OK;
2677}
2678
2679HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2680{
2681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2682
2683 aMediumAttachments.resize(mMediumAttachments->size());
2684 size_t i = 0;
2685 for (MediumAttachmentList::const_iterator
2686 it = mMediumAttachments->begin();
2687 it != mMediumAttachments->end();
2688 ++it, ++i)
2689 aMediumAttachments[i] = *it;
2690
2691 return S_OK;
2692}
2693
2694HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2695{
2696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2697
2698 Assert(!!mVRDEServer);
2699
2700 aVRDEServer = mVRDEServer;
2701
2702 return S_OK;
2703}
2704
2705HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2706{
2707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2708
2709 aAudioAdapter = mAudioAdapter;
2710
2711 return S_OK;
2712}
2713
2714HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2715{
2716#ifdef VBOX_WITH_VUSB
2717 clearError();
2718 MultiResult rc(S_OK);
2719
2720# ifdef VBOX_WITH_USB
2721 rc = mParent->i_host()->i_checkUSBProxyService();
2722 if (FAILED(rc)) return rc;
2723# endif
2724
2725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2726
2727 aUSBControllers.resize(mUSBControllers->size());
2728 size_t i = 0;
2729 for (USBControllerList::const_iterator
2730 it = mUSBControllers->begin();
2731 it != mUSBControllers->end();
2732 ++it, ++i)
2733 aUSBControllers[i] = *it;
2734
2735 return S_OK;
2736#else
2737 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2738 * extended error info to indicate that USB is simply not available
2739 * (w/o treating it as a failure), for example, as in OSE */
2740 NOREF(aUSBControllers);
2741 ReturnComNotImplemented();
2742#endif /* VBOX_WITH_VUSB */
2743}
2744
2745HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2746{
2747#ifdef VBOX_WITH_VUSB
2748 clearError();
2749 MultiResult rc(S_OK);
2750
2751# ifdef VBOX_WITH_USB
2752 rc = mParent->i_host()->i_checkUSBProxyService();
2753 if (FAILED(rc)) return rc;
2754# endif
2755
2756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2757
2758 aUSBDeviceFilters = mUSBDeviceFilters;
2759 return rc;
2760#else
2761 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2762 * extended error info to indicate that USB is simply not available
2763 * (w/o treating it as a failure), for example, as in OSE */
2764 NOREF(aUSBDeviceFilters);
2765 ReturnComNotImplemented();
2766#endif /* VBOX_WITH_VUSB */
2767}
2768
2769HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2770{
2771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2772
2773 aSettingsFilePath = mData->m_strConfigFileFull;
2774
2775 return S_OK;
2776}
2777
2778HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2779{
2780 RT_NOREF(aSettingsFilePath);
2781 ReturnComNotImplemented();
2782}
2783
2784HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2785{
2786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2787
2788 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2789 if (FAILED(rc)) return rc;
2790
2791 if (!mData->pMachineConfigFile->fileExists())
2792 // this is a new machine, and no config file exists yet:
2793 *aSettingsModified = TRUE;
2794 else
2795 *aSettingsModified = (mData->flModifications != 0);
2796
2797 return S_OK;
2798}
2799
2800HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2801{
2802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2803
2804 *aSessionState = mData->mSession.mState;
2805
2806 return S_OK;
2807}
2808
2809HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2810{
2811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2812
2813 aSessionName = mData->mSession.mName;
2814
2815 return S_OK;
2816}
2817
2818HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2819{
2820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 *aSessionPID = mData->mSession.mPID;
2823
2824 return S_OK;
2825}
2826
2827HRESULT Machine::getState(MachineState_T *aState)
2828{
2829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2830
2831 *aState = mData->mMachineState;
2832 Assert(mData->mMachineState != MachineState_Null);
2833
2834 return S_OK;
2835}
2836
2837HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2838{
2839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2840
2841 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2842
2843 return S_OK;
2844}
2845
2846HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2847{
2848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2849
2850 aStateFilePath = mSSData->strStateFilePath;
2851
2852 return S_OK;
2853}
2854
2855HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2856{
2857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2858
2859 i_getLogFolder(aLogFolder);
2860
2861 return S_OK;
2862}
2863
2864HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2865{
2866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2867
2868 aCurrentSnapshot = mData->mCurrentSnapshot;
2869
2870 return S_OK;
2871}
2872
2873HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2874{
2875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2876
2877 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2878 ? 0
2879 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2880
2881 return S_OK;
2882}
2883
2884HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2885{
2886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2887
2888 /* Note: for machines with no snapshots, we always return FALSE
2889 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2890 * reasons :) */
2891
2892 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2893 ? FALSE
2894 : mData->mCurrentStateModified;
2895
2896 return S_OK;
2897}
2898
2899HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2900{
2901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2902
2903 aSharedFolders.resize(mHWData->mSharedFolders.size());
2904 size_t i = 0;
2905 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2906 it = mHWData->mSharedFolders.begin();
2907 it != mHWData->mSharedFolders.end();
2908 ++it, ++i)
2909 aSharedFolders[i] = *it;
2910
2911 return S_OK;
2912}
2913
2914HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2915{
2916 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2917
2918 *aClipboardMode = mHWData->mClipboardMode;
2919
2920 return S_OK;
2921}
2922
2923HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2924{
2925 HRESULT rc = S_OK;
2926
2927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 alock.release();
2930 rc = i_onClipboardModeChange(aClipboardMode);
2931 alock.acquire();
2932 if (FAILED(rc)) return rc;
2933
2934 i_setModified(IsModified_MachineData);
2935 mHWData.backup();
2936 mHWData->mClipboardMode = aClipboardMode;
2937
2938 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2939 if (Global::IsOnline(mData->mMachineState))
2940 i_saveSettings(NULL);
2941
2942 return S_OK;
2943}
2944
2945HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2946{
2947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2948
2949 *aDnDMode = mHWData->mDnDMode;
2950
2951 return S_OK;
2952}
2953
2954HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2955{
2956 HRESULT rc = S_OK;
2957
2958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2959
2960 alock.release();
2961 rc = i_onDnDModeChange(aDnDMode);
2962
2963 alock.acquire();
2964 if (FAILED(rc)) return rc;
2965
2966 i_setModified(IsModified_MachineData);
2967 mHWData.backup();
2968 mHWData->mDnDMode = aDnDMode;
2969
2970 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2971 if (Global::IsOnline(mData->mMachineState))
2972 i_saveSettings(NULL);
2973
2974 return S_OK;
2975}
2976
2977HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2978{
2979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2980
2981 aStorageControllers.resize(mStorageControllers->size());
2982 size_t i = 0;
2983 for (StorageControllerList::const_iterator
2984 it = mStorageControllers->begin();
2985 it != mStorageControllers->end();
2986 ++it, ++i)
2987 aStorageControllers[i] = *it;
2988
2989 return S_OK;
2990}
2991
2992HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2993{
2994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2995
2996 *aEnabled = mUserData->s.fTeleporterEnabled;
2997
2998 return S_OK;
2999}
3000
3001HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3002{
3003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3004
3005 /* Only allow it to be set to true when PoweredOff or Aborted.
3006 (Clearing it is always permitted.) */
3007 if ( aTeleporterEnabled
3008 && mData->mRegistered
3009 && ( !i_isSessionMachine()
3010 || ( mData->mMachineState != MachineState_PoweredOff
3011 && mData->mMachineState != MachineState_Teleported
3012 && mData->mMachineState != MachineState_Aborted
3013 )
3014 )
3015 )
3016 return setError(VBOX_E_INVALID_VM_STATE,
3017 tr("The machine is not powered off (state is %s)"),
3018 Global::stringifyMachineState(mData->mMachineState));
3019
3020 i_setModified(IsModified_MachineData);
3021 mUserData.backup();
3022 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3023
3024 return S_OK;
3025}
3026
3027HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3028{
3029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3030
3031 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3032
3033 return S_OK;
3034}
3035
3036HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3037{
3038 if (aTeleporterPort >= _64K)
3039 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3040
3041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3042
3043 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3044 if (FAILED(rc)) return rc;
3045
3046 i_setModified(IsModified_MachineData);
3047 mUserData.backup();
3048 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3049
3050 return S_OK;
3051}
3052
3053HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3054{
3055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3056
3057 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3058
3059 return S_OK;
3060}
3061
3062HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3063{
3064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3065
3066 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3067 if (FAILED(rc)) return rc;
3068
3069 i_setModified(IsModified_MachineData);
3070 mUserData.backup();
3071 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3072
3073 return S_OK;
3074}
3075
3076HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3077{
3078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3079 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3080
3081 return S_OK;
3082}
3083
3084HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3085{
3086 /*
3087 * Hash the password first.
3088 */
3089 com::Utf8Str aT = aTeleporterPassword;
3090
3091 if (!aT.isEmpty())
3092 {
3093 if (VBoxIsPasswordHashed(&aT))
3094 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3095 VBoxHashPassword(&aT);
3096 }
3097
3098 /*
3099 * Do the update.
3100 */
3101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3102 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3103 if (SUCCEEDED(hrc))
3104 {
3105 i_setModified(IsModified_MachineData);
3106 mUserData.backup();
3107 mUserData->s.strTeleporterPassword = aT;
3108 }
3109
3110 return hrc;
3111}
3112
3113HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3114{
3115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3116
3117 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3118 return S_OK;
3119}
3120
3121HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3122{
3123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3124
3125 /** @todo deal with running state change. */
3126 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3127 if (FAILED(rc)) return rc;
3128
3129 i_setModified(IsModified_MachineData);
3130 mUserData.backup();
3131 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3132 return S_OK;
3133}
3134
3135HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3136{
3137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3140 return S_OK;
3141}
3142
3143HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3144{
3145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3146
3147 /** @todo deal with running state change. */
3148 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3149 if (FAILED(rc)) return rc;
3150
3151 i_setModified(IsModified_MachineData);
3152 mUserData.backup();
3153 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3154 return S_OK;
3155}
3156
3157HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3158{
3159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3160
3161 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3162 return S_OK;
3163}
3164
3165HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3166{
3167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3168
3169 /** @todo deal with running state change. */
3170 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3171 if (FAILED(rc)) return rc;
3172
3173 i_setModified(IsModified_MachineData);
3174 mUserData.backup();
3175 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3176 return S_OK;
3177}
3178
3179HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3180{
3181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3182
3183 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3184
3185 return S_OK;
3186}
3187
3188HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3189{
3190 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3191
3192 /** @todo deal with running state change. */
3193 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3194 if (FAILED(rc)) return rc;
3195
3196 i_setModified(IsModified_MachineData);
3197 mUserData.backup();
3198 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3199
3200 return S_OK;
3201}
3202
3203HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3204{
3205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3206
3207 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3208 return S_OK;
3209}
3210
3211HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3212{
3213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3214
3215 /** @todo deal with running state change. */
3216 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3217 if (FAILED(rc)) return rc;
3218
3219 i_setModified(IsModified_MachineData);
3220 mUserData.backup();
3221 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3222 return S_OK;
3223}
3224
3225HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3226{
3227 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3228
3229 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3230
3231 return S_OK;
3232}
3233
3234HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3235{
3236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3237
3238 /* Only allow it to be set to true when PoweredOff or Aborted.
3239 (Clearing it is always permitted.) */
3240 if ( aRTCUseUTC
3241 && mData->mRegistered
3242 && ( !i_isSessionMachine()
3243 || ( mData->mMachineState != MachineState_PoweredOff
3244 && mData->mMachineState != MachineState_Teleported
3245 && mData->mMachineState != MachineState_Aborted
3246 )
3247 )
3248 )
3249 return setError(VBOX_E_INVALID_VM_STATE,
3250 tr("The machine is not powered off (state is %s)"),
3251 Global::stringifyMachineState(mData->mMachineState));
3252
3253 i_setModified(IsModified_MachineData);
3254 mUserData.backup();
3255 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3256
3257 return S_OK;
3258}
3259
3260HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3261{
3262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3263
3264 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3265
3266 return S_OK;
3267}
3268
3269HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3270{
3271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3272
3273 HRESULT rc = i_checkStateDependency(MutableStateDep);
3274 if (FAILED(rc)) return rc;
3275
3276 i_setModified(IsModified_MachineData);
3277 mHWData.backup();
3278 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3279
3280 return S_OK;
3281}
3282
3283HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3284{
3285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3286
3287 *aIOCacheSize = mHWData->mIOCacheSize;
3288
3289 return S_OK;
3290}
3291
3292HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3293{
3294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3295
3296 HRESULT rc = i_checkStateDependency(MutableStateDep);
3297 if (FAILED(rc)) return rc;
3298
3299 i_setModified(IsModified_MachineData);
3300 mHWData.backup();
3301 mHWData->mIOCacheSize = aIOCacheSize;
3302
3303 return S_OK;
3304}
3305
3306
3307/**
3308 * @note Locks objects!
3309 */
3310HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3311 LockType_T aLockType)
3312{
3313 /* check the session state */
3314 SessionState_T state;
3315 HRESULT rc = aSession->COMGETTER(State)(&state);
3316 if (FAILED(rc)) return rc;
3317
3318 if (state != SessionState_Unlocked)
3319 return setError(VBOX_E_INVALID_OBJECT_STATE,
3320 tr("The given session is busy"));
3321
3322 // get the client's IInternalSessionControl interface
3323 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3324 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3325 E_INVALIDARG);
3326
3327 // session name (only used in some code paths)
3328 Utf8Str strSessionName;
3329
3330 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3331
3332 if (!mData->mRegistered)
3333 return setError(E_UNEXPECTED,
3334 tr("The machine '%s' is not registered"),
3335 mUserData->s.strName.c_str());
3336
3337 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3338
3339 SessionState_T oldState = mData->mSession.mState;
3340 /* Hack: in case the session is closing and there is a progress object
3341 * which allows waiting for the session to be closed, take the opportunity
3342 * and do a limited wait (max. 1 second). This helps a lot when the system
3343 * is busy and thus session closing can take a little while. */
3344 if ( mData->mSession.mState == SessionState_Unlocking
3345 && mData->mSession.mProgress)
3346 {
3347 alock.release();
3348 mData->mSession.mProgress->WaitForCompletion(1000);
3349 alock.acquire();
3350 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3351 }
3352
3353 // try again now
3354 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3355 // (i.e. session machine exists)
3356 && (aLockType == LockType_Shared) // caller wants a shared link to the
3357 // existing session that holds the write lock:
3358 )
3359 {
3360 // OK, share the session... we are now dealing with three processes:
3361 // 1) VBoxSVC (where this code runs);
3362 // 2) process C: the caller's client process (who wants a shared session);
3363 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3364
3365 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3366 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3367 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3368 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3369 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3370
3371 /*
3372 * Release the lock before calling the client process. It's safe here
3373 * since the only thing to do after we get the lock again is to add
3374 * the remote control to the list (which doesn't directly influence
3375 * anything).
3376 */
3377 alock.release();
3378
3379 // get the console of the session holding the write lock (this is a remote call)
3380 ComPtr<IConsole> pConsoleW;
3381 if (mData->mSession.mLockType == LockType_VM)
3382 {
3383 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3384 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3385 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3386 if (FAILED(rc))
3387 // the failure may occur w/o any error info (from RPC), so provide one
3388 return setError(VBOX_E_VM_ERROR,
3389 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3390 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3391 }
3392
3393 // share the session machine and W's console with the caller's session
3394 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3395 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3396 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3397
3398 if (FAILED(rc))
3399 // the failure may occur w/o any error info (from RPC), so provide one
3400 return setError(VBOX_E_VM_ERROR,
3401 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3402 alock.acquire();
3403
3404 // need to revalidate the state after acquiring the lock again
3405 if (mData->mSession.mState != SessionState_Locked)
3406 {
3407 pSessionControl->Uninitialize();
3408 return setError(VBOX_E_INVALID_SESSION_STATE,
3409 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3410 mUserData->s.strName.c_str());
3411 }
3412
3413 // add the caller's session to the list
3414 mData->mSession.mRemoteControls.push_back(pSessionControl);
3415 }
3416 else if ( mData->mSession.mState == SessionState_Locked
3417 || mData->mSession.mState == SessionState_Unlocking
3418 )
3419 {
3420 // sharing not permitted, or machine still unlocking:
3421 return setError(VBOX_E_INVALID_OBJECT_STATE,
3422 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3423 mUserData->s.strName.c_str());
3424 }
3425 else
3426 {
3427 // machine is not locked: then write-lock the machine (create the session machine)
3428
3429 // must not be busy
3430 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3431
3432 // get the caller's session PID
3433 RTPROCESS pid = NIL_RTPROCESS;
3434 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3435 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3436 Assert(pid != NIL_RTPROCESS);
3437
3438 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3439
3440 if (fLaunchingVMProcess)
3441 {
3442 if (mData->mSession.mPID == NIL_RTPROCESS)
3443 {
3444 // two or more clients racing for a lock, the one which set the
3445 // session state to Spawning will win, the others will get an
3446 // error as we can't decide here if waiting a little would help
3447 // (only for shared locks this would avoid an error)
3448 return setError(VBOX_E_INVALID_OBJECT_STATE,
3449 tr("The machine '%s' already has a lock request pending"),
3450 mUserData->s.strName.c_str());
3451 }
3452
3453 // this machine is awaiting for a spawning session to be opened:
3454 // then the calling process must be the one that got started by
3455 // LaunchVMProcess()
3456
3457 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3458 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3459
3460#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3461 /* Hardened windows builds spawns three processes when a VM is
3462 launched, the 3rd one is the one that will end up here. */
3463 RTPROCESS ppid;
3464 int rc = RTProcQueryParent(pid, &ppid);
3465 if (RT_SUCCESS(rc))
3466 rc = RTProcQueryParent(ppid, &ppid);
3467 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3468 || rc == VERR_ACCESS_DENIED)
3469 {
3470 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3471 mData->mSession.mPID = pid;
3472 }
3473#endif
3474
3475 if (mData->mSession.mPID != pid)
3476 return setError(E_ACCESSDENIED,
3477 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3478 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3479 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3480 }
3481
3482 // create the mutable SessionMachine from the current machine
3483 ComObjPtr<SessionMachine> sessionMachine;
3484 sessionMachine.createObject();
3485 rc = sessionMachine->init(this);
3486 AssertComRC(rc);
3487
3488 /* NOTE: doing return from this function after this point but
3489 * before the end is forbidden since it may call SessionMachine::uninit()
3490 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3491 * lock while still holding the Machine lock in alock so that a deadlock
3492 * is possible due to the wrong lock order. */
3493
3494 if (SUCCEEDED(rc))
3495 {
3496 /*
3497 * Set the session state to Spawning to protect against subsequent
3498 * attempts to open a session and to unregister the machine after
3499 * we release the lock.
3500 */
3501 SessionState_T origState = mData->mSession.mState;
3502 mData->mSession.mState = SessionState_Spawning;
3503
3504#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3505 /* Get the client token ID to be passed to the client process */
3506 Utf8Str strTokenId;
3507 sessionMachine->i_getTokenId(strTokenId);
3508 Assert(!strTokenId.isEmpty());
3509#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3510 /* Get the client token to be passed to the client process */
3511 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3512 /* The token is now "owned" by pToken, fix refcount */
3513 if (!pToken.isNull())
3514 pToken->Release();
3515#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3516
3517 /*
3518 * Release the lock before calling the client process -- it will call
3519 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3520 * because the state is Spawning, so that LaunchVMProcess() and
3521 * LockMachine() calls will fail. This method, called before we
3522 * acquire the lock again, will fail because of the wrong PID.
3523 *
3524 * Note that mData->mSession.mRemoteControls accessed outside
3525 * the lock may not be modified when state is Spawning, so it's safe.
3526 */
3527 alock.release();
3528
3529 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3530#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3531 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3532#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3533 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3534 /* Now the token is owned by the client process. */
3535 pToken.setNull();
3536#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3537 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3538
3539 /* The failure may occur w/o any error info (from RPC), so provide one */
3540 if (FAILED(rc))
3541 setError(VBOX_E_VM_ERROR,
3542 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3543
3544 // get session name, either to remember or to compare against
3545 // the already known session name.
3546 {
3547 Bstr bstrSessionName;
3548 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3549 if (SUCCEEDED(rc2))
3550 strSessionName = bstrSessionName;
3551 }
3552
3553 if ( SUCCEEDED(rc)
3554 && fLaunchingVMProcess
3555 )
3556 {
3557 /* complete the remote session initialization */
3558
3559 /* get the console from the direct session */
3560 ComPtr<IConsole> console;
3561 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3562 ComAssertComRC(rc);
3563
3564 if (SUCCEEDED(rc) && !console)
3565 {
3566 ComAssert(!!console);
3567 rc = E_FAIL;
3568 }
3569
3570 /* assign machine & console to the remote session */
3571 if (SUCCEEDED(rc))
3572 {
3573 /*
3574 * after LaunchVMProcess(), the first and the only
3575 * entry in remoteControls is that remote session
3576 */
3577 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3578 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3579 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3580
3581 /* The failure may occur w/o any error info (from RPC), so provide one */
3582 if (FAILED(rc))
3583 setError(VBOX_E_VM_ERROR,
3584 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3585 }
3586
3587 if (FAILED(rc))
3588 pSessionControl->Uninitialize();
3589 }
3590
3591 /* acquire the lock again */
3592 alock.acquire();
3593
3594 /* Restore the session state */
3595 mData->mSession.mState = origState;
3596 }
3597
3598 // finalize spawning anyway (this is why we don't return on errors above)
3599 if (fLaunchingVMProcess)
3600 {
3601 Assert(mData->mSession.mName == strSessionName);
3602 /* Note that the progress object is finalized later */
3603 /** @todo Consider checking mData->mSession.mProgress for cancellation
3604 * around here. */
3605
3606 /* We don't reset mSession.mPID here because it is necessary for
3607 * SessionMachine::uninit() to reap the child process later. */
3608
3609 if (FAILED(rc))
3610 {
3611 /* Close the remote session, remove the remote control from the list
3612 * and reset session state to Closed (@note keep the code in sync
3613 * with the relevant part in checkForSpawnFailure()). */
3614
3615 Assert(mData->mSession.mRemoteControls.size() == 1);
3616 if (mData->mSession.mRemoteControls.size() == 1)
3617 {
3618 ErrorInfoKeeper eik;
3619 mData->mSession.mRemoteControls.front()->Uninitialize();
3620 }
3621
3622 mData->mSession.mRemoteControls.clear();
3623 mData->mSession.mState = SessionState_Unlocked;
3624 }
3625 }
3626 else
3627 {
3628 /* memorize PID of the directly opened session */
3629 if (SUCCEEDED(rc))
3630 mData->mSession.mPID = pid;
3631 }
3632
3633 if (SUCCEEDED(rc))
3634 {
3635 mData->mSession.mLockType = aLockType;
3636 /* memorize the direct session control and cache IUnknown for it */
3637 mData->mSession.mDirectControl = pSessionControl;
3638 mData->mSession.mState = SessionState_Locked;
3639 if (!fLaunchingVMProcess)
3640 mData->mSession.mName = strSessionName;
3641 /* associate the SessionMachine with this Machine */
3642 mData->mSession.mMachine = sessionMachine;
3643
3644 /* request an IUnknown pointer early from the remote party for later
3645 * identity checks (it will be internally cached within mDirectControl
3646 * at least on XPCOM) */
3647 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3648 NOREF(unk);
3649 }
3650
3651 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3652 * would break the lock order */
3653 alock.release();
3654
3655 /* uninitialize the created session machine on failure */
3656 if (FAILED(rc))
3657 sessionMachine->uninit();
3658 }
3659
3660 if (SUCCEEDED(rc))
3661 {
3662 /*
3663 * tell the client watcher thread to update the set of
3664 * machines that have open sessions
3665 */
3666 mParent->i_updateClientWatcher();
3667
3668 if (oldState != SessionState_Locked)
3669 /* fire an event */
3670 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3671 }
3672
3673 return rc;
3674}
3675
3676/**
3677 * @note Locks objects!
3678 */
3679HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3680 const com::Utf8Str &aName,
3681 const com::Utf8Str &aEnvironment,
3682 ComPtr<IProgress> &aProgress)
3683{
3684 Utf8Str strFrontend(aName);
3685 /* "emergencystop" doesn't need the session, so skip the checks/interface
3686 * retrieval. This code doesn't quite fit in here, but introducing a
3687 * special API method would be even more effort, and would require explicit
3688 * support by every API client. It's better to hide the feature a bit. */
3689 if (strFrontend != "emergencystop")
3690 CheckComArgNotNull(aSession);
3691
3692 HRESULT rc = S_OK;
3693 if (strFrontend.isEmpty())
3694 {
3695 Bstr bstrFrontend;
3696 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3697 if (FAILED(rc))
3698 return rc;
3699 strFrontend = bstrFrontend;
3700 if (strFrontend.isEmpty())
3701 {
3702 ComPtr<ISystemProperties> systemProperties;
3703 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3704 if (FAILED(rc))
3705 return rc;
3706 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3707 if (FAILED(rc))
3708 return rc;
3709 strFrontend = bstrFrontend;
3710 }
3711 /* paranoia - emergencystop is not a valid default */
3712 if (strFrontend == "emergencystop")
3713 strFrontend = Utf8Str::Empty;
3714 }
3715 /* default frontend: Qt GUI */
3716 if (strFrontend.isEmpty())
3717 strFrontend = "GUI/Qt";
3718
3719 if (strFrontend != "emergencystop")
3720 {
3721 /* check the session state */
3722 SessionState_T state;
3723 rc = aSession->COMGETTER(State)(&state);
3724 if (FAILED(rc))
3725 return rc;
3726
3727 if (state != SessionState_Unlocked)
3728 return setError(VBOX_E_INVALID_OBJECT_STATE,
3729 tr("The given session is busy"));
3730
3731 /* get the IInternalSessionControl interface */
3732 ComPtr<IInternalSessionControl> control(aSession);
3733 ComAssertMsgRet(!control.isNull(),
3734 ("No IInternalSessionControl interface"),
3735 E_INVALIDARG);
3736
3737 /* get the teleporter enable state for the progress object init. */
3738 BOOL fTeleporterEnabled;
3739 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3740 if (FAILED(rc))
3741 return rc;
3742
3743 /* create a progress object */
3744 ComObjPtr<ProgressProxy> progress;
3745 progress.createObject();
3746 rc = progress->init(mParent,
3747 static_cast<IMachine*>(this),
3748 Bstr(tr("Starting VM")).raw(),
3749 TRUE /* aCancelable */,
3750 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3751 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3752 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3753 2 /* uFirstOperationWeight */,
3754 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3755
3756 if (SUCCEEDED(rc))
3757 {
3758 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3759 if (SUCCEEDED(rc))
3760 {
3761 aProgress = progress;
3762
3763 /* signal the client watcher thread */
3764 mParent->i_updateClientWatcher();
3765
3766 /* fire an event */
3767 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3768 }
3769 }
3770 }
3771 else
3772 {
3773 /* no progress object - either instant success or failure */
3774 aProgress = NULL;
3775
3776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3777
3778 if (mData->mSession.mState != SessionState_Locked)
3779 return setError(VBOX_E_INVALID_OBJECT_STATE,
3780 tr("The machine '%s' is not locked by a session"),
3781 mUserData->s.strName.c_str());
3782
3783 /* must have a VM process associated - do not kill normal API clients
3784 * with an open session */
3785 if (!Global::IsOnline(mData->mMachineState))
3786 return setError(VBOX_E_INVALID_OBJECT_STATE,
3787 tr("The machine '%s' does not have a VM process"),
3788 mUserData->s.strName.c_str());
3789
3790 /* forcibly terminate the VM process */
3791 if (mData->mSession.mPID != NIL_RTPROCESS)
3792 RTProcTerminate(mData->mSession.mPID);
3793
3794 /* signal the client watcher thread, as most likely the client has
3795 * been terminated */
3796 mParent->i_updateClientWatcher();
3797 }
3798
3799 return rc;
3800}
3801
3802HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3803{
3804 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3805 return setError(E_INVALIDARG,
3806 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3807 aPosition, SchemaDefs::MaxBootPosition);
3808
3809 if (aDevice == DeviceType_USB)
3810 return setError(E_NOTIMPL,
3811 tr("Booting from USB device is currently not supported"));
3812
3813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3814
3815 HRESULT rc = i_checkStateDependency(MutableStateDep);
3816 if (FAILED(rc)) return rc;
3817
3818 i_setModified(IsModified_MachineData);
3819 mHWData.backup();
3820 mHWData->mBootOrder[aPosition - 1] = aDevice;
3821
3822 return S_OK;
3823}
3824
3825HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3826{
3827 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3828 return setError(E_INVALIDARG,
3829 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3830 aPosition, SchemaDefs::MaxBootPosition);
3831
3832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3833
3834 *aDevice = mHWData->mBootOrder[aPosition - 1];
3835
3836 return S_OK;
3837}
3838
3839HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3840 LONG aControllerPort,
3841 LONG aDevice,
3842 DeviceType_T aType,
3843 const ComPtr<IMedium> &aMedium)
3844{
3845 IMedium *aM = aMedium;
3846 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3847 aName.c_str(), aControllerPort, aDevice, aType, aM));
3848
3849 // request the host lock first, since might be calling Host methods for getting host drives;
3850 // next, protect the media tree all the while we're in here, as well as our member variables
3851 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3852 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3853
3854 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3855 if (FAILED(rc)) return rc;
3856
3857 /// @todo NEWMEDIA implicit machine registration
3858 if (!mData->mRegistered)
3859 return setError(VBOX_E_INVALID_OBJECT_STATE,
3860 tr("Cannot attach storage devices to an unregistered machine"));
3861
3862 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3863
3864 /* Check for an existing controller. */
3865 ComObjPtr<StorageController> ctl;
3866 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3867 if (FAILED(rc)) return rc;
3868
3869 StorageControllerType_T ctrlType;
3870 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3871 if (FAILED(rc))
3872 return setError(E_FAIL,
3873 tr("Could not get type of controller '%s'"),
3874 aName.c_str());
3875
3876 bool fSilent = false;
3877 Utf8Str strReconfig;
3878
3879 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3880 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3881 if ( mData->mMachineState == MachineState_Paused
3882 && strReconfig == "1")
3883 fSilent = true;
3884
3885 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3886 bool fHotplug = false;
3887 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3888 fHotplug = true;
3889
3890 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3891 return setError(VBOX_E_INVALID_VM_STATE,
3892 tr("Controller '%s' does not support hotplugging"),
3893 aName.c_str());
3894
3895 // check that the port and device are not out of range
3896 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3897 if (FAILED(rc)) return rc;
3898
3899 /* check if the device slot is already busy */
3900 MediumAttachment *pAttachTemp;
3901 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3902 aName,
3903 aControllerPort,
3904 aDevice)))
3905 {
3906 Medium *pMedium = pAttachTemp->i_getMedium();
3907 if (pMedium)
3908 {
3909 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3910 return setError(VBOX_E_OBJECT_IN_USE,
3911 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3912 pMedium->i_getLocationFull().c_str(),
3913 aControllerPort,
3914 aDevice,
3915 aName.c_str());
3916 }
3917 else
3918 return setError(VBOX_E_OBJECT_IN_USE,
3919 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3920 aControllerPort, aDevice, aName.c_str());
3921 }
3922
3923 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3924 if (aMedium && medium.isNull())
3925 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3926
3927 AutoCaller mediumCaller(medium);
3928 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3929
3930 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3931
3932 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3933 && !medium.isNull()
3934 )
3935 return setError(VBOX_E_OBJECT_IN_USE,
3936 tr("Medium '%s' is already attached to this virtual machine"),
3937 medium->i_getLocationFull().c_str());
3938
3939 if (!medium.isNull())
3940 {
3941 MediumType_T mtype = medium->i_getType();
3942 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3943 // For DVDs it's not written to the config file, so needs no global config
3944 // version bump. For floppies it's a new attribute "type", which is ignored
3945 // by older VirtualBox version, so needs no global config version bump either.
3946 // For hard disks this type is not accepted.
3947 if (mtype == MediumType_MultiAttach)
3948 {
3949 // This type is new with VirtualBox 4.0 and therefore requires settings
3950 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3951 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3952 // two reasons: The medium type is a property of the media registry tree, which
3953 // can reside in the global config file (for pre-4.0 media); we would therefore
3954 // possibly need to bump the global config version. We don't want to do that though
3955 // because that might make downgrading to pre-4.0 impossible.
3956 // As a result, we can only use these two new types if the medium is NOT in the
3957 // global registry:
3958 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3959 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3960 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3961 )
3962 return setError(VBOX_E_INVALID_OBJECT_STATE,
3963 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3964 "to machines that were created with VirtualBox 4.0 or later"),
3965 medium->i_getLocationFull().c_str());
3966 }
3967 }
3968
3969 bool fIndirect = false;
3970 if (!medium.isNull())
3971 fIndirect = medium->i_isReadOnly();
3972 bool associate = true;
3973
3974 do
3975 {
3976 if ( aType == DeviceType_HardDisk
3977 && mMediumAttachments.isBackedUp())
3978 {
3979 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3980
3981 /* check if the medium was attached to the VM before we started
3982 * changing attachments in which case the attachment just needs to
3983 * be restored */
3984 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3985 {
3986 AssertReturn(!fIndirect, E_FAIL);
3987
3988 /* see if it's the same bus/channel/device */
3989 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3990 {
3991 /* the simplest case: restore the whole attachment
3992 * and return, nothing else to do */
3993 mMediumAttachments->push_back(pAttachTemp);
3994
3995 /* Reattach the medium to the VM. */
3996 if (fHotplug || fSilent)
3997 {
3998 mediumLock.release();
3999 treeLock.release();
4000 alock.release();
4001
4002 MediumLockList *pMediumLockList(new MediumLockList());
4003
4004 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4005 medium /* pToLockWrite */,
4006 false /* fMediumLockWriteAll */,
4007 NULL,
4008 *pMediumLockList);
4009 alock.acquire();
4010 if (FAILED(rc))
4011 delete pMediumLockList;
4012 else
4013 {
4014 mData->mSession.mLockedMedia.Unlock();
4015 alock.release();
4016 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4017 mData->mSession.mLockedMedia.Lock();
4018 alock.acquire();
4019 }
4020 alock.release();
4021
4022 if (SUCCEEDED(rc))
4023 {
4024 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4025 /* Remove lock list in case of error. */
4026 if (FAILED(rc))
4027 {
4028 mData->mSession.mLockedMedia.Unlock();
4029 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4030 mData->mSession.mLockedMedia.Lock();
4031 }
4032 }
4033 }
4034
4035 return S_OK;
4036 }
4037
4038 /* bus/channel/device differ; we need a new attachment object,
4039 * but don't try to associate it again */
4040 associate = false;
4041 break;
4042 }
4043 }
4044
4045 /* go further only if the attachment is to be indirect */
4046 if (!fIndirect)
4047 break;
4048
4049 /* perform the so called smart attachment logic for indirect
4050 * attachments. Note that smart attachment is only applicable to base
4051 * hard disks. */
4052
4053 if (medium->i_getParent().isNull())
4054 {
4055 /* first, investigate the backup copy of the current hard disk
4056 * attachments to make it possible to re-attach existing diffs to
4057 * another device slot w/o losing their contents */
4058 if (mMediumAttachments.isBackedUp())
4059 {
4060 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4061
4062 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4063 uint32_t foundLevel = 0;
4064
4065 for (MediumAttachmentList::const_iterator
4066 it = oldAtts.begin();
4067 it != oldAtts.end();
4068 ++it)
4069 {
4070 uint32_t level = 0;
4071 MediumAttachment *pAttach = *it;
4072 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4073 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4074 if (pMedium.isNull())
4075 continue;
4076
4077 if (pMedium->i_getBase(&level) == medium)
4078 {
4079 /* skip the hard disk if its currently attached (we
4080 * cannot attach the same hard disk twice) */
4081 if (i_findAttachment(*mMediumAttachments.data(),
4082 pMedium))
4083 continue;
4084
4085 /* matched device, channel and bus (i.e. attached to the
4086 * same place) will win and immediately stop the search;
4087 * otherwise the attachment that has the youngest
4088 * descendant of medium will be used
4089 */
4090 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4091 {
4092 /* the simplest case: restore the whole attachment
4093 * and return, nothing else to do */
4094 mMediumAttachments->push_back(*it);
4095
4096 /* Reattach the medium to the VM. */
4097 if (fHotplug || fSilent)
4098 {
4099 mediumLock.release();
4100 treeLock.release();
4101 alock.release();
4102
4103 MediumLockList *pMediumLockList(new MediumLockList());
4104
4105 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4106 medium /* pToLockWrite */,
4107 false /* fMediumLockWriteAll */,
4108 NULL,
4109 *pMediumLockList);
4110 alock.acquire();
4111 if (FAILED(rc))
4112 delete pMediumLockList;
4113 else
4114 {
4115 mData->mSession.mLockedMedia.Unlock();
4116 alock.release();
4117 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4118 mData->mSession.mLockedMedia.Lock();
4119 alock.acquire();
4120 }
4121 alock.release();
4122
4123 if (SUCCEEDED(rc))
4124 {
4125 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4126 /* Remove lock list in case of error. */
4127 if (FAILED(rc))
4128 {
4129 mData->mSession.mLockedMedia.Unlock();
4130 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4131 mData->mSession.mLockedMedia.Lock();
4132 }
4133 }
4134 }
4135
4136 return S_OK;
4137 }
4138 else if ( foundIt == oldAtts.end()
4139 || level > foundLevel /* prefer younger */
4140 )
4141 {
4142 foundIt = it;
4143 foundLevel = level;
4144 }
4145 }
4146 }
4147
4148 if (foundIt != oldAtts.end())
4149 {
4150 /* use the previously attached hard disk */
4151 medium = (*foundIt)->i_getMedium();
4152 mediumCaller.attach(medium);
4153 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4154 mediumLock.attach(medium);
4155 /* not implicit, doesn't require association with this VM */
4156 fIndirect = false;
4157 associate = false;
4158 /* go right to the MediumAttachment creation */
4159 break;
4160 }
4161 }
4162
4163 /* must give up the medium lock and medium tree lock as below we
4164 * go over snapshots, which needs a lock with higher lock order. */
4165 mediumLock.release();
4166 treeLock.release();
4167
4168 /* then, search through snapshots for the best diff in the given
4169 * hard disk's chain to base the new diff on */
4170
4171 ComObjPtr<Medium> base;
4172 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4173 while (snap)
4174 {
4175 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4176
4177 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4178
4179 MediumAttachment *pAttachFound = NULL;
4180 uint32_t foundLevel = 0;
4181
4182 for (MediumAttachmentList::const_iterator
4183 it = snapAtts.begin();
4184 it != snapAtts.end();
4185 ++it)
4186 {
4187 MediumAttachment *pAttach = *it;
4188 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4189 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4190 if (pMedium.isNull())
4191 continue;
4192
4193 uint32_t level = 0;
4194 if (pMedium->i_getBase(&level) == medium)
4195 {
4196 /* matched device, channel and bus (i.e. attached to the
4197 * same place) will win and immediately stop the search;
4198 * otherwise the attachment that has the youngest
4199 * descendant of medium will be used
4200 */
4201 if ( pAttach->i_getDevice() == aDevice
4202 && pAttach->i_getPort() == aControllerPort
4203 && pAttach->i_getControllerName() == aName
4204 )
4205 {
4206 pAttachFound = pAttach;
4207 break;
4208 }
4209 else if ( !pAttachFound
4210 || level > foundLevel /* prefer younger */
4211 )
4212 {
4213 pAttachFound = pAttach;
4214 foundLevel = level;
4215 }
4216 }
4217 }
4218
4219 if (pAttachFound)
4220 {
4221 base = pAttachFound->i_getMedium();
4222 break;
4223 }
4224
4225 snap = snap->i_getParent();
4226 }
4227
4228 /* re-lock medium tree and the medium, as we need it below */
4229 treeLock.acquire();
4230 mediumLock.acquire();
4231
4232 /* found a suitable diff, use it as a base */
4233 if (!base.isNull())
4234 {
4235 medium = base;
4236 mediumCaller.attach(medium);
4237 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4238 mediumLock.attach(medium);
4239 }
4240 }
4241
4242 Utf8Str strFullSnapshotFolder;
4243 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4244
4245 ComObjPtr<Medium> diff;
4246 diff.createObject();
4247 // store this diff in the same registry as the parent
4248 Guid uuidRegistryParent;
4249 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4250 {
4251 // parent image has no registry: this can happen if we're attaching a new immutable
4252 // image that has not yet been attached (medium then points to the base and we're
4253 // creating the diff image for the immutable, and the parent is not yet registered);
4254 // put the parent in the machine registry then
4255 mediumLock.release();
4256 treeLock.release();
4257 alock.release();
4258 i_addMediumToRegistry(medium);
4259 alock.acquire();
4260 treeLock.acquire();
4261 mediumLock.acquire();
4262 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4263 }
4264 rc = diff->init(mParent,
4265 medium->i_getPreferredDiffFormat(),
4266 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4267 uuidRegistryParent,
4268 DeviceType_HardDisk);
4269 if (FAILED(rc)) return rc;
4270
4271 /* Apply the normal locking logic to the entire chain. */
4272 MediumLockList *pMediumLockList(new MediumLockList());
4273 mediumLock.release();
4274 treeLock.release();
4275 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4276 diff /* pToLockWrite */,
4277 false /* fMediumLockWriteAll */,
4278 medium,
4279 *pMediumLockList);
4280 treeLock.acquire();
4281 mediumLock.acquire();
4282 if (SUCCEEDED(rc))
4283 {
4284 mediumLock.release();
4285 treeLock.release();
4286 rc = pMediumLockList->Lock();
4287 treeLock.acquire();
4288 mediumLock.acquire();
4289 if (FAILED(rc))
4290 setError(rc,
4291 tr("Could not lock medium when creating diff '%s'"),
4292 diff->i_getLocationFull().c_str());
4293 else
4294 {
4295 /* will release the lock before the potentially lengthy
4296 * operation, so protect with the special state */
4297 MachineState_T oldState = mData->mMachineState;
4298 i_setMachineState(MachineState_SettingUp);
4299
4300 mediumLock.release();
4301 treeLock.release();
4302 alock.release();
4303
4304 rc = medium->i_createDiffStorage(diff,
4305 medium->i_getPreferredDiffVariant(),
4306 pMediumLockList,
4307 NULL /* aProgress */,
4308 true /* aWait */);
4309
4310 alock.acquire();
4311 treeLock.acquire();
4312 mediumLock.acquire();
4313
4314 i_setMachineState(oldState);
4315 }
4316 }
4317
4318 /* Unlock the media and free the associated memory. */
4319 delete pMediumLockList;
4320
4321 if (FAILED(rc)) return rc;
4322
4323 /* use the created diff for the actual attachment */
4324 medium = diff;
4325 mediumCaller.attach(medium);
4326 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4327 mediumLock.attach(medium);
4328 }
4329 while (0);
4330
4331 ComObjPtr<MediumAttachment> attachment;
4332 attachment.createObject();
4333 rc = attachment->init(this,
4334 medium,
4335 aName,
4336 aControllerPort,
4337 aDevice,
4338 aType,
4339 fIndirect,
4340 false /* fPassthrough */,
4341 false /* fTempEject */,
4342 false /* fNonRotational */,
4343 false /* fDiscard */,
4344 fHotplug /* fHotPluggable */,
4345 Utf8Str::Empty);
4346 if (FAILED(rc)) return rc;
4347
4348 if (associate && !medium.isNull())
4349 {
4350 // as the last step, associate the medium to the VM
4351 rc = medium->i_addBackReference(mData->mUuid);
4352 // here we can fail because of Deleting, or being in process of creating a Diff
4353 if (FAILED(rc)) return rc;
4354
4355 mediumLock.release();
4356 treeLock.release();
4357 alock.release();
4358 i_addMediumToRegistry(medium);
4359 alock.acquire();
4360 treeLock.acquire();
4361 mediumLock.acquire();
4362 }
4363
4364 /* success: finally remember the attachment */
4365 i_setModified(IsModified_Storage);
4366 mMediumAttachments.backup();
4367 mMediumAttachments->push_back(attachment);
4368
4369 mediumLock.release();
4370 treeLock.release();
4371 alock.release();
4372
4373 if (fHotplug || fSilent)
4374 {
4375 if (!medium.isNull())
4376 {
4377 MediumLockList *pMediumLockList(new MediumLockList());
4378
4379 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4380 medium /* pToLockWrite */,
4381 false /* fMediumLockWriteAll */,
4382 NULL,
4383 *pMediumLockList);
4384 alock.acquire();
4385 if (FAILED(rc))
4386 delete pMediumLockList;
4387 else
4388 {
4389 mData->mSession.mLockedMedia.Unlock();
4390 alock.release();
4391 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4392 mData->mSession.mLockedMedia.Lock();
4393 alock.acquire();
4394 }
4395 alock.release();
4396 }
4397
4398 if (SUCCEEDED(rc))
4399 {
4400 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4401 /* Remove lock list in case of error. */
4402 if (FAILED(rc))
4403 {
4404 mData->mSession.mLockedMedia.Unlock();
4405 mData->mSession.mLockedMedia.Remove(attachment);
4406 mData->mSession.mLockedMedia.Lock();
4407 }
4408 }
4409 }
4410
4411 /* Save modified registries, but skip this machine as it's the caller's
4412 * job to save its settings like all other settings changes. */
4413 mParent->i_unmarkRegistryModified(i_getId());
4414 mParent->i_saveModifiedRegistries();
4415
4416 return rc;
4417}
4418
4419HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4420 LONG aDevice)
4421{
4422 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4423 aName.c_str(), aControllerPort, aDevice));
4424
4425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4426
4427 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4428 if (FAILED(rc)) return rc;
4429
4430 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4431
4432 /* Check for an existing controller. */
4433 ComObjPtr<StorageController> ctl;
4434 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4435 if (FAILED(rc)) return rc;
4436
4437 StorageControllerType_T ctrlType;
4438 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4439 if (FAILED(rc))
4440 return setError(E_FAIL,
4441 tr("Could not get type of controller '%s'"),
4442 aName.c_str());
4443
4444 bool fSilent = false;
4445 Utf8Str strReconfig;
4446
4447 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4448 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4449 if ( mData->mMachineState == MachineState_Paused
4450 && strReconfig == "1")
4451 fSilent = true;
4452
4453 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4454 bool fHotplug = false;
4455 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4456 fHotplug = true;
4457
4458 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4459 return setError(VBOX_E_INVALID_VM_STATE,
4460 tr("Controller '%s' does not support hotplugging"),
4461 aName.c_str());
4462
4463 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4464 aName,
4465 aControllerPort,
4466 aDevice);
4467 if (!pAttach)
4468 return setError(VBOX_E_OBJECT_NOT_FOUND,
4469 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4470 aDevice, aControllerPort, aName.c_str());
4471
4472 if (fHotplug && !pAttach->i_getHotPluggable())
4473 return setError(VBOX_E_NOT_SUPPORTED,
4474 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4475 aDevice, aControllerPort, aName.c_str());
4476
4477 /*
4478 * The VM has to detach the device before we delete any implicit diffs.
4479 * If this fails we can roll back without loosing data.
4480 */
4481 if (fHotplug || fSilent)
4482 {
4483 alock.release();
4484 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4485 alock.acquire();
4486 }
4487 if (FAILED(rc)) return rc;
4488
4489 /* If we are here everything went well and we can delete the implicit now. */
4490 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4491
4492 alock.release();
4493
4494 /* Save modified registries, but skip this machine as it's the caller's
4495 * job to save its settings like all other settings changes. */
4496 mParent->i_unmarkRegistryModified(i_getId());
4497 mParent->i_saveModifiedRegistries();
4498
4499 return rc;
4500}
4501
4502HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4503 LONG aDevice, BOOL aPassthrough)
4504{
4505 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4506 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4507
4508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4509
4510 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4511 if (FAILED(rc)) return rc;
4512
4513 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4514
4515 /* Check for an existing controller. */
4516 ComObjPtr<StorageController> ctl;
4517 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4518 if (FAILED(rc)) return rc;
4519
4520 StorageControllerType_T ctrlType;
4521 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4522 if (FAILED(rc))
4523 return setError(E_FAIL,
4524 tr("Could not get type of controller '%s'"),
4525 aName.c_str());
4526
4527 bool fSilent = false;
4528 Utf8Str strReconfig;
4529
4530 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4531 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4532 if ( mData->mMachineState == MachineState_Paused
4533 && strReconfig == "1")
4534 fSilent = true;
4535
4536 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4537 bool fHotplug = false;
4538 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4539 fHotplug = true;
4540
4541 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4542 return setError(VBOX_E_INVALID_VM_STATE,
4543 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4544 aName.c_str());
4545
4546 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4547 aName,
4548 aControllerPort,
4549 aDevice);
4550 if (!pAttach)
4551 return setError(VBOX_E_OBJECT_NOT_FOUND,
4552 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4553 aDevice, aControllerPort, aName.c_str());
4554
4555
4556 i_setModified(IsModified_Storage);
4557 mMediumAttachments.backup();
4558
4559 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4560
4561 if (pAttach->i_getType() != DeviceType_DVD)
4562 return setError(E_INVALIDARG,
4563 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4564 aDevice, aControllerPort, aName.c_str());
4565 pAttach->i_updatePassthrough(!!aPassthrough);
4566
4567 attLock.release();
4568 alock.release();
4569 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4570
4571 return rc;
4572}
4573
4574HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4575 LONG aDevice, BOOL aTemporaryEject)
4576{
4577
4578 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4579 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4580
4581 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4582
4583 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4584 if (FAILED(rc)) return rc;
4585
4586 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4587 aName,
4588 aControllerPort,
4589 aDevice);
4590 if (!pAttach)
4591 return setError(VBOX_E_OBJECT_NOT_FOUND,
4592 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4593 aDevice, aControllerPort, aName.c_str());
4594
4595
4596 i_setModified(IsModified_Storage);
4597 mMediumAttachments.backup();
4598
4599 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4600
4601 if (pAttach->i_getType() != DeviceType_DVD)
4602 return setError(E_INVALIDARG,
4603 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4604 aDevice, aControllerPort, aName.c_str());
4605 pAttach->i_updateTempEject(!!aTemporaryEject);
4606
4607 return S_OK;
4608}
4609
4610HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4611 LONG aDevice, BOOL aNonRotational)
4612{
4613
4614 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4615 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4616
4617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4618
4619 HRESULT rc = i_checkStateDependency(MutableStateDep);
4620 if (FAILED(rc)) return rc;
4621
4622 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4623
4624 if (Global::IsOnlineOrTransient(mData->mMachineState))
4625 return setError(VBOX_E_INVALID_VM_STATE,
4626 tr("Invalid machine state: %s"),
4627 Global::stringifyMachineState(mData->mMachineState));
4628
4629 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4630 aName,
4631 aControllerPort,
4632 aDevice);
4633 if (!pAttach)
4634 return setError(VBOX_E_OBJECT_NOT_FOUND,
4635 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4636 aDevice, aControllerPort, aName.c_str());
4637
4638
4639 i_setModified(IsModified_Storage);
4640 mMediumAttachments.backup();
4641
4642 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4643
4644 if (pAttach->i_getType() != DeviceType_HardDisk)
4645 return setError(E_INVALIDARG,
4646 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4647 aDevice, aControllerPort, aName.c_str());
4648 pAttach->i_updateNonRotational(!!aNonRotational);
4649
4650 return S_OK;
4651}
4652
4653HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4654 LONG aDevice, BOOL aDiscard)
4655{
4656
4657 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4658 aName.c_str(), aControllerPort, aDevice, aDiscard));
4659
4660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4661
4662 HRESULT rc = i_checkStateDependency(MutableStateDep);
4663 if (FAILED(rc)) return rc;
4664
4665 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4666
4667 if (Global::IsOnlineOrTransient(mData->mMachineState))
4668 return setError(VBOX_E_INVALID_VM_STATE,
4669 tr("Invalid machine state: %s"),
4670 Global::stringifyMachineState(mData->mMachineState));
4671
4672 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4673 aName,
4674 aControllerPort,
4675 aDevice);
4676 if (!pAttach)
4677 return setError(VBOX_E_OBJECT_NOT_FOUND,
4678 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4679 aDevice, aControllerPort, aName.c_str());
4680
4681
4682 i_setModified(IsModified_Storage);
4683 mMediumAttachments.backup();
4684
4685 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4686
4687 if (pAttach->i_getType() != DeviceType_HardDisk)
4688 return setError(E_INVALIDARG,
4689 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4690 aDevice, aControllerPort, aName.c_str());
4691 pAttach->i_updateDiscard(!!aDiscard);
4692
4693 return S_OK;
4694}
4695
4696HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4697 LONG aDevice, BOOL aHotPluggable)
4698{
4699 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4700 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4701
4702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4703
4704 HRESULT rc = i_checkStateDependency(MutableStateDep);
4705 if (FAILED(rc)) return rc;
4706
4707 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4708
4709 if (Global::IsOnlineOrTransient(mData->mMachineState))
4710 return setError(VBOX_E_INVALID_VM_STATE,
4711 tr("Invalid machine state: %s"),
4712 Global::stringifyMachineState(mData->mMachineState));
4713
4714 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4715 aName,
4716 aControllerPort,
4717 aDevice);
4718 if (!pAttach)
4719 return setError(VBOX_E_OBJECT_NOT_FOUND,
4720 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4721 aDevice, aControllerPort, aName.c_str());
4722
4723 /* Check for an existing controller. */
4724 ComObjPtr<StorageController> ctl;
4725 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4726 if (FAILED(rc)) return rc;
4727
4728 StorageControllerType_T ctrlType;
4729 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4730 if (FAILED(rc))
4731 return setError(E_FAIL,
4732 tr("Could not get type of controller '%s'"),
4733 aName.c_str());
4734
4735 if (!i_isControllerHotplugCapable(ctrlType))
4736 return setError(VBOX_E_NOT_SUPPORTED,
4737 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4738 aName.c_str());
4739
4740 i_setModified(IsModified_Storage);
4741 mMediumAttachments.backup();
4742
4743 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4744
4745 if (pAttach->i_getType() == DeviceType_Floppy)
4746 return setError(E_INVALIDARG,
4747 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4748 aDevice, aControllerPort, aName.c_str());
4749 pAttach->i_updateHotPluggable(!!aHotPluggable);
4750
4751 return S_OK;
4752}
4753
4754HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4755 LONG aDevice)
4756{
4757 int rc = S_OK;
4758 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4759 aName.c_str(), aControllerPort, aDevice));
4760
4761 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4762
4763 return rc;
4764}
4765
4766HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4767 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4768{
4769 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4770 aName.c_str(), aControllerPort, aDevice));
4771
4772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4773
4774 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4775 if (FAILED(rc)) return rc;
4776
4777 if (Global::IsOnlineOrTransient(mData->mMachineState))
4778 return setError(VBOX_E_INVALID_VM_STATE,
4779 tr("Invalid machine state: %s"),
4780 Global::stringifyMachineState(mData->mMachineState));
4781
4782 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4783 aName,
4784 aControllerPort,
4785 aDevice);
4786 if (!pAttach)
4787 return setError(VBOX_E_OBJECT_NOT_FOUND,
4788 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4789 aDevice, aControllerPort, aName.c_str());
4790
4791
4792 i_setModified(IsModified_Storage);
4793 mMediumAttachments.backup();
4794
4795 IBandwidthGroup *iB = aBandwidthGroup;
4796 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4797 if (aBandwidthGroup && group.isNull())
4798 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4799
4800 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4801
4802 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4803 if (strBandwidthGroupOld.isNotEmpty())
4804 {
4805 /* Get the bandwidth group object and release it - this must not fail. */
4806 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4807 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4808 Assert(SUCCEEDED(rc));
4809
4810 pBandwidthGroupOld->i_release();
4811 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4812 }
4813
4814 if (!group.isNull())
4815 {
4816 group->i_reference();
4817 pAttach->i_updateBandwidthGroup(group->i_getName());
4818 }
4819
4820 return S_OK;
4821}
4822
4823HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4824 LONG aControllerPort,
4825 LONG aDevice,
4826 DeviceType_T aType)
4827{
4828 HRESULT rc = S_OK;
4829
4830 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4831 aName.c_str(), aControllerPort, aDevice, aType));
4832
4833 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4834
4835 return rc;
4836}
4837
4838
4839HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4840 LONG aControllerPort,
4841 LONG aDevice,
4842 BOOL aForce)
4843{
4844 int rc = S_OK;
4845 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4846 aName.c_str(), aControllerPort, aForce));
4847
4848 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4849
4850 return rc;
4851}
4852
4853HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4854 LONG aControllerPort,
4855 LONG aDevice,
4856 const ComPtr<IMedium> &aMedium,
4857 BOOL aForce)
4858{
4859 int rc = S_OK;
4860 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4861 aName.c_str(), aControllerPort, aDevice, aForce));
4862
4863 // request the host lock first, since might be calling Host methods for getting host drives;
4864 // next, protect the media tree all the while we're in here, as well as our member variables
4865 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4866 this->lockHandle(),
4867 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4868
4869 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4870 aName,
4871 aControllerPort,
4872 aDevice);
4873 if (pAttach.isNull())
4874 return setError(VBOX_E_OBJECT_NOT_FOUND,
4875 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4876 aDevice, aControllerPort, aName.c_str());
4877
4878 /* Remember previously mounted medium. The medium before taking the
4879 * backup is not necessarily the same thing. */
4880 ComObjPtr<Medium> oldmedium;
4881 oldmedium = pAttach->i_getMedium();
4882
4883 IMedium *iM = aMedium;
4884 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4885 if (aMedium && pMedium.isNull())
4886 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4887
4888 AutoCaller mediumCaller(pMedium);
4889 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4890
4891 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4892 if (pMedium)
4893 {
4894 DeviceType_T mediumType = pAttach->i_getType();
4895 switch (mediumType)
4896 {
4897 case DeviceType_DVD:
4898 case DeviceType_Floppy:
4899 break;
4900
4901 default:
4902 return setError(VBOX_E_INVALID_OBJECT_STATE,
4903 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4904 aControllerPort,
4905 aDevice,
4906 aName.c_str());
4907 }
4908 }
4909
4910 i_setModified(IsModified_Storage);
4911 mMediumAttachments.backup();
4912
4913 {
4914 // The backup operation makes the pAttach reference point to the
4915 // old settings. Re-get the correct reference.
4916 pAttach = i_findAttachment(*mMediumAttachments.data(),
4917 aName,
4918 aControllerPort,
4919 aDevice);
4920 if (!oldmedium.isNull())
4921 oldmedium->i_removeBackReference(mData->mUuid);
4922 if (!pMedium.isNull())
4923 {
4924 pMedium->i_addBackReference(mData->mUuid);
4925
4926 mediumLock.release();
4927 multiLock.release();
4928 i_addMediumToRegistry(pMedium);
4929 multiLock.acquire();
4930 mediumLock.acquire();
4931 }
4932
4933 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4934 pAttach->i_updateMedium(pMedium);
4935 }
4936
4937 i_setModified(IsModified_Storage);
4938
4939 mediumLock.release();
4940 multiLock.release();
4941 rc = i_onMediumChange(pAttach, aForce);
4942 multiLock.acquire();
4943 mediumLock.acquire();
4944
4945 /* On error roll back this change only. */
4946 if (FAILED(rc))
4947 {
4948 if (!pMedium.isNull())
4949 pMedium->i_removeBackReference(mData->mUuid);
4950 pAttach = i_findAttachment(*mMediumAttachments.data(),
4951 aName,
4952 aControllerPort,
4953 aDevice);
4954 /* If the attachment is gone in the meantime, bail out. */
4955 if (pAttach.isNull())
4956 return rc;
4957 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4958 if (!oldmedium.isNull())
4959 oldmedium->i_addBackReference(mData->mUuid);
4960 pAttach->i_updateMedium(oldmedium);
4961 }
4962
4963 mediumLock.release();
4964 multiLock.release();
4965
4966 /* Save modified registries, but skip this machine as it's the caller's
4967 * job to save its settings like all other settings changes. */
4968 mParent->i_unmarkRegistryModified(i_getId());
4969 mParent->i_saveModifiedRegistries();
4970
4971 return rc;
4972}
4973HRESULT Machine::getMedium(const com::Utf8Str &aName,
4974 LONG aControllerPort,
4975 LONG aDevice,
4976 ComPtr<IMedium> &aMedium)
4977{
4978 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4979 aName.c_str(), aControllerPort, aDevice));
4980
4981 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4982
4983 aMedium = NULL;
4984
4985 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4986 aName,
4987 aControllerPort,
4988 aDevice);
4989 if (pAttach.isNull())
4990 return setError(VBOX_E_OBJECT_NOT_FOUND,
4991 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4992 aDevice, aControllerPort, aName.c_str());
4993
4994 aMedium = pAttach->i_getMedium();
4995
4996 return S_OK;
4997}
4998
4999HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
5000{
5001
5002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5003
5004 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5005
5006 return S_OK;
5007}
5008
5009HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
5010{
5011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5012
5013 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5014
5015 return S_OK;
5016}
5017
5018HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
5019{
5020 /* Do not assert if slot is out of range, just return the advertised
5021 status. testdriver/vbox.py triggers this in logVmInfo. */
5022 if (aSlot >= mNetworkAdapters.size())
5023 return setError(E_INVALIDARG,
5024 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5025 aSlot, mNetworkAdapters.size());
5026
5027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5028
5029 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5030
5031 return S_OK;
5032}
5033
5034HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5035{
5036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5037
5038 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5039 size_t i = 0;
5040 for (settings::StringsMap::const_iterator
5041 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5042 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5043 ++it, ++i)
5044 aKeys[i] = it->first;
5045
5046 return S_OK;
5047}
5048
5049 /**
5050 * @note Locks this object for reading.
5051 */
5052HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5053 com::Utf8Str &aValue)
5054{
5055 /* start with nothing found */
5056 aValue = "";
5057
5058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5059
5060 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5061 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5062 // found:
5063 aValue = it->second; // source is a Utf8Str
5064
5065 /* return the result to caller (may be empty) */
5066 return S_OK;
5067}
5068
5069 /**
5070 * @note Locks mParent for writing + this object for writing.
5071 */
5072HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5073{
5074 Utf8Str strOldValue; // empty
5075
5076 // locking note: we only hold the read lock briefly to look up the old value,
5077 // then release it and call the onExtraCanChange callbacks. There is a small
5078 // chance of a race insofar as the callback might be called twice if two callers
5079 // change the same key at the same time, but that's a much better solution
5080 // than the deadlock we had here before. The actual changing of the extradata
5081 // is then performed under the write lock and race-free.
5082
5083 // look up the old value first; if nothing has changed then we need not do anything
5084 {
5085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5086
5087 // For snapshots don't even think about allowing changes, extradata
5088 // is global for a machine, so there is nothing snapshot specific.
5089 if (i_isSnapshotMachine())
5090 return setError(VBOX_E_INVALID_VM_STATE,
5091 tr("Cannot set extradata for a snapshot"));
5092
5093 // check if the right IMachine instance is used
5094 if (mData->mRegistered && !i_isSessionMachine())
5095 return setError(VBOX_E_INVALID_VM_STATE,
5096 tr("Cannot set extradata for an immutable machine"));
5097
5098 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5099 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5100 strOldValue = it->second;
5101 }
5102
5103 bool fChanged;
5104 if ((fChanged = (strOldValue != aValue)))
5105 {
5106 // ask for permission from all listeners outside the locks;
5107 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5108 // lock to copy the list of callbacks to invoke
5109 Bstr error;
5110 Bstr bstrValue(aValue);
5111
5112 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5113 {
5114 const char *sep = error.isEmpty() ? "" : ": ";
5115 CBSTR err = error.raw();
5116 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5117 return setError(E_ACCESSDENIED,
5118 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5119 aKey.c_str(),
5120 aValue.c_str(),
5121 sep,
5122 err);
5123 }
5124
5125 // data is changing and change not vetoed: then write it out under the lock
5126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5127
5128 if (aValue.isEmpty())
5129 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5130 else
5131 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5132 // creates a new key if needed
5133
5134 bool fNeedsGlobalSaveSettings = false;
5135 // This saving of settings is tricky: there is no "old state" for the
5136 // extradata items at all (unlike all other settings), so the old/new
5137 // settings comparison would give a wrong result!
5138 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5139
5140 if (fNeedsGlobalSaveSettings)
5141 {
5142 // save the global settings; for that we should hold only the VirtualBox lock
5143 alock.release();
5144 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5145 mParent->i_saveSettings();
5146 }
5147 }
5148
5149 // fire notification outside the lock
5150 if (fChanged)
5151 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5152
5153 return S_OK;
5154}
5155
5156HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5157{
5158 aProgress = NULL;
5159 NOREF(aSettingsFilePath);
5160 ReturnComNotImplemented();
5161}
5162
5163HRESULT Machine::saveSettings()
5164{
5165 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5166
5167 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5168 if (FAILED(rc)) return rc;
5169
5170 /* the settings file path may never be null */
5171 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5172
5173 /* save all VM data excluding snapshots */
5174 bool fNeedsGlobalSaveSettings = false;
5175 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5176 mlock.release();
5177
5178 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5179 {
5180 // save the global settings; for that we should hold only the VirtualBox lock
5181 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5182 rc = mParent->i_saveSettings();
5183 }
5184
5185 return rc;
5186}
5187
5188
5189HRESULT Machine::discardSettings()
5190{
5191 /*
5192 * We need to take the machine list lock here as well as the machine one
5193 * or we'll get into trouble should any media stuff require rolling back.
5194 *
5195 * Details:
5196 *
5197 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5198 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5199 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other lock: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x]
5200 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: class=0000000000d5eb10 4-LISTOFMACHINES created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5201 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5202 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5203 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5204 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: class=0000000000d5ecd0 5-MACHINEOBJECT created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5205 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5206 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5207 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5208 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5209 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5210 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #00: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=2 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(11705) Machine::i_rollback 00007ff6853f6ce4} [x/r]
5211 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #01: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x] (*)
5212 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5213 * 0:005> k
5214 * # Child-SP RetAddr Call Site
5215 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5216 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5217 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5218 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5219 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5220 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5221 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5222 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5223 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5224 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5225 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5226 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5227 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5228 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5229 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5230 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5231 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5232 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5233 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5234 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5235 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5236 *
5237 */
5238 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5240
5241 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5242 if (FAILED(rc)) return rc;
5243
5244 /*
5245 * during this rollback, the session will be notified if data has
5246 * been actually changed
5247 */
5248 i_rollback(true /* aNotify */);
5249
5250 return S_OK;
5251}
5252
5253/** @note Locks objects! */
5254HRESULT Machine::unregister(AutoCaller &autoCaller,
5255 CleanupMode_T aCleanupMode,
5256 std::vector<ComPtr<IMedium> > &aMedia)
5257{
5258 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5259
5260 Guid id(i_getId());
5261
5262 if (mData->mSession.mState != SessionState_Unlocked)
5263 return setError(VBOX_E_INVALID_OBJECT_STATE,
5264 tr("Cannot unregister the machine '%s' while it is locked"),
5265 mUserData->s.strName.c_str());
5266
5267 // wait for state dependents to drop to zero
5268 i_ensureNoStateDependencies();
5269
5270 if (!mData->mAccessible)
5271 {
5272 // inaccessible maschines can only be unregistered; uninitialize ourselves
5273 // here because currently there may be no unregistered that are inaccessible
5274 // (this state combination is not supported). Note releasing the caller and
5275 // leaving the lock before calling uninit()
5276 alock.release();
5277 autoCaller.release();
5278
5279 uninit();
5280
5281 mParent->i_unregisterMachine(this, id);
5282 // calls VirtualBox::i_saveSettings()
5283
5284 return S_OK;
5285 }
5286
5287 HRESULT rc = S_OK;
5288
5289 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5290 // discard saved state
5291 if (mData->mMachineState == MachineState_Saved)
5292 {
5293 // add the saved state file to the list of files the caller should delete
5294 Assert(!mSSData->strStateFilePath.isEmpty());
5295 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5296
5297 mSSData->strStateFilePath.setNull();
5298
5299 // unconditionally set the machine state to powered off, we now
5300 // know no session has locked the machine
5301 mData->mMachineState = MachineState_PoweredOff;
5302 }
5303
5304 size_t cSnapshots = 0;
5305 if (mData->mFirstSnapshot)
5306 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5307 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5308 // fail now before we start detaching media
5309 return setError(VBOX_E_INVALID_OBJECT_STATE,
5310 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5311 mUserData->s.strName.c_str(), cSnapshots);
5312
5313 // This list collects the medium objects from all medium attachments
5314 // which we will detach from the machine and its snapshots, in a specific
5315 // order which allows for closing all media without getting "media in use"
5316 // errors, simply by going through the list from the front to the back:
5317 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5318 // and must be closed before the parent media from the snapshots, or closing the parents
5319 // will fail because they still have children);
5320 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5321 // the root ("first") snapshot of the machine.
5322 MediaList llMedia;
5323
5324 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5325 && mMediumAttachments->size()
5326 )
5327 {
5328 // we have media attachments: detach them all and add the Medium objects to our list
5329 if (aCleanupMode != CleanupMode_UnregisterOnly)
5330 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5331 else
5332 return setError(VBOX_E_INVALID_OBJECT_STATE,
5333 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5334 mUserData->s.strName.c_str(), mMediumAttachments->size());
5335 }
5336
5337 if (cSnapshots)
5338 {
5339 // add the media from the medium attachments of the snapshots to llMedia
5340 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5341 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5342 // into the children first
5343
5344 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5345 MachineState_T oldState = mData->mMachineState;
5346 mData->mMachineState = MachineState_DeletingSnapshot;
5347
5348 // make a copy of the first snapshot so the refcount does not drop to 0
5349 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5350 // because of the AutoCaller voodoo)
5351 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5352
5353 // GO!
5354 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5355
5356 mData->mMachineState = oldState;
5357 }
5358
5359 if (FAILED(rc))
5360 {
5361 i_rollbackMedia();
5362 return rc;
5363 }
5364
5365 // commit all the media changes made above
5366 i_commitMedia();
5367
5368 mData->mRegistered = false;
5369
5370 // machine lock no longer needed
5371 alock.release();
5372
5373 // return media to caller
5374 aMedia.resize(llMedia.size());
5375 size_t i = 0;
5376 for (MediaList::const_iterator
5377 it = llMedia.begin();
5378 it != llMedia.end();
5379 ++it, ++i)
5380 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5381
5382 mParent->i_unregisterMachine(this, id);
5383 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5384
5385 return S_OK;
5386}
5387
5388/**
5389 * Task record for deleting a machine config.
5390 */
5391class Machine::DeleteConfigTask
5392 : public Machine::Task
5393{
5394public:
5395 DeleteConfigTask(Machine *m,
5396 Progress *p,
5397 const Utf8Str &t,
5398 const RTCList<ComPtr<IMedium> > &llMediums,
5399 const StringsList &llFilesToDelete)
5400 : Task(m, p, t),
5401 m_llMediums(llMediums),
5402 m_llFilesToDelete(llFilesToDelete)
5403 {}
5404
5405private:
5406 void handler()
5407 {
5408 try
5409 {
5410 m_pMachine->i_deleteConfigHandler(*this);
5411 }
5412 catch (...)
5413 {
5414 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5415 }
5416 }
5417
5418 RTCList<ComPtr<IMedium> > m_llMediums;
5419 StringsList m_llFilesToDelete;
5420
5421 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5422};
5423
5424/**
5425 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5426 * SessionMachine::taskHandler().
5427 *
5428 * @note Locks this object for writing.
5429 *
5430 * @param task
5431 * @return
5432 */
5433void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5434{
5435 LogFlowThisFuncEnter();
5436
5437 AutoCaller autoCaller(this);
5438 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5439 if (FAILED(autoCaller.rc()))
5440 {
5441 /* we might have been uninitialized because the session was accidentally
5442 * closed by the client, so don't assert */
5443 HRESULT rc = setError(E_FAIL,
5444 tr("The session has been accidentally closed"));
5445 task.m_pProgress->i_notifyComplete(rc);
5446 LogFlowThisFuncLeave();
5447 return;
5448 }
5449
5450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5451
5452 HRESULT rc = S_OK;
5453
5454 try
5455 {
5456 ULONG uLogHistoryCount = 3;
5457 ComPtr<ISystemProperties> systemProperties;
5458 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5459 if (FAILED(rc)) throw rc;
5460
5461 if (!systemProperties.isNull())
5462 {
5463 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5464 if (FAILED(rc)) throw rc;
5465 }
5466
5467 MachineState_T oldState = mData->mMachineState;
5468 i_setMachineState(MachineState_SettingUp);
5469 alock.release();
5470 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5471 {
5472 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5473 {
5474 AutoCaller mac(pMedium);
5475 if (FAILED(mac.rc())) throw mac.rc();
5476 Utf8Str strLocation = pMedium->i_getLocationFull();
5477 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5478 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5479 if (FAILED(rc)) throw rc;
5480 }
5481 if (pMedium->i_isMediumFormatFile())
5482 {
5483 ComPtr<IProgress> pProgress2;
5484 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5485 if (FAILED(rc)) throw rc;
5486 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5487 if (FAILED(rc)) throw rc;
5488 /* Check the result of the asynchronous process. */
5489 LONG iRc;
5490 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5491 if (FAILED(rc)) throw rc;
5492 /* If the thread of the progress object has an error, then
5493 * retrieve the error info from there, or it'll be lost. */
5494 if (FAILED(iRc))
5495 throw setError(ProgressErrorInfo(pProgress2));
5496 }
5497
5498 /* Close the medium, deliberately without checking the return
5499 * code, and without leaving any trace in the error info, as
5500 * a failure here is a very minor issue, which shouldn't happen
5501 * as above we even managed to delete the medium. */
5502 {
5503 ErrorInfoKeeper eik;
5504 pMedium->Close();
5505 }
5506 }
5507 i_setMachineState(oldState);
5508 alock.acquire();
5509
5510 // delete the files pushed on the task list by Machine::Delete()
5511 // (this includes saved states of the machine and snapshots and
5512 // medium storage files from the IMedium list passed in, and the
5513 // machine XML file)
5514 for (StringsList::const_iterator
5515 it = task.m_llFilesToDelete.begin();
5516 it != task.m_llFilesToDelete.end();
5517 ++it)
5518 {
5519 const Utf8Str &strFile = *it;
5520 LogFunc(("Deleting file %s\n", strFile.c_str()));
5521 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5522 if (FAILED(rc)) throw rc;
5523
5524 int vrc = RTFileDelete(strFile.c_str());
5525 if (RT_FAILURE(vrc))
5526 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5527 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5528 }
5529
5530 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5531 if (FAILED(rc)) throw rc;
5532
5533 /* delete the settings only when the file actually exists */
5534 if (mData->pMachineConfigFile->fileExists())
5535 {
5536 /* Delete any backup or uncommitted XML files. Ignore failures.
5537 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5538 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5539 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5540 RTFileDelete(otherXml.c_str());
5541 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5542 RTFileDelete(otherXml.c_str());
5543
5544 /* delete the Logs folder, nothing important should be left
5545 * there (we don't check for errors because the user might have
5546 * some private files there that we don't want to delete) */
5547 Utf8Str logFolder;
5548 getLogFolder(logFolder);
5549 Assert(logFolder.length());
5550 if (RTDirExists(logFolder.c_str()))
5551 {
5552 /* Delete all VBox.log[.N] files from the Logs folder
5553 * (this must be in sync with the rotation logic in
5554 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5555 * files that may have been created by the GUI. */
5556 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5557 logFolder.c_str(), RTPATH_DELIMITER);
5558 RTFileDelete(log.c_str());
5559 log = Utf8StrFmt("%s%cVBox.png",
5560 logFolder.c_str(), RTPATH_DELIMITER);
5561 RTFileDelete(log.c_str());
5562 for (int i = uLogHistoryCount; i > 0; i--)
5563 {
5564 log = Utf8StrFmt("%s%cVBox.log.%d",
5565 logFolder.c_str(), RTPATH_DELIMITER, i);
5566 RTFileDelete(log.c_str());
5567 log = Utf8StrFmt("%s%cVBox.png.%d",
5568 logFolder.c_str(), RTPATH_DELIMITER, i);
5569 RTFileDelete(log.c_str());
5570 }
5571#if defined(RT_OS_WINDOWS)
5572 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5573 RTFileDelete(log.c_str());
5574 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5575 RTFileDelete(log.c_str());
5576#endif
5577
5578 RTDirRemove(logFolder.c_str());
5579 }
5580
5581 /* delete the Snapshots folder, nothing important should be left
5582 * there (we don't check for errors because the user might have
5583 * some private files there that we don't want to delete) */
5584 Utf8Str strFullSnapshotFolder;
5585 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5586 Assert(!strFullSnapshotFolder.isEmpty());
5587 if (RTDirExists(strFullSnapshotFolder.c_str()))
5588 RTDirRemove(strFullSnapshotFolder.c_str());
5589
5590 // delete the directory that contains the settings file, but only
5591 // if it matches the VM name
5592 Utf8Str settingsDir;
5593 if (i_isInOwnDir(&settingsDir))
5594 RTDirRemove(settingsDir.c_str());
5595 }
5596
5597 alock.release();
5598
5599 mParent->i_saveModifiedRegistries();
5600 }
5601 catch (HRESULT aRC) { rc = aRC; }
5602
5603 task.m_pProgress->i_notifyComplete(rc);
5604
5605 LogFlowThisFuncLeave();
5606}
5607
5608HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5609{
5610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5611
5612 HRESULT rc = i_checkStateDependency(MutableStateDep);
5613 if (FAILED(rc)) return rc;
5614
5615 if (mData->mRegistered)
5616 return setError(VBOX_E_INVALID_VM_STATE,
5617 tr("Cannot delete settings of a registered machine"));
5618
5619 // collect files to delete
5620 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5621 if (mData->pMachineConfigFile->fileExists())
5622 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5623
5624 RTCList<ComPtr<IMedium> > llMediums;
5625 for (size_t i = 0; i < aMedia.size(); ++i)
5626 {
5627 IMedium *pIMedium(aMedia[i]);
5628 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5629 if (pMedium.isNull())
5630 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5631 SafeArray<BSTR> ids;
5632 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5633 if (FAILED(rc)) return rc;
5634 /* At this point the medium should not have any back references
5635 * anymore. If it has it is attached to another VM and *must* not
5636 * deleted. */
5637 if (ids.size() < 1)
5638 llMediums.append(pMedium);
5639 }
5640
5641 ComObjPtr<Progress> pProgress;
5642 pProgress.createObject();
5643 rc = pProgress->init(i_getVirtualBox(),
5644 static_cast<IMachine*>(this) /* aInitiator */,
5645 tr("Deleting files"),
5646 true /* fCancellable */,
5647 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5648 tr("Collecting file inventory"));
5649 if (FAILED(rc))
5650 return rc;
5651
5652 /* create and start the task on a separate thread (note that it will not
5653 * start working until we release alock) */
5654 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5655 rc = pTask->createThread();
5656 if (FAILED(rc))
5657 return rc;
5658
5659 pProgress.queryInterfaceTo(aProgress.asOutParam());
5660
5661 LogFlowFuncLeave();
5662
5663 return S_OK;
5664}
5665
5666HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5667{
5668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5669
5670 ComObjPtr<Snapshot> pSnapshot;
5671 HRESULT rc;
5672
5673 if (aNameOrId.isEmpty())
5674 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5675 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5676 else
5677 {
5678 Guid uuid(aNameOrId);
5679 if (uuid.isValid())
5680 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5681 else
5682 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5683 }
5684 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5685
5686 return rc;
5687}
5688
5689HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5690{
5691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5692
5693 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5694 if (FAILED(rc)) return rc;
5695
5696 ComObjPtr<SharedFolder> sharedFolder;
5697 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5698 if (SUCCEEDED(rc))
5699 return setError(VBOX_E_OBJECT_IN_USE,
5700 tr("Shared folder named '%s' already exists"),
5701 aName.c_str());
5702
5703 sharedFolder.createObject();
5704 rc = sharedFolder->init(i_getMachine(),
5705 aName,
5706 aHostPath,
5707 !!aWritable,
5708 !!aAutomount,
5709 true /* fFailOnError */);
5710 if (FAILED(rc)) return rc;
5711
5712 i_setModified(IsModified_SharedFolders);
5713 mHWData.backup();
5714 mHWData->mSharedFolders.push_back(sharedFolder);
5715
5716 /* inform the direct session if any */
5717 alock.release();
5718 i_onSharedFolderChange();
5719
5720 return S_OK;
5721}
5722
5723HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5724{
5725 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5726
5727 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5728 if (FAILED(rc)) return rc;
5729
5730 ComObjPtr<SharedFolder> sharedFolder;
5731 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5732 if (FAILED(rc)) return rc;
5733
5734 i_setModified(IsModified_SharedFolders);
5735 mHWData.backup();
5736 mHWData->mSharedFolders.remove(sharedFolder);
5737
5738 /* inform the direct session if any */
5739 alock.release();
5740 i_onSharedFolderChange();
5741
5742 return S_OK;
5743}
5744
5745HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5746{
5747 /* start with No */
5748 *aCanShow = FALSE;
5749
5750 ComPtr<IInternalSessionControl> directControl;
5751 {
5752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5753
5754 if (mData->mSession.mState != SessionState_Locked)
5755 return setError(VBOX_E_INVALID_VM_STATE,
5756 tr("Machine is not locked for session (session state: %s)"),
5757 Global::stringifySessionState(mData->mSession.mState));
5758
5759 if (mData->mSession.mLockType == LockType_VM)
5760 directControl = mData->mSession.mDirectControl;
5761 }
5762
5763 /* ignore calls made after #OnSessionEnd() is called */
5764 if (!directControl)
5765 return S_OK;
5766
5767 LONG64 dummy;
5768 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5769}
5770
5771HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5772{
5773 ComPtr<IInternalSessionControl> directControl;
5774 {
5775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5776
5777 if (mData->mSession.mState != SessionState_Locked)
5778 return setError(E_FAIL,
5779 tr("Machine is not locked for session (session state: %s)"),
5780 Global::stringifySessionState(mData->mSession.mState));
5781
5782 if (mData->mSession.mLockType == LockType_VM)
5783 directControl = mData->mSession.mDirectControl;
5784 }
5785
5786 /* ignore calls made after #OnSessionEnd() is called */
5787 if (!directControl)
5788 return S_OK;
5789
5790 BOOL dummy;
5791 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5792}
5793
5794#ifdef VBOX_WITH_GUEST_PROPS
5795/**
5796 * Look up a guest property in VBoxSVC's internal structures.
5797 */
5798HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5799 com::Utf8Str &aValue,
5800 LONG64 *aTimestamp,
5801 com::Utf8Str &aFlags) const
5802{
5803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5804
5805 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5806 if (it != mHWData->mGuestProperties.end())
5807 {
5808 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5809 aValue = it->second.strValue;
5810 *aTimestamp = it->second.mTimestamp;
5811 GuestPropWriteFlags(it->second.mFlags, szFlags);
5812 aFlags = Utf8Str(szFlags);
5813 }
5814
5815 return S_OK;
5816}
5817
5818/**
5819 * Query the VM that a guest property belongs to for the property.
5820 * @returns E_ACCESSDENIED if the VM process is not available or not
5821 * currently handling queries and the lookup should then be done in
5822 * VBoxSVC.
5823 */
5824HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5825 com::Utf8Str &aValue,
5826 LONG64 *aTimestamp,
5827 com::Utf8Str &aFlags) const
5828{
5829 HRESULT rc = S_OK;
5830 BSTR bValue = NULL;
5831 BSTR bFlags = NULL;
5832
5833 ComPtr<IInternalSessionControl> directControl;
5834 {
5835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5836 if (mData->mSession.mLockType == LockType_VM)
5837 directControl = mData->mSession.mDirectControl;
5838 }
5839
5840 /* ignore calls made after #OnSessionEnd() is called */
5841 if (!directControl)
5842 rc = E_ACCESSDENIED;
5843 else
5844 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5845 0 /* accessMode */,
5846 &bValue, aTimestamp, &bFlags);
5847
5848 aValue = bValue;
5849 aFlags = bFlags;
5850
5851 return rc;
5852}
5853#endif // VBOX_WITH_GUEST_PROPS
5854
5855HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5856 com::Utf8Str &aValue,
5857 LONG64 *aTimestamp,
5858 com::Utf8Str &aFlags)
5859{
5860#ifndef VBOX_WITH_GUEST_PROPS
5861 ReturnComNotImplemented();
5862#else // VBOX_WITH_GUEST_PROPS
5863
5864 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5865
5866 if (rc == E_ACCESSDENIED)
5867 /* The VM is not running or the service is not (yet) accessible */
5868 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5869 return rc;
5870#endif // VBOX_WITH_GUEST_PROPS
5871}
5872
5873HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5874{
5875 LONG64 dummyTimestamp;
5876 com::Utf8Str dummyFlags;
5877 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5878 return rc;
5879
5880}
5881HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5882{
5883 com::Utf8Str dummyFlags;
5884 com::Utf8Str dummyValue;
5885 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5886 return rc;
5887}
5888
5889#ifdef VBOX_WITH_GUEST_PROPS
5890/**
5891 * Set a guest property in VBoxSVC's internal structures.
5892 */
5893HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5894 const com::Utf8Str &aFlags, bool fDelete)
5895{
5896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5897 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5898 if (FAILED(rc)) return rc;
5899
5900 try
5901 {
5902 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5903 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5904 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5905
5906 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5907 if (it == mHWData->mGuestProperties.end())
5908 {
5909 if (!fDelete)
5910 {
5911 i_setModified(IsModified_MachineData);
5912 mHWData.backupEx();
5913
5914 RTTIMESPEC time;
5915 HWData::GuestProperty prop;
5916 prop.strValue = Bstr(aValue).raw();
5917 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5918 prop.mFlags = fFlags;
5919 mHWData->mGuestProperties[aName] = prop;
5920 }
5921 }
5922 else
5923 {
5924 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5925 {
5926 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5927 }
5928 else
5929 {
5930 i_setModified(IsModified_MachineData);
5931 mHWData.backupEx();
5932
5933 /* The backupEx() operation invalidates our iterator,
5934 * so get a new one. */
5935 it = mHWData->mGuestProperties.find(aName);
5936 Assert(it != mHWData->mGuestProperties.end());
5937
5938 if (!fDelete)
5939 {
5940 RTTIMESPEC time;
5941 it->second.strValue = aValue;
5942 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5943 it->second.mFlags = fFlags;
5944 }
5945 else
5946 mHWData->mGuestProperties.erase(it);
5947 }
5948 }
5949
5950 if (SUCCEEDED(rc))
5951 {
5952 alock.release();
5953
5954 mParent->i_onGuestPropertyChange(mData->mUuid,
5955 Bstr(aName).raw(),
5956 Bstr(aValue).raw(),
5957 Bstr(aFlags).raw());
5958 }
5959 }
5960 catch (std::bad_alloc &)
5961 {
5962 rc = E_OUTOFMEMORY;
5963 }
5964
5965 return rc;
5966}
5967
5968/**
5969 * Set a property on the VM that that property belongs to.
5970 * @returns E_ACCESSDENIED if the VM process is not available or not
5971 * currently handling queries and the setting should then be done in
5972 * VBoxSVC.
5973 */
5974HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5975 const com::Utf8Str &aFlags, bool fDelete)
5976{
5977 HRESULT rc;
5978
5979 try
5980 {
5981 ComPtr<IInternalSessionControl> directControl;
5982 {
5983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5984 if (mData->mSession.mLockType == LockType_VM)
5985 directControl = mData->mSession.mDirectControl;
5986 }
5987
5988 BSTR dummy = NULL; /* will not be changed (setter) */
5989 LONG64 dummy64;
5990 if (!directControl)
5991 rc = E_ACCESSDENIED;
5992 else
5993 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5994 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5995 fDelete? 2: 1 /* accessMode */,
5996 &dummy, &dummy64, &dummy);
5997 }
5998 catch (std::bad_alloc &)
5999 {
6000 rc = E_OUTOFMEMORY;
6001 }
6002
6003 return rc;
6004}
6005#endif // VBOX_WITH_GUEST_PROPS
6006
6007HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
6008 const com::Utf8Str &aFlags)
6009{
6010#ifndef VBOX_WITH_GUEST_PROPS
6011 ReturnComNotImplemented();
6012#else // VBOX_WITH_GUEST_PROPS
6013 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
6014 if (rc == E_ACCESSDENIED)
6015 /* The VM is not running or the service is not (yet) accessible */
6016 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
6017 return rc;
6018#endif // VBOX_WITH_GUEST_PROPS
6019}
6020
6021HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
6022{
6023 return setGuestProperty(aProperty, aValue, "");
6024}
6025
6026HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6027{
6028#ifndef VBOX_WITH_GUEST_PROPS
6029 ReturnComNotImplemented();
6030#else // VBOX_WITH_GUEST_PROPS
6031 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6032 if (rc == E_ACCESSDENIED)
6033 /* The VM is not running or the service is not (yet) accessible */
6034 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6035 return rc;
6036#endif // VBOX_WITH_GUEST_PROPS
6037}
6038
6039#ifdef VBOX_WITH_GUEST_PROPS
6040/**
6041 * Enumerate the guest properties in VBoxSVC's internal structures.
6042 */
6043HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6044 std::vector<com::Utf8Str> &aNames,
6045 std::vector<com::Utf8Str> &aValues,
6046 std::vector<LONG64> &aTimestamps,
6047 std::vector<com::Utf8Str> &aFlags)
6048{
6049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6050 Utf8Str strPatterns(aPatterns);
6051
6052 /*
6053 * Look for matching patterns and build up a list.
6054 */
6055 HWData::GuestPropertyMap propMap;
6056 for (HWData::GuestPropertyMap::const_iterator
6057 it = mHWData->mGuestProperties.begin();
6058 it != mHWData->mGuestProperties.end();
6059 ++it)
6060 {
6061 if ( strPatterns.isEmpty()
6062 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6063 RTSTR_MAX,
6064 it->first.c_str(),
6065 RTSTR_MAX,
6066 NULL)
6067 )
6068 propMap.insert(*it);
6069 }
6070
6071 alock.release();
6072
6073 /*
6074 * And build up the arrays for returning the property information.
6075 */
6076 size_t cEntries = propMap.size();
6077
6078 aNames.resize(cEntries);
6079 aValues.resize(cEntries);
6080 aTimestamps.resize(cEntries);
6081 aFlags.resize(cEntries);
6082
6083 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6084 size_t i = 0;
6085 for (HWData::GuestPropertyMap::const_iterator
6086 it = propMap.begin();
6087 it != propMap.end();
6088 ++it, ++i)
6089 {
6090 aNames[i] = it->first;
6091 aValues[i] = it->second.strValue;
6092 aTimestamps[i] = it->second.mTimestamp;
6093 GuestPropWriteFlags(it->second.mFlags, szFlags);
6094 aFlags[i] = Utf8Str(szFlags);
6095 }
6096
6097 return S_OK;
6098}
6099
6100/**
6101 * Enumerate the properties managed by a VM.
6102 * @returns E_ACCESSDENIED if the VM process is not available or not
6103 * currently handling queries and the setting should then be done in
6104 * VBoxSVC.
6105 */
6106HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6107 std::vector<com::Utf8Str> &aNames,
6108 std::vector<com::Utf8Str> &aValues,
6109 std::vector<LONG64> &aTimestamps,
6110 std::vector<com::Utf8Str> &aFlags)
6111{
6112 HRESULT rc;
6113 ComPtr<IInternalSessionControl> directControl;
6114 {
6115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6116 if (mData->mSession.mLockType == LockType_VM)
6117 directControl = mData->mSession.mDirectControl;
6118 }
6119
6120 com::SafeArray<BSTR> bNames;
6121 com::SafeArray<BSTR> bValues;
6122 com::SafeArray<LONG64> bTimestamps;
6123 com::SafeArray<BSTR> bFlags;
6124
6125 if (!directControl)
6126 rc = E_ACCESSDENIED;
6127 else
6128 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6129 ComSafeArrayAsOutParam(bNames),
6130 ComSafeArrayAsOutParam(bValues),
6131 ComSafeArrayAsOutParam(bTimestamps),
6132 ComSafeArrayAsOutParam(bFlags));
6133 size_t i;
6134 aNames.resize(bNames.size());
6135 for (i = 0; i < bNames.size(); ++i)
6136 aNames[i] = Utf8Str(bNames[i]);
6137 aValues.resize(bValues.size());
6138 for (i = 0; i < bValues.size(); ++i)
6139 aValues[i] = Utf8Str(bValues[i]);
6140 aTimestamps.resize(bTimestamps.size());
6141 for (i = 0; i < bTimestamps.size(); ++i)
6142 aTimestamps[i] = bTimestamps[i];
6143 aFlags.resize(bFlags.size());
6144 for (i = 0; i < bFlags.size(); ++i)
6145 aFlags[i] = Utf8Str(bFlags[i]);
6146
6147 return rc;
6148}
6149#endif // VBOX_WITH_GUEST_PROPS
6150HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6151 std::vector<com::Utf8Str> &aNames,
6152 std::vector<com::Utf8Str> &aValues,
6153 std::vector<LONG64> &aTimestamps,
6154 std::vector<com::Utf8Str> &aFlags)
6155{
6156#ifndef VBOX_WITH_GUEST_PROPS
6157 ReturnComNotImplemented();
6158#else // VBOX_WITH_GUEST_PROPS
6159
6160 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6161
6162 if (rc == E_ACCESSDENIED)
6163 /* The VM is not running or the service is not (yet) accessible */
6164 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6165 return rc;
6166#endif // VBOX_WITH_GUEST_PROPS
6167}
6168
6169HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6170 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6171{
6172 MediumAttachmentList atts;
6173
6174 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6175 if (FAILED(rc)) return rc;
6176
6177 aMediumAttachments.resize(atts.size());
6178 size_t i = 0;
6179 for (MediumAttachmentList::const_iterator
6180 it = atts.begin();
6181 it != atts.end();
6182 ++it, ++i)
6183 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6184
6185 return S_OK;
6186}
6187
6188HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6189 LONG aControllerPort,
6190 LONG aDevice,
6191 ComPtr<IMediumAttachment> &aAttachment)
6192{
6193 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6194 aName.c_str(), aControllerPort, aDevice));
6195
6196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6197
6198 aAttachment = NULL;
6199
6200 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6201 aName,
6202 aControllerPort,
6203 aDevice);
6204 if (pAttach.isNull())
6205 return setError(VBOX_E_OBJECT_NOT_FOUND,
6206 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6207 aDevice, aControllerPort, aName.c_str());
6208
6209 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6210
6211 return S_OK;
6212}
6213
6214
6215HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6216 StorageBus_T aConnectionType,
6217 ComPtr<IStorageController> &aController)
6218{
6219 if ( (aConnectionType <= StorageBus_Null)
6220 || (aConnectionType > StorageBus_PCIe))
6221 return setError(E_INVALIDARG,
6222 tr("Invalid connection type: %d"),
6223 aConnectionType);
6224
6225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6226
6227 HRESULT rc = i_checkStateDependency(MutableStateDep);
6228 if (FAILED(rc)) return rc;
6229
6230 /* try to find one with the name first. */
6231 ComObjPtr<StorageController> ctrl;
6232
6233 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6234 if (SUCCEEDED(rc))
6235 return setError(VBOX_E_OBJECT_IN_USE,
6236 tr("Storage controller named '%s' already exists"),
6237 aName.c_str());
6238
6239 ctrl.createObject();
6240
6241 /* get a new instance number for the storage controller */
6242 ULONG ulInstance = 0;
6243 bool fBootable = true;
6244 for (StorageControllerList::const_iterator
6245 it = mStorageControllers->begin();
6246 it != mStorageControllers->end();
6247 ++it)
6248 {
6249 if ((*it)->i_getStorageBus() == aConnectionType)
6250 {
6251 ULONG ulCurInst = (*it)->i_getInstance();
6252
6253 if (ulCurInst >= ulInstance)
6254 ulInstance = ulCurInst + 1;
6255
6256 /* Only one controller of each type can be marked as bootable. */
6257 if ((*it)->i_getBootable())
6258 fBootable = false;
6259 }
6260 }
6261
6262 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6263 if (FAILED(rc)) return rc;
6264
6265 i_setModified(IsModified_Storage);
6266 mStorageControllers.backup();
6267 mStorageControllers->push_back(ctrl);
6268
6269 ctrl.queryInterfaceTo(aController.asOutParam());
6270
6271 /* inform the direct session if any */
6272 alock.release();
6273 i_onStorageControllerChange();
6274
6275 return S_OK;
6276}
6277
6278HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6279 ComPtr<IStorageController> &aStorageController)
6280{
6281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6282
6283 ComObjPtr<StorageController> ctrl;
6284
6285 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6286 if (SUCCEEDED(rc))
6287 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6288
6289 return rc;
6290}
6291
6292HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6293 ULONG aInstance,
6294 ComPtr<IStorageController> &aStorageController)
6295{
6296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6297
6298 for (StorageControllerList::const_iterator
6299 it = mStorageControllers->begin();
6300 it != mStorageControllers->end();
6301 ++it)
6302 {
6303 if ( (*it)->i_getStorageBus() == aConnectionType
6304 && (*it)->i_getInstance() == aInstance)
6305 {
6306 (*it).queryInterfaceTo(aStorageController.asOutParam());
6307 return S_OK;
6308 }
6309 }
6310
6311 return setError(VBOX_E_OBJECT_NOT_FOUND,
6312 tr("Could not find a storage controller with instance number '%lu'"),
6313 aInstance);
6314}
6315
6316HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6317{
6318 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6319
6320 HRESULT rc = i_checkStateDependency(MutableStateDep);
6321 if (FAILED(rc)) return rc;
6322
6323 ComObjPtr<StorageController> ctrl;
6324
6325 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6326 if (SUCCEEDED(rc))
6327 {
6328 /* Ensure that only one controller of each type is marked as bootable. */
6329 if (aBootable == TRUE)
6330 {
6331 for (StorageControllerList::const_iterator
6332 it = mStorageControllers->begin();
6333 it != mStorageControllers->end();
6334 ++it)
6335 {
6336 ComObjPtr<StorageController> aCtrl = (*it);
6337
6338 if ( (aCtrl->i_getName() != aName)
6339 && aCtrl->i_getBootable() == TRUE
6340 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6341 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6342 {
6343 aCtrl->i_setBootable(FALSE);
6344 break;
6345 }
6346 }
6347 }
6348
6349 if (SUCCEEDED(rc))
6350 {
6351 ctrl->i_setBootable(aBootable);
6352 i_setModified(IsModified_Storage);
6353 }
6354 }
6355
6356 if (SUCCEEDED(rc))
6357 {
6358 /* inform the direct session if any */
6359 alock.release();
6360 i_onStorageControllerChange();
6361 }
6362
6363 return rc;
6364}
6365
6366HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6367{
6368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6369
6370 HRESULT rc = i_checkStateDependency(MutableStateDep);
6371 if (FAILED(rc)) return rc;
6372
6373 ComObjPtr<StorageController> ctrl;
6374 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6375 if (FAILED(rc)) return rc;
6376
6377 {
6378 /* find all attached devices to the appropriate storage controller and detach them all */
6379 // make a temporary list because detachDevice invalidates iterators into
6380 // mMediumAttachments
6381 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6382
6383 for (MediumAttachmentList::const_iterator
6384 it = llAttachments2.begin();
6385 it != llAttachments2.end();
6386 ++it)
6387 {
6388 MediumAttachment *pAttachTemp = *it;
6389
6390 AutoCaller localAutoCaller(pAttachTemp);
6391 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6392
6393 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6394
6395 if (pAttachTemp->i_getControllerName() == aName)
6396 {
6397 rc = i_detachDevice(pAttachTemp, alock, NULL);
6398 if (FAILED(rc)) return rc;
6399 }
6400 }
6401 }
6402
6403 /* We can remove it now. */
6404 i_setModified(IsModified_Storage);
6405 mStorageControllers.backup();
6406
6407 ctrl->i_unshare();
6408
6409 mStorageControllers->remove(ctrl);
6410
6411 /* inform the direct session if any */
6412 alock.release();
6413 i_onStorageControllerChange();
6414
6415 return S_OK;
6416}
6417
6418HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6419 ComPtr<IUSBController> &aController)
6420{
6421 if ( (aType <= USBControllerType_Null)
6422 || (aType >= USBControllerType_Last))
6423 return setError(E_INVALIDARG,
6424 tr("Invalid USB controller type: %d"),
6425 aType);
6426
6427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6428
6429 HRESULT rc = i_checkStateDependency(MutableStateDep);
6430 if (FAILED(rc)) return rc;
6431
6432 /* try to find one with the same type first. */
6433 ComObjPtr<USBController> ctrl;
6434
6435 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6436 if (SUCCEEDED(rc))
6437 return setError(VBOX_E_OBJECT_IN_USE,
6438 tr("USB controller named '%s' already exists"),
6439 aName.c_str());
6440
6441 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6442 ULONG maxInstances;
6443 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6444 if (FAILED(rc))
6445 return rc;
6446
6447 ULONG cInstances = i_getUSBControllerCountByType(aType);
6448 if (cInstances >= maxInstances)
6449 return setError(E_INVALIDARG,
6450 tr("Too many USB controllers of this type"));
6451
6452 ctrl.createObject();
6453
6454 rc = ctrl->init(this, aName, aType);
6455 if (FAILED(rc)) return rc;
6456
6457 i_setModified(IsModified_USB);
6458 mUSBControllers.backup();
6459 mUSBControllers->push_back(ctrl);
6460
6461 ctrl.queryInterfaceTo(aController.asOutParam());
6462
6463 /* inform the direct session if any */
6464 alock.release();
6465 i_onUSBControllerChange();
6466
6467 return S_OK;
6468}
6469
6470HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6471{
6472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6473
6474 ComObjPtr<USBController> ctrl;
6475
6476 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6477 if (SUCCEEDED(rc))
6478 ctrl.queryInterfaceTo(aController.asOutParam());
6479
6480 return rc;
6481}
6482
6483HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6484 ULONG *aControllers)
6485{
6486 if ( (aType <= USBControllerType_Null)
6487 || (aType >= USBControllerType_Last))
6488 return setError(E_INVALIDARG,
6489 tr("Invalid USB controller type: %d"),
6490 aType);
6491
6492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6493
6494 ComObjPtr<USBController> ctrl;
6495
6496 *aControllers = i_getUSBControllerCountByType(aType);
6497
6498 return S_OK;
6499}
6500
6501HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6502{
6503
6504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6505
6506 HRESULT rc = i_checkStateDependency(MutableStateDep);
6507 if (FAILED(rc)) return rc;
6508
6509 ComObjPtr<USBController> ctrl;
6510 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6511 if (FAILED(rc)) return rc;
6512
6513 i_setModified(IsModified_USB);
6514 mUSBControllers.backup();
6515
6516 ctrl->i_unshare();
6517
6518 mUSBControllers->remove(ctrl);
6519
6520 /* inform the direct session if any */
6521 alock.release();
6522 i_onUSBControllerChange();
6523
6524 return S_OK;
6525}
6526
6527HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6528 ULONG *aOriginX,
6529 ULONG *aOriginY,
6530 ULONG *aWidth,
6531 ULONG *aHeight,
6532 BOOL *aEnabled)
6533{
6534 uint32_t u32OriginX= 0;
6535 uint32_t u32OriginY= 0;
6536 uint32_t u32Width = 0;
6537 uint32_t u32Height = 0;
6538 uint16_t u16Flags = 0;
6539
6540 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6541 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6542 if (RT_FAILURE(vrc))
6543 {
6544#ifdef RT_OS_WINDOWS
6545 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6546 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6547 * So just assign fEnable to TRUE again.
6548 * The right fix would be to change GUI API wrappers to make sure that parameters
6549 * are changed only if API succeeds.
6550 */
6551 *aEnabled = TRUE;
6552#endif
6553 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6554 tr("Saved guest size is not available (%Rrc)"),
6555 vrc);
6556 }
6557
6558 *aOriginX = u32OriginX;
6559 *aOriginY = u32OriginY;
6560 *aWidth = u32Width;
6561 *aHeight = u32Height;
6562 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6563
6564 return S_OK;
6565}
6566
6567HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6568 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6569{
6570 if (aScreenId != 0)
6571 return E_NOTIMPL;
6572
6573 if ( aBitmapFormat != BitmapFormat_BGR0
6574 && aBitmapFormat != BitmapFormat_BGRA
6575 && aBitmapFormat != BitmapFormat_RGBA
6576 && aBitmapFormat != BitmapFormat_PNG)
6577 return setError(E_NOTIMPL,
6578 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6579
6580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6581
6582 uint8_t *pu8Data = NULL;
6583 uint32_t cbData = 0;
6584 uint32_t u32Width = 0;
6585 uint32_t u32Height = 0;
6586
6587 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6588
6589 if (RT_FAILURE(vrc))
6590 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6591 tr("Saved thumbnail data is not available (%Rrc)"),
6592 vrc);
6593
6594 HRESULT hr = S_OK;
6595
6596 *aWidth = u32Width;
6597 *aHeight = u32Height;
6598
6599 if (cbData > 0)
6600 {
6601 /* Convert pixels to the format expected by the API caller. */
6602 if (aBitmapFormat == BitmapFormat_BGR0)
6603 {
6604 /* [0] B, [1] G, [2] R, [3] 0. */
6605 aData.resize(cbData);
6606 memcpy(&aData.front(), pu8Data, cbData);
6607 }
6608 else if (aBitmapFormat == BitmapFormat_BGRA)
6609 {
6610 /* [0] B, [1] G, [2] R, [3] A. */
6611 aData.resize(cbData);
6612 for (uint32_t i = 0; i < cbData; i += 4)
6613 {
6614 aData[i] = pu8Data[i];
6615 aData[i + 1] = pu8Data[i + 1];
6616 aData[i + 2] = pu8Data[i + 2];
6617 aData[i + 3] = 0xff;
6618 }
6619 }
6620 else if (aBitmapFormat == BitmapFormat_RGBA)
6621 {
6622 /* [0] R, [1] G, [2] B, [3] A. */
6623 aData.resize(cbData);
6624 for (uint32_t i = 0; i < cbData; i += 4)
6625 {
6626 aData[i] = pu8Data[i + 2];
6627 aData[i + 1] = pu8Data[i + 1];
6628 aData[i + 2] = pu8Data[i];
6629 aData[i + 3] = 0xff;
6630 }
6631 }
6632 else if (aBitmapFormat == BitmapFormat_PNG)
6633 {
6634 uint8_t *pu8PNG = NULL;
6635 uint32_t cbPNG = 0;
6636 uint32_t cxPNG = 0;
6637 uint32_t cyPNG = 0;
6638
6639 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6640
6641 if (RT_SUCCESS(vrc))
6642 {
6643 aData.resize(cbPNG);
6644 if (cbPNG)
6645 memcpy(&aData.front(), pu8PNG, cbPNG);
6646 }
6647 else
6648 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6649 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6650 vrc);
6651
6652 RTMemFree(pu8PNG);
6653 }
6654 }
6655
6656 freeSavedDisplayScreenshot(pu8Data);
6657
6658 return hr;
6659}
6660
6661HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6662 ULONG *aWidth,
6663 ULONG *aHeight,
6664 std::vector<BitmapFormat_T> &aBitmapFormats)
6665{
6666 if (aScreenId != 0)
6667 return E_NOTIMPL;
6668
6669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6670
6671 uint8_t *pu8Data = NULL;
6672 uint32_t cbData = 0;
6673 uint32_t u32Width = 0;
6674 uint32_t u32Height = 0;
6675
6676 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6677
6678 if (RT_FAILURE(vrc))
6679 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6680 tr("Saved screenshot data is not available (%Rrc)"),
6681 vrc);
6682
6683 *aWidth = u32Width;
6684 *aHeight = u32Height;
6685 aBitmapFormats.resize(1);
6686 aBitmapFormats[0] = BitmapFormat_PNG;
6687
6688 freeSavedDisplayScreenshot(pu8Data);
6689
6690 return S_OK;
6691}
6692
6693HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6694 BitmapFormat_T aBitmapFormat,
6695 ULONG *aWidth,
6696 ULONG *aHeight,
6697 std::vector<BYTE> &aData)
6698{
6699 if (aScreenId != 0)
6700 return E_NOTIMPL;
6701
6702 if (aBitmapFormat != BitmapFormat_PNG)
6703 return E_NOTIMPL;
6704
6705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6706
6707 uint8_t *pu8Data = NULL;
6708 uint32_t cbData = 0;
6709 uint32_t u32Width = 0;
6710 uint32_t u32Height = 0;
6711
6712 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6713
6714 if (RT_FAILURE(vrc))
6715 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6716 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6717 vrc);
6718
6719 *aWidth = u32Width;
6720 *aHeight = u32Height;
6721
6722 aData.resize(cbData);
6723 if (cbData)
6724 memcpy(&aData.front(), pu8Data, cbData);
6725
6726 freeSavedDisplayScreenshot(pu8Data);
6727
6728 return S_OK;
6729}
6730
6731HRESULT Machine::hotPlugCPU(ULONG aCpu)
6732{
6733 HRESULT rc = S_OK;
6734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6735
6736 if (!mHWData->mCPUHotPlugEnabled)
6737 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6738
6739 if (aCpu >= mHWData->mCPUCount)
6740 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6741
6742 if (mHWData->mCPUAttached[aCpu])
6743 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6744
6745 alock.release();
6746 rc = i_onCPUChange(aCpu, false);
6747 alock.acquire();
6748 if (FAILED(rc)) return rc;
6749
6750 i_setModified(IsModified_MachineData);
6751 mHWData.backup();
6752 mHWData->mCPUAttached[aCpu] = true;
6753
6754 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6755 if (Global::IsOnline(mData->mMachineState))
6756 i_saveSettings(NULL);
6757
6758 return S_OK;
6759}
6760
6761HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6762{
6763 HRESULT rc = S_OK;
6764
6765 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6766
6767 if (!mHWData->mCPUHotPlugEnabled)
6768 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6769
6770 if (aCpu >= SchemaDefs::MaxCPUCount)
6771 return setError(E_INVALIDARG,
6772 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6773 SchemaDefs::MaxCPUCount);
6774
6775 if (!mHWData->mCPUAttached[aCpu])
6776 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6777
6778 /* CPU 0 can't be detached */
6779 if (aCpu == 0)
6780 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6781
6782 alock.release();
6783 rc = i_onCPUChange(aCpu, true);
6784 alock.acquire();
6785 if (FAILED(rc)) return rc;
6786
6787 i_setModified(IsModified_MachineData);
6788 mHWData.backup();
6789 mHWData->mCPUAttached[aCpu] = false;
6790
6791 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6792 if (Global::IsOnline(mData->mMachineState))
6793 i_saveSettings(NULL);
6794
6795 return S_OK;
6796}
6797
6798HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6799{
6800 *aAttached = false;
6801
6802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6803
6804 /* If hotplug is enabled the CPU is always enabled. */
6805 if (!mHWData->mCPUHotPlugEnabled)
6806 {
6807 if (aCpu < mHWData->mCPUCount)
6808 *aAttached = true;
6809 }
6810 else
6811 {
6812 if (aCpu < SchemaDefs::MaxCPUCount)
6813 *aAttached = mHWData->mCPUAttached[aCpu];
6814 }
6815
6816 return S_OK;
6817}
6818
6819HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6820{
6821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6822
6823 Utf8Str log = i_getLogFilename(aIdx);
6824 if (!RTFileExists(log.c_str()))
6825 log.setNull();
6826 aFilename = log;
6827
6828 return S_OK;
6829}
6830
6831HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6832{
6833 if (aSize < 0)
6834 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6835
6836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6837
6838 HRESULT rc = S_OK;
6839 Utf8Str log = i_getLogFilename(aIdx);
6840
6841 /* do not unnecessarily hold the lock while doing something which does
6842 * not need the lock and potentially takes a long time. */
6843 alock.release();
6844
6845 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6846 * keeps the SOAP reply size under 1M for the webservice (we're using
6847 * base64 encoded strings for binary data for years now, avoiding the
6848 * expansion of each byte array element to approx. 25 bytes of XML. */
6849 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6850 aData.resize(cbData);
6851
6852 RTFILE LogFile;
6853 int vrc = RTFileOpen(&LogFile, log.c_str(),
6854 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6855 if (RT_SUCCESS(vrc))
6856 {
6857 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6858 if (RT_SUCCESS(vrc))
6859 aData.resize(cbData);
6860 else
6861 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6862 tr("Could not read log file '%s' (%Rrc)"),
6863 log.c_str(), vrc);
6864 RTFileClose(LogFile);
6865 }
6866 else
6867 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6868 tr("Could not open log file '%s' (%Rrc)"),
6869 log.c_str(), vrc);
6870
6871 if (FAILED(rc))
6872 aData.resize(0);
6873
6874 return rc;
6875}
6876
6877
6878/**
6879 * Currently this method doesn't attach device to the running VM,
6880 * just makes sure it's plugged on next VM start.
6881 */
6882HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6883{
6884 // lock scope
6885 {
6886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6887
6888 HRESULT rc = i_checkStateDependency(MutableStateDep);
6889 if (FAILED(rc)) return rc;
6890
6891 ChipsetType_T aChipset = ChipsetType_PIIX3;
6892 COMGETTER(ChipsetType)(&aChipset);
6893
6894 if (aChipset != ChipsetType_ICH9)
6895 {
6896 return setError(E_INVALIDARG,
6897 tr("Host PCI attachment only supported with ICH9 chipset"));
6898 }
6899
6900 // check if device with this host PCI address already attached
6901 for (HWData::PCIDeviceAssignmentList::const_iterator
6902 it = mHWData->mPCIDeviceAssignments.begin();
6903 it != mHWData->mPCIDeviceAssignments.end();
6904 ++it)
6905 {
6906 LONG iHostAddress = -1;
6907 ComPtr<PCIDeviceAttachment> pAttach;
6908 pAttach = *it;
6909 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6910 if (iHostAddress == aHostAddress)
6911 return setError(E_INVALIDARG,
6912 tr("Device with host PCI address already attached to this VM"));
6913 }
6914
6915 ComObjPtr<PCIDeviceAttachment> pda;
6916 char name[32];
6917
6918 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6919 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6920 pda.createObject();
6921 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6922 i_setModified(IsModified_MachineData);
6923 mHWData.backup();
6924 mHWData->mPCIDeviceAssignments.push_back(pda);
6925 }
6926
6927 return S_OK;
6928}
6929
6930/**
6931 * Currently this method doesn't detach device from the running VM,
6932 * just makes sure it's not plugged on next VM start.
6933 */
6934HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6935{
6936 ComObjPtr<PCIDeviceAttachment> pAttach;
6937 bool fRemoved = false;
6938 HRESULT rc;
6939
6940 // lock scope
6941 {
6942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6943
6944 rc = i_checkStateDependency(MutableStateDep);
6945 if (FAILED(rc)) return rc;
6946
6947 for (HWData::PCIDeviceAssignmentList::const_iterator
6948 it = mHWData->mPCIDeviceAssignments.begin();
6949 it != mHWData->mPCIDeviceAssignments.end();
6950 ++it)
6951 {
6952 LONG iHostAddress = -1;
6953 pAttach = *it;
6954 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6955 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6956 {
6957 i_setModified(IsModified_MachineData);
6958 mHWData.backup();
6959 mHWData->mPCIDeviceAssignments.remove(pAttach);
6960 fRemoved = true;
6961 break;
6962 }
6963 }
6964 }
6965
6966
6967 /* Fire event outside of the lock */
6968 if (fRemoved)
6969 {
6970 Assert(!pAttach.isNull());
6971 ComPtr<IEventSource> es;
6972 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6973 Assert(SUCCEEDED(rc));
6974 Bstr mid;
6975 rc = this->COMGETTER(Id)(mid.asOutParam());
6976 Assert(SUCCEEDED(rc));
6977 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6978 }
6979
6980 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6981 tr("No host PCI device %08x attached"),
6982 aHostAddress
6983 );
6984}
6985
6986HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6987{
6988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6989
6990 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6991 size_t i = 0;
6992 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6993 it = mHWData->mPCIDeviceAssignments.begin();
6994 it != mHWData->mPCIDeviceAssignments.end();
6995 ++it, ++i)
6996 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6997
6998 return S_OK;
6999}
7000
7001HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
7002{
7003 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
7004
7005 return S_OK;
7006}
7007
7008HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7009{
7010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7011
7012 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7013
7014 return S_OK;
7015}
7016
7017HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7018{
7019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7020 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7021 if (SUCCEEDED(hrc))
7022 {
7023 hrc = mHWData.backupEx();
7024 if (SUCCEEDED(hrc))
7025 {
7026 i_setModified(IsModified_MachineData);
7027 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7028 }
7029 }
7030 return hrc;
7031}
7032
7033HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7034{
7035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7036 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7037 return S_OK;
7038}
7039
7040HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7041{
7042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7043 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7044 if (SUCCEEDED(hrc))
7045 {
7046 hrc = mHWData.backupEx();
7047 if (SUCCEEDED(hrc))
7048 {
7049 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7050 if (SUCCEEDED(hrc))
7051 i_setModified(IsModified_MachineData);
7052 }
7053 }
7054 return hrc;
7055}
7056
7057HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7058{
7059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7060
7061 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7062
7063 return S_OK;
7064}
7065
7066HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7067{
7068 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7069 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7070 if (SUCCEEDED(hrc))
7071 {
7072 hrc = mHWData.backupEx();
7073 if (SUCCEEDED(hrc))
7074 {
7075 i_setModified(IsModified_MachineData);
7076 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7077 }
7078 }
7079 return hrc;
7080}
7081
7082HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7083{
7084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7085
7086 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7087
7088 return S_OK;
7089}
7090
7091HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7092{
7093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7094
7095 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7096 if ( SUCCEEDED(hrc)
7097 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7098 {
7099 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7100 int vrc;
7101
7102 if (aAutostartEnabled)
7103 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7104 else
7105 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7106
7107 if (RT_SUCCESS(vrc))
7108 {
7109 hrc = mHWData.backupEx();
7110 if (SUCCEEDED(hrc))
7111 {
7112 i_setModified(IsModified_MachineData);
7113 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7114 }
7115 }
7116 else if (vrc == VERR_NOT_SUPPORTED)
7117 hrc = setError(VBOX_E_NOT_SUPPORTED,
7118 tr("The VM autostart feature is not supported on this platform"));
7119 else if (vrc == VERR_PATH_NOT_FOUND)
7120 hrc = setError(E_FAIL,
7121 tr("The path to the autostart database is not set"));
7122 else
7123 hrc = setError(E_UNEXPECTED,
7124 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7125 aAutostartEnabled ? "Adding" : "Removing",
7126 mUserData->s.strName.c_str(), vrc);
7127 }
7128 return hrc;
7129}
7130
7131HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7132{
7133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7134
7135 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7136
7137 return S_OK;
7138}
7139
7140HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7141{
7142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7143 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7144 if (SUCCEEDED(hrc))
7145 {
7146 hrc = mHWData.backupEx();
7147 if (SUCCEEDED(hrc))
7148 {
7149 i_setModified(IsModified_MachineData);
7150 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7151 }
7152 }
7153 return hrc;
7154}
7155
7156HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7157{
7158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7159
7160 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7161
7162 return S_OK;
7163}
7164
7165HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7166{
7167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7168 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7169 if ( SUCCEEDED(hrc)
7170 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7171 {
7172 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7173 int vrc;
7174
7175 if (aAutostopType != AutostopType_Disabled)
7176 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7177 else
7178 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7179
7180 if (RT_SUCCESS(vrc))
7181 {
7182 hrc = mHWData.backupEx();
7183 if (SUCCEEDED(hrc))
7184 {
7185 i_setModified(IsModified_MachineData);
7186 mHWData->mAutostart.enmAutostopType = aAutostopType;
7187 }
7188 }
7189 else if (vrc == VERR_NOT_SUPPORTED)
7190 hrc = setError(VBOX_E_NOT_SUPPORTED,
7191 tr("The VM autostop feature is not supported on this platform"));
7192 else if (vrc == VERR_PATH_NOT_FOUND)
7193 hrc = setError(E_FAIL,
7194 tr("The path to the autostart database is not set"));
7195 else
7196 hrc = setError(E_UNEXPECTED,
7197 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7198 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7199 mUserData->s.strName.c_str(), vrc);
7200 }
7201 return hrc;
7202}
7203
7204HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7205{
7206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7207
7208 aDefaultFrontend = mHWData->mDefaultFrontend;
7209
7210 return S_OK;
7211}
7212
7213HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7214{
7215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7216 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7217 if (SUCCEEDED(hrc))
7218 {
7219 hrc = mHWData.backupEx();
7220 if (SUCCEEDED(hrc))
7221 {
7222 i_setModified(IsModified_MachineData);
7223 mHWData->mDefaultFrontend = aDefaultFrontend;
7224 }
7225 }
7226 return hrc;
7227}
7228
7229HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7230{
7231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7232 size_t cbIcon = mUserData->s.ovIcon.size();
7233 aIcon.resize(cbIcon);
7234 if (cbIcon)
7235 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7236 return S_OK;
7237}
7238
7239HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7240{
7241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7242 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7243 if (SUCCEEDED(hrc))
7244 {
7245 i_setModified(IsModified_MachineData);
7246 mUserData.backup();
7247 size_t cbIcon = aIcon.size();
7248 mUserData->s.ovIcon.resize(cbIcon);
7249 if (cbIcon)
7250 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7251 }
7252 return hrc;
7253}
7254
7255HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7256{
7257#ifdef VBOX_WITH_USB
7258 *aUSBProxyAvailable = true;
7259#else
7260 *aUSBProxyAvailable = false;
7261#endif
7262 return S_OK;
7263}
7264
7265HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7266{
7267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7268
7269 aVMProcessPriority = mUserData->s.strVMPriority;
7270
7271 return S_OK;
7272}
7273
7274HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7275{
7276 RT_NOREF(aVMProcessPriority);
7277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7278 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7279 if (SUCCEEDED(hrc))
7280 {
7281 /** @todo r=klaus: currently this is marked as not implemented, as
7282 * the code for setting the priority of the process is not there
7283 * (neither when starting the VM nor at runtime). */
7284 ReturnComNotImplemented();
7285#if 0
7286 hrc = mUserData.backupEx();
7287 if (SUCCEEDED(hrc))
7288 {
7289 i_setModified(IsModified_MachineData);
7290 mUserData->s.strVMPriority = aVMProcessPriority;
7291 }
7292#endif
7293 }
7294 return hrc;
7295}
7296
7297HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7298 ComPtr<IProgress> &aProgress)
7299{
7300 ComObjPtr<Progress> pP;
7301 Progress *ppP = pP;
7302 IProgress *iP = static_cast<IProgress *>(ppP);
7303 IProgress **pProgress = &iP;
7304
7305 IMachine *pTarget = aTarget;
7306
7307 /* Convert the options. */
7308 RTCList<CloneOptions_T> optList;
7309 if (aOptions.size())
7310 for (size_t i = 0; i < aOptions.size(); ++i)
7311 optList.append(aOptions[i]);
7312
7313 if (optList.contains(CloneOptions_Link))
7314 {
7315 if (!i_isSnapshotMachine())
7316 return setError(E_INVALIDARG,
7317 tr("Linked clone can only be created from a snapshot"));
7318 if (aMode != CloneMode_MachineState)
7319 return setError(E_INVALIDARG,
7320 tr("Linked clone can only be created for a single machine state"));
7321 }
7322 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7323
7324 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7325
7326 HRESULT rc = pWorker->start(pProgress);
7327
7328 pP = static_cast<Progress *>(*pProgress);
7329 pP.queryInterfaceTo(aProgress.asOutParam());
7330
7331 return rc;
7332
7333}
7334
7335HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7336 const com::Utf8Str &aType,
7337 ComPtr<IProgress> &aProgress)
7338{
7339 LogFlowThisFuncEnter();
7340
7341 ComObjPtr<Progress> progress;
7342
7343 progress.createObject();
7344
7345 HRESULT rc = S_OK;
7346 Utf8Str targetPath = aTargetPath;
7347 Utf8Str type = aType;
7348
7349 /* Initialize our worker task */
7350 MachineMoveVM* task = NULL;
7351 try
7352 {
7353 task = new MachineMoveVM(this, targetPath, type, progress);
7354 }
7355 catch(...)
7356 {
7357 delete task;
7358 return rc;
7359 }
7360
7361 /*
7362 * task pointer will be owned by the ThreadTask class.
7363 * There is no need to call operator "delete" in the end.
7364 */
7365 rc = task->init();
7366 if (SUCCEEDED(rc))
7367 {
7368 rc = task->createThread();
7369 if (FAILED(rc))
7370 {
7371 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7372 }
7373
7374 /* Return progress to the caller */
7375 progress.queryInterfaceTo(aProgress.asOutParam());
7376 }
7377
7378 LogFlowThisFuncLeave();
7379 return rc;
7380
7381}
7382
7383HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7384{
7385 NOREF(aProgress);
7386 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7387
7388 // This check should always fail.
7389 HRESULT rc = i_checkStateDependency(MutableStateDep);
7390 if (FAILED(rc)) return rc;
7391
7392 AssertFailedReturn(E_NOTIMPL);
7393}
7394
7395HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7396{
7397 NOREF(aSavedStateFile);
7398 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7399
7400 // This check should always fail.
7401 HRESULT rc = i_checkStateDependency(MutableStateDep);
7402 if (FAILED(rc)) return rc;
7403
7404 AssertFailedReturn(E_NOTIMPL);
7405}
7406
7407HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7408{
7409 NOREF(aFRemoveFile);
7410 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7411
7412 // This check should always fail.
7413 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7414 if (FAILED(rc)) return rc;
7415
7416 AssertFailedReturn(E_NOTIMPL);
7417}
7418
7419// public methods for internal purposes
7420/////////////////////////////////////////////////////////////////////////////
7421
7422/**
7423 * Adds the given IsModified_* flag to the dirty flags of the machine.
7424 * This must be called either during i_loadSettings or under the machine write lock.
7425 * @param fl Flag
7426 * @param fAllowStateModification If state modifications are allowed.
7427 */
7428void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7429{
7430 mData->flModifications |= fl;
7431 if (fAllowStateModification && i_isStateModificationAllowed())
7432 mData->mCurrentStateModified = true;
7433}
7434
7435/**
7436 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7437 * care of the write locking.
7438 *
7439 * @param fModification The flag to add.
7440 * @param fAllowStateModification If state modifications are allowed.
7441 */
7442void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7443{
7444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7445 i_setModified(fModification, fAllowStateModification);
7446}
7447
7448/**
7449 * Saves the registry entry of this machine to the given configuration node.
7450 *
7451 * @param data Machine registry data.
7452 *
7453 * @note locks this object for reading.
7454 */
7455HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7456{
7457 AutoLimitedCaller autoCaller(this);
7458 AssertComRCReturnRC(autoCaller.rc());
7459
7460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7461
7462 data.uuid = mData->mUuid;
7463 data.strSettingsFile = mData->m_strConfigFile;
7464
7465 return S_OK;
7466}
7467
7468/**
7469 * Calculates the absolute path of the given path taking the directory of the
7470 * machine settings file as the current directory.
7471 *
7472 * @param strPath Path to calculate the absolute path for.
7473 * @param aResult Where to put the result (used only on success, can be the
7474 * same Utf8Str instance as passed in @a aPath).
7475 * @return IPRT result.
7476 *
7477 * @note Locks this object for reading.
7478 */
7479int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7480{
7481 AutoCaller autoCaller(this);
7482 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7483
7484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7485
7486 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7487
7488 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7489
7490 strSettingsDir.stripFilename();
7491 char folder[RTPATH_MAX];
7492 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7493 if (RT_SUCCESS(vrc))
7494 aResult = folder;
7495
7496 return vrc;
7497}
7498
7499/**
7500 * Copies strSource to strTarget, making it relative to the machine folder
7501 * if it is a subdirectory thereof, or simply copying it otherwise.
7502 *
7503 * @param strSource Path to evaluate and copy.
7504 * @param strTarget Buffer to receive target path.
7505 *
7506 * @note Locks this object for reading.
7507 */
7508void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7509 Utf8Str &strTarget)
7510{
7511 AutoCaller autoCaller(this);
7512 AssertComRCReturn(autoCaller.rc(), (void)0);
7513
7514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7515
7516 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7517 // use strTarget as a temporary buffer to hold the machine settings dir
7518 strTarget = mData->m_strConfigFileFull;
7519 strTarget.stripFilename();
7520 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7521 {
7522 // is relative: then append what's left
7523 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7524 // for empty paths (only possible for subdirs) use "." to avoid
7525 // triggering default settings for not present config attributes.
7526 if (strTarget.isEmpty())
7527 strTarget = ".";
7528 }
7529 else
7530 // is not relative: then overwrite
7531 strTarget = strSource;
7532}
7533
7534/**
7535 * Returns the full path to the machine's log folder in the
7536 * \a aLogFolder argument.
7537 */
7538void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7539{
7540 AutoCaller autoCaller(this);
7541 AssertComRCReturnVoid(autoCaller.rc());
7542
7543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7544
7545 char szTmp[RTPATH_MAX];
7546 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7547 if (RT_SUCCESS(vrc))
7548 {
7549 if (szTmp[0] && !mUserData.isNull())
7550 {
7551 char szTmp2[RTPATH_MAX];
7552 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7553 if (RT_SUCCESS(vrc))
7554 aLogFolder = Utf8StrFmt("%s%c%s",
7555 szTmp2,
7556 RTPATH_DELIMITER,
7557 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7558 }
7559 else
7560 vrc = VERR_PATH_IS_RELATIVE;
7561 }
7562
7563 if (RT_FAILURE(vrc))
7564 {
7565 // fallback if VBOX_USER_LOGHOME is not set or invalid
7566 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7567 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7568 aLogFolder.append(RTPATH_DELIMITER);
7569 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7570 }
7571}
7572
7573/**
7574 * Returns the full path to the machine's log file for an given index.
7575 */
7576Utf8Str Machine::i_getLogFilename(ULONG idx)
7577{
7578 Utf8Str logFolder;
7579 getLogFolder(logFolder);
7580 Assert(logFolder.length());
7581
7582 Utf8Str log;
7583 if (idx == 0)
7584 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7585#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7586 else if (idx == 1)
7587 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7588 else
7589 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7590#else
7591 else
7592 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7593#endif
7594 return log;
7595}
7596
7597/**
7598 * Returns the full path to the machine's hardened log file.
7599 */
7600Utf8Str Machine::i_getHardeningLogFilename(void)
7601{
7602 Utf8Str strFilename;
7603 getLogFolder(strFilename);
7604 Assert(strFilename.length());
7605 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7606 return strFilename;
7607}
7608
7609
7610/**
7611 * Composes a unique saved state filename based on the current system time. The filename is
7612 * granular to the second so this will work so long as no more than one snapshot is taken on
7613 * a machine per second.
7614 *
7615 * Before version 4.1, we used this formula for saved state files:
7616 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7617 * which no longer works because saved state files can now be shared between the saved state of the
7618 * "saved" machine and an online snapshot, and the following would cause problems:
7619 * 1) save machine
7620 * 2) create online snapshot from that machine state --> reusing saved state file
7621 * 3) save machine again --> filename would be reused, breaking the online snapshot
7622 *
7623 * So instead we now use a timestamp.
7624 *
7625 * @param strStateFilePath
7626 */
7627
7628void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7629{
7630 AutoCaller autoCaller(this);
7631 AssertComRCReturnVoid(autoCaller.rc());
7632
7633 {
7634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7635 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7636 }
7637
7638 RTTIMESPEC ts;
7639 RTTimeNow(&ts);
7640 RTTIME time;
7641 RTTimeExplode(&time, &ts);
7642
7643 strStateFilePath += RTPATH_DELIMITER;
7644 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7645 time.i32Year, time.u8Month, time.u8MonthDay,
7646 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7647}
7648
7649/**
7650 * Returns the full path to the default video capture file.
7651 */
7652void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7653{
7654 AutoCaller autoCaller(this);
7655 AssertComRCReturnVoid(autoCaller.rc());
7656
7657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7658
7659 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7660 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7661 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7662}
7663
7664/**
7665 * Returns whether at least one USB controller is present for the VM.
7666 */
7667bool Machine::i_isUSBControllerPresent()
7668{
7669 AutoCaller autoCaller(this);
7670 AssertComRCReturn(autoCaller.rc(), false);
7671
7672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7673
7674 return (mUSBControllers->size() > 0);
7675}
7676
7677/**
7678 * @note Locks this object for writing, calls the client process
7679 * (inside the lock).
7680 */
7681HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7682 const Utf8Str &strFrontend,
7683 const Utf8Str &strEnvironment,
7684 ProgressProxy *aProgress)
7685{
7686 LogFlowThisFuncEnter();
7687
7688 AssertReturn(aControl, E_FAIL);
7689 AssertReturn(aProgress, E_FAIL);
7690 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7691
7692 AutoCaller autoCaller(this);
7693 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7694
7695 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7696
7697 if (!mData->mRegistered)
7698 return setError(E_UNEXPECTED,
7699 tr("The machine '%s' is not registered"),
7700 mUserData->s.strName.c_str());
7701
7702 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7703
7704 /* The process started when launching a VM with separate UI/VM processes is always
7705 * the UI process, i.e. needs special handling as it won't claim the session. */
7706 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7707
7708 if (fSeparate)
7709 {
7710 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7711 return setError(VBOX_E_INVALID_OBJECT_STATE,
7712 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7713 mUserData->s.strName.c_str());
7714 }
7715 else
7716 {
7717 if ( mData->mSession.mState == SessionState_Locked
7718 || mData->mSession.mState == SessionState_Spawning
7719 || mData->mSession.mState == SessionState_Unlocking)
7720 return setError(VBOX_E_INVALID_OBJECT_STATE,
7721 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7722 mUserData->s.strName.c_str());
7723
7724 /* may not be busy */
7725 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7726 }
7727
7728 /* get the path to the executable */
7729 char szPath[RTPATH_MAX];
7730 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7731 size_t cchBufLeft = strlen(szPath);
7732 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7733 szPath[cchBufLeft] = 0;
7734 char *pszNamePart = szPath + cchBufLeft;
7735 cchBufLeft = sizeof(szPath) - cchBufLeft;
7736
7737 int vrc = VINF_SUCCESS;
7738 RTPROCESS pid = NIL_RTPROCESS;
7739
7740 RTENV env = RTENV_DEFAULT;
7741
7742 if (!strEnvironment.isEmpty())
7743 {
7744 char *newEnvStr = NULL;
7745
7746 do
7747 {
7748 /* clone the current environment */
7749 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7750 AssertRCBreakStmt(vrc2, vrc = vrc2);
7751
7752 newEnvStr = RTStrDup(strEnvironment.c_str());
7753 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7754
7755 /* put new variables to the environment
7756 * (ignore empty variable names here since RTEnv API
7757 * intentionally doesn't do that) */
7758 char *var = newEnvStr;
7759 for (char *p = newEnvStr; *p; ++p)
7760 {
7761 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7762 {
7763 *p = '\0';
7764 if (*var)
7765 {
7766 char *val = strchr(var, '=');
7767 if (val)
7768 {
7769 *val++ = '\0';
7770 vrc2 = RTEnvSetEx(env, var, val);
7771 }
7772 else
7773 vrc2 = RTEnvUnsetEx(env, var);
7774 if (RT_FAILURE(vrc2))
7775 break;
7776 }
7777 var = p + 1;
7778 }
7779 }
7780 if (RT_SUCCESS(vrc2) && *var)
7781 vrc2 = RTEnvPutEx(env, var);
7782
7783 AssertRCBreakStmt(vrc2, vrc = vrc2);
7784 }
7785 while (0);
7786
7787 if (newEnvStr != NULL)
7788 RTStrFree(newEnvStr);
7789 }
7790
7791 /* Hardening logging */
7792#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7793 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7794 {
7795 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7796 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7797 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7798 {
7799 Utf8Str strStartupLogDir = strHardeningLogFile;
7800 strStartupLogDir.stripFilename();
7801 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7802 file without stripping the file. */
7803 }
7804 strSupHardeningLogArg.append(strHardeningLogFile);
7805
7806 /* Remove legacy log filename to avoid confusion. */
7807 Utf8Str strOldStartupLogFile;
7808 getLogFolder(strOldStartupLogFile);
7809 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7810 RTFileDelete(strOldStartupLogFile.c_str());
7811 }
7812 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7813#else
7814 const char *pszSupHardeningLogArg = NULL;
7815#endif
7816
7817 Utf8Str strCanonicalName;
7818
7819#ifdef VBOX_WITH_QTGUI
7820 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7821 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7822 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7823 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7824 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7825 {
7826 strCanonicalName = "GUI/Qt";
7827# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7828 /* Modify the base path so that we don't need to use ".." below. */
7829 RTPathStripTrailingSlash(szPath);
7830 RTPathStripFilename(szPath);
7831 cchBufLeft = strlen(szPath);
7832 pszNamePart = szPath + cchBufLeft;
7833 cchBufLeft = sizeof(szPath) - cchBufLeft;
7834
7835# define OSX_APP_NAME "VirtualBoxVM"
7836# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7837
7838 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7839 if ( strAppOverride.contains(".")
7840 || strAppOverride.contains("/")
7841 || strAppOverride.contains("\\")
7842 || strAppOverride.contains(":"))
7843 strAppOverride.setNull();
7844 Utf8Str strAppPath;
7845 if (!strAppOverride.isEmpty())
7846 {
7847 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7848 Utf8Str strFullPath(szPath);
7849 strFullPath.append(strAppPath);
7850 /* there is a race, but people using this deserve the failure */
7851 if (!RTFileExists(strFullPath.c_str()))
7852 strAppOverride.setNull();
7853 }
7854 if (strAppOverride.isEmpty())
7855 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7856 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7857 strcpy(pszNamePart, strAppPath.c_str());
7858# else
7859# ifndef VBOX_GUI_WITH_SHARED_LIBRARY
7860 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7861# else
7862 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7863# endif
7864 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7865 strcpy(pszNamePart, s_szVirtualBox_exe);
7866# endif
7867
7868 Utf8Str idStr = mData->mUuid.toString();
7869 const char *apszArgs[] =
7870 {
7871 szPath,
7872 "--comment", mUserData->s.strName.c_str(),
7873 "--startvm", idStr.c_str(),
7874 "--no-startvm-errormsgbox",
7875 NULL, /* For "--separate". */
7876 NULL, /* For "--sup-startup-log". */
7877 NULL
7878 };
7879 unsigned iArg = 6;
7880 if (fSeparate)
7881 apszArgs[iArg++] = "--separate";
7882 apszArgs[iArg++] = pszSupHardeningLogArg;
7883
7884 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7885 }
7886#else /* !VBOX_WITH_QTGUI */
7887 if (0)
7888 ;
7889#endif /* VBOX_WITH_QTGUI */
7890
7891 else
7892
7893#ifdef VBOX_WITH_VBOXSDL
7894 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7895 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7896 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7897 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7898 {
7899 strCanonicalName = "GUI/SDL";
7900 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7901 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7902 strcpy(pszNamePart, s_szVBoxSDL_exe);
7903
7904 Utf8Str idStr = mData->mUuid.toString();
7905 const char *apszArgs[] =
7906 {
7907 szPath,
7908 "--comment", mUserData->s.strName.c_str(),
7909 "--startvm", idStr.c_str(),
7910 NULL, /* For "--separate". */
7911 NULL, /* For "--sup-startup-log". */
7912 NULL
7913 };
7914 unsigned iArg = 5;
7915 if (fSeparate)
7916 apszArgs[iArg++] = "--separate";
7917 apszArgs[iArg++] = pszSupHardeningLogArg;
7918
7919 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7920 }
7921#else /* !VBOX_WITH_VBOXSDL */
7922 if (0)
7923 ;
7924#endif /* !VBOX_WITH_VBOXSDL */
7925
7926 else
7927
7928#ifdef VBOX_WITH_HEADLESS
7929 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7930 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7931 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7932 )
7933 {
7934 strCanonicalName = "headless";
7935 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7936 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7937 * and a VM works even if the server has not been installed.
7938 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7939 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7940 * differently in 4.0 and 3.x.
7941 */
7942 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7943 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7944 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7945
7946 Utf8Str idStr = mData->mUuid.toString();
7947 const char *apszArgs[] =
7948 {
7949 szPath,
7950 "--comment", mUserData->s.strName.c_str(),
7951 "--startvm", idStr.c_str(),
7952 "--vrde", "config",
7953 NULL, /* For "--capture". */
7954 NULL, /* For "--sup-startup-log". */
7955 NULL
7956 };
7957 unsigned iArg = 7;
7958 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7959 apszArgs[iArg++] = "--capture";
7960 apszArgs[iArg++] = pszSupHardeningLogArg;
7961
7962# ifdef RT_OS_WINDOWS
7963 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7964# else
7965 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7966# endif
7967 }
7968#else /* !VBOX_WITH_HEADLESS */
7969 if (0)
7970 ;
7971#endif /* !VBOX_WITH_HEADLESS */
7972 else
7973 {
7974 RTEnvDestroy(env);
7975 return setError(E_INVALIDARG,
7976 tr("Invalid frontend name: '%s'"),
7977 strFrontend.c_str());
7978 }
7979
7980 RTEnvDestroy(env);
7981
7982 if (RT_FAILURE(vrc))
7983 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7984 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7985 mUserData->s.strName.c_str(), vrc);
7986
7987 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7988
7989 if (!fSeparate)
7990 {
7991 /*
7992 * Note that we don't release the lock here before calling the client,
7993 * because it doesn't need to call us back if called with a NULL argument.
7994 * Releasing the lock here is dangerous because we didn't prepare the
7995 * launch data yet, but the client we've just started may happen to be
7996 * too fast and call LockMachine() that will fail (because of PID, etc.),
7997 * so that the Machine will never get out of the Spawning session state.
7998 */
7999
8000 /* inform the session that it will be a remote one */
8001 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8002#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8003 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8004#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8005 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8006#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8007 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8008
8009 if (FAILED(rc))
8010 {
8011 /* restore the session state */
8012 mData->mSession.mState = SessionState_Unlocked;
8013 alock.release();
8014 mParent->i_addProcessToReap(pid);
8015 /* The failure may occur w/o any error info (from RPC), so provide one */
8016 return setError(VBOX_E_VM_ERROR,
8017 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8018 }
8019
8020 /* attach launch data to the machine */
8021 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8022 mData->mSession.mRemoteControls.push_back(aControl);
8023 mData->mSession.mProgress = aProgress;
8024 mData->mSession.mPID = pid;
8025 mData->mSession.mState = SessionState_Spawning;
8026 Assert(strCanonicalName.isNotEmpty());
8027 mData->mSession.mName = strCanonicalName;
8028 }
8029 else
8030 {
8031 /* For separate UI process we declare the launch as completed instantly, as the
8032 * actual headless VM start may or may not come. No point in remembering anything
8033 * yet, as what matters for us is when the headless VM gets started. */
8034 aProgress->i_notifyComplete(S_OK);
8035 }
8036
8037 alock.release();
8038 mParent->i_addProcessToReap(pid);
8039
8040 LogFlowThisFuncLeave();
8041 return S_OK;
8042}
8043
8044/**
8045 * Returns @c true if the given session machine instance has an open direct
8046 * session (and optionally also for direct sessions which are closing) and
8047 * returns the session control machine instance if so.
8048 *
8049 * Note that when the method returns @c false, the arguments remain unchanged.
8050 *
8051 * @param aMachine Session machine object.
8052 * @param aControl Direct session control object (optional).
8053 * @param aRequireVM If true then only allow VM sessions.
8054 * @param aAllowClosing If true then additionally a session which is currently
8055 * being closed will also be allowed.
8056 *
8057 * @note locks this object for reading.
8058 */
8059bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8060 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8061 bool aRequireVM /*= false*/,
8062 bool aAllowClosing /*= false*/)
8063{
8064 AutoLimitedCaller autoCaller(this);
8065 AssertComRCReturn(autoCaller.rc(), false);
8066
8067 /* just return false for inaccessible machines */
8068 if (getObjectState().getState() != ObjectState::Ready)
8069 return false;
8070
8071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8072
8073 if ( ( mData->mSession.mState == SessionState_Locked
8074 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8075 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8076 )
8077 {
8078 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8079
8080 aMachine = mData->mSession.mMachine;
8081
8082 if (aControl != NULL)
8083 *aControl = mData->mSession.mDirectControl;
8084
8085 return true;
8086 }
8087
8088 return false;
8089}
8090
8091/**
8092 * Returns @c true if the given machine has an spawning direct session.
8093 *
8094 * @note locks this object for reading.
8095 */
8096bool Machine::i_isSessionSpawning()
8097{
8098 AutoLimitedCaller autoCaller(this);
8099 AssertComRCReturn(autoCaller.rc(), false);
8100
8101 /* just return false for inaccessible machines */
8102 if (getObjectState().getState() != ObjectState::Ready)
8103 return false;
8104
8105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8106
8107 if (mData->mSession.mState == SessionState_Spawning)
8108 return true;
8109
8110 return false;
8111}
8112
8113/**
8114 * Called from the client watcher thread to check for unexpected client process
8115 * death during Session_Spawning state (e.g. before it successfully opened a
8116 * direct session).
8117 *
8118 * On Win32 and on OS/2, this method is called only when we've got the
8119 * direct client's process termination notification, so it always returns @c
8120 * true.
8121 *
8122 * On other platforms, this method returns @c true if the client process is
8123 * terminated and @c false if it's still alive.
8124 *
8125 * @note Locks this object for writing.
8126 */
8127bool Machine::i_checkForSpawnFailure()
8128{
8129 AutoCaller autoCaller(this);
8130 if (!autoCaller.isOk())
8131 {
8132 /* nothing to do */
8133 LogFlowThisFunc(("Already uninitialized!\n"));
8134 return true;
8135 }
8136
8137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8138
8139 if (mData->mSession.mState != SessionState_Spawning)
8140 {
8141 /* nothing to do */
8142 LogFlowThisFunc(("Not spawning any more!\n"));
8143 return true;
8144 }
8145
8146 HRESULT rc = S_OK;
8147
8148 /* PID not yet initialized, skip check. */
8149 if (mData->mSession.mPID == NIL_RTPROCESS)
8150 return false;
8151
8152 RTPROCSTATUS status;
8153 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8154
8155 if (vrc != VERR_PROCESS_RUNNING)
8156 {
8157 Utf8Str strExtraInfo;
8158
8159#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8160 /* If the startup logfile exists and is of non-zero length, tell the
8161 user to look there for more details to encourage them to attach it
8162 when reporting startup issues. */
8163 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8164 uint64_t cbStartupLogFile = 0;
8165 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
8166 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8167 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8168#endif
8169
8170 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8171 rc = setError(E_FAIL,
8172 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8173 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8174 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8175 rc = setError(E_FAIL,
8176 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8177 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8178 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8179 rc = setError(E_FAIL,
8180 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8181 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8182 else
8183 rc = setErrorBoth(E_FAIL, vrc,
8184 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8185 i_getName().c_str(), vrc, strExtraInfo.c_str());
8186 }
8187
8188 if (FAILED(rc))
8189 {
8190 /* Close the remote session, remove the remote control from the list
8191 * and reset session state to Closed (@note keep the code in sync with
8192 * the relevant part in LockMachine()). */
8193
8194 Assert(mData->mSession.mRemoteControls.size() == 1);
8195 if (mData->mSession.mRemoteControls.size() == 1)
8196 {
8197 ErrorInfoKeeper eik;
8198 mData->mSession.mRemoteControls.front()->Uninitialize();
8199 }
8200
8201 mData->mSession.mRemoteControls.clear();
8202 mData->mSession.mState = SessionState_Unlocked;
8203
8204 /* finalize the progress after setting the state */
8205 if (!mData->mSession.mProgress.isNull())
8206 {
8207 mData->mSession.mProgress->notifyComplete(rc);
8208 mData->mSession.mProgress.setNull();
8209 }
8210
8211 mData->mSession.mPID = NIL_RTPROCESS;
8212
8213 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8214 return true;
8215 }
8216
8217 return false;
8218}
8219
8220/**
8221 * Checks whether the machine can be registered. If so, commits and saves
8222 * all settings.
8223 *
8224 * @note Must be called from mParent's write lock. Locks this object and
8225 * children for writing.
8226 */
8227HRESULT Machine::i_prepareRegister()
8228{
8229 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8230
8231 AutoLimitedCaller autoCaller(this);
8232 AssertComRCReturnRC(autoCaller.rc());
8233
8234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8235
8236 /* wait for state dependents to drop to zero */
8237 i_ensureNoStateDependencies();
8238
8239 if (!mData->mAccessible)
8240 return setError(VBOX_E_INVALID_OBJECT_STATE,
8241 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8242 mUserData->s.strName.c_str(),
8243 mData->mUuid.toString().c_str());
8244
8245 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8246
8247 if (mData->mRegistered)
8248 return setError(VBOX_E_INVALID_OBJECT_STATE,
8249 tr("The machine '%s' with UUID {%s} is already registered"),
8250 mUserData->s.strName.c_str(),
8251 mData->mUuid.toString().c_str());
8252
8253 HRESULT rc = S_OK;
8254
8255 // Ensure the settings are saved. If we are going to be registered and
8256 // no config file exists yet, create it by calling i_saveSettings() too.
8257 if ( (mData->flModifications)
8258 || (!mData->pMachineConfigFile->fileExists())
8259 )
8260 {
8261 rc = i_saveSettings(NULL);
8262 // no need to check whether VirtualBox.xml needs saving too since
8263 // we can't have a machine XML file rename pending
8264 if (FAILED(rc)) return rc;
8265 }
8266
8267 /* more config checking goes here */
8268
8269 if (SUCCEEDED(rc))
8270 {
8271 /* we may have had implicit modifications we want to fix on success */
8272 i_commit();
8273
8274 mData->mRegistered = true;
8275 }
8276 else
8277 {
8278 /* we may have had implicit modifications we want to cancel on failure*/
8279 i_rollback(false /* aNotify */);
8280 }
8281
8282 return rc;
8283}
8284
8285/**
8286 * Increases the number of objects dependent on the machine state or on the
8287 * registered state. Guarantees that these two states will not change at least
8288 * until #i_releaseStateDependency() is called.
8289 *
8290 * Depending on the @a aDepType value, additional state checks may be made.
8291 * These checks will set extended error info on failure. See
8292 * #i_checkStateDependency() for more info.
8293 *
8294 * If this method returns a failure, the dependency is not added and the caller
8295 * is not allowed to rely on any particular machine state or registration state
8296 * value and may return the failed result code to the upper level.
8297 *
8298 * @param aDepType Dependency type to add.
8299 * @param aState Current machine state (NULL if not interested).
8300 * @param aRegistered Current registered state (NULL if not interested).
8301 *
8302 * @note Locks this object for writing.
8303 */
8304HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8305 MachineState_T *aState /* = NULL */,
8306 BOOL *aRegistered /* = NULL */)
8307{
8308 AutoCaller autoCaller(this);
8309 AssertComRCReturnRC(autoCaller.rc());
8310
8311 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8312
8313 HRESULT rc = i_checkStateDependency(aDepType);
8314 if (FAILED(rc)) return rc;
8315
8316 {
8317 if (mData->mMachineStateChangePending != 0)
8318 {
8319 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8320 * drop to zero so don't add more. It may make sense to wait a bit
8321 * and retry before reporting an error (since the pending state
8322 * transition should be really quick) but let's just assert for
8323 * now to see if it ever happens on practice. */
8324
8325 AssertFailed();
8326
8327 return setError(E_ACCESSDENIED,
8328 tr("Machine state change is in progress. Please retry the operation later."));
8329 }
8330
8331 ++mData->mMachineStateDeps;
8332 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8333 }
8334
8335 if (aState)
8336 *aState = mData->mMachineState;
8337 if (aRegistered)
8338 *aRegistered = mData->mRegistered;
8339
8340 return S_OK;
8341}
8342
8343/**
8344 * Decreases the number of objects dependent on the machine state.
8345 * Must always complete the #i_addStateDependency() call after the state
8346 * dependency is no more necessary.
8347 */
8348void Machine::i_releaseStateDependency()
8349{
8350 AutoCaller autoCaller(this);
8351 AssertComRCReturnVoid(autoCaller.rc());
8352
8353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8354
8355 /* releaseStateDependency() w/o addStateDependency()? */
8356 AssertReturnVoid(mData->mMachineStateDeps != 0);
8357 -- mData->mMachineStateDeps;
8358
8359 if (mData->mMachineStateDeps == 0)
8360 {
8361 /* inform i_ensureNoStateDependencies() that there are no more deps */
8362 if (mData->mMachineStateChangePending != 0)
8363 {
8364 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8365 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8366 }
8367 }
8368}
8369
8370Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8371{
8372 /* start with nothing found */
8373 Utf8Str strResult("");
8374
8375 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8376
8377 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8378 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8379 // found:
8380 strResult = it->second; // source is a Utf8Str
8381
8382 return strResult;
8383}
8384
8385// protected methods
8386/////////////////////////////////////////////////////////////////////////////
8387
8388/**
8389 * Performs machine state checks based on the @a aDepType value. If a check
8390 * fails, this method will set extended error info, otherwise it will return
8391 * S_OK. It is supposed, that on failure, the caller will immediately return
8392 * the return value of this method to the upper level.
8393 *
8394 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8395 *
8396 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8397 * current state of this machine object allows to change settings of the
8398 * machine (i.e. the machine is not registered, or registered but not running
8399 * and not saved). It is useful to call this method from Machine setters
8400 * before performing any change.
8401 *
8402 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8403 * as for MutableStateDep except that if the machine is saved, S_OK is also
8404 * returned. This is useful in setters which allow changing machine
8405 * properties when it is in the saved state.
8406 *
8407 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8408 * if the current state of this machine object allows to change runtime
8409 * changeable settings of the machine (i.e. the machine is not registered, or
8410 * registered but either running or not running and not saved). It is useful
8411 * to call this method from Machine setters before performing any changes to
8412 * runtime changeable settings.
8413 *
8414 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8415 * the same as for MutableOrRunningStateDep except that if the machine is
8416 * saved, S_OK is also returned. This is useful in setters which allow
8417 * changing runtime and saved state changeable machine properties.
8418 *
8419 * @param aDepType Dependency type to check.
8420 *
8421 * @note Non Machine based classes should use #i_addStateDependency() and
8422 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8423 * template.
8424 *
8425 * @note This method must be called from under this object's read or write
8426 * lock.
8427 */
8428HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8429{
8430 switch (aDepType)
8431 {
8432 case AnyStateDep:
8433 {
8434 break;
8435 }
8436 case MutableStateDep:
8437 {
8438 if ( mData->mRegistered
8439 && ( !i_isSessionMachine()
8440 || ( mData->mMachineState != MachineState_Aborted
8441 && mData->mMachineState != MachineState_Teleported
8442 && mData->mMachineState != MachineState_PoweredOff
8443 )
8444 )
8445 )
8446 return setError(VBOX_E_INVALID_VM_STATE,
8447 tr("The machine is not mutable (state is %s)"),
8448 Global::stringifyMachineState(mData->mMachineState));
8449 break;
8450 }
8451 case MutableOrSavedStateDep:
8452 {
8453 if ( mData->mRegistered
8454 && ( !i_isSessionMachine()
8455 || ( mData->mMachineState != MachineState_Aborted
8456 && mData->mMachineState != MachineState_Teleported
8457 && mData->mMachineState != MachineState_Saved
8458 && mData->mMachineState != MachineState_PoweredOff
8459 )
8460 )
8461 )
8462 return setError(VBOX_E_INVALID_VM_STATE,
8463 tr("The machine is not mutable or saved (state is %s)"),
8464 Global::stringifyMachineState(mData->mMachineState));
8465 break;
8466 }
8467 case MutableOrRunningStateDep:
8468 {
8469 if ( mData->mRegistered
8470 && ( !i_isSessionMachine()
8471 || ( mData->mMachineState != MachineState_Aborted
8472 && mData->mMachineState != MachineState_Teleported
8473 && mData->mMachineState != MachineState_PoweredOff
8474 && !Global::IsOnline(mData->mMachineState)
8475 )
8476 )
8477 )
8478 return setError(VBOX_E_INVALID_VM_STATE,
8479 tr("The machine is not mutable or running (state is %s)"),
8480 Global::stringifyMachineState(mData->mMachineState));
8481 break;
8482 }
8483 case MutableOrSavedOrRunningStateDep:
8484 {
8485 if ( mData->mRegistered
8486 && ( !i_isSessionMachine()
8487 || ( mData->mMachineState != MachineState_Aborted
8488 && mData->mMachineState != MachineState_Teleported
8489 && mData->mMachineState != MachineState_Saved
8490 && mData->mMachineState != MachineState_PoweredOff
8491 && !Global::IsOnline(mData->mMachineState)
8492 )
8493 )
8494 )
8495 return setError(VBOX_E_INVALID_VM_STATE,
8496 tr("The machine is not mutable, saved or running (state is %s)"),
8497 Global::stringifyMachineState(mData->mMachineState));
8498 break;
8499 }
8500 }
8501
8502 return S_OK;
8503}
8504
8505/**
8506 * Helper to initialize all associated child objects and allocate data
8507 * structures.
8508 *
8509 * This method must be called as a part of the object's initialization procedure
8510 * (usually done in the #init() method).
8511 *
8512 * @note Must be called only from #init() or from #i_registeredInit().
8513 */
8514HRESULT Machine::initDataAndChildObjects()
8515{
8516 AutoCaller autoCaller(this);
8517 AssertComRCReturnRC(autoCaller.rc());
8518 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8519 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8520
8521 AssertReturn(!mData->mAccessible, E_FAIL);
8522
8523 /* allocate data structures */
8524 mSSData.allocate();
8525 mUserData.allocate();
8526 mHWData.allocate();
8527 mMediumAttachments.allocate();
8528 mStorageControllers.allocate();
8529 mUSBControllers.allocate();
8530
8531 /* initialize mOSTypeId */
8532 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8533
8534/** @todo r=bird: init() methods never fails, right? Why don't we make them
8535 * return void then! */
8536
8537 /* create associated BIOS settings object */
8538 unconst(mBIOSSettings).createObject();
8539 mBIOSSettings->init(this);
8540
8541 /* create an associated VRDE object (default is disabled) */
8542 unconst(mVRDEServer).createObject();
8543 mVRDEServer->init(this);
8544
8545 /* create associated serial port objects */
8546 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8547 {
8548 unconst(mSerialPorts[slot]).createObject();
8549 mSerialPorts[slot]->init(this, slot);
8550 }
8551
8552 /* create associated parallel port objects */
8553 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8554 {
8555 unconst(mParallelPorts[slot]).createObject();
8556 mParallelPorts[slot]->init(this, slot);
8557 }
8558
8559 /* create the audio adapter object (always present, default is disabled) */
8560 unconst(mAudioAdapter).createObject();
8561 mAudioAdapter->init(this);
8562
8563 /* create the USB device filters object (always present) */
8564 unconst(mUSBDeviceFilters).createObject();
8565 mUSBDeviceFilters->init(this);
8566
8567 /* create associated network adapter objects */
8568 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8569 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8570 {
8571 unconst(mNetworkAdapters[slot]).createObject();
8572 mNetworkAdapters[slot]->init(this, slot);
8573 }
8574
8575 /* create the bandwidth control */
8576 unconst(mBandwidthControl).createObject();
8577 mBandwidthControl->init(this);
8578
8579 return S_OK;
8580}
8581
8582/**
8583 * Helper to uninitialize all associated child objects and to free all data
8584 * structures.
8585 *
8586 * This method must be called as a part of the object's uninitialization
8587 * procedure (usually done in the #uninit() method).
8588 *
8589 * @note Must be called only from #uninit() or from #i_registeredInit().
8590 */
8591void Machine::uninitDataAndChildObjects()
8592{
8593 AutoCaller autoCaller(this);
8594 AssertComRCReturnVoid(autoCaller.rc());
8595 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8596 || getObjectState().getState() == ObjectState::Limited);
8597
8598 /* tell all our other child objects we've been uninitialized */
8599 if (mBandwidthControl)
8600 {
8601 mBandwidthControl->uninit();
8602 unconst(mBandwidthControl).setNull();
8603 }
8604
8605 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8606 {
8607 if (mNetworkAdapters[slot])
8608 {
8609 mNetworkAdapters[slot]->uninit();
8610 unconst(mNetworkAdapters[slot]).setNull();
8611 }
8612 }
8613
8614 if (mUSBDeviceFilters)
8615 {
8616 mUSBDeviceFilters->uninit();
8617 unconst(mUSBDeviceFilters).setNull();
8618 }
8619
8620 if (mAudioAdapter)
8621 {
8622 mAudioAdapter->uninit();
8623 unconst(mAudioAdapter).setNull();
8624 }
8625
8626 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8627 {
8628 if (mParallelPorts[slot])
8629 {
8630 mParallelPorts[slot]->uninit();
8631 unconst(mParallelPorts[slot]).setNull();
8632 }
8633 }
8634
8635 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8636 {
8637 if (mSerialPorts[slot])
8638 {
8639 mSerialPorts[slot]->uninit();
8640 unconst(mSerialPorts[slot]).setNull();
8641 }
8642 }
8643
8644 if (mVRDEServer)
8645 {
8646 mVRDEServer->uninit();
8647 unconst(mVRDEServer).setNull();
8648 }
8649
8650 if (mBIOSSettings)
8651 {
8652 mBIOSSettings->uninit();
8653 unconst(mBIOSSettings).setNull();
8654 }
8655
8656 /* Deassociate media (only when a real Machine or a SnapshotMachine
8657 * instance is uninitialized; SessionMachine instances refer to real
8658 * Machine media). This is necessary for a clean re-initialization of
8659 * the VM after successfully re-checking the accessibility state. Note
8660 * that in case of normal Machine or SnapshotMachine uninitialization (as
8661 * a result of unregistering or deleting the snapshot), outdated media
8662 * attachments will already be uninitialized and deleted, so this
8663 * code will not affect them. */
8664 if ( !mMediumAttachments.isNull()
8665 && !i_isSessionMachine()
8666 )
8667 {
8668 for (MediumAttachmentList::const_iterator
8669 it = mMediumAttachments->begin();
8670 it != mMediumAttachments->end();
8671 ++it)
8672 {
8673 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8674 if (pMedium.isNull())
8675 continue;
8676 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8677 AssertComRC(rc);
8678 }
8679 }
8680
8681 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8682 {
8683 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8684 if (mData->mFirstSnapshot)
8685 {
8686 // snapshots tree is protected by machine write lock; strictly
8687 // this isn't necessary here since we're deleting the entire
8688 // machine, but otherwise we assert in Snapshot::uninit()
8689 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8690 mData->mFirstSnapshot->uninit();
8691 mData->mFirstSnapshot.setNull();
8692 }
8693
8694 mData->mCurrentSnapshot.setNull();
8695 }
8696
8697 /* free data structures (the essential mData structure is not freed here
8698 * since it may be still in use) */
8699 mMediumAttachments.free();
8700 mStorageControllers.free();
8701 mUSBControllers.free();
8702 mHWData.free();
8703 mUserData.free();
8704 mSSData.free();
8705}
8706
8707/**
8708 * Returns a pointer to the Machine object for this machine that acts like a
8709 * parent for complex machine data objects such as shared folders, etc.
8710 *
8711 * For primary Machine objects and for SnapshotMachine objects, returns this
8712 * object's pointer itself. For SessionMachine objects, returns the peer
8713 * (primary) machine pointer.
8714 */
8715Machine *Machine::i_getMachine()
8716{
8717 if (i_isSessionMachine())
8718 return (Machine*)mPeer;
8719 return this;
8720}
8721
8722/**
8723 * Makes sure that there are no machine state dependents. If necessary, waits
8724 * for the number of dependents to drop to zero.
8725 *
8726 * Make sure this method is called from under this object's write lock to
8727 * guarantee that no new dependents may be added when this method returns
8728 * control to the caller.
8729 *
8730 * @note Locks this object for writing. The lock will be released while waiting
8731 * (if necessary).
8732 *
8733 * @warning To be used only in methods that change the machine state!
8734 */
8735void Machine::i_ensureNoStateDependencies()
8736{
8737 AssertReturnVoid(isWriteLockOnCurrentThread());
8738
8739 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8740
8741 /* Wait for all state dependents if necessary */
8742 if (mData->mMachineStateDeps != 0)
8743 {
8744 /* lazy semaphore creation */
8745 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8746 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8747
8748 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8749 mData->mMachineStateDeps));
8750
8751 ++mData->mMachineStateChangePending;
8752
8753 /* reset the semaphore before waiting, the last dependent will signal
8754 * it */
8755 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8756
8757 alock.release();
8758
8759 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8760
8761 alock.acquire();
8762
8763 -- mData->mMachineStateChangePending;
8764 }
8765}
8766
8767/**
8768 * Changes the machine state and informs callbacks.
8769 *
8770 * This method is not intended to fail so it either returns S_OK or asserts (and
8771 * returns a failure).
8772 *
8773 * @note Locks this object for writing.
8774 */
8775HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8776{
8777 LogFlowThisFuncEnter();
8778 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8779 Assert(aMachineState != MachineState_Null);
8780
8781 AutoCaller autoCaller(this);
8782 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8783
8784 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8785
8786 /* wait for state dependents to drop to zero */
8787 i_ensureNoStateDependencies();
8788
8789 MachineState_T const enmOldState = mData->mMachineState;
8790 if (enmOldState != aMachineState)
8791 {
8792 mData->mMachineState = aMachineState;
8793 RTTimeNow(&mData->mLastStateChange);
8794
8795#ifdef VBOX_WITH_DTRACE_R3_MAIN
8796 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8797#endif
8798 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8799 }
8800
8801 LogFlowThisFuncLeave();
8802 return S_OK;
8803}
8804
8805/**
8806 * Searches for a shared folder with the given logical name
8807 * in the collection of shared folders.
8808 *
8809 * @param aName logical name of the shared folder
8810 * @param aSharedFolder where to return the found object
8811 * @param aSetError whether to set the error info if the folder is
8812 * not found
8813 * @return
8814 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8815 *
8816 * @note
8817 * must be called from under the object's lock!
8818 */
8819HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8820 ComObjPtr<SharedFolder> &aSharedFolder,
8821 bool aSetError /* = false */)
8822{
8823 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8824 for (HWData::SharedFolderList::const_iterator
8825 it = mHWData->mSharedFolders.begin();
8826 it != mHWData->mSharedFolders.end();
8827 ++it)
8828 {
8829 SharedFolder *pSF = *it;
8830 AutoCaller autoCaller(pSF);
8831 if (pSF->i_getName() == aName)
8832 {
8833 aSharedFolder = pSF;
8834 rc = S_OK;
8835 break;
8836 }
8837 }
8838
8839 if (aSetError && FAILED(rc))
8840 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8841
8842 return rc;
8843}
8844
8845/**
8846 * Initializes all machine instance data from the given settings structures
8847 * from XML. The exception is the machine UUID which needs special handling
8848 * depending on the caller's use case, so the caller needs to set that herself.
8849 *
8850 * This gets called in several contexts during machine initialization:
8851 *
8852 * -- When machine XML exists on disk already and needs to be loaded into memory,
8853 * for example, from #i_registeredInit() to load all registered machines on
8854 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8855 * attached to the machine should be part of some media registry already.
8856 *
8857 * -- During OVF import, when a machine config has been constructed from an
8858 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8859 * ensure that the media listed as attachments in the config (which have
8860 * been imported from the OVF) receive the correct registry ID.
8861 *
8862 * -- During VM cloning.
8863 *
8864 * @param config Machine settings from XML.
8865 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8866 * for each attached medium in the config.
8867 * @return
8868 */
8869HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8870 const Guid *puuidRegistry)
8871{
8872 // copy name, description, OS type, teleporter, UTC etc.
8873 mUserData->s = config.machineUserData;
8874
8875 // look up the object by Id to check it is valid
8876 ComObjPtr<GuestOSType> pGuestOSType;
8877 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8878 if (!pGuestOSType.isNull())
8879 mUserData->s.strOsType = pGuestOSType->i_id();
8880
8881 // stateFile (optional)
8882 if (config.strStateFile.isEmpty())
8883 mSSData->strStateFilePath.setNull();
8884 else
8885 {
8886 Utf8Str stateFilePathFull(config.strStateFile);
8887 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8888 if (RT_FAILURE(vrc))
8889 return setErrorBoth(E_FAIL, vrc,
8890 tr("Invalid saved state file path '%s' (%Rrc)"),
8891 config.strStateFile.c_str(),
8892 vrc);
8893 mSSData->strStateFilePath = stateFilePathFull;
8894 }
8895
8896 // snapshot folder needs special processing so set it again
8897 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8898 if (FAILED(rc)) return rc;
8899
8900 /* Copy the extra data items (config may or may not be the same as
8901 * mData->pMachineConfigFile) if necessary. When loading the XML files
8902 * from disk they are the same, but not for OVF import. */
8903 if (mData->pMachineConfigFile != &config)
8904 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8905
8906 /* currentStateModified (optional, default is true) */
8907 mData->mCurrentStateModified = config.fCurrentStateModified;
8908
8909 mData->mLastStateChange = config.timeLastStateChange;
8910
8911 /*
8912 * note: all mUserData members must be assigned prior this point because
8913 * we need to commit changes in order to let mUserData be shared by all
8914 * snapshot machine instances.
8915 */
8916 mUserData.commitCopy();
8917
8918 // machine registry, if present (must be loaded before snapshots)
8919 if (config.canHaveOwnMediaRegistry())
8920 {
8921 // determine machine folder
8922 Utf8Str strMachineFolder = i_getSettingsFileFull();
8923 strMachineFolder.stripFilename();
8924 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8925 config.mediaRegistry,
8926 strMachineFolder);
8927 if (FAILED(rc)) return rc;
8928 }
8929
8930 /* Snapshot node (optional) */
8931 size_t cRootSnapshots;
8932 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8933 {
8934 // there must be only one root snapshot
8935 Assert(cRootSnapshots == 1);
8936
8937 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8938
8939 rc = i_loadSnapshot(snap,
8940 config.uuidCurrentSnapshot,
8941 NULL); // no parent == first snapshot
8942 if (FAILED(rc)) return rc;
8943 }
8944
8945 // hardware data
8946 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8947 if (FAILED(rc)) return rc;
8948
8949 /*
8950 * NOTE: the assignment below must be the last thing to do,
8951 * otherwise it will be not possible to change the settings
8952 * somewhere in the code above because all setters will be
8953 * blocked by i_checkStateDependency(MutableStateDep).
8954 */
8955
8956 /* set the machine state to Aborted or Saved when appropriate */
8957 if (config.fAborted)
8958 {
8959 mSSData->strStateFilePath.setNull();
8960
8961 /* no need to use i_setMachineState() during init() */
8962 mData->mMachineState = MachineState_Aborted;
8963 }
8964 else if (!mSSData->strStateFilePath.isEmpty())
8965 {
8966 /* no need to use i_setMachineState() during init() */
8967 mData->mMachineState = MachineState_Saved;
8968 }
8969
8970 // after loading settings, we are no longer different from the XML on disk
8971 mData->flModifications = 0;
8972
8973 return S_OK;
8974}
8975
8976/**
8977 * Recursively loads all snapshots starting from the given.
8978 *
8979 * @param data snapshot settings.
8980 * @param aCurSnapshotId Current snapshot ID from the settings file.
8981 * @param aParentSnapshot Parent snapshot.
8982 */
8983HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8984 const Guid &aCurSnapshotId,
8985 Snapshot *aParentSnapshot)
8986{
8987 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8988 AssertReturn(!i_isSessionMachine(), E_FAIL);
8989
8990 HRESULT rc = S_OK;
8991
8992 Utf8Str strStateFile;
8993 if (!data.strStateFile.isEmpty())
8994 {
8995 /* optional */
8996 strStateFile = data.strStateFile;
8997 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8998 if (RT_FAILURE(vrc))
8999 return setErrorBoth(E_FAIL, vrc,
9000 tr("Invalid saved state file path '%s' (%Rrc)"),
9001 strStateFile.c_str(),
9002 vrc);
9003 }
9004
9005 /* create a snapshot machine object */
9006 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9007 pSnapshotMachine.createObject();
9008 rc = pSnapshotMachine->initFromSettings(this,
9009 data.hardware,
9010 &data.debugging,
9011 &data.autostart,
9012 data.uuid.ref(),
9013 strStateFile);
9014 if (FAILED(rc)) return rc;
9015
9016 /* create a snapshot object */
9017 ComObjPtr<Snapshot> pSnapshot;
9018 pSnapshot.createObject();
9019 /* initialize the snapshot */
9020 rc = pSnapshot->init(mParent, // VirtualBox object
9021 data.uuid,
9022 data.strName,
9023 data.strDescription,
9024 data.timestamp,
9025 pSnapshotMachine,
9026 aParentSnapshot);
9027 if (FAILED(rc)) return rc;
9028
9029 /* memorize the first snapshot if necessary */
9030 if (!mData->mFirstSnapshot)
9031 mData->mFirstSnapshot = pSnapshot;
9032
9033 /* memorize the current snapshot when appropriate */
9034 if ( !mData->mCurrentSnapshot
9035 && pSnapshot->i_getId() == aCurSnapshotId
9036 )
9037 mData->mCurrentSnapshot = pSnapshot;
9038
9039 // now create the children
9040 for (settings::SnapshotsList::const_iterator
9041 it = data.llChildSnapshots.begin();
9042 it != data.llChildSnapshots.end();
9043 ++it)
9044 {
9045 const settings::Snapshot &childData = *it;
9046 // recurse
9047 rc = i_loadSnapshot(childData,
9048 aCurSnapshotId,
9049 pSnapshot); // parent = the one we created above
9050 if (FAILED(rc)) return rc;
9051 }
9052
9053 return rc;
9054}
9055
9056/**
9057 * Loads settings into mHWData.
9058 *
9059 * @param puuidRegistry Registry ID.
9060 * @param puuidSnapshot Snapshot ID
9061 * @param data Reference to the hardware settings.
9062 * @param pDbg Pointer to the debugging settings.
9063 * @param pAutostart Pointer to the autostart settings.
9064 */
9065HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9066 const Guid *puuidSnapshot,
9067 const settings::Hardware &data,
9068 const settings::Debugging *pDbg,
9069 const settings::Autostart *pAutostart)
9070{
9071 AssertReturn(!i_isSessionMachine(), E_FAIL);
9072
9073 HRESULT rc = S_OK;
9074
9075 try
9076 {
9077 ComObjPtr<GuestOSType> pGuestOSType;
9078 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9079
9080 /* The hardware version attribute (optional). */
9081 mHWData->mHWVersion = data.strVersion;
9082 mHWData->mHardwareUUID = data.uuid;
9083
9084 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9085 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9086 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9087 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9088 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9089 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9090 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9091 mHWData->mPAEEnabled = data.fPAE;
9092 mHWData->mLongMode = data.enmLongMode;
9093 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9094 mHWData->mAPIC = data.fAPIC;
9095 mHWData->mX2APIC = data.fX2APIC;
9096 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9097 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9098 mHWData->mSpecCtrl = data.fSpecCtrl;
9099 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9100 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9101 mHWData->mCPUCount = data.cCPUs;
9102 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9103 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9104 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9105 mHWData->mCpuProfile = data.strCpuProfile;
9106
9107 // cpu
9108 if (mHWData->mCPUHotPlugEnabled)
9109 {
9110 for (settings::CpuList::const_iterator
9111 it = data.llCpus.begin();
9112 it != data.llCpus.end();
9113 ++it)
9114 {
9115 const settings::Cpu &cpu = *it;
9116
9117 mHWData->mCPUAttached[cpu.ulId] = true;
9118 }
9119 }
9120
9121 // cpuid leafs
9122 for (settings::CpuIdLeafsList::const_iterator
9123 it = data.llCpuIdLeafs.begin();
9124 it != data.llCpuIdLeafs.end();
9125 ++it)
9126 {
9127 const settings::CpuIdLeaf &rLeaf= *it;
9128 if ( rLeaf.idx < UINT32_C(0x20)
9129 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9130 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9131 mHWData->mCpuIdLeafList.push_back(rLeaf);
9132 /* else: just ignore */
9133 }
9134
9135 mHWData->mMemorySize = data.ulMemorySizeMB;
9136 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9137
9138 // boot order
9139 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9140 {
9141 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9142 if (it == data.mapBootOrder.end())
9143 mHWData->mBootOrder[i] = DeviceType_Null;
9144 else
9145 mHWData->mBootOrder[i] = it->second;
9146 }
9147
9148 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9149 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9150 mHWData->mMonitorCount = data.cMonitors;
9151 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9152 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9153 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9154 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9155 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9156 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9157 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9158 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9159 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9160 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9161 if (!data.strVideoCaptureFile.isEmpty())
9162 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9163 else
9164 mHWData->mVideoCaptureFile.setNull();
9165 mHWData->mVideoCaptureOptions = data.strVideoCaptureOptions;
9166 mHWData->mFirmwareType = data.firmwareType;
9167 mHWData->mPointingHIDType = data.pointingHIDType;
9168 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9169 mHWData->mChipsetType = data.chipsetType;
9170 mHWData->mParavirtProvider = data.paravirtProvider;
9171 mHWData->mParavirtDebug = data.strParavirtDebug;
9172 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9173 mHWData->mHPETEnabled = data.fHPETEnabled;
9174
9175 /* VRDEServer */
9176 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9177 if (FAILED(rc)) return rc;
9178
9179 /* BIOS */
9180 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9181 if (FAILED(rc)) return rc;
9182
9183 // Bandwidth control (must come before network adapters)
9184 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9185 if (FAILED(rc)) return rc;
9186
9187 /* Shared folders */
9188 for (settings::USBControllerList::const_iterator
9189 it = data.usbSettings.llUSBControllers.begin();
9190 it != data.usbSettings.llUSBControllers.end();
9191 ++it)
9192 {
9193 const settings::USBController &settingsCtrl = *it;
9194 ComObjPtr<USBController> newCtrl;
9195
9196 newCtrl.createObject();
9197 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9198 mUSBControllers->push_back(newCtrl);
9199 }
9200
9201 /* USB device filters */
9202 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9203 if (FAILED(rc)) return rc;
9204
9205 // network adapters (establish array size first and apply defaults, to
9206 // ensure reading the same settings as we saved, since the list skips
9207 // adapters having defaults)
9208 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9209 size_t oldCount = mNetworkAdapters.size();
9210 if (newCount > oldCount)
9211 {
9212 mNetworkAdapters.resize(newCount);
9213 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9214 {
9215 unconst(mNetworkAdapters[slot]).createObject();
9216 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9217 }
9218 }
9219 else if (newCount < oldCount)
9220 mNetworkAdapters.resize(newCount);
9221 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9222 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9223 for (settings::NetworkAdaptersList::const_iterator
9224 it = data.llNetworkAdapters.begin();
9225 it != data.llNetworkAdapters.end();
9226 ++it)
9227 {
9228 const settings::NetworkAdapter &nic = *it;
9229
9230 /* slot uniqueness is guaranteed by XML Schema */
9231 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9232 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9233 if (FAILED(rc)) return rc;
9234 }
9235
9236 // serial ports (establish defaults first, to ensure reading the same
9237 // settings as we saved, since the list skips ports having defaults)
9238 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9239 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9240 for (settings::SerialPortsList::const_iterator
9241 it = data.llSerialPorts.begin();
9242 it != data.llSerialPorts.end();
9243 ++it)
9244 {
9245 const settings::SerialPort &s = *it;
9246
9247 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9248 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9249 if (FAILED(rc)) return rc;
9250 }
9251
9252 // parallel ports (establish defaults first, to ensure reading the same
9253 // settings as we saved, since the list skips ports having defaults)
9254 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9255 mParallelPorts[i]->i_applyDefaults();
9256 for (settings::ParallelPortsList::const_iterator
9257 it = data.llParallelPorts.begin();
9258 it != data.llParallelPorts.end();
9259 ++it)
9260 {
9261 const settings::ParallelPort &p = *it;
9262
9263 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9264 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9265 if (FAILED(rc)) return rc;
9266 }
9267
9268 /* AudioAdapter */
9269 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9270 if (FAILED(rc)) return rc;
9271
9272 /* storage controllers */
9273 rc = i_loadStorageControllers(data.storage,
9274 puuidRegistry,
9275 puuidSnapshot);
9276 if (FAILED(rc)) return rc;
9277
9278 /* Shared folders */
9279 for (settings::SharedFoldersList::const_iterator
9280 it = data.llSharedFolders.begin();
9281 it != data.llSharedFolders.end();
9282 ++it)
9283 {
9284 const settings::SharedFolder &sf = *it;
9285
9286 ComObjPtr<SharedFolder> sharedFolder;
9287 /* Check for double entries. Not allowed! */
9288 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9289 if (SUCCEEDED(rc))
9290 return setError(VBOX_E_OBJECT_IN_USE,
9291 tr("Shared folder named '%s' already exists"),
9292 sf.strName.c_str());
9293
9294 /* Create the new shared folder. Don't break on error. This will be
9295 * reported when the machine starts. */
9296 sharedFolder.createObject();
9297 rc = sharedFolder->init(i_getMachine(),
9298 sf.strName,
9299 sf.strHostPath,
9300 RT_BOOL(sf.fWritable),
9301 RT_BOOL(sf.fAutoMount),
9302 false /* fFailOnError */);
9303 if (FAILED(rc)) return rc;
9304 mHWData->mSharedFolders.push_back(sharedFolder);
9305 }
9306
9307 // Clipboard
9308 mHWData->mClipboardMode = data.clipboardMode;
9309
9310 // drag'n'drop
9311 mHWData->mDnDMode = data.dndMode;
9312
9313 // guest settings
9314 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9315
9316 // IO settings
9317 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9318 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9319
9320 // Host PCI devices
9321 for (settings::HostPCIDeviceAttachmentList::const_iterator
9322 it = data.pciAttachments.begin();
9323 it != data.pciAttachments.end();
9324 ++it)
9325 {
9326 const settings::HostPCIDeviceAttachment &hpda = *it;
9327 ComObjPtr<PCIDeviceAttachment> pda;
9328
9329 pda.createObject();
9330 pda->i_loadSettings(this, hpda);
9331 mHWData->mPCIDeviceAssignments.push_back(pda);
9332 }
9333
9334 /*
9335 * (The following isn't really real hardware, but it lives in HWData
9336 * for reasons of convenience.)
9337 */
9338
9339#ifdef VBOX_WITH_GUEST_PROPS
9340 /* Guest properties (optional) */
9341
9342 /* Only load transient guest properties for configs which have saved
9343 * state, because there shouldn't be any for powered off VMs. The same
9344 * logic applies for snapshots, as offline snapshots shouldn't have
9345 * any such properties. They confuse the code in various places.
9346 * Note: can't rely on the machine state, as it isn't set yet. */
9347 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9348 /* apologies for the hacky unconst() usage, but this needs hacking
9349 * actually inconsistent settings into consistency, otherwise there
9350 * will be some corner cases where the inconsistency survives
9351 * surprisingly long without getting fixed, especially for snapshots
9352 * as there are no config changes. */
9353 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9354 for (settings::GuestPropertiesList::iterator
9355 it = llGuestProperties.begin();
9356 it != llGuestProperties.end();
9357 /*nothing*/)
9358 {
9359 const settings::GuestProperty &prop = *it;
9360 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9361 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9362 if ( fSkipTransientGuestProperties
9363 && ( fFlags & GUEST_PROP_F_TRANSIENT
9364 || fFlags & GUEST_PROP_F_TRANSRESET))
9365 {
9366 it = llGuestProperties.erase(it);
9367 continue;
9368 }
9369 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9370 mHWData->mGuestProperties[prop.strName] = property;
9371 ++it;
9372 }
9373#endif /* VBOX_WITH_GUEST_PROPS defined */
9374
9375 rc = i_loadDebugging(pDbg);
9376 if (FAILED(rc))
9377 return rc;
9378
9379 mHWData->mAutostart = *pAutostart;
9380
9381 /* default frontend */
9382 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9383 }
9384 catch (std::bad_alloc &)
9385 {
9386 return E_OUTOFMEMORY;
9387 }
9388
9389 AssertComRC(rc);
9390 return rc;
9391}
9392
9393/**
9394 * Called from i_loadHardware() to load the debugging settings of the
9395 * machine.
9396 *
9397 * @param pDbg Pointer to the settings.
9398 */
9399HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9400{
9401 mHWData->mDebugging = *pDbg;
9402 /* no more processing currently required, this will probably change. */
9403 return S_OK;
9404}
9405
9406/**
9407 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9408 *
9409 * @param data storage settings.
9410 * @param puuidRegistry media registry ID to set media to or NULL;
9411 * see Machine::i_loadMachineDataFromSettings()
9412 * @param puuidSnapshot snapshot ID
9413 * @return
9414 */
9415HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9416 const Guid *puuidRegistry,
9417 const Guid *puuidSnapshot)
9418{
9419 AssertReturn(!i_isSessionMachine(), E_FAIL);
9420
9421 HRESULT rc = S_OK;
9422
9423 for (settings::StorageControllersList::const_iterator
9424 it = data.llStorageControllers.begin();
9425 it != data.llStorageControllers.end();
9426 ++it)
9427 {
9428 const settings::StorageController &ctlData = *it;
9429
9430 ComObjPtr<StorageController> pCtl;
9431 /* Try to find one with the name first. */
9432 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9433 if (SUCCEEDED(rc))
9434 return setError(VBOX_E_OBJECT_IN_USE,
9435 tr("Storage controller named '%s' already exists"),
9436 ctlData.strName.c_str());
9437
9438 pCtl.createObject();
9439 rc = pCtl->init(this,
9440 ctlData.strName,
9441 ctlData.storageBus,
9442 ctlData.ulInstance,
9443 ctlData.fBootable);
9444 if (FAILED(rc)) return rc;
9445
9446 mStorageControllers->push_back(pCtl);
9447
9448 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9449 if (FAILED(rc)) return rc;
9450
9451 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9452 if (FAILED(rc)) return rc;
9453
9454 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9455 if (FAILED(rc)) return rc;
9456
9457 /* Load the attached devices now. */
9458 rc = i_loadStorageDevices(pCtl,
9459 ctlData,
9460 puuidRegistry,
9461 puuidSnapshot);
9462 if (FAILED(rc)) return rc;
9463 }
9464
9465 return S_OK;
9466}
9467
9468/**
9469 * Called from i_loadStorageControllers for a controller's devices.
9470 *
9471 * @param aStorageController
9472 * @param data
9473 * @param puuidRegistry media registry ID to set media to or NULL; see
9474 * Machine::i_loadMachineDataFromSettings()
9475 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9476 * @return
9477 */
9478HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9479 const settings::StorageController &data,
9480 const Guid *puuidRegistry,
9481 const Guid *puuidSnapshot)
9482{
9483 HRESULT rc = S_OK;
9484
9485 /* paranoia: detect duplicate attachments */
9486 for (settings::AttachedDevicesList::const_iterator
9487 it = data.llAttachedDevices.begin();
9488 it != data.llAttachedDevices.end();
9489 ++it)
9490 {
9491 const settings::AttachedDevice &ad = *it;
9492
9493 for (settings::AttachedDevicesList::const_iterator it2 = it;
9494 it2 != data.llAttachedDevices.end();
9495 ++it2)
9496 {
9497 if (it == it2)
9498 continue;
9499
9500 const settings::AttachedDevice &ad2 = *it2;
9501
9502 if ( ad.lPort == ad2.lPort
9503 && ad.lDevice == ad2.lDevice)
9504 {
9505 return setError(E_FAIL,
9506 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9507 aStorageController->i_getName().c_str(),
9508 ad.lPort,
9509 ad.lDevice,
9510 mUserData->s.strName.c_str());
9511 }
9512 }
9513 }
9514
9515 for (settings::AttachedDevicesList::const_iterator
9516 it = data.llAttachedDevices.begin();
9517 it != data.llAttachedDevices.end();
9518 ++it)
9519 {
9520 const settings::AttachedDevice &dev = *it;
9521 ComObjPtr<Medium> medium;
9522
9523 switch (dev.deviceType)
9524 {
9525 case DeviceType_Floppy:
9526 case DeviceType_DVD:
9527 if (dev.strHostDriveSrc.isNotEmpty())
9528 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9529 false /* fRefresh */, medium);
9530 else
9531 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9532 dev.uuid,
9533 false /* fRefresh */,
9534 false /* aSetError */,
9535 medium);
9536 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9537 // This is not an error. The host drive or UUID might have vanished, so just go
9538 // ahead without this removeable medium attachment
9539 rc = S_OK;
9540 break;
9541
9542 case DeviceType_HardDisk:
9543 {
9544 /* find a hard disk by UUID */
9545 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9546 if (FAILED(rc))
9547 {
9548 if (i_isSnapshotMachine())
9549 {
9550 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9551 // so the user knows that the bad disk is in a snapshot somewhere
9552 com::ErrorInfo info;
9553 return setError(E_FAIL,
9554 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9555 puuidSnapshot->raw(),
9556 info.getText().raw());
9557 }
9558 else
9559 return rc;
9560 }
9561
9562 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9563
9564 if (medium->i_getType() == MediumType_Immutable)
9565 {
9566 if (i_isSnapshotMachine())
9567 return setError(E_FAIL,
9568 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9569 "of the virtual machine '%s' ('%s')"),
9570 medium->i_getLocationFull().c_str(),
9571 dev.uuid.raw(),
9572 puuidSnapshot->raw(),
9573 mUserData->s.strName.c_str(),
9574 mData->m_strConfigFileFull.c_str());
9575
9576 return setError(E_FAIL,
9577 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9578 medium->i_getLocationFull().c_str(),
9579 dev.uuid.raw(),
9580 mUserData->s.strName.c_str(),
9581 mData->m_strConfigFileFull.c_str());
9582 }
9583
9584 if (medium->i_getType() == MediumType_MultiAttach)
9585 {
9586 if (i_isSnapshotMachine())
9587 return setError(E_FAIL,
9588 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9589 "of the virtual machine '%s' ('%s')"),
9590 medium->i_getLocationFull().c_str(),
9591 dev.uuid.raw(),
9592 puuidSnapshot->raw(),
9593 mUserData->s.strName.c_str(),
9594 mData->m_strConfigFileFull.c_str());
9595
9596 return setError(E_FAIL,
9597 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9598 medium->i_getLocationFull().c_str(),
9599 dev.uuid.raw(),
9600 mUserData->s.strName.c_str(),
9601 mData->m_strConfigFileFull.c_str());
9602 }
9603
9604 if ( !i_isSnapshotMachine()
9605 && medium->i_getChildren().size() != 0
9606 )
9607 return setError(E_FAIL,
9608 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9609 "because it has %d differencing child hard disks"),
9610 medium->i_getLocationFull().c_str(),
9611 dev.uuid.raw(),
9612 mUserData->s.strName.c_str(),
9613 mData->m_strConfigFileFull.c_str(),
9614 medium->i_getChildren().size());
9615
9616 if (i_findAttachment(*mMediumAttachments.data(),
9617 medium))
9618 return setError(E_FAIL,
9619 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9620 medium->i_getLocationFull().c_str(),
9621 dev.uuid.raw(),
9622 mUserData->s.strName.c_str(),
9623 mData->m_strConfigFileFull.c_str());
9624
9625 break;
9626 }
9627
9628 default:
9629 return setError(E_FAIL,
9630 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9631 medium->i_getLocationFull().c_str(),
9632 mUserData->s.strName.c_str(),
9633 mData->m_strConfigFileFull.c_str());
9634 }
9635
9636 if (FAILED(rc))
9637 break;
9638
9639 /* Bandwidth groups are loaded at this point. */
9640 ComObjPtr<BandwidthGroup> pBwGroup;
9641
9642 if (!dev.strBwGroup.isEmpty())
9643 {
9644 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9645 if (FAILED(rc))
9646 return setError(E_FAIL,
9647 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9648 medium->i_getLocationFull().c_str(),
9649 dev.strBwGroup.c_str(),
9650 mUserData->s.strName.c_str(),
9651 mData->m_strConfigFileFull.c_str());
9652 pBwGroup->i_reference();
9653 }
9654
9655 const Utf8Str controllerName = aStorageController->i_getName();
9656 ComObjPtr<MediumAttachment> pAttachment;
9657 pAttachment.createObject();
9658 rc = pAttachment->init(this,
9659 medium,
9660 controllerName,
9661 dev.lPort,
9662 dev.lDevice,
9663 dev.deviceType,
9664 false,
9665 dev.fPassThrough,
9666 dev.fTempEject,
9667 dev.fNonRotational,
9668 dev.fDiscard,
9669 dev.fHotPluggable,
9670 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9671 if (FAILED(rc)) break;
9672
9673 /* associate the medium with this machine and snapshot */
9674 if (!medium.isNull())
9675 {
9676 AutoCaller medCaller(medium);
9677 if (FAILED(medCaller.rc())) return medCaller.rc();
9678 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9679
9680 if (i_isSnapshotMachine())
9681 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9682 else
9683 rc = medium->i_addBackReference(mData->mUuid);
9684 /* If the medium->addBackReference fails it sets an appropriate
9685 * error message, so no need to do any guesswork here. */
9686
9687 if (puuidRegistry)
9688 // caller wants registry ID to be set on all attached media (OVF import case)
9689 medium->i_addRegistry(*puuidRegistry);
9690 }
9691
9692 if (FAILED(rc))
9693 break;
9694
9695 /* back up mMediumAttachments to let registeredInit() properly rollback
9696 * on failure (= limited accessibility) */
9697 i_setModified(IsModified_Storage);
9698 mMediumAttachments.backup();
9699 mMediumAttachments->push_back(pAttachment);
9700 }
9701
9702 return rc;
9703}
9704
9705/**
9706 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9707 *
9708 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9709 * @param aSnapshot where to return the found snapshot
9710 * @param aSetError true to set extended error info on failure
9711 */
9712HRESULT Machine::i_findSnapshotById(const Guid &aId,
9713 ComObjPtr<Snapshot> &aSnapshot,
9714 bool aSetError /* = false */)
9715{
9716 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9717
9718 if (!mData->mFirstSnapshot)
9719 {
9720 if (aSetError)
9721 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9722 return E_FAIL;
9723 }
9724
9725 if (aId.isZero())
9726 aSnapshot = mData->mFirstSnapshot;
9727 else
9728 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9729
9730 if (!aSnapshot)
9731 {
9732 if (aSetError)
9733 return setError(E_FAIL,
9734 tr("Could not find a snapshot with UUID {%s}"),
9735 aId.toString().c_str());
9736 return E_FAIL;
9737 }
9738
9739 return S_OK;
9740}
9741
9742/**
9743 * Returns the snapshot with the given name or fails of no such snapshot.
9744 *
9745 * @param strName snapshot name to find
9746 * @param aSnapshot where to return the found snapshot
9747 * @param aSetError true to set extended error info on failure
9748 */
9749HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9750 ComObjPtr<Snapshot> &aSnapshot,
9751 bool aSetError /* = false */)
9752{
9753 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9754
9755 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9756
9757 if (!mData->mFirstSnapshot)
9758 {
9759 if (aSetError)
9760 return setError(VBOX_E_OBJECT_NOT_FOUND,
9761 tr("This machine does not have any snapshots"));
9762 return VBOX_E_OBJECT_NOT_FOUND;
9763 }
9764
9765 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9766
9767 if (!aSnapshot)
9768 {
9769 if (aSetError)
9770 return setError(VBOX_E_OBJECT_NOT_FOUND,
9771 tr("Could not find a snapshot named '%s'"), strName.c_str());
9772 return VBOX_E_OBJECT_NOT_FOUND;
9773 }
9774
9775 return S_OK;
9776}
9777
9778/**
9779 * Returns a storage controller object with the given name.
9780 *
9781 * @param aName storage controller name to find
9782 * @param aStorageController where to return the found storage controller
9783 * @param aSetError true to set extended error info on failure
9784 */
9785HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9786 ComObjPtr<StorageController> &aStorageController,
9787 bool aSetError /* = false */)
9788{
9789 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9790
9791 for (StorageControllerList::const_iterator
9792 it = mStorageControllers->begin();
9793 it != mStorageControllers->end();
9794 ++it)
9795 {
9796 if ((*it)->i_getName() == aName)
9797 {
9798 aStorageController = (*it);
9799 return S_OK;
9800 }
9801 }
9802
9803 if (aSetError)
9804 return setError(VBOX_E_OBJECT_NOT_FOUND,
9805 tr("Could not find a storage controller named '%s'"),
9806 aName.c_str());
9807 return VBOX_E_OBJECT_NOT_FOUND;
9808}
9809
9810/**
9811 * Returns a USB controller object with the given name.
9812 *
9813 * @param aName USB controller name to find
9814 * @param aUSBController where to return the found USB controller
9815 * @param aSetError true to set extended error info on failure
9816 */
9817HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9818 ComObjPtr<USBController> &aUSBController,
9819 bool aSetError /* = false */)
9820{
9821 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9822
9823 for (USBControllerList::const_iterator
9824 it = mUSBControllers->begin();
9825 it != mUSBControllers->end();
9826 ++it)
9827 {
9828 if ((*it)->i_getName() == aName)
9829 {
9830 aUSBController = (*it);
9831 return S_OK;
9832 }
9833 }
9834
9835 if (aSetError)
9836 return setError(VBOX_E_OBJECT_NOT_FOUND,
9837 tr("Could not find a storage controller named '%s'"),
9838 aName.c_str());
9839 return VBOX_E_OBJECT_NOT_FOUND;
9840}
9841
9842/**
9843 * Returns the number of USB controller instance of the given type.
9844 *
9845 * @param enmType USB controller type.
9846 */
9847ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9848{
9849 ULONG cCtrls = 0;
9850
9851 for (USBControllerList::const_iterator
9852 it = mUSBControllers->begin();
9853 it != mUSBControllers->end();
9854 ++it)
9855 {
9856 if ((*it)->i_getControllerType() == enmType)
9857 cCtrls++;
9858 }
9859
9860 return cCtrls;
9861}
9862
9863HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9864 MediumAttachmentList &atts)
9865{
9866 AutoCaller autoCaller(this);
9867 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9868
9869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9870
9871 for (MediumAttachmentList::const_iterator
9872 it = mMediumAttachments->begin();
9873 it != mMediumAttachments->end();
9874 ++it)
9875 {
9876 const ComObjPtr<MediumAttachment> &pAtt = *it;
9877 // should never happen, but deal with NULL pointers in the list.
9878 AssertContinue(!pAtt.isNull());
9879
9880 // getControllerName() needs caller+read lock
9881 AutoCaller autoAttCaller(pAtt);
9882 if (FAILED(autoAttCaller.rc()))
9883 {
9884 atts.clear();
9885 return autoAttCaller.rc();
9886 }
9887 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9888
9889 if (pAtt->i_getControllerName() == aName)
9890 atts.push_back(pAtt);
9891 }
9892
9893 return S_OK;
9894}
9895
9896
9897/**
9898 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9899 * file if the machine name was changed and about creating a new settings file
9900 * if this is a new machine.
9901 *
9902 * @note Must be never called directly but only from #saveSettings().
9903 */
9904HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9905{
9906 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9907
9908 HRESULT rc = S_OK;
9909
9910 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9911
9912 /// @todo need to handle primary group change, too
9913
9914 /* attempt to rename the settings file if machine name is changed */
9915 if ( mUserData->s.fNameSync
9916 && mUserData.isBackedUp()
9917 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9918 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9919 )
9920 {
9921 bool dirRenamed = false;
9922 bool fileRenamed = false;
9923
9924 Utf8Str configFile, newConfigFile;
9925 Utf8Str configFilePrev, newConfigFilePrev;
9926 Utf8Str configDir, newConfigDir;
9927
9928 do
9929 {
9930 int vrc = VINF_SUCCESS;
9931
9932 Utf8Str name = mUserData.backedUpData()->s.strName;
9933 Utf8Str newName = mUserData->s.strName;
9934 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9935 if (group == "/")
9936 group.setNull();
9937 Utf8Str newGroup = mUserData->s.llGroups.front();
9938 if (newGroup == "/")
9939 newGroup.setNull();
9940
9941 configFile = mData->m_strConfigFileFull;
9942
9943 /* first, rename the directory if it matches the group and machine name */
9944 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9945 group.c_str(), RTPATH_DELIMITER, name.c_str());
9946 /** @todo hack, make somehow use of ComposeMachineFilename */
9947 if (mUserData->s.fDirectoryIncludesUUID)
9948 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9949 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9950 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9951 /** @todo hack, make somehow use of ComposeMachineFilename */
9952 if (mUserData->s.fDirectoryIncludesUUID)
9953 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9954 configDir = configFile;
9955 configDir.stripFilename();
9956 newConfigDir = configDir;
9957 if ( configDir.length() >= groupPlusName.length()
9958 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9959 groupPlusName.c_str()))
9960 {
9961 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9962 Utf8Str newConfigBaseDir(newConfigDir);
9963 newConfigDir.append(newGroupPlusName);
9964 /* consistency: use \ if appropriate on the platform */
9965 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9966 /* new dir and old dir cannot be equal here because of 'if'
9967 * above and because name != newName */
9968 Assert(configDir != newConfigDir);
9969 if (!fSettingsFileIsNew)
9970 {
9971 /* perform real rename only if the machine is not new */
9972 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9973 if ( vrc == VERR_FILE_NOT_FOUND
9974 || vrc == VERR_PATH_NOT_FOUND)
9975 {
9976 /* create the parent directory, then retry renaming */
9977 Utf8Str parent(newConfigDir);
9978 parent.stripFilename();
9979 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9980 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9981 }
9982 if (RT_FAILURE(vrc))
9983 {
9984 rc = setErrorBoth(E_FAIL, vrc,
9985 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9986 configDir.c_str(),
9987 newConfigDir.c_str(),
9988 vrc);
9989 break;
9990 }
9991 /* delete subdirectories which are no longer needed */
9992 Utf8Str dir(configDir);
9993 dir.stripFilename();
9994 while (dir != newConfigBaseDir && dir != ".")
9995 {
9996 vrc = RTDirRemove(dir.c_str());
9997 if (RT_FAILURE(vrc))
9998 break;
9999 dir.stripFilename();
10000 }
10001 dirRenamed = true;
10002 }
10003 }
10004
10005 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10006 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10007
10008 /* then try to rename the settings file itself */
10009 if (newConfigFile != configFile)
10010 {
10011 /* get the path to old settings file in renamed directory */
10012 configFile = Utf8StrFmt("%s%c%s",
10013 newConfigDir.c_str(),
10014 RTPATH_DELIMITER,
10015 RTPathFilename(configFile.c_str()));
10016 if (!fSettingsFileIsNew)
10017 {
10018 /* perform real rename only if the machine is not new */
10019 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10020 if (RT_FAILURE(vrc))
10021 {
10022 rc = setErrorBoth(E_FAIL, vrc,
10023 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10024 configFile.c_str(),
10025 newConfigFile.c_str(),
10026 vrc);
10027 break;
10028 }
10029 fileRenamed = true;
10030 configFilePrev = configFile;
10031 configFilePrev += "-prev";
10032 newConfigFilePrev = newConfigFile;
10033 newConfigFilePrev += "-prev";
10034 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10035 }
10036 }
10037
10038 // update m_strConfigFileFull amd mConfigFile
10039 mData->m_strConfigFileFull = newConfigFile;
10040 // compute the relative path too
10041 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10042
10043 // store the old and new so that VirtualBox::i_saveSettings() can update
10044 // the media registry
10045 if ( mData->mRegistered
10046 && (configDir != newConfigDir || configFile != newConfigFile))
10047 {
10048 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10049
10050 if (pfNeedsGlobalSaveSettings)
10051 *pfNeedsGlobalSaveSettings = true;
10052 }
10053
10054 // in the saved state file path, replace the old directory with the new directory
10055 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10056 {
10057 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10058 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10059 }
10060
10061 // and do the same thing for the saved state file paths of all the online snapshots
10062 if (mData->mFirstSnapshot)
10063 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10064 newConfigDir.c_str());
10065 }
10066 while (0);
10067
10068 if (FAILED(rc))
10069 {
10070 /* silently try to rename everything back */
10071 if (fileRenamed)
10072 {
10073 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10074 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10075 }
10076 if (dirRenamed)
10077 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10078 }
10079
10080 if (FAILED(rc)) return rc;
10081 }
10082
10083 if (fSettingsFileIsNew)
10084 {
10085 /* create a virgin config file */
10086 int vrc = VINF_SUCCESS;
10087
10088 /* ensure the settings directory exists */
10089 Utf8Str path(mData->m_strConfigFileFull);
10090 path.stripFilename();
10091 if (!RTDirExists(path.c_str()))
10092 {
10093 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10094 if (RT_FAILURE(vrc))
10095 {
10096 return setErrorBoth(E_FAIL, vrc,
10097 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10098 path.c_str(),
10099 vrc);
10100 }
10101 }
10102
10103 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10104 path = Utf8Str(mData->m_strConfigFileFull);
10105 RTFILE f = NIL_RTFILE;
10106 vrc = RTFileOpen(&f, path.c_str(),
10107 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10108 if (RT_FAILURE(vrc))
10109 return setErrorBoth(E_FAIL, vrc,
10110 tr("Could not create the settings file '%s' (%Rrc)"),
10111 path.c_str(),
10112 vrc);
10113 RTFileClose(f);
10114 }
10115
10116 return rc;
10117}
10118
10119/**
10120 * Saves and commits machine data, user data and hardware data.
10121 *
10122 * Note that on failure, the data remains uncommitted.
10123 *
10124 * @a aFlags may combine the following flags:
10125 *
10126 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10127 * Used when saving settings after an operation that makes them 100%
10128 * correspond to the settings from the current snapshot.
10129 * - SaveS_Force: settings will be saved without doing a deep compare of the
10130 * settings structures. This is used when this is called because snapshots
10131 * have changed to avoid the overhead of the deep compare.
10132 *
10133 * @note Must be called from under this object's write lock. Locks children for
10134 * writing.
10135 *
10136 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10137 * initialized to false and that will be set to true by this function if
10138 * the caller must invoke VirtualBox::i_saveSettings() because the global
10139 * settings have changed. This will happen if a machine rename has been
10140 * saved and the global machine and media registries will therefore need
10141 * updating.
10142 * @param aFlags Flags.
10143 */
10144HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10145 int aFlags /*= 0*/)
10146{
10147 LogFlowThisFuncEnter();
10148
10149 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10150
10151 /* make sure child objects are unable to modify the settings while we are
10152 * saving them */
10153 i_ensureNoStateDependencies();
10154
10155 AssertReturn(!i_isSnapshotMachine(),
10156 E_FAIL);
10157
10158 HRESULT rc = S_OK;
10159 bool fNeedsWrite = false;
10160
10161 /* First, prepare to save settings. It will care about renaming the
10162 * settings directory and file if the machine name was changed and about
10163 * creating a new settings file if this is a new machine. */
10164 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10165 if (FAILED(rc)) return rc;
10166
10167 // keep a pointer to the current settings structures
10168 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10169 settings::MachineConfigFile *pNewConfig = NULL;
10170
10171 try
10172 {
10173 // make a fresh one to have everyone write stuff into
10174 pNewConfig = new settings::MachineConfigFile(NULL);
10175 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10176
10177 // now go and copy all the settings data from COM to the settings structures
10178 // (this calls i_saveSettings() on all the COM objects in the machine)
10179 i_copyMachineDataToSettings(*pNewConfig);
10180
10181 if (aFlags & SaveS_ResetCurStateModified)
10182 {
10183 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10184 mData->mCurrentStateModified = FALSE;
10185 fNeedsWrite = true; // always, no need to compare
10186 }
10187 else if (aFlags & SaveS_Force)
10188 {
10189 fNeedsWrite = true; // always, no need to compare
10190 }
10191 else
10192 {
10193 if (!mData->mCurrentStateModified)
10194 {
10195 // do a deep compare of the settings that we just saved with the settings
10196 // previously stored in the config file; this invokes MachineConfigFile::operator==
10197 // which does a deep compare of all the settings, which is expensive but less expensive
10198 // than writing out XML in vain
10199 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10200
10201 // could still be modified if any settings changed
10202 mData->mCurrentStateModified = fAnySettingsChanged;
10203
10204 fNeedsWrite = fAnySettingsChanged;
10205 }
10206 else
10207 fNeedsWrite = true;
10208 }
10209
10210 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10211
10212 if (fNeedsWrite)
10213 // now spit it all out!
10214 pNewConfig->write(mData->m_strConfigFileFull);
10215
10216 mData->pMachineConfigFile = pNewConfig;
10217 delete pOldConfig;
10218 i_commit();
10219
10220 // after saving settings, we are no longer different from the XML on disk
10221 mData->flModifications = 0;
10222 }
10223 catch (HRESULT err)
10224 {
10225 // we assume that error info is set by the thrower
10226 rc = err;
10227
10228 // restore old config
10229 delete pNewConfig;
10230 mData->pMachineConfigFile = pOldConfig;
10231 }
10232 catch (...)
10233 {
10234 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10235 }
10236
10237 if (fNeedsWrite)
10238 {
10239 /* Fire the data change event, even on failure (since we've already
10240 * committed all data). This is done only for SessionMachines because
10241 * mutable Machine instances are always not registered (i.e. private
10242 * to the client process that creates them) and thus don't need to
10243 * inform callbacks. */
10244 if (i_isSessionMachine())
10245 mParent->i_onMachineDataChange(mData->mUuid);
10246 }
10247
10248 LogFlowThisFunc(("rc=%08X\n", rc));
10249 LogFlowThisFuncLeave();
10250 return rc;
10251}
10252
10253/**
10254 * Implementation for saving the machine settings into the given
10255 * settings::MachineConfigFile instance. This copies machine extradata
10256 * from the previous machine config file in the instance data, if any.
10257 *
10258 * This gets called from two locations:
10259 *
10260 * -- Machine::i_saveSettings(), during the regular XML writing;
10261 *
10262 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10263 * exported to OVF and we write the VirtualBox proprietary XML
10264 * into a <vbox:Machine> tag.
10265 *
10266 * This routine fills all the fields in there, including snapshots, *except*
10267 * for the following:
10268 *
10269 * -- fCurrentStateModified. There is some special logic associated with that.
10270 *
10271 * The caller can then call MachineConfigFile::write() or do something else
10272 * with it.
10273 *
10274 * Caller must hold the machine lock!
10275 *
10276 * This throws XML errors and HRESULT, so the caller must have a catch block!
10277 */
10278void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10279{
10280 // deep copy extradata, being extra careful with self assignment (the STL
10281 // map assignment on Mac OS X clang based Xcode isn't checking)
10282 if (&config != mData->pMachineConfigFile)
10283 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10284
10285 config.uuid = mData->mUuid;
10286
10287 // copy name, description, OS type, teleport, UTC etc.
10288 config.machineUserData = mUserData->s;
10289
10290 if ( mData->mMachineState == MachineState_Saved
10291 || mData->mMachineState == MachineState_Restoring
10292 // when doing certain snapshot operations we may or may not have
10293 // a saved state in the current state, so keep everything as is
10294 || ( ( mData->mMachineState == MachineState_Snapshotting
10295 || mData->mMachineState == MachineState_DeletingSnapshot
10296 || mData->mMachineState == MachineState_RestoringSnapshot)
10297 && (!mSSData->strStateFilePath.isEmpty())
10298 )
10299 )
10300 {
10301 Assert(!mSSData->strStateFilePath.isEmpty());
10302 /* try to make the file name relative to the settings file dir */
10303 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10304 }
10305 else
10306 {
10307 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10308 config.strStateFile.setNull();
10309 }
10310
10311 if (mData->mCurrentSnapshot)
10312 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10313 else
10314 config.uuidCurrentSnapshot.clear();
10315
10316 config.timeLastStateChange = mData->mLastStateChange;
10317 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10318 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10319
10320 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10321 if (FAILED(rc)) throw rc;
10322
10323 // save machine's media registry if this is VirtualBox 4.0 or later
10324 if (config.canHaveOwnMediaRegistry())
10325 {
10326 // determine machine folder
10327 Utf8Str strMachineFolder = i_getSettingsFileFull();
10328 strMachineFolder.stripFilename();
10329 mParent->i_saveMediaRegistry(config.mediaRegistry,
10330 i_getId(), // only media with registry ID == machine UUID
10331 strMachineFolder);
10332 // this throws HRESULT
10333 }
10334
10335 // save snapshots
10336 rc = i_saveAllSnapshots(config);
10337 if (FAILED(rc)) throw rc;
10338}
10339
10340/**
10341 * Saves all snapshots of the machine into the given machine config file. Called
10342 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10343 * @param config
10344 * @return
10345 */
10346HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10347{
10348 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10349
10350 HRESULT rc = S_OK;
10351
10352 try
10353 {
10354 config.llFirstSnapshot.clear();
10355
10356 if (mData->mFirstSnapshot)
10357 {
10358 // the settings use a list for "the first snapshot"
10359 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10360
10361 // get reference to the snapshot on the list and work on that
10362 // element straight in the list to avoid excessive copying later
10363 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10364 if (FAILED(rc)) throw rc;
10365 }
10366
10367// if (mType == IsSessionMachine)
10368// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10369
10370 }
10371 catch (HRESULT err)
10372 {
10373 /* we assume that error info is set by the thrower */
10374 rc = err;
10375 }
10376 catch (...)
10377 {
10378 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10379 }
10380
10381 return rc;
10382}
10383
10384/**
10385 * Saves the VM hardware configuration. It is assumed that the
10386 * given node is empty.
10387 *
10388 * @param data Reference to the settings object for the hardware config.
10389 * @param pDbg Pointer to the settings object for the debugging config
10390 * which happens to live in mHWData.
10391 * @param pAutostart Pointer to the settings object for the autostart config
10392 * which happens to live in mHWData.
10393 */
10394HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10395 settings::Autostart *pAutostart)
10396{
10397 HRESULT rc = S_OK;
10398
10399 try
10400 {
10401 /* The hardware version attribute (optional).
10402 Automatically upgrade from 1 to current default hardware version
10403 when there is no saved state. (ugly!) */
10404 if ( mHWData->mHWVersion == "1"
10405 && mSSData->strStateFilePath.isEmpty()
10406 )
10407 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10408
10409 data.strVersion = mHWData->mHWVersion;
10410 data.uuid = mHWData->mHardwareUUID;
10411
10412 // CPU
10413 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10414 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10415 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10416 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10417 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10418 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10419 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10420 data.fPAE = !!mHWData->mPAEEnabled;
10421 data.enmLongMode = mHWData->mLongMode;
10422 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10423 data.fAPIC = !!mHWData->mAPIC;
10424 data.fX2APIC = !!mHWData->mX2APIC;
10425 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10426 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10427 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10428 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10429 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10430 data.cCPUs = mHWData->mCPUCount;
10431 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10432 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10433 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10434 data.strCpuProfile = mHWData->mCpuProfile;
10435
10436 data.llCpus.clear();
10437 if (data.fCpuHotPlug)
10438 {
10439 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10440 {
10441 if (mHWData->mCPUAttached[idx])
10442 {
10443 settings::Cpu cpu;
10444 cpu.ulId = idx;
10445 data.llCpus.push_back(cpu);
10446 }
10447 }
10448 }
10449
10450 /* Standard and Extended CPUID leafs. */
10451 data.llCpuIdLeafs.clear();
10452 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10453
10454 // memory
10455 data.ulMemorySizeMB = mHWData->mMemorySize;
10456 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10457
10458 // firmware
10459 data.firmwareType = mHWData->mFirmwareType;
10460
10461 // HID
10462 data.pointingHIDType = mHWData->mPointingHIDType;
10463 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10464
10465 // chipset
10466 data.chipsetType = mHWData->mChipsetType;
10467
10468 // paravirt
10469 data.paravirtProvider = mHWData->mParavirtProvider;
10470 data.strParavirtDebug = mHWData->mParavirtDebug;
10471
10472 // emulated USB card reader
10473 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10474
10475 // HPET
10476 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10477
10478 // boot order
10479 data.mapBootOrder.clear();
10480 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10481 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10482
10483 // display
10484 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10485 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10486 data.cMonitors = mHWData->mMonitorCount;
10487 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10488 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10489 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10490 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10491 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10492 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10493 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10494 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10495 {
10496 if (mHWData->maVideoCaptureScreens[i])
10497 ASMBitSet(&data.u64VideoCaptureScreens, i);
10498 else
10499 ASMBitClear(&data.u64VideoCaptureScreens, i);
10500 }
10501 /* store relative video capture file if possible */
10502 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10503 data.strVideoCaptureOptions = mHWData->mVideoCaptureOptions;
10504
10505 /* VRDEServer settings (optional) */
10506 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10507 if (FAILED(rc)) throw rc;
10508
10509 /* BIOS (required) */
10510 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10511 if (FAILED(rc)) throw rc;
10512
10513 /* USB Controller (required) */
10514 data.usbSettings.llUSBControllers.clear();
10515 for (USBControllerList::const_iterator
10516 it = mUSBControllers->begin();
10517 it != mUSBControllers->end();
10518 ++it)
10519 {
10520 ComObjPtr<USBController> ctrl = *it;
10521 settings::USBController settingsCtrl;
10522
10523 settingsCtrl.strName = ctrl->i_getName();
10524 settingsCtrl.enmType = ctrl->i_getControllerType();
10525
10526 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10527 }
10528
10529 /* USB device filters (required) */
10530 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10531 if (FAILED(rc)) throw rc;
10532
10533 /* Network adapters (required) */
10534 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10535 data.llNetworkAdapters.clear();
10536 /* Write out only the nominal number of network adapters for this
10537 * chipset type. Since Machine::commit() hasn't been called there
10538 * may be extra NIC settings in the vector. */
10539 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10540 {
10541 settings::NetworkAdapter nic;
10542 nic.ulSlot = (uint32_t)slot;
10543 /* paranoia check... must not be NULL, but must not crash either. */
10544 if (mNetworkAdapters[slot])
10545 {
10546 if (mNetworkAdapters[slot]->i_hasDefaults())
10547 continue;
10548
10549 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10550 if (FAILED(rc)) throw rc;
10551
10552 data.llNetworkAdapters.push_back(nic);
10553 }
10554 }
10555
10556 /* Serial ports */
10557 data.llSerialPorts.clear();
10558 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10559 {
10560 if (mSerialPorts[slot]->i_hasDefaults())
10561 continue;
10562
10563 settings::SerialPort s;
10564 s.ulSlot = slot;
10565 rc = mSerialPorts[slot]->i_saveSettings(s);
10566 if (FAILED(rc)) return rc;
10567
10568 data.llSerialPorts.push_back(s);
10569 }
10570
10571 /* Parallel ports */
10572 data.llParallelPorts.clear();
10573 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10574 {
10575 if (mParallelPorts[slot]->i_hasDefaults())
10576 continue;
10577
10578 settings::ParallelPort p;
10579 p.ulSlot = slot;
10580 rc = mParallelPorts[slot]->i_saveSettings(p);
10581 if (FAILED(rc)) return rc;
10582
10583 data.llParallelPorts.push_back(p);
10584 }
10585
10586 /* Audio adapter */
10587 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10588 if (FAILED(rc)) return rc;
10589
10590 rc = i_saveStorageControllers(data.storage);
10591 if (FAILED(rc)) return rc;
10592
10593 /* Shared folders */
10594 data.llSharedFolders.clear();
10595 for (HWData::SharedFolderList::const_iterator
10596 it = mHWData->mSharedFolders.begin();
10597 it != mHWData->mSharedFolders.end();
10598 ++it)
10599 {
10600 SharedFolder *pSF = *it;
10601 AutoCaller sfCaller(pSF);
10602 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10603 settings::SharedFolder sf;
10604 sf.strName = pSF->i_getName();
10605 sf.strHostPath = pSF->i_getHostPath();
10606 sf.fWritable = !!pSF->i_isWritable();
10607 sf.fAutoMount = !!pSF->i_isAutoMounted();
10608
10609 data.llSharedFolders.push_back(sf);
10610 }
10611
10612 // clipboard
10613 data.clipboardMode = mHWData->mClipboardMode;
10614
10615 // drag'n'drop
10616 data.dndMode = mHWData->mDnDMode;
10617
10618 /* Guest */
10619 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10620
10621 // IO settings
10622 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10623 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10624
10625 /* BandwidthControl (required) */
10626 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10627 if (FAILED(rc)) throw rc;
10628
10629 /* Host PCI devices */
10630 data.pciAttachments.clear();
10631 for (HWData::PCIDeviceAssignmentList::const_iterator
10632 it = mHWData->mPCIDeviceAssignments.begin();
10633 it != mHWData->mPCIDeviceAssignments.end();
10634 ++it)
10635 {
10636 ComObjPtr<PCIDeviceAttachment> pda = *it;
10637 settings::HostPCIDeviceAttachment hpda;
10638
10639 rc = pda->i_saveSettings(hpda);
10640 if (FAILED(rc)) throw rc;
10641
10642 data.pciAttachments.push_back(hpda);
10643 }
10644
10645 // guest properties
10646 data.llGuestProperties.clear();
10647#ifdef VBOX_WITH_GUEST_PROPS
10648 for (HWData::GuestPropertyMap::const_iterator
10649 it = mHWData->mGuestProperties.begin();
10650 it != mHWData->mGuestProperties.end();
10651 ++it)
10652 {
10653 HWData::GuestProperty property = it->second;
10654
10655 /* Remove transient guest properties at shutdown unless we
10656 * are saving state. Note that restoring snapshot intentionally
10657 * keeps them, they will be removed if appropriate once the final
10658 * machine state is set (as crashes etc. need to work). */
10659 if ( ( mData->mMachineState == MachineState_PoweredOff
10660 || mData->mMachineState == MachineState_Aborted
10661 || mData->mMachineState == MachineState_Teleported)
10662 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10663 continue;
10664 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10665 prop.strName = it->first;
10666 prop.strValue = property.strValue;
10667 prop.timestamp = property.mTimestamp;
10668 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10669 GuestPropWriteFlags(property.mFlags, szFlags);
10670 prop.strFlags = szFlags;
10671
10672 data.llGuestProperties.push_back(prop);
10673 }
10674
10675 /* I presume this doesn't require a backup(). */
10676 mData->mGuestPropertiesModified = FALSE;
10677#endif /* VBOX_WITH_GUEST_PROPS defined */
10678
10679 *pDbg = mHWData->mDebugging;
10680 *pAutostart = mHWData->mAutostart;
10681
10682 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10683 }
10684 catch (std::bad_alloc &)
10685 {
10686 return E_OUTOFMEMORY;
10687 }
10688
10689 AssertComRC(rc);
10690 return rc;
10691}
10692
10693/**
10694 * Saves the storage controller configuration.
10695 *
10696 * @param data storage settings.
10697 */
10698HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10699{
10700 data.llStorageControllers.clear();
10701
10702 for (StorageControllerList::const_iterator
10703 it = mStorageControllers->begin();
10704 it != mStorageControllers->end();
10705 ++it)
10706 {
10707 HRESULT rc;
10708 ComObjPtr<StorageController> pCtl = *it;
10709
10710 settings::StorageController ctl;
10711 ctl.strName = pCtl->i_getName();
10712 ctl.controllerType = pCtl->i_getControllerType();
10713 ctl.storageBus = pCtl->i_getStorageBus();
10714 ctl.ulInstance = pCtl->i_getInstance();
10715 ctl.fBootable = pCtl->i_getBootable();
10716
10717 /* Save the port count. */
10718 ULONG portCount;
10719 rc = pCtl->COMGETTER(PortCount)(&portCount);
10720 ComAssertComRCRet(rc, rc);
10721 ctl.ulPortCount = portCount;
10722
10723 /* Save fUseHostIOCache */
10724 BOOL fUseHostIOCache;
10725 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10726 ComAssertComRCRet(rc, rc);
10727 ctl.fUseHostIOCache = !!fUseHostIOCache;
10728
10729 /* save the devices now. */
10730 rc = i_saveStorageDevices(pCtl, ctl);
10731 ComAssertComRCRet(rc, rc);
10732
10733 data.llStorageControllers.push_back(ctl);
10734 }
10735
10736 return S_OK;
10737}
10738
10739/**
10740 * Saves the hard disk configuration.
10741 */
10742HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10743 settings::StorageController &data)
10744{
10745 MediumAttachmentList atts;
10746
10747 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10748 if (FAILED(rc)) return rc;
10749
10750 data.llAttachedDevices.clear();
10751 for (MediumAttachmentList::const_iterator
10752 it = atts.begin();
10753 it != atts.end();
10754 ++it)
10755 {
10756 settings::AttachedDevice dev;
10757 IMediumAttachment *iA = *it;
10758 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10759 Medium *pMedium = pAttach->i_getMedium();
10760
10761 dev.deviceType = pAttach->i_getType();
10762 dev.lPort = pAttach->i_getPort();
10763 dev.lDevice = pAttach->i_getDevice();
10764 dev.fPassThrough = pAttach->i_getPassthrough();
10765 dev.fHotPluggable = pAttach->i_getHotPluggable();
10766 if (pMedium)
10767 {
10768 if (pMedium->i_isHostDrive())
10769 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10770 else
10771 dev.uuid = pMedium->i_getId();
10772 dev.fTempEject = pAttach->i_getTempEject();
10773 dev.fNonRotational = pAttach->i_getNonRotational();
10774 dev.fDiscard = pAttach->i_getDiscard();
10775 }
10776
10777 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10778
10779 data.llAttachedDevices.push_back(dev);
10780 }
10781
10782 return S_OK;
10783}
10784
10785/**
10786 * Saves machine state settings as defined by aFlags
10787 * (SaveSTS_* values).
10788 *
10789 * @param aFlags Combination of SaveSTS_* flags.
10790 *
10791 * @note Locks objects for writing.
10792 */
10793HRESULT Machine::i_saveStateSettings(int aFlags)
10794{
10795 if (aFlags == 0)
10796 return S_OK;
10797
10798 AutoCaller autoCaller(this);
10799 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10800
10801 /* This object's write lock is also necessary to serialize file access
10802 * (prevent concurrent reads and writes) */
10803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10804
10805 HRESULT rc = S_OK;
10806
10807 Assert(mData->pMachineConfigFile);
10808
10809 try
10810 {
10811 if (aFlags & SaveSTS_CurStateModified)
10812 mData->pMachineConfigFile->fCurrentStateModified = true;
10813
10814 if (aFlags & SaveSTS_StateFilePath)
10815 {
10816 if (!mSSData->strStateFilePath.isEmpty())
10817 /* try to make the file name relative to the settings file dir */
10818 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10819 else
10820 mData->pMachineConfigFile->strStateFile.setNull();
10821 }
10822
10823 if (aFlags & SaveSTS_StateTimeStamp)
10824 {
10825 Assert( mData->mMachineState != MachineState_Aborted
10826 || mSSData->strStateFilePath.isEmpty());
10827
10828 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10829
10830 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10831/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10832 }
10833
10834 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10835 }
10836 catch (...)
10837 {
10838 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10839 }
10840
10841 return rc;
10842}
10843
10844/**
10845 * Ensures that the given medium is added to a media registry. If this machine
10846 * was created with 4.0 or later, then the machine registry is used. Otherwise
10847 * the global VirtualBox media registry is used.
10848 *
10849 * Caller must NOT hold machine lock, media tree or any medium locks!
10850 *
10851 * @param pMedium
10852 */
10853void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10854{
10855 /* Paranoia checks: do not hold machine or media tree locks. */
10856 AssertReturnVoid(!isWriteLockOnCurrentThread());
10857 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10858
10859 ComObjPtr<Medium> pBase;
10860 {
10861 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10862 pBase = pMedium->i_getBase();
10863 }
10864
10865 /* Paranoia checks: do not hold medium locks. */
10866 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10867 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10868
10869 // decide which medium registry to use now that the medium is attached:
10870 Guid uuid;
10871 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10872 if (fCanHaveOwnMediaRegistry)
10873 // machine XML is VirtualBox 4.0 or higher:
10874 uuid = i_getId(); // machine UUID
10875 else
10876 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10877
10878 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10879 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10880 if (pMedium->i_addRegistry(uuid))
10881 mParent->i_markRegistryModified(uuid);
10882
10883 /* For more complex hard disk structures it can happen that the base
10884 * medium isn't yet associated with any medium registry. Do that now. */
10885 if (pMedium != pBase)
10886 {
10887 /* Tree lock needed by Medium::addRegistry when recursing. */
10888 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10889 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10890 {
10891 treeLock.release();
10892 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10893 treeLock.acquire();
10894 }
10895 if (pBase->i_addRegistryRecursive(uuid))
10896 {
10897 treeLock.release();
10898 mParent->i_markRegistryModified(uuid);
10899 }
10900 }
10901}
10902
10903/**
10904 * Creates differencing hard disks for all normal hard disks attached to this
10905 * machine and a new set of attachments to refer to created disks.
10906 *
10907 * Used when taking a snapshot or when deleting the current state. Gets called
10908 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10909 *
10910 * This method assumes that mMediumAttachments contains the original hard disk
10911 * attachments it needs to create diffs for. On success, these attachments will
10912 * be replaced with the created diffs.
10913 *
10914 * Attachments with non-normal hard disks are left as is.
10915 *
10916 * If @a aOnline is @c false then the original hard disks that require implicit
10917 * diffs will be locked for reading. Otherwise it is assumed that they are
10918 * already locked for writing (when the VM was started). Note that in the latter
10919 * case it is responsibility of the caller to lock the newly created diffs for
10920 * writing if this method succeeds.
10921 *
10922 * @param aProgress Progress object to run (must contain at least as
10923 * many operations left as the number of hard disks
10924 * attached).
10925 * @param aWeight Weight of this operation.
10926 * @param aOnline Whether the VM was online prior to this operation.
10927 *
10928 * @note The progress object is not marked as completed, neither on success nor
10929 * on failure. This is a responsibility of the caller.
10930 *
10931 * @note Locks this object and the media tree for writing.
10932 */
10933HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10934 ULONG aWeight,
10935 bool aOnline)
10936{
10937 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10938
10939 AutoCaller autoCaller(this);
10940 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10941
10942 AutoMultiWriteLock2 alock(this->lockHandle(),
10943 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10944
10945 /* must be in a protective state because we release the lock below */
10946 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10947 || mData->mMachineState == MachineState_OnlineSnapshotting
10948 || mData->mMachineState == MachineState_LiveSnapshotting
10949 || mData->mMachineState == MachineState_RestoringSnapshot
10950 || mData->mMachineState == MachineState_DeletingSnapshot
10951 , E_FAIL);
10952
10953 HRESULT rc = S_OK;
10954
10955 // use appropriate locked media map (online or offline)
10956 MediumLockListMap lockedMediaOffline;
10957 MediumLockListMap *lockedMediaMap;
10958 if (aOnline)
10959 lockedMediaMap = &mData->mSession.mLockedMedia;
10960 else
10961 lockedMediaMap = &lockedMediaOffline;
10962
10963 try
10964 {
10965 if (!aOnline)
10966 {
10967 /* lock all attached hard disks early to detect "in use"
10968 * situations before creating actual diffs */
10969 for (MediumAttachmentList::const_iterator
10970 it = mMediumAttachments->begin();
10971 it != mMediumAttachments->end();
10972 ++it)
10973 {
10974 MediumAttachment *pAtt = *it;
10975 if (pAtt->i_getType() == DeviceType_HardDisk)
10976 {
10977 Medium *pMedium = pAtt->i_getMedium();
10978 Assert(pMedium);
10979
10980 MediumLockList *pMediumLockList(new MediumLockList());
10981 alock.release();
10982 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10983 NULL /* pToLockWrite */,
10984 false /* fMediumLockWriteAll */,
10985 NULL,
10986 *pMediumLockList);
10987 alock.acquire();
10988 if (FAILED(rc))
10989 {
10990 delete pMediumLockList;
10991 throw rc;
10992 }
10993 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10994 if (FAILED(rc))
10995 {
10996 throw setError(rc,
10997 tr("Collecting locking information for all attached media failed"));
10998 }
10999 }
11000 }
11001
11002 /* Now lock all media. If this fails, nothing is locked. */
11003 alock.release();
11004 rc = lockedMediaMap->Lock();
11005 alock.acquire();
11006 if (FAILED(rc))
11007 {
11008 throw setError(rc,
11009 tr("Locking of attached media failed"));
11010 }
11011 }
11012
11013 /* remember the current list (note that we don't use backup() since
11014 * mMediumAttachments may be already backed up) */
11015 MediumAttachmentList atts = *mMediumAttachments.data();
11016
11017 /* start from scratch */
11018 mMediumAttachments->clear();
11019
11020 /* go through remembered attachments and create diffs for normal hard
11021 * disks and attach them */
11022 for (MediumAttachmentList::const_iterator
11023 it = atts.begin();
11024 it != atts.end();
11025 ++it)
11026 {
11027 MediumAttachment *pAtt = *it;
11028
11029 DeviceType_T devType = pAtt->i_getType();
11030 Medium *pMedium = pAtt->i_getMedium();
11031
11032 if ( devType != DeviceType_HardDisk
11033 || pMedium == NULL
11034 || pMedium->i_getType() != MediumType_Normal)
11035 {
11036 /* copy the attachment as is */
11037
11038 /** @todo the progress object created in SessionMachine::TakeSnaphot
11039 * only expects operations for hard disks. Later other
11040 * device types need to show up in the progress as well. */
11041 if (devType == DeviceType_HardDisk)
11042 {
11043 if (pMedium == NULL)
11044 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11045 aWeight); // weight
11046 else
11047 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11048 pMedium->i_getBase()->i_getName().c_str()).raw(),
11049 aWeight); // weight
11050 }
11051
11052 mMediumAttachments->push_back(pAtt);
11053 continue;
11054 }
11055
11056 /* need a diff */
11057 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11058 pMedium->i_getBase()->i_getName().c_str()).raw(),
11059 aWeight); // weight
11060
11061 Utf8Str strFullSnapshotFolder;
11062 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11063
11064 ComObjPtr<Medium> diff;
11065 diff.createObject();
11066 // store the diff in the same registry as the parent
11067 // (this cannot fail here because we can't create implicit diffs for
11068 // unregistered images)
11069 Guid uuidRegistryParent;
11070 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11071 Assert(fInRegistry); NOREF(fInRegistry);
11072 rc = diff->init(mParent,
11073 pMedium->i_getPreferredDiffFormat(),
11074 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11075 uuidRegistryParent,
11076 DeviceType_HardDisk);
11077 if (FAILED(rc)) throw rc;
11078
11079 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11080 * the push_back? Looks like we're going to release medium with the
11081 * wrong kind of lock (general issue with if we fail anywhere at all)
11082 * and an orphaned VDI in the snapshots folder. */
11083
11084 /* update the appropriate lock list */
11085 MediumLockList *pMediumLockList;
11086 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11087 AssertComRCThrowRC(rc);
11088 if (aOnline)
11089 {
11090 alock.release();
11091 /* The currently attached medium will be read-only, change
11092 * the lock type to read. */
11093 rc = pMediumLockList->Update(pMedium, false);
11094 alock.acquire();
11095 AssertComRCThrowRC(rc);
11096 }
11097
11098 /* release the locks before the potentially lengthy operation */
11099 alock.release();
11100 rc = pMedium->i_createDiffStorage(diff,
11101 pMedium->i_getPreferredDiffVariant(),
11102 pMediumLockList,
11103 NULL /* aProgress */,
11104 true /* aWait */);
11105 alock.acquire();
11106 if (FAILED(rc)) throw rc;
11107
11108 /* actual lock list update is done in Machine::i_commitMedia */
11109
11110 rc = diff->i_addBackReference(mData->mUuid);
11111 AssertComRCThrowRC(rc);
11112
11113 /* add a new attachment */
11114 ComObjPtr<MediumAttachment> attachment;
11115 attachment.createObject();
11116 rc = attachment->init(this,
11117 diff,
11118 pAtt->i_getControllerName(),
11119 pAtt->i_getPort(),
11120 pAtt->i_getDevice(),
11121 DeviceType_HardDisk,
11122 true /* aImplicit */,
11123 false /* aPassthrough */,
11124 false /* aTempEject */,
11125 pAtt->i_getNonRotational(),
11126 pAtt->i_getDiscard(),
11127 pAtt->i_getHotPluggable(),
11128 pAtt->i_getBandwidthGroup());
11129 if (FAILED(rc)) throw rc;
11130
11131 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11132 AssertComRCThrowRC(rc);
11133 mMediumAttachments->push_back(attachment);
11134 }
11135 }
11136 catch (HRESULT aRC) { rc = aRC; }
11137
11138 /* unlock all hard disks we locked when there is no VM */
11139 if (!aOnline)
11140 {
11141 ErrorInfoKeeper eik;
11142
11143 HRESULT rc1 = lockedMediaMap->Clear();
11144 AssertComRC(rc1);
11145 }
11146
11147 return rc;
11148}
11149
11150/**
11151 * Deletes implicit differencing hard disks created either by
11152 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11153 * mMediumAttachments.
11154 *
11155 * Note that to delete hard disks created by #attachDevice() this method is
11156 * called from #i_rollbackMedia() when the changes are rolled back.
11157 *
11158 * @note Locks this object and the media tree for writing.
11159 */
11160HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11161{
11162 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11163
11164 AutoCaller autoCaller(this);
11165 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11166
11167 AutoMultiWriteLock2 alock(this->lockHandle(),
11168 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11169
11170 /* We absolutely must have backed up state. */
11171 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11172
11173 /* Check if there are any implicitly created diff images. */
11174 bool fImplicitDiffs = false;
11175 for (MediumAttachmentList::const_iterator
11176 it = mMediumAttachments->begin();
11177 it != mMediumAttachments->end();
11178 ++it)
11179 {
11180 const ComObjPtr<MediumAttachment> &pAtt = *it;
11181 if (pAtt->i_isImplicit())
11182 {
11183 fImplicitDiffs = true;
11184 break;
11185 }
11186 }
11187 /* If there is nothing to do, leave early. This saves lots of image locking
11188 * effort. It also avoids a MachineStateChanged event without real reason.
11189 * This is important e.g. when loading a VM config, because there should be
11190 * no events. Otherwise API clients can become thoroughly confused for
11191 * inaccessible VMs (the code for loading VM configs uses this method for
11192 * cleanup if the config makes no sense), as they take such events as an
11193 * indication that the VM is alive, and they would force the VM config to
11194 * be reread, leading to an endless loop. */
11195 if (!fImplicitDiffs)
11196 return S_OK;
11197
11198 HRESULT rc = S_OK;
11199 MachineState_T oldState = mData->mMachineState;
11200
11201 /* will release the lock before the potentially lengthy operation,
11202 * so protect with the special state (unless already protected) */
11203 if ( oldState != MachineState_Snapshotting
11204 && oldState != MachineState_OnlineSnapshotting
11205 && oldState != MachineState_LiveSnapshotting
11206 && oldState != MachineState_RestoringSnapshot
11207 && oldState != MachineState_DeletingSnapshot
11208 && oldState != MachineState_DeletingSnapshotOnline
11209 && oldState != MachineState_DeletingSnapshotPaused
11210 )
11211 i_setMachineState(MachineState_SettingUp);
11212
11213 // use appropriate locked media map (online or offline)
11214 MediumLockListMap lockedMediaOffline;
11215 MediumLockListMap *lockedMediaMap;
11216 if (aOnline)
11217 lockedMediaMap = &mData->mSession.mLockedMedia;
11218 else
11219 lockedMediaMap = &lockedMediaOffline;
11220
11221 try
11222 {
11223 if (!aOnline)
11224 {
11225 /* lock all attached hard disks early to detect "in use"
11226 * situations before deleting actual diffs */
11227 for (MediumAttachmentList::const_iterator
11228 it = mMediumAttachments->begin();
11229 it != mMediumAttachments->end();
11230 ++it)
11231 {
11232 MediumAttachment *pAtt = *it;
11233 if (pAtt->i_getType() == DeviceType_HardDisk)
11234 {
11235 Medium *pMedium = pAtt->i_getMedium();
11236 Assert(pMedium);
11237
11238 MediumLockList *pMediumLockList(new MediumLockList());
11239 alock.release();
11240 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11241 NULL /* pToLockWrite */,
11242 false /* fMediumLockWriteAll */,
11243 NULL,
11244 *pMediumLockList);
11245 alock.acquire();
11246
11247 if (FAILED(rc))
11248 {
11249 delete pMediumLockList;
11250 throw rc;
11251 }
11252
11253 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11254 if (FAILED(rc))
11255 throw rc;
11256 }
11257 }
11258
11259 if (FAILED(rc))
11260 throw rc;
11261 } // end of offline
11262
11263 /* Lock lists are now up to date and include implicitly created media */
11264
11265 /* Go through remembered attachments and delete all implicitly created
11266 * diffs and fix up the attachment information */
11267 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11268 MediumAttachmentList implicitAtts;
11269 for (MediumAttachmentList::const_iterator
11270 it = mMediumAttachments->begin();
11271 it != mMediumAttachments->end();
11272 ++it)
11273 {
11274 ComObjPtr<MediumAttachment> pAtt = *it;
11275 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11276 if (pMedium.isNull())
11277 continue;
11278
11279 // Implicit attachments go on the list for deletion and back references are removed.
11280 if (pAtt->i_isImplicit())
11281 {
11282 /* Deassociate and mark for deletion */
11283 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11284 rc = pMedium->i_removeBackReference(mData->mUuid);
11285 if (FAILED(rc))
11286 throw rc;
11287 implicitAtts.push_back(pAtt);
11288 continue;
11289 }
11290
11291 /* Was this medium attached before? */
11292 if (!i_findAttachment(oldAtts, pMedium))
11293 {
11294 /* no: de-associate */
11295 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11296 rc = pMedium->i_removeBackReference(mData->mUuid);
11297 if (FAILED(rc))
11298 throw rc;
11299 continue;
11300 }
11301 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11302 }
11303
11304 /* If there are implicit attachments to delete, throw away the lock
11305 * map contents (which will unlock all media) since the medium
11306 * attachments will be rolled back. Below we need to completely
11307 * recreate the lock map anyway since it is infinitely complex to
11308 * do this incrementally (would need reconstructing each attachment
11309 * change, which would be extremely hairy). */
11310 if (implicitAtts.size() != 0)
11311 {
11312 ErrorInfoKeeper eik;
11313
11314 HRESULT rc1 = lockedMediaMap->Clear();
11315 AssertComRC(rc1);
11316 }
11317
11318 /* rollback hard disk changes */
11319 mMediumAttachments.rollback();
11320
11321 MultiResult mrc(S_OK);
11322
11323 // Delete unused implicit diffs.
11324 if (implicitAtts.size() != 0)
11325 {
11326 alock.release();
11327
11328 for (MediumAttachmentList::const_iterator
11329 it = implicitAtts.begin();
11330 it != implicitAtts.end();
11331 ++it)
11332 {
11333 // Remove medium associated with this attachment.
11334 ComObjPtr<MediumAttachment> pAtt = *it;
11335 Assert(pAtt);
11336 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11337 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11338 Assert(pMedium);
11339
11340 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11341 // continue on delete failure, just collect error messages
11342 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11343 pMedium->i_getLocationFull().c_str() ));
11344 mrc = rc;
11345 }
11346 // Clear the list of deleted implicit attachments now, while not
11347 // holding the lock, as it will ultimately trigger Medium::uninit()
11348 // calls which assume that the media tree lock isn't held.
11349 implicitAtts.clear();
11350
11351 alock.acquire();
11352
11353 /* if there is a VM recreate media lock map as mentioned above,
11354 * otherwise it is a waste of time and we leave things unlocked */
11355 if (aOnline)
11356 {
11357 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11358 /* must never be NULL, but better safe than sorry */
11359 if (!pMachine.isNull())
11360 {
11361 alock.release();
11362 rc = mData->mSession.mMachine->i_lockMedia();
11363 alock.acquire();
11364 if (FAILED(rc))
11365 throw rc;
11366 }
11367 }
11368 }
11369 }
11370 catch (HRESULT aRC) {rc = aRC;}
11371
11372 if (mData->mMachineState == MachineState_SettingUp)
11373 i_setMachineState(oldState);
11374
11375 /* unlock all hard disks we locked when there is no VM */
11376 if (!aOnline)
11377 {
11378 ErrorInfoKeeper eik;
11379
11380 HRESULT rc1 = lockedMediaMap->Clear();
11381 AssertComRC(rc1);
11382 }
11383
11384 return rc;
11385}
11386
11387
11388/**
11389 * Looks through the given list of media attachments for one with the given parameters
11390 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11391 * can be searched as well if needed.
11392 *
11393 * @param ll
11394 * @param aControllerName
11395 * @param aControllerPort
11396 * @param aDevice
11397 * @return
11398 */
11399MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11400 const Utf8Str &aControllerName,
11401 LONG aControllerPort,
11402 LONG aDevice)
11403{
11404 for (MediumAttachmentList::const_iterator
11405 it = ll.begin();
11406 it != ll.end();
11407 ++it)
11408 {
11409 MediumAttachment *pAttach = *it;
11410 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11411 return pAttach;
11412 }
11413
11414 return NULL;
11415}
11416
11417/**
11418 * Looks through the given list of media attachments for one with the given parameters
11419 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11420 * can be searched as well if needed.
11421 *
11422 * @param ll
11423 * @param pMedium
11424 * @return
11425 */
11426MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11427 ComObjPtr<Medium> pMedium)
11428{
11429 for (MediumAttachmentList::const_iterator
11430 it = ll.begin();
11431 it != ll.end();
11432 ++it)
11433 {
11434 MediumAttachment *pAttach = *it;
11435 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11436 if (pMediumThis == pMedium)
11437 return pAttach;
11438 }
11439
11440 return NULL;
11441}
11442
11443/**
11444 * Looks through the given list of media attachments for one with the given parameters
11445 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11446 * can be searched as well if needed.
11447 *
11448 * @param ll
11449 * @param id
11450 * @return
11451 */
11452MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11453 Guid &id)
11454{
11455 for (MediumAttachmentList::const_iterator
11456 it = ll.begin();
11457 it != ll.end();
11458 ++it)
11459 {
11460 MediumAttachment *pAttach = *it;
11461 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11462 if (pMediumThis->i_getId() == id)
11463 return pAttach;
11464 }
11465
11466 return NULL;
11467}
11468
11469/**
11470 * Main implementation for Machine::DetachDevice. This also gets called
11471 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11472 *
11473 * @param pAttach Medium attachment to detach.
11474 * @param writeLock Machine write lock which the caller must have locked once.
11475 * This may be released temporarily in here.
11476 * @param pSnapshot If NULL, then the detachment is for the current machine.
11477 * Otherwise this is for a SnapshotMachine, and this must be
11478 * its snapshot.
11479 * @return
11480 */
11481HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11482 AutoWriteLock &writeLock,
11483 Snapshot *pSnapshot)
11484{
11485 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11486 DeviceType_T mediumType = pAttach->i_getType();
11487
11488 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11489
11490 if (pAttach->i_isImplicit())
11491 {
11492 /* attempt to implicitly delete the implicitly created diff */
11493
11494 /// @todo move the implicit flag from MediumAttachment to Medium
11495 /// and forbid any hard disk operation when it is implicit. Or maybe
11496 /// a special media state for it to make it even more simple.
11497
11498 Assert(mMediumAttachments.isBackedUp());
11499
11500 /* will release the lock before the potentially lengthy operation, so
11501 * protect with the special state */
11502 MachineState_T oldState = mData->mMachineState;
11503 i_setMachineState(MachineState_SettingUp);
11504
11505 writeLock.release();
11506
11507 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11508 true /*aWait*/);
11509
11510 writeLock.acquire();
11511
11512 i_setMachineState(oldState);
11513
11514 if (FAILED(rc)) return rc;
11515 }
11516
11517 i_setModified(IsModified_Storage);
11518 mMediumAttachments.backup();
11519 mMediumAttachments->remove(pAttach);
11520
11521 if (!oldmedium.isNull())
11522 {
11523 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11524 if (pSnapshot)
11525 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11526 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11527 else if (mediumType != DeviceType_HardDisk)
11528 oldmedium->i_removeBackReference(mData->mUuid);
11529 }
11530
11531 return S_OK;
11532}
11533
11534/**
11535 * Goes thru all media of the given list and
11536 *
11537 * 1) calls i_detachDevice() on each of them for this machine and
11538 * 2) adds all Medium objects found in the process to the given list,
11539 * depending on cleanupMode.
11540 *
11541 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11542 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11543 * media to the list.
11544 *
11545 * This gets called from Machine::Unregister, both for the actual Machine and
11546 * the SnapshotMachine objects that might be found in the snapshots.
11547 *
11548 * Requires caller and locking. The machine lock must be passed in because it
11549 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11550 *
11551 * @param writeLock Machine lock from top-level caller; this gets passed to
11552 * i_detachDevice.
11553 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11554 * object if called for a SnapshotMachine.
11555 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11556 * added to llMedia; if Full, then all media get added;
11557 * otherwise no media get added.
11558 * @param llMedia Caller's list to receive Medium objects which got detached so
11559 * caller can close() them, depending on cleanupMode.
11560 * @return
11561 */
11562HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11563 Snapshot *pSnapshot,
11564 CleanupMode_T cleanupMode,
11565 MediaList &llMedia)
11566{
11567 Assert(isWriteLockOnCurrentThread());
11568
11569 HRESULT rc;
11570
11571 // make a temporary list because i_detachDevice invalidates iterators into
11572 // mMediumAttachments
11573 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11574
11575 for (MediumAttachmentList::iterator
11576 it = llAttachments2.begin();
11577 it != llAttachments2.end();
11578 ++it)
11579 {
11580 ComObjPtr<MediumAttachment> &pAttach = *it;
11581 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11582
11583 if (!pMedium.isNull())
11584 {
11585 AutoCaller mac(pMedium);
11586 if (FAILED(mac.rc())) return mac.rc();
11587 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11588 DeviceType_T devType = pMedium->i_getDeviceType();
11589 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11590 && devType == DeviceType_HardDisk)
11591 || (cleanupMode == CleanupMode_Full)
11592 )
11593 {
11594 llMedia.push_back(pMedium);
11595 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11596 /* Not allowed to keep this lock as below we need the parent
11597 * medium lock, and the lock order is parent to child. */
11598 lock.release();
11599 /*
11600 * Search for medias which are not attached to any machine, but
11601 * in the chain to an attached disk. Mediums are only consided
11602 * if they are:
11603 * - have only one child
11604 * - no references to any machines
11605 * - are of normal medium type
11606 */
11607 while (!pParent.isNull())
11608 {
11609 AutoCaller mac1(pParent);
11610 if (FAILED(mac1.rc())) return mac1.rc();
11611 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11612 if (pParent->i_getChildren().size() == 1)
11613 {
11614 if ( pParent->i_getMachineBackRefCount() == 0
11615 && pParent->i_getType() == MediumType_Normal
11616 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11617 llMedia.push_back(pParent);
11618 }
11619 else
11620 break;
11621 pParent = pParent->i_getParent();
11622 }
11623 }
11624 }
11625
11626 // real machine: then we need to use the proper method
11627 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11628
11629 if (FAILED(rc))
11630 return rc;
11631 }
11632
11633 return S_OK;
11634}
11635
11636/**
11637 * Perform deferred hard disk detachments.
11638 *
11639 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11640 * changed (not backed up).
11641 *
11642 * If @a aOnline is @c true then this method will also unlock the old hard
11643 * disks for which the new implicit diffs were created and will lock these new
11644 * diffs for writing.
11645 *
11646 * @param aOnline Whether the VM was online prior to this operation.
11647 *
11648 * @note Locks this object for writing!
11649 */
11650void Machine::i_commitMedia(bool aOnline /*= false*/)
11651{
11652 AutoCaller autoCaller(this);
11653 AssertComRCReturnVoid(autoCaller.rc());
11654
11655 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11656
11657 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11658
11659 HRESULT rc = S_OK;
11660
11661 /* no attach/detach operations -- nothing to do */
11662 if (!mMediumAttachments.isBackedUp())
11663 return;
11664
11665 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11666 bool fMediaNeedsLocking = false;
11667
11668 /* enumerate new attachments */
11669 for (MediumAttachmentList::const_iterator
11670 it = mMediumAttachments->begin();
11671 it != mMediumAttachments->end();
11672 ++it)
11673 {
11674 MediumAttachment *pAttach = *it;
11675
11676 pAttach->i_commit();
11677
11678 Medium *pMedium = pAttach->i_getMedium();
11679 bool fImplicit = pAttach->i_isImplicit();
11680
11681 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11682 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11683 fImplicit));
11684
11685 /** @todo convert all this Machine-based voodoo to MediumAttachment
11686 * based commit logic. */
11687 if (fImplicit)
11688 {
11689 /* convert implicit attachment to normal */
11690 pAttach->i_setImplicit(false);
11691
11692 if ( aOnline
11693 && pMedium
11694 && pAttach->i_getType() == DeviceType_HardDisk
11695 )
11696 {
11697 /* update the appropriate lock list */
11698 MediumLockList *pMediumLockList;
11699 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11700 AssertComRC(rc);
11701 if (pMediumLockList)
11702 {
11703 /* unlock if there's a need to change the locking */
11704 if (!fMediaNeedsLocking)
11705 {
11706 rc = mData->mSession.mLockedMedia.Unlock();
11707 AssertComRC(rc);
11708 fMediaNeedsLocking = true;
11709 }
11710 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11711 AssertComRC(rc);
11712 rc = pMediumLockList->Append(pMedium, true);
11713 AssertComRC(rc);
11714 }
11715 }
11716
11717 continue;
11718 }
11719
11720 if (pMedium)
11721 {
11722 /* was this medium attached before? */
11723 for (MediumAttachmentList::iterator
11724 oldIt = oldAtts.begin();
11725 oldIt != oldAtts.end();
11726 ++oldIt)
11727 {
11728 MediumAttachment *pOldAttach = *oldIt;
11729 if (pOldAttach->i_getMedium() == pMedium)
11730 {
11731 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11732
11733 /* yes: remove from old to avoid de-association */
11734 oldAtts.erase(oldIt);
11735 break;
11736 }
11737 }
11738 }
11739 }
11740
11741 /* enumerate remaining old attachments and de-associate from the
11742 * current machine state */
11743 for (MediumAttachmentList::const_iterator
11744 it = oldAtts.begin();
11745 it != oldAtts.end();
11746 ++it)
11747 {
11748 MediumAttachment *pAttach = *it;
11749 Medium *pMedium = pAttach->i_getMedium();
11750
11751 /* Detach only hard disks, since DVD/floppy media is detached
11752 * instantly in MountMedium. */
11753 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11754 {
11755 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11756
11757 /* now de-associate from the current machine state */
11758 rc = pMedium->i_removeBackReference(mData->mUuid);
11759 AssertComRC(rc);
11760
11761 if (aOnline)
11762 {
11763 /* unlock since medium is not used anymore */
11764 MediumLockList *pMediumLockList;
11765 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11766 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11767 {
11768 /* this happens for online snapshots, there the attachment
11769 * is changing, but only to a diff image created under
11770 * the old one, so there is no separate lock list */
11771 Assert(!pMediumLockList);
11772 }
11773 else
11774 {
11775 AssertComRC(rc);
11776 if (pMediumLockList)
11777 {
11778 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11779 AssertComRC(rc);
11780 }
11781 }
11782 }
11783 }
11784 }
11785
11786 /* take media locks again so that the locking state is consistent */
11787 if (fMediaNeedsLocking)
11788 {
11789 Assert(aOnline);
11790 rc = mData->mSession.mLockedMedia.Lock();
11791 AssertComRC(rc);
11792 }
11793
11794 /* commit the hard disk changes */
11795 mMediumAttachments.commit();
11796
11797 if (i_isSessionMachine())
11798 {
11799 /*
11800 * Update the parent machine to point to the new owner.
11801 * This is necessary because the stored parent will point to the
11802 * session machine otherwise and cause crashes or errors later
11803 * when the session machine gets invalid.
11804 */
11805 /** @todo Change the MediumAttachment class to behave like any other
11806 * class in this regard by creating peer MediumAttachment
11807 * objects for session machines and share the data with the peer
11808 * machine.
11809 */
11810 for (MediumAttachmentList::const_iterator
11811 it = mMediumAttachments->begin();
11812 it != mMediumAttachments->end();
11813 ++it)
11814 (*it)->i_updateParentMachine(mPeer);
11815
11816 /* attach new data to the primary machine and reshare it */
11817 mPeer->mMediumAttachments.attach(mMediumAttachments);
11818 }
11819
11820 return;
11821}
11822
11823/**
11824 * Perform deferred deletion of implicitly created diffs.
11825 *
11826 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11827 * changed (not backed up).
11828 *
11829 * @note Locks this object for writing!
11830 */
11831void Machine::i_rollbackMedia()
11832{
11833 AutoCaller autoCaller(this);
11834 AssertComRCReturnVoid(autoCaller.rc());
11835
11836 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11837 LogFlowThisFunc(("Entering rollbackMedia\n"));
11838
11839 HRESULT rc = S_OK;
11840
11841 /* no attach/detach operations -- nothing to do */
11842 if (!mMediumAttachments.isBackedUp())
11843 return;
11844
11845 /* enumerate new attachments */
11846 for (MediumAttachmentList::const_iterator
11847 it = mMediumAttachments->begin();
11848 it != mMediumAttachments->end();
11849 ++it)
11850 {
11851 MediumAttachment *pAttach = *it;
11852 /* Fix up the backrefs for DVD/floppy media. */
11853 if (pAttach->i_getType() != DeviceType_HardDisk)
11854 {
11855 Medium *pMedium = pAttach->i_getMedium();
11856 if (pMedium)
11857 {
11858 rc = pMedium->i_removeBackReference(mData->mUuid);
11859 AssertComRC(rc);
11860 }
11861 }
11862
11863 (*it)->i_rollback();
11864
11865 pAttach = *it;
11866 /* Fix up the backrefs for DVD/floppy media. */
11867 if (pAttach->i_getType() != DeviceType_HardDisk)
11868 {
11869 Medium *pMedium = pAttach->i_getMedium();
11870 if (pMedium)
11871 {
11872 rc = pMedium->i_addBackReference(mData->mUuid);
11873 AssertComRC(rc);
11874 }
11875 }
11876 }
11877
11878 /** @todo convert all this Machine-based voodoo to MediumAttachment
11879 * based rollback logic. */
11880 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11881
11882 return;
11883}
11884
11885/**
11886 * Returns true if the settings file is located in the directory named exactly
11887 * as the machine; this means, among other things, that the machine directory
11888 * should be auto-renamed.
11889 *
11890 * @param aSettingsDir if not NULL, the full machine settings file directory
11891 * name will be assigned there.
11892 *
11893 * @note Doesn't lock anything.
11894 * @note Not thread safe (must be called from this object's lock).
11895 */
11896bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11897{
11898 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11899 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11900 if (aSettingsDir)
11901 *aSettingsDir = strMachineDirName;
11902 strMachineDirName.stripPath(); // vmname
11903 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11904 strConfigFileOnly.stripPath() // vmname.vbox
11905 .stripSuffix(); // vmname
11906 /** @todo hack, make somehow use of ComposeMachineFilename */
11907 if (mUserData->s.fDirectoryIncludesUUID)
11908 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11909
11910 AssertReturn(!strMachineDirName.isEmpty(), false);
11911 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11912
11913 return strMachineDirName == strConfigFileOnly;
11914}
11915
11916/**
11917 * Discards all changes to machine settings.
11918 *
11919 * @param aNotify Whether to notify the direct session about changes or not.
11920 *
11921 * @note Locks objects for writing!
11922 */
11923void Machine::i_rollback(bool aNotify)
11924{
11925 AutoCaller autoCaller(this);
11926 AssertComRCReturn(autoCaller.rc(), (void)0);
11927
11928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11929
11930 if (!mStorageControllers.isNull())
11931 {
11932 if (mStorageControllers.isBackedUp())
11933 {
11934 /* unitialize all new devices (absent in the backed up list). */
11935 StorageControllerList *backedList = mStorageControllers.backedUpData();
11936 for (StorageControllerList::const_iterator
11937 it = mStorageControllers->begin();
11938 it != mStorageControllers->end();
11939 ++it)
11940 {
11941 if ( std::find(backedList->begin(), backedList->end(), *it)
11942 == backedList->end()
11943 )
11944 {
11945 (*it)->uninit();
11946 }
11947 }
11948
11949 /* restore the list */
11950 mStorageControllers.rollback();
11951 }
11952
11953 /* rollback any changes to devices after restoring the list */
11954 if (mData->flModifications & IsModified_Storage)
11955 {
11956 for (StorageControllerList::const_iterator
11957 it = mStorageControllers->begin();
11958 it != mStorageControllers->end();
11959 ++it)
11960 {
11961 (*it)->i_rollback();
11962 }
11963 }
11964 }
11965
11966 if (!mUSBControllers.isNull())
11967 {
11968 if (mUSBControllers.isBackedUp())
11969 {
11970 /* unitialize all new devices (absent in the backed up list). */
11971 USBControllerList *backedList = mUSBControllers.backedUpData();
11972 for (USBControllerList::const_iterator
11973 it = mUSBControllers->begin();
11974 it != mUSBControllers->end();
11975 ++it)
11976 {
11977 if ( std::find(backedList->begin(), backedList->end(), *it)
11978 == backedList->end()
11979 )
11980 {
11981 (*it)->uninit();
11982 }
11983 }
11984
11985 /* restore the list */
11986 mUSBControllers.rollback();
11987 }
11988
11989 /* rollback any changes to devices after restoring the list */
11990 if (mData->flModifications & IsModified_USB)
11991 {
11992 for (USBControllerList::const_iterator
11993 it = mUSBControllers->begin();
11994 it != mUSBControllers->end();
11995 ++it)
11996 {
11997 (*it)->i_rollback();
11998 }
11999 }
12000 }
12001
12002 mUserData.rollback();
12003
12004 mHWData.rollback();
12005
12006 if (mData->flModifications & IsModified_Storage)
12007 i_rollbackMedia();
12008
12009 if (mBIOSSettings)
12010 mBIOSSettings->i_rollback();
12011
12012 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12013 mVRDEServer->i_rollback();
12014
12015 if (mAudioAdapter)
12016 mAudioAdapter->i_rollback();
12017
12018 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12019 mUSBDeviceFilters->i_rollback();
12020
12021 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12022 mBandwidthControl->i_rollback();
12023
12024 if (!mHWData.isNull())
12025 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12026 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12027 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12028 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12029
12030 if (mData->flModifications & IsModified_NetworkAdapters)
12031 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12032 if ( mNetworkAdapters[slot]
12033 && mNetworkAdapters[slot]->i_isModified())
12034 {
12035 mNetworkAdapters[slot]->i_rollback();
12036 networkAdapters[slot] = mNetworkAdapters[slot];
12037 }
12038
12039 if (mData->flModifications & IsModified_SerialPorts)
12040 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12041 if ( mSerialPorts[slot]
12042 && mSerialPorts[slot]->i_isModified())
12043 {
12044 mSerialPorts[slot]->i_rollback();
12045 serialPorts[slot] = mSerialPorts[slot];
12046 }
12047
12048 if (mData->flModifications & IsModified_ParallelPorts)
12049 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12050 if ( mParallelPorts[slot]
12051 && mParallelPorts[slot]->i_isModified())
12052 {
12053 mParallelPorts[slot]->i_rollback();
12054 parallelPorts[slot] = mParallelPorts[slot];
12055 }
12056
12057 if (aNotify)
12058 {
12059 /* inform the direct session about changes */
12060
12061 ComObjPtr<Machine> that = this;
12062 uint32_t flModifications = mData->flModifications;
12063 alock.release();
12064
12065 if (flModifications & IsModified_SharedFolders)
12066 that->i_onSharedFolderChange();
12067
12068 if (flModifications & IsModified_VRDEServer)
12069 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12070 if (flModifications & IsModified_USB)
12071 that->i_onUSBControllerChange();
12072
12073 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12074 if (networkAdapters[slot])
12075 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12076 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12077 if (serialPorts[slot])
12078 that->i_onSerialPortChange(serialPorts[slot]);
12079 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12080 if (parallelPorts[slot])
12081 that->i_onParallelPortChange(parallelPorts[slot]);
12082
12083 if (flModifications & IsModified_Storage)
12084 that->i_onStorageControllerChange();
12085
12086#if 0
12087 if (flModifications & IsModified_BandwidthControl)
12088 that->onBandwidthControlChange();
12089#endif
12090 }
12091}
12092
12093/**
12094 * Commits all the changes to machine settings.
12095 *
12096 * Note that this operation is supposed to never fail.
12097 *
12098 * @note Locks this object and children for writing.
12099 */
12100void Machine::i_commit()
12101{
12102 AutoCaller autoCaller(this);
12103 AssertComRCReturnVoid(autoCaller.rc());
12104
12105 AutoCaller peerCaller(mPeer);
12106 AssertComRCReturnVoid(peerCaller.rc());
12107
12108 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12109
12110 /*
12111 * use safe commit to ensure Snapshot machines (that share mUserData)
12112 * will still refer to a valid memory location
12113 */
12114 mUserData.commitCopy();
12115
12116 mHWData.commit();
12117
12118 if (mMediumAttachments.isBackedUp())
12119 i_commitMedia(Global::IsOnline(mData->mMachineState));
12120
12121 mBIOSSettings->i_commit();
12122 mVRDEServer->i_commit();
12123 mAudioAdapter->i_commit();
12124 mUSBDeviceFilters->i_commit();
12125 mBandwidthControl->i_commit();
12126
12127 /* Since mNetworkAdapters is a list which might have been changed (resized)
12128 * without using the Backupable<> template we need to handle the copying
12129 * of the list entries manually, including the creation of peers for the
12130 * new objects. */
12131 bool commitNetworkAdapters = false;
12132 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12133 if (mPeer)
12134 {
12135 /* commit everything, even the ones which will go away */
12136 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12137 mNetworkAdapters[slot]->i_commit();
12138 /* copy over the new entries, creating a peer and uninit the original */
12139 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12140 for (size_t slot = 0; slot < newSize; slot++)
12141 {
12142 /* look if this adapter has a peer device */
12143 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12144 if (!peer)
12145 {
12146 /* no peer means the adapter is a newly created one;
12147 * create a peer owning data this data share it with */
12148 peer.createObject();
12149 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12150 }
12151 mPeer->mNetworkAdapters[slot] = peer;
12152 }
12153 /* uninit any no longer needed network adapters */
12154 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12155 mNetworkAdapters[slot]->uninit();
12156 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12157 {
12158 if (mPeer->mNetworkAdapters[slot])
12159 mPeer->mNetworkAdapters[slot]->uninit();
12160 }
12161 /* Keep the original network adapter count until this point, so that
12162 * discarding a chipset type change will not lose settings. */
12163 mNetworkAdapters.resize(newSize);
12164 mPeer->mNetworkAdapters.resize(newSize);
12165 }
12166 else
12167 {
12168 /* we have no peer (our parent is the newly created machine);
12169 * just commit changes to the network adapters */
12170 commitNetworkAdapters = true;
12171 }
12172 if (commitNetworkAdapters)
12173 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12174 mNetworkAdapters[slot]->i_commit();
12175
12176 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12177 mSerialPorts[slot]->i_commit();
12178 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12179 mParallelPorts[slot]->i_commit();
12180
12181 bool commitStorageControllers = false;
12182
12183 if (mStorageControllers.isBackedUp())
12184 {
12185 mStorageControllers.commit();
12186
12187 if (mPeer)
12188 {
12189 /* Commit all changes to new controllers (this will reshare data with
12190 * peers for those who have peers) */
12191 StorageControllerList *newList = new StorageControllerList();
12192 for (StorageControllerList::const_iterator
12193 it = mStorageControllers->begin();
12194 it != mStorageControllers->end();
12195 ++it)
12196 {
12197 (*it)->i_commit();
12198
12199 /* look if this controller has a peer device */
12200 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12201 if (!peer)
12202 {
12203 /* no peer means the device is a newly created one;
12204 * create a peer owning data this device share it with */
12205 peer.createObject();
12206 peer->init(mPeer, *it, true /* aReshare */);
12207 }
12208 else
12209 {
12210 /* remove peer from the old list */
12211 mPeer->mStorageControllers->remove(peer);
12212 }
12213 /* and add it to the new list */
12214 newList->push_back(peer);
12215 }
12216
12217 /* uninit old peer's controllers that are left */
12218 for (StorageControllerList::const_iterator
12219 it = mPeer->mStorageControllers->begin();
12220 it != mPeer->mStorageControllers->end();
12221 ++it)
12222 {
12223 (*it)->uninit();
12224 }
12225
12226 /* attach new list of controllers to our peer */
12227 mPeer->mStorageControllers.attach(newList);
12228 }
12229 else
12230 {
12231 /* we have no peer (our parent is the newly created machine);
12232 * just commit changes to devices */
12233 commitStorageControllers = true;
12234 }
12235 }
12236 else
12237 {
12238 /* the list of controllers itself is not changed,
12239 * just commit changes to controllers themselves */
12240 commitStorageControllers = true;
12241 }
12242
12243 if (commitStorageControllers)
12244 {
12245 for (StorageControllerList::const_iterator
12246 it = mStorageControllers->begin();
12247 it != mStorageControllers->end();
12248 ++it)
12249 {
12250 (*it)->i_commit();
12251 }
12252 }
12253
12254 bool commitUSBControllers = false;
12255
12256 if (mUSBControllers.isBackedUp())
12257 {
12258 mUSBControllers.commit();
12259
12260 if (mPeer)
12261 {
12262 /* Commit all changes to new controllers (this will reshare data with
12263 * peers for those who have peers) */
12264 USBControllerList *newList = new USBControllerList();
12265 for (USBControllerList::const_iterator
12266 it = mUSBControllers->begin();
12267 it != mUSBControllers->end();
12268 ++it)
12269 {
12270 (*it)->i_commit();
12271
12272 /* look if this controller has a peer device */
12273 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12274 if (!peer)
12275 {
12276 /* no peer means the device is a newly created one;
12277 * create a peer owning data this device share it with */
12278 peer.createObject();
12279 peer->init(mPeer, *it, true /* aReshare */);
12280 }
12281 else
12282 {
12283 /* remove peer from the old list */
12284 mPeer->mUSBControllers->remove(peer);
12285 }
12286 /* and add it to the new list */
12287 newList->push_back(peer);
12288 }
12289
12290 /* uninit old peer's controllers that are left */
12291 for (USBControllerList::const_iterator
12292 it = mPeer->mUSBControllers->begin();
12293 it != mPeer->mUSBControllers->end();
12294 ++it)
12295 {
12296 (*it)->uninit();
12297 }
12298
12299 /* attach new list of controllers to our peer */
12300 mPeer->mUSBControllers.attach(newList);
12301 }
12302 else
12303 {
12304 /* we have no peer (our parent is the newly created machine);
12305 * just commit changes to devices */
12306 commitUSBControllers = true;
12307 }
12308 }
12309 else
12310 {
12311 /* the list of controllers itself is not changed,
12312 * just commit changes to controllers themselves */
12313 commitUSBControllers = true;
12314 }
12315
12316 if (commitUSBControllers)
12317 {
12318 for (USBControllerList::const_iterator
12319 it = mUSBControllers->begin();
12320 it != mUSBControllers->end();
12321 ++it)
12322 {
12323 (*it)->i_commit();
12324 }
12325 }
12326
12327 if (i_isSessionMachine())
12328 {
12329 /* attach new data to the primary machine and reshare it */
12330 mPeer->mUserData.attach(mUserData);
12331 mPeer->mHWData.attach(mHWData);
12332 /* mmMediumAttachments is reshared by fixupMedia */
12333 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12334 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12335 }
12336}
12337
12338/**
12339 * Copies all the hardware data from the given machine.
12340 *
12341 * Currently, only called when the VM is being restored from a snapshot. In
12342 * particular, this implies that the VM is not running during this method's
12343 * call.
12344 *
12345 * @note This method must be called from under this object's lock.
12346 *
12347 * @note This method doesn't call #i_commit(), so all data remains backed up and
12348 * unsaved.
12349 */
12350void Machine::i_copyFrom(Machine *aThat)
12351{
12352 AssertReturnVoid(!i_isSnapshotMachine());
12353 AssertReturnVoid(aThat->i_isSnapshotMachine());
12354
12355 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12356
12357 mHWData.assignCopy(aThat->mHWData);
12358
12359 // create copies of all shared folders (mHWData after attaching a copy
12360 // contains just references to original objects)
12361 for (HWData::SharedFolderList::iterator
12362 it = mHWData->mSharedFolders.begin();
12363 it != mHWData->mSharedFolders.end();
12364 ++it)
12365 {
12366 ComObjPtr<SharedFolder> folder;
12367 folder.createObject();
12368 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12369 AssertComRC(rc);
12370 *it = folder;
12371 }
12372
12373 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12374 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12375 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12376 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12377 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12378
12379 /* create private copies of all controllers */
12380 mStorageControllers.backup();
12381 mStorageControllers->clear();
12382 for (StorageControllerList::const_iterator
12383 it = aThat->mStorageControllers->begin();
12384 it != aThat->mStorageControllers->end();
12385 ++it)
12386 {
12387 ComObjPtr<StorageController> ctrl;
12388 ctrl.createObject();
12389 ctrl->initCopy(this, *it);
12390 mStorageControllers->push_back(ctrl);
12391 }
12392
12393 /* create private copies of all USB controllers */
12394 mUSBControllers.backup();
12395 mUSBControllers->clear();
12396 for (USBControllerList::const_iterator
12397 it = aThat->mUSBControllers->begin();
12398 it != aThat->mUSBControllers->end();
12399 ++it)
12400 {
12401 ComObjPtr<USBController> ctrl;
12402 ctrl.createObject();
12403 ctrl->initCopy(this, *it);
12404 mUSBControllers->push_back(ctrl);
12405 }
12406
12407 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12408 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12409 {
12410 if (mNetworkAdapters[slot].isNotNull())
12411 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12412 else
12413 {
12414 unconst(mNetworkAdapters[slot]).createObject();
12415 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12416 }
12417 }
12418 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12419 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12420 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12421 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12422}
12423
12424/**
12425 * Returns whether the given storage controller is hotplug capable.
12426 *
12427 * @returns true if the controller supports hotplugging
12428 * false otherwise.
12429 * @param enmCtrlType The controller type to check for.
12430 */
12431bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12432{
12433 ComPtr<ISystemProperties> systemProperties;
12434 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12435 if (FAILED(rc))
12436 return false;
12437
12438 BOOL aHotplugCapable = FALSE;
12439 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12440
12441 return RT_BOOL(aHotplugCapable);
12442}
12443
12444#ifdef VBOX_WITH_RESOURCE_USAGE_API
12445
12446void Machine::i_getDiskList(MediaList &list)
12447{
12448 for (MediumAttachmentList::const_iterator
12449 it = mMediumAttachments->begin();
12450 it != mMediumAttachments->end();
12451 ++it)
12452 {
12453 MediumAttachment *pAttach = *it;
12454 /* just in case */
12455 AssertContinue(pAttach);
12456
12457 AutoCaller localAutoCallerA(pAttach);
12458 if (FAILED(localAutoCallerA.rc())) continue;
12459
12460 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12461
12462 if (pAttach->i_getType() == DeviceType_HardDisk)
12463 list.push_back(pAttach->i_getMedium());
12464 }
12465}
12466
12467void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12468{
12469 AssertReturnVoid(isWriteLockOnCurrentThread());
12470 AssertPtrReturnVoid(aCollector);
12471
12472 pm::CollectorHAL *hal = aCollector->getHAL();
12473 /* Create sub metrics */
12474 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12475 "Percentage of processor time spent in user mode by the VM process.");
12476 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12477 "Percentage of processor time spent in kernel mode by the VM process.");
12478 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12479 "Size of resident portion of VM process in memory.");
12480 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12481 "Actual size of all VM disks combined.");
12482 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12483 "Network receive rate.");
12484 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12485 "Network transmit rate.");
12486 /* Create and register base metrics */
12487 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12488 cpuLoadUser, cpuLoadKernel);
12489 aCollector->registerBaseMetric(cpuLoad);
12490 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12491 ramUsageUsed);
12492 aCollector->registerBaseMetric(ramUsage);
12493 MediaList disks;
12494 i_getDiskList(disks);
12495 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12496 diskUsageUsed);
12497 aCollector->registerBaseMetric(diskUsage);
12498
12499 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12500 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12501 new pm::AggregateAvg()));
12502 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12503 new pm::AggregateMin()));
12504 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12505 new pm::AggregateMax()));
12506 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12507 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12508 new pm::AggregateAvg()));
12509 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12510 new pm::AggregateMin()));
12511 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12512 new pm::AggregateMax()));
12513
12514 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12515 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12516 new pm::AggregateAvg()));
12517 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12518 new pm::AggregateMin()));
12519 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12520 new pm::AggregateMax()));
12521
12522 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12523 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12524 new pm::AggregateAvg()));
12525 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12526 new pm::AggregateMin()));
12527 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12528 new pm::AggregateMax()));
12529
12530
12531 /* Guest metrics collector */
12532 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12533 aCollector->registerGuest(mCollectorGuest);
12534 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12535
12536 /* Create sub metrics */
12537 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12538 "Percentage of processor time spent in user mode as seen by the guest.");
12539 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12540 "Percentage of processor time spent in kernel mode as seen by the guest.");
12541 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12542 "Percentage of processor time spent idling as seen by the guest.");
12543
12544 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12545 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12546 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12547 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12548 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12549 pm::SubMetric *guestMemCache = new pm::SubMetric(
12550 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12551
12552 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12553 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12554
12555 /* Create and register base metrics */
12556 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12557 machineNetRx, machineNetTx);
12558 aCollector->registerBaseMetric(machineNetRate);
12559
12560 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12561 guestLoadUser, guestLoadKernel, guestLoadIdle);
12562 aCollector->registerBaseMetric(guestCpuLoad);
12563
12564 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12565 guestMemTotal, guestMemFree,
12566 guestMemBalloon, guestMemShared,
12567 guestMemCache, guestPagedTotal);
12568 aCollector->registerBaseMetric(guestCpuMem);
12569
12570 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12571 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12572 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12573 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12574
12575 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12576 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12577 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12578 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12579
12580 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12581 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12582 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12583 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12584
12585 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12586 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12587 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12588 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12589
12590 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12591 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12592 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12593 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12594
12595 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12596 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12597 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12598 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12599
12600 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12601 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12602 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12603 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12604
12605 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12606 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12607 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12608 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12609
12610 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12611 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12612 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12613 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12614
12615 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12616 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12617 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12618 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12619
12620 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12621 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12622 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12623 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12624}
12625
12626void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12627{
12628 AssertReturnVoid(isWriteLockOnCurrentThread());
12629
12630 if (aCollector)
12631 {
12632 aCollector->unregisterMetricsFor(aMachine);
12633 aCollector->unregisterBaseMetricsFor(aMachine);
12634 }
12635}
12636
12637#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12638
12639
12640////////////////////////////////////////////////////////////////////////////////
12641
12642DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12643
12644HRESULT SessionMachine::FinalConstruct()
12645{
12646 LogFlowThisFunc(("\n"));
12647
12648 mClientToken = NULL;
12649
12650 return BaseFinalConstruct();
12651}
12652
12653void SessionMachine::FinalRelease()
12654{
12655 LogFlowThisFunc(("\n"));
12656
12657 Assert(!mClientToken);
12658 /* paranoia, should not hang around any more */
12659 if (mClientToken)
12660 {
12661 delete mClientToken;
12662 mClientToken = NULL;
12663 }
12664
12665 uninit(Uninit::Unexpected);
12666
12667 BaseFinalRelease();
12668}
12669
12670/**
12671 * @note Must be called only by Machine::LockMachine() from its own write lock.
12672 */
12673HRESULT SessionMachine::init(Machine *aMachine)
12674{
12675 LogFlowThisFuncEnter();
12676 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12677
12678 AssertReturn(aMachine, E_INVALIDARG);
12679
12680 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12681
12682 /* Enclose the state transition NotReady->InInit->Ready */
12683 AutoInitSpan autoInitSpan(this);
12684 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12685
12686 HRESULT rc = S_OK;
12687
12688 RT_ZERO(mAuthLibCtx);
12689
12690 /* create the machine client token */
12691 try
12692 {
12693 mClientToken = new ClientToken(aMachine, this);
12694 if (!mClientToken->isReady())
12695 {
12696 delete mClientToken;
12697 mClientToken = NULL;
12698 rc = E_FAIL;
12699 }
12700 }
12701 catch (std::bad_alloc &)
12702 {
12703 rc = E_OUTOFMEMORY;
12704 }
12705 if (FAILED(rc))
12706 return rc;
12707
12708 /* memorize the peer Machine */
12709 unconst(mPeer) = aMachine;
12710 /* share the parent pointer */
12711 unconst(mParent) = aMachine->mParent;
12712
12713 /* take the pointers to data to share */
12714 mData.share(aMachine->mData);
12715 mSSData.share(aMachine->mSSData);
12716
12717 mUserData.share(aMachine->mUserData);
12718 mHWData.share(aMachine->mHWData);
12719 mMediumAttachments.share(aMachine->mMediumAttachments);
12720
12721 mStorageControllers.allocate();
12722 for (StorageControllerList::const_iterator
12723 it = aMachine->mStorageControllers->begin();
12724 it != aMachine->mStorageControllers->end();
12725 ++it)
12726 {
12727 ComObjPtr<StorageController> ctl;
12728 ctl.createObject();
12729 ctl->init(this, *it);
12730 mStorageControllers->push_back(ctl);
12731 }
12732
12733 mUSBControllers.allocate();
12734 for (USBControllerList::const_iterator
12735 it = aMachine->mUSBControllers->begin();
12736 it != aMachine->mUSBControllers->end();
12737 ++it)
12738 {
12739 ComObjPtr<USBController> ctl;
12740 ctl.createObject();
12741 ctl->init(this, *it);
12742 mUSBControllers->push_back(ctl);
12743 }
12744
12745 unconst(mBIOSSettings).createObject();
12746 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12747 /* create another VRDEServer object that will be mutable */
12748 unconst(mVRDEServer).createObject();
12749 mVRDEServer->init(this, aMachine->mVRDEServer);
12750 /* create another audio adapter object that will be mutable */
12751 unconst(mAudioAdapter).createObject();
12752 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12753 /* create a list of serial ports that will be mutable */
12754 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12755 {
12756 unconst(mSerialPorts[slot]).createObject();
12757 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12758 }
12759 /* create a list of parallel ports that will be mutable */
12760 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12761 {
12762 unconst(mParallelPorts[slot]).createObject();
12763 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12764 }
12765
12766 /* create another USB device filters object that will be mutable */
12767 unconst(mUSBDeviceFilters).createObject();
12768 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12769
12770 /* create a list of network adapters that will be mutable */
12771 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12772 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12773 {
12774 unconst(mNetworkAdapters[slot]).createObject();
12775 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12776 }
12777
12778 /* create another bandwidth control object that will be mutable */
12779 unconst(mBandwidthControl).createObject();
12780 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12781
12782 /* default is to delete saved state on Saved -> PoweredOff transition */
12783 mRemoveSavedState = true;
12784
12785 /* Confirm a successful initialization when it's the case */
12786 autoInitSpan.setSucceeded();
12787
12788 miNATNetworksStarted = 0;
12789
12790 LogFlowThisFuncLeave();
12791 return rc;
12792}
12793
12794/**
12795 * Uninitializes this session object. If the reason is other than
12796 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12797 * or the client watcher code.
12798 *
12799 * @param aReason uninitialization reason
12800 *
12801 * @note Locks mParent + this object for writing.
12802 */
12803void SessionMachine::uninit(Uninit::Reason aReason)
12804{
12805 LogFlowThisFuncEnter();
12806 LogFlowThisFunc(("reason=%d\n", aReason));
12807
12808 /*
12809 * Strongly reference ourselves to prevent this object deletion after
12810 * mData->mSession.mMachine.setNull() below (which can release the last
12811 * reference and call the destructor). Important: this must be done before
12812 * accessing any members (and before AutoUninitSpan that does it as well).
12813 * This self reference will be released as the very last step on return.
12814 */
12815 ComObjPtr<SessionMachine> selfRef;
12816 if (aReason != Uninit::Unexpected)
12817 selfRef = this;
12818
12819 /* Enclose the state transition Ready->InUninit->NotReady */
12820 AutoUninitSpan autoUninitSpan(this);
12821 if (autoUninitSpan.uninitDone())
12822 {
12823 LogFlowThisFunc(("Already uninitialized\n"));
12824 LogFlowThisFuncLeave();
12825 return;
12826 }
12827
12828 if (autoUninitSpan.initFailed())
12829 {
12830 /* We've been called by init() because it's failed. It's not really
12831 * necessary (nor it's safe) to perform the regular uninit sequence
12832 * below, the following is enough.
12833 */
12834 LogFlowThisFunc(("Initialization failed.\n"));
12835 /* destroy the machine client token */
12836 if (mClientToken)
12837 {
12838 delete mClientToken;
12839 mClientToken = NULL;
12840 }
12841 uninitDataAndChildObjects();
12842 mData.free();
12843 unconst(mParent) = NULL;
12844 unconst(mPeer) = NULL;
12845 LogFlowThisFuncLeave();
12846 return;
12847 }
12848
12849 MachineState_T lastState;
12850 {
12851 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12852 lastState = mData->mMachineState;
12853 }
12854 NOREF(lastState);
12855
12856#ifdef VBOX_WITH_USB
12857 // release all captured USB devices, but do this before requesting the locks below
12858 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12859 {
12860 /* Console::captureUSBDevices() is called in the VM process only after
12861 * setting the machine state to Starting or Restoring.
12862 * Console::detachAllUSBDevices() will be called upon successful
12863 * termination. So, we need to release USB devices only if there was
12864 * an abnormal termination of a running VM.
12865 *
12866 * This is identical to SessionMachine::DetachAllUSBDevices except
12867 * for the aAbnormal argument. */
12868 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12869 AssertComRC(rc);
12870 NOREF(rc);
12871
12872 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12873 if (service)
12874 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12875 }
12876#endif /* VBOX_WITH_USB */
12877
12878 // we need to lock this object in uninit() because the lock is shared
12879 // with mPeer (as well as data we modify below). mParent lock is needed
12880 // by several calls to it.
12881 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12882
12883#ifdef VBOX_WITH_RESOURCE_USAGE_API
12884 /*
12885 * It is safe to call Machine::i_unregisterMetrics() here because
12886 * PerformanceCollector::samplerCallback no longer accesses guest methods
12887 * holding the lock.
12888 */
12889 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12890 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12891 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12892 if (mCollectorGuest)
12893 {
12894 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12895 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12896 mCollectorGuest = NULL;
12897 }
12898#endif
12899
12900 if (aReason == Uninit::Abnormal)
12901 {
12902 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12903
12904 /* reset the state to Aborted */
12905 if (mData->mMachineState != MachineState_Aborted)
12906 i_setMachineState(MachineState_Aborted);
12907 }
12908
12909 // any machine settings modified?
12910 if (mData->flModifications)
12911 {
12912 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12913 i_rollback(false /* aNotify */);
12914 }
12915
12916 mData->mSession.mPID = NIL_RTPROCESS;
12917
12918 if (aReason == Uninit::Unexpected)
12919 {
12920 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12921 * client watcher thread to update the set of machines that have open
12922 * sessions. */
12923 mParent->i_updateClientWatcher();
12924 }
12925
12926 /* uninitialize all remote controls */
12927 if (mData->mSession.mRemoteControls.size())
12928 {
12929 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12930 mData->mSession.mRemoteControls.size()));
12931
12932 /* Always restart a the beginning, since the iterator is invalidated
12933 * by using erase(). */
12934 for (Data::Session::RemoteControlList::iterator
12935 it = mData->mSession.mRemoteControls.begin();
12936 it != mData->mSession.mRemoteControls.end();
12937 it = mData->mSession.mRemoteControls.begin())
12938 {
12939 ComPtr<IInternalSessionControl> pControl = *it;
12940 mData->mSession.mRemoteControls.erase(it);
12941 multilock.release();
12942 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12943 HRESULT rc = pControl->Uninitialize();
12944 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12945 if (FAILED(rc))
12946 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12947 multilock.acquire();
12948 }
12949 mData->mSession.mRemoteControls.clear();
12950 }
12951
12952 /* Remove all references to the NAT network service. The service will stop
12953 * if all references (also from other VMs) are removed. */
12954 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12955 {
12956 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12957 {
12958 BOOL enabled;
12959 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12960 if ( FAILED(hrc)
12961 || !enabled)
12962 continue;
12963
12964 NetworkAttachmentType_T type;
12965 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12966 if ( SUCCEEDED(hrc)
12967 && type == NetworkAttachmentType_NATNetwork)
12968 {
12969 Bstr name;
12970 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12971 if (SUCCEEDED(hrc))
12972 {
12973 multilock.release();
12974 Utf8Str strName(name);
12975 LogRel(("VM '%s' stops using NAT network '%s'\n",
12976 mUserData->s.strName.c_str(), strName.c_str()));
12977 mParent->i_natNetworkRefDec(strName);
12978 multilock.acquire();
12979 }
12980 }
12981 }
12982 }
12983
12984 /*
12985 * An expected uninitialization can come only from #i_checkForDeath().
12986 * Otherwise it means that something's gone really wrong (for example,
12987 * the Session implementation has released the VirtualBox reference
12988 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12989 * etc). However, it's also possible, that the client releases the IPC
12990 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12991 * but the VirtualBox release event comes first to the server process.
12992 * This case is practically possible, so we should not assert on an
12993 * unexpected uninit, just log a warning.
12994 */
12995
12996 if (aReason == Uninit::Unexpected)
12997 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12998
12999 if (aReason != Uninit::Normal)
13000 {
13001 mData->mSession.mDirectControl.setNull();
13002 }
13003 else
13004 {
13005 /* this must be null here (see #OnSessionEnd()) */
13006 Assert(mData->mSession.mDirectControl.isNull());
13007 Assert(mData->mSession.mState == SessionState_Unlocking);
13008 Assert(!mData->mSession.mProgress.isNull());
13009 }
13010 if (mData->mSession.mProgress)
13011 {
13012 if (aReason == Uninit::Normal)
13013 mData->mSession.mProgress->i_notifyComplete(S_OK);
13014 else
13015 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13016 COM_IIDOF(ISession),
13017 getComponentName(),
13018 tr("The VM session was aborted"));
13019 mData->mSession.mProgress.setNull();
13020 }
13021
13022 if (mConsoleTaskData.mProgress)
13023 {
13024 Assert(aReason == Uninit::Abnormal);
13025 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13026 COM_IIDOF(ISession),
13027 getComponentName(),
13028 tr("The VM session was aborted"));
13029 mConsoleTaskData.mProgress.setNull();
13030 }
13031
13032 /* remove the association between the peer machine and this session machine */
13033 Assert( (SessionMachine*)mData->mSession.mMachine == this
13034 || aReason == Uninit::Unexpected);
13035
13036 /* reset the rest of session data */
13037 mData->mSession.mLockType = LockType_Null;
13038 mData->mSession.mMachine.setNull();
13039 mData->mSession.mState = SessionState_Unlocked;
13040 mData->mSession.mName.setNull();
13041
13042 /* destroy the machine client token before leaving the exclusive lock */
13043 if (mClientToken)
13044 {
13045 delete mClientToken;
13046 mClientToken = NULL;
13047 }
13048
13049 /* fire an event */
13050 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13051
13052 uninitDataAndChildObjects();
13053
13054 /* free the essential data structure last */
13055 mData.free();
13056
13057 /* release the exclusive lock before setting the below two to NULL */
13058 multilock.release();
13059
13060 unconst(mParent) = NULL;
13061 unconst(mPeer) = NULL;
13062
13063 AuthLibUnload(&mAuthLibCtx);
13064
13065 LogFlowThisFuncLeave();
13066}
13067
13068// util::Lockable interface
13069////////////////////////////////////////////////////////////////////////////////
13070
13071/**
13072 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13073 * with the primary Machine instance (mPeer).
13074 */
13075RWLockHandle *SessionMachine::lockHandle() const
13076{
13077 AssertReturn(mPeer != NULL, NULL);
13078 return mPeer->lockHandle();
13079}
13080
13081// IInternalMachineControl methods
13082////////////////////////////////////////////////////////////////////////////////
13083
13084/**
13085 * Passes collected guest statistics to performance collector object
13086 */
13087HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13088 ULONG aCpuKernel, ULONG aCpuIdle,
13089 ULONG aMemTotal, ULONG aMemFree,
13090 ULONG aMemBalloon, ULONG aMemShared,
13091 ULONG aMemCache, ULONG aPageTotal,
13092 ULONG aAllocVMM, ULONG aFreeVMM,
13093 ULONG aBalloonedVMM, ULONG aSharedVMM,
13094 ULONG aVmNetRx, ULONG aVmNetTx)
13095{
13096#ifdef VBOX_WITH_RESOURCE_USAGE_API
13097 if (mCollectorGuest)
13098 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13099 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13100 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13101 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13102
13103 return S_OK;
13104#else
13105 NOREF(aValidStats);
13106 NOREF(aCpuUser);
13107 NOREF(aCpuKernel);
13108 NOREF(aCpuIdle);
13109 NOREF(aMemTotal);
13110 NOREF(aMemFree);
13111 NOREF(aMemBalloon);
13112 NOREF(aMemShared);
13113 NOREF(aMemCache);
13114 NOREF(aPageTotal);
13115 NOREF(aAllocVMM);
13116 NOREF(aFreeVMM);
13117 NOREF(aBalloonedVMM);
13118 NOREF(aSharedVMM);
13119 NOREF(aVmNetRx);
13120 NOREF(aVmNetTx);
13121 return E_NOTIMPL;
13122#endif
13123}
13124
13125////////////////////////////////////////////////////////////////////////////////
13126//
13127// SessionMachine task records
13128//
13129////////////////////////////////////////////////////////////////////////////////
13130
13131/**
13132 * Task record for saving the machine state.
13133 */
13134class SessionMachine::SaveStateTask
13135 : public Machine::Task
13136{
13137public:
13138 SaveStateTask(SessionMachine *m,
13139 Progress *p,
13140 const Utf8Str &t,
13141 Reason_T enmReason,
13142 const Utf8Str &strStateFilePath)
13143 : Task(m, p, t),
13144 m_enmReason(enmReason),
13145 m_strStateFilePath(strStateFilePath)
13146 {}
13147
13148private:
13149 void handler()
13150 {
13151 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13152 }
13153
13154 Reason_T m_enmReason;
13155 Utf8Str m_strStateFilePath;
13156
13157 friend class SessionMachine;
13158};
13159
13160/**
13161 * Task thread implementation for SessionMachine::SaveState(), called from
13162 * SessionMachine::taskHandler().
13163 *
13164 * @note Locks this object for writing.
13165 *
13166 * @param task
13167 * @return
13168 */
13169void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13170{
13171 LogFlowThisFuncEnter();
13172
13173 AutoCaller autoCaller(this);
13174 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13175 if (FAILED(autoCaller.rc()))
13176 {
13177 /* we might have been uninitialized because the session was accidentally
13178 * closed by the client, so don't assert */
13179 HRESULT rc = setError(E_FAIL,
13180 tr("The session has been accidentally closed"));
13181 task.m_pProgress->i_notifyComplete(rc);
13182 LogFlowThisFuncLeave();
13183 return;
13184 }
13185
13186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13187
13188 HRESULT rc = S_OK;
13189
13190 try
13191 {
13192 ComPtr<IInternalSessionControl> directControl;
13193 if (mData->mSession.mLockType == LockType_VM)
13194 directControl = mData->mSession.mDirectControl;
13195 if (directControl.isNull())
13196 throw setError(VBOX_E_INVALID_VM_STATE,
13197 tr("Trying to save state without a running VM"));
13198 alock.release();
13199 BOOL fSuspendedBySave;
13200 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13201 Assert(!fSuspendedBySave);
13202 alock.acquire();
13203
13204 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13205 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13206 throw E_FAIL);
13207
13208 if (SUCCEEDED(rc))
13209 {
13210 mSSData->strStateFilePath = task.m_strStateFilePath;
13211
13212 /* save all VM settings */
13213 rc = i_saveSettings(NULL);
13214 // no need to check whether VirtualBox.xml needs saving also since
13215 // we can't have a name change pending at this point
13216 }
13217 else
13218 {
13219 // On failure, set the state to the state we had at the beginning.
13220 i_setMachineState(task.m_machineStateBackup);
13221 i_updateMachineStateOnClient();
13222
13223 // Delete the saved state file (might have been already created).
13224 // No need to check whether this is shared with a snapshot here
13225 // because we certainly created a fresh saved state file here.
13226 RTFileDelete(task.m_strStateFilePath.c_str());
13227 }
13228 }
13229 catch (HRESULT aRC) { rc = aRC; }
13230
13231 task.m_pProgress->i_notifyComplete(rc);
13232
13233 LogFlowThisFuncLeave();
13234}
13235
13236/**
13237 * @note Locks this object for writing.
13238 */
13239HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13240{
13241 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13242}
13243
13244HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13245{
13246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13247
13248 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13249 if (FAILED(rc)) return rc;
13250
13251 if ( mData->mMachineState != MachineState_Running
13252 && mData->mMachineState != MachineState_Paused
13253 )
13254 return setError(VBOX_E_INVALID_VM_STATE,
13255 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13256 Global::stringifyMachineState(mData->mMachineState));
13257
13258 ComObjPtr<Progress> pProgress;
13259 pProgress.createObject();
13260 rc = pProgress->init(i_getVirtualBox(),
13261 static_cast<IMachine *>(this) /* aInitiator */,
13262 tr("Saving the execution state of the virtual machine"),
13263 FALSE /* aCancelable */);
13264 if (FAILED(rc))
13265 return rc;
13266
13267 Utf8Str strStateFilePath;
13268 i_composeSavedStateFilename(strStateFilePath);
13269
13270 /* create and start the task on a separate thread (note that it will not
13271 * start working until we release alock) */
13272 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13273 rc = pTask->createThread();
13274 if (FAILED(rc))
13275 return rc;
13276
13277 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13278 i_setMachineState(MachineState_Saving);
13279 i_updateMachineStateOnClient();
13280
13281 pProgress.queryInterfaceTo(aProgress.asOutParam());
13282
13283 return S_OK;
13284}
13285
13286/**
13287 * @note Locks this object for writing.
13288 */
13289HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13290{
13291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13292
13293 HRESULT rc = i_checkStateDependency(MutableStateDep);
13294 if (FAILED(rc)) return rc;
13295
13296 if ( mData->mMachineState != MachineState_PoweredOff
13297 && mData->mMachineState != MachineState_Teleported
13298 && mData->mMachineState != MachineState_Aborted
13299 )
13300 return setError(VBOX_E_INVALID_VM_STATE,
13301 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13302 Global::stringifyMachineState(mData->mMachineState));
13303
13304 com::Utf8Str stateFilePathFull;
13305 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13306 if (RT_FAILURE(vrc))
13307 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13308 tr("Invalid saved state file path '%s' (%Rrc)"),
13309 aSavedStateFile.c_str(),
13310 vrc);
13311
13312 mSSData->strStateFilePath = stateFilePathFull;
13313
13314 /* The below i_setMachineState() will detect the state transition and will
13315 * update the settings file */
13316
13317 return i_setMachineState(MachineState_Saved);
13318}
13319
13320/**
13321 * @note Locks this object for writing.
13322 */
13323HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13324{
13325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13326
13327 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13328 if (FAILED(rc)) return rc;
13329
13330 if (mData->mMachineState != MachineState_Saved)
13331 return setError(VBOX_E_INVALID_VM_STATE,
13332 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13333 Global::stringifyMachineState(mData->mMachineState));
13334
13335 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13336
13337 /*
13338 * Saved -> PoweredOff transition will be detected in the SessionMachine
13339 * and properly handled.
13340 */
13341 rc = i_setMachineState(MachineState_PoweredOff);
13342 return rc;
13343}
13344
13345
13346/**
13347 * @note Locks the same as #i_setMachineState() does.
13348 */
13349HRESULT SessionMachine::updateState(MachineState_T aState)
13350{
13351 return i_setMachineState(aState);
13352}
13353
13354/**
13355 * @note Locks this object for writing.
13356 */
13357HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13358{
13359 IProgress *pProgress(aProgress);
13360
13361 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13362
13363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13364
13365 if (mData->mSession.mState != SessionState_Locked)
13366 return VBOX_E_INVALID_OBJECT_STATE;
13367
13368 if (!mData->mSession.mProgress.isNull())
13369 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13370
13371 /* If we didn't reference the NAT network service yet, add a reference to
13372 * force a start */
13373 if (miNATNetworksStarted < 1)
13374 {
13375 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13376 {
13377 BOOL enabled;
13378 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13379 if ( FAILED(hrc)
13380 || !enabled)
13381 continue;
13382
13383 NetworkAttachmentType_T type;
13384 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13385 if ( SUCCEEDED(hrc)
13386 && type == NetworkAttachmentType_NATNetwork)
13387 {
13388 Bstr name;
13389 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13390 if (SUCCEEDED(hrc))
13391 {
13392 Utf8Str strName(name);
13393 LogRel(("VM '%s' starts using NAT network '%s'\n",
13394 mUserData->s.strName.c_str(), strName.c_str()));
13395 mPeer->lockHandle()->unlockWrite();
13396 mParent->i_natNetworkRefInc(strName);
13397#ifdef RT_LOCK_STRICT
13398 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13399#else
13400 mPeer->lockHandle()->lockWrite();
13401#endif
13402 }
13403 }
13404 }
13405 miNATNetworksStarted++;
13406 }
13407
13408 LogFlowThisFunc(("returns S_OK.\n"));
13409 return S_OK;
13410}
13411
13412/**
13413 * @note Locks this object for writing.
13414 */
13415HRESULT SessionMachine::endPowerUp(LONG aResult)
13416{
13417 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13418
13419 if (mData->mSession.mState != SessionState_Locked)
13420 return VBOX_E_INVALID_OBJECT_STATE;
13421
13422 /* Finalize the LaunchVMProcess progress object. */
13423 if (mData->mSession.mProgress)
13424 {
13425 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13426 mData->mSession.mProgress.setNull();
13427 }
13428
13429 if (SUCCEEDED((HRESULT)aResult))
13430 {
13431#ifdef VBOX_WITH_RESOURCE_USAGE_API
13432 /* The VM has been powered up successfully, so it makes sense
13433 * now to offer the performance metrics for a running machine
13434 * object. Doing it earlier wouldn't be safe. */
13435 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13436 mData->mSession.mPID);
13437#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13438 }
13439
13440 return S_OK;
13441}
13442
13443/**
13444 * @note Locks this object for writing.
13445 */
13446HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13447{
13448 LogFlowThisFuncEnter();
13449
13450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13451
13452 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13453 E_FAIL);
13454
13455 /* create a progress object to track operation completion */
13456 ComObjPtr<Progress> pProgress;
13457 pProgress.createObject();
13458 pProgress->init(i_getVirtualBox(),
13459 static_cast<IMachine *>(this) /* aInitiator */,
13460 tr("Stopping the virtual machine"),
13461 FALSE /* aCancelable */);
13462
13463 /* fill in the console task data */
13464 mConsoleTaskData.mLastState = mData->mMachineState;
13465 mConsoleTaskData.mProgress = pProgress;
13466
13467 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13468 i_setMachineState(MachineState_Stopping);
13469
13470 pProgress.queryInterfaceTo(aProgress.asOutParam());
13471
13472 return S_OK;
13473}
13474
13475/**
13476 * @note Locks this object for writing.
13477 */
13478HRESULT SessionMachine::endPoweringDown(LONG aResult,
13479 const com::Utf8Str &aErrMsg)
13480{
13481 LogFlowThisFuncEnter();
13482
13483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13484
13485 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13486 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13487 && mConsoleTaskData.mLastState != MachineState_Null,
13488 E_FAIL);
13489
13490 /*
13491 * On failure, set the state to the state we had when BeginPoweringDown()
13492 * was called (this is expected by Console::PowerDown() and the associated
13493 * task). On success the VM process already changed the state to
13494 * MachineState_PoweredOff, so no need to do anything.
13495 */
13496 if (FAILED(aResult))
13497 i_setMachineState(mConsoleTaskData.mLastState);
13498
13499 /* notify the progress object about operation completion */
13500 Assert(mConsoleTaskData.mProgress);
13501 if (SUCCEEDED(aResult))
13502 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13503 else
13504 {
13505 if (aErrMsg.length())
13506 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13507 COM_IIDOF(ISession),
13508 getComponentName(),
13509 aErrMsg.c_str());
13510 else
13511 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13512 }
13513
13514 /* clear out the temporary saved state data */
13515 mConsoleTaskData.mLastState = MachineState_Null;
13516 mConsoleTaskData.mProgress.setNull();
13517
13518 LogFlowThisFuncLeave();
13519 return S_OK;
13520}
13521
13522
13523/**
13524 * Goes through the USB filters of the given machine to see if the given
13525 * device matches any filter or not.
13526 *
13527 * @note Locks the same as USBController::hasMatchingFilter() does.
13528 */
13529HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13530 BOOL *aMatched,
13531 ULONG *aMaskedInterfaces)
13532{
13533 LogFlowThisFunc(("\n"));
13534
13535#ifdef VBOX_WITH_USB
13536 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13537#else
13538 NOREF(aDevice);
13539 NOREF(aMaskedInterfaces);
13540 *aMatched = FALSE;
13541#endif
13542
13543 return S_OK;
13544}
13545
13546/**
13547 * @note Locks the same as Host::captureUSBDevice() does.
13548 */
13549HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13550{
13551 LogFlowThisFunc(("\n"));
13552
13553#ifdef VBOX_WITH_USB
13554 /* if captureDeviceForVM() fails, it must have set extended error info */
13555 clearError();
13556 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13557 if (FAILED(rc)) return rc;
13558
13559 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13560 AssertReturn(service, E_FAIL);
13561 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13562#else
13563 NOREF(aId);
13564 return E_NOTIMPL;
13565#endif
13566}
13567
13568/**
13569 * @note Locks the same as Host::detachUSBDevice() does.
13570 */
13571HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13572 BOOL aDone)
13573{
13574 LogFlowThisFunc(("\n"));
13575
13576#ifdef VBOX_WITH_USB
13577 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13578 AssertReturn(service, E_FAIL);
13579 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13580#else
13581 NOREF(aId);
13582 NOREF(aDone);
13583 return E_NOTIMPL;
13584#endif
13585}
13586
13587/**
13588 * Inserts all machine filters to the USB proxy service and then calls
13589 * Host::autoCaptureUSBDevices().
13590 *
13591 * Called by Console from the VM process upon VM startup.
13592 *
13593 * @note Locks what called methods lock.
13594 */
13595HRESULT SessionMachine::autoCaptureUSBDevices()
13596{
13597 LogFlowThisFunc(("\n"));
13598
13599#ifdef VBOX_WITH_USB
13600 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13601 AssertComRC(rc);
13602 NOREF(rc);
13603
13604 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13605 AssertReturn(service, E_FAIL);
13606 return service->autoCaptureDevicesForVM(this);
13607#else
13608 return S_OK;
13609#endif
13610}
13611
13612/**
13613 * Removes all machine filters from the USB proxy service and then calls
13614 * Host::detachAllUSBDevices().
13615 *
13616 * Called by Console from the VM process upon normal VM termination or by
13617 * SessionMachine::uninit() upon abnormal VM termination (from under the
13618 * Machine/SessionMachine lock).
13619 *
13620 * @note Locks what called methods lock.
13621 */
13622HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13623{
13624 LogFlowThisFunc(("\n"));
13625
13626#ifdef VBOX_WITH_USB
13627 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13628 AssertComRC(rc);
13629 NOREF(rc);
13630
13631 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13632 AssertReturn(service, E_FAIL);
13633 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13634#else
13635 NOREF(aDone);
13636 return S_OK;
13637#endif
13638}
13639
13640/**
13641 * @note Locks this object for writing.
13642 */
13643HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13644 ComPtr<IProgress> &aProgress)
13645{
13646 LogFlowThisFuncEnter();
13647
13648 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13649 /*
13650 * We don't assert below because it might happen that a non-direct session
13651 * informs us it is closed right after we've been uninitialized -- it's ok.
13652 */
13653
13654 /* get IInternalSessionControl interface */
13655 ComPtr<IInternalSessionControl> control(aSession);
13656
13657 ComAssertRet(!control.isNull(), E_INVALIDARG);
13658
13659 /* Creating a Progress object requires the VirtualBox lock, and
13660 * thus locking it here is required by the lock order rules. */
13661 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13662
13663 if (control == mData->mSession.mDirectControl)
13664 {
13665 /* The direct session is being normally closed by the client process
13666 * ----------------------------------------------------------------- */
13667
13668 /* go to the closing state (essential for all open*Session() calls and
13669 * for #i_checkForDeath()) */
13670 Assert(mData->mSession.mState == SessionState_Locked);
13671 mData->mSession.mState = SessionState_Unlocking;
13672
13673 /* set direct control to NULL to release the remote instance */
13674 mData->mSession.mDirectControl.setNull();
13675 LogFlowThisFunc(("Direct control is set to NULL\n"));
13676
13677 if (mData->mSession.mProgress)
13678 {
13679 /* finalize the progress, someone might wait if a frontend
13680 * closes the session before powering on the VM. */
13681 mData->mSession.mProgress->notifyComplete(E_FAIL,
13682 COM_IIDOF(ISession),
13683 getComponentName(),
13684 tr("The VM session was closed before any attempt to power it on"));
13685 mData->mSession.mProgress.setNull();
13686 }
13687
13688 /* Create the progress object the client will use to wait until
13689 * #i_checkForDeath() is called to uninitialize this session object after
13690 * it releases the IPC semaphore.
13691 * Note! Because we're "reusing" mProgress here, this must be a proxy
13692 * object just like for LaunchVMProcess. */
13693 Assert(mData->mSession.mProgress.isNull());
13694 ComObjPtr<ProgressProxy> progress;
13695 progress.createObject();
13696 ComPtr<IUnknown> pPeer(mPeer);
13697 progress->init(mParent, pPeer,
13698 Bstr(tr("Closing session")).raw(),
13699 FALSE /* aCancelable */);
13700 progress.queryInterfaceTo(aProgress.asOutParam());
13701 mData->mSession.mProgress = progress;
13702 }
13703 else
13704 {
13705 /* the remote session is being normally closed */
13706 bool found = false;
13707 for (Data::Session::RemoteControlList::iterator
13708 it = mData->mSession.mRemoteControls.begin();
13709 it != mData->mSession.mRemoteControls.end();
13710 ++it)
13711 {
13712 if (control == *it)
13713 {
13714 found = true;
13715 // This MUST be erase(it), not remove(*it) as the latter
13716 // triggers a very nasty use after free due to the place where
13717 // the value "lives".
13718 mData->mSession.mRemoteControls.erase(it);
13719 break;
13720 }
13721 }
13722 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13723 E_INVALIDARG);
13724 }
13725
13726 /* signal the client watcher thread, because the client is going away */
13727 mParent->i_updateClientWatcher();
13728
13729 LogFlowThisFuncLeave();
13730 return S_OK;
13731}
13732
13733HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13734 std::vector<com::Utf8Str> &aValues,
13735 std::vector<LONG64> &aTimestamps,
13736 std::vector<com::Utf8Str> &aFlags)
13737{
13738 LogFlowThisFunc(("\n"));
13739
13740#ifdef VBOX_WITH_GUEST_PROPS
13741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13742
13743 size_t cEntries = mHWData->mGuestProperties.size();
13744 aNames.resize(cEntries);
13745 aValues.resize(cEntries);
13746 aTimestamps.resize(cEntries);
13747 aFlags.resize(cEntries);
13748
13749 size_t i = 0;
13750 for (HWData::GuestPropertyMap::const_iterator
13751 it = mHWData->mGuestProperties.begin();
13752 it != mHWData->mGuestProperties.end();
13753 ++it, ++i)
13754 {
13755 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13756 aNames[i] = it->first;
13757 aValues[i] = it->second.strValue;
13758 aTimestamps[i] = it->second.mTimestamp;
13759
13760 /* If it is NULL, keep it NULL. */
13761 if (it->second.mFlags)
13762 {
13763 GuestPropWriteFlags(it->second.mFlags, szFlags);
13764 aFlags[i] = szFlags;
13765 }
13766 else
13767 aFlags[i] = "";
13768 }
13769 return S_OK;
13770#else
13771 ReturnComNotImplemented();
13772#endif
13773}
13774
13775HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13776 const com::Utf8Str &aValue,
13777 LONG64 aTimestamp,
13778 const com::Utf8Str &aFlags)
13779{
13780 LogFlowThisFunc(("\n"));
13781
13782#ifdef VBOX_WITH_GUEST_PROPS
13783 try
13784 {
13785 /*
13786 * Convert input up front.
13787 */
13788 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13789 if (aFlags.length())
13790 {
13791 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13792 AssertRCReturn(vrc, E_INVALIDARG);
13793 }
13794
13795 /*
13796 * Now grab the object lock, validate the state and do the update.
13797 */
13798
13799 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13800
13801 if (!Global::IsOnline(mData->mMachineState))
13802 {
13803 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13804 VBOX_E_INVALID_VM_STATE);
13805 }
13806
13807 i_setModified(IsModified_MachineData);
13808 mHWData.backup();
13809
13810 bool fDelete = !aValue.length();
13811 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13812 if (it != mHWData->mGuestProperties.end())
13813 {
13814 if (!fDelete)
13815 {
13816 it->second.strValue = aValue;
13817 it->second.mTimestamp = aTimestamp;
13818 it->second.mFlags = fFlags;
13819 }
13820 else
13821 mHWData->mGuestProperties.erase(it);
13822
13823 mData->mGuestPropertiesModified = TRUE;
13824 }
13825 else if (!fDelete)
13826 {
13827 HWData::GuestProperty prop;
13828 prop.strValue = aValue;
13829 prop.mTimestamp = aTimestamp;
13830 prop.mFlags = fFlags;
13831
13832 mHWData->mGuestProperties[aName] = prop;
13833 mData->mGuestPropertiesModified = TRUE;
13834 }
13835
13836 alock.release();
13837
13838 mParent->i_onGuestPropertyChange(mData->mUuid,
13839 Bstr(aName).raw(),
13840 Bstr(aValue).raw(),
13841 Bstr(aFlags).raw());
13842 }
13843 catch (...)
13844 {
13845 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13846 }
13847 return S_OK;
13848#else
13849 ReturnComNotImplemented();
13850#endif
13851}
13852
13853
13854HRESULT SessionMachine::lockMedia()
13855{
13856 AutoMultiWriteLock2 alock(this->lockHandle(),
13857 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13858
13859 AssertReturn( mData->mMachineState == MachineState_Starting
13860 || mData->mMachineState == MachineState_Restoring
13861 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13862
13863 clearError();
13864 alock.release();
13865 return i_lockMedia();
13866}
13867
13868HRESULT SessionMachine::unlockMedia()
13869{
13870 HRESULT hrc = i_unlockMedia();
13871 return hrc;
13872}
13873
13874HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13875 ComPtr<IMediumAttachment> &aNewAttachment)
13876{
13877 // request the host lock first, since might be calling Host methods for getting host drives;
13878 // next, protect the media tree all the while we're in here, as well as our member variables
13879 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13880 this->lockHandle(),
13881 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13882
13883 IMediumAttachment *iAttach = aAttachment;
13884 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13885
13886 Utf8Str ctrlName;
13887 LONG lPort;
13888 LONG lDevice;
13889 bool fTempEject;
13890 {
13891 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13892
13893 /* Need to query the details first, as the IMediumAttachment reference
13894 * might be to the original settings, which we are going to change. */
13895 ctrlName = pAttach->i_getControllerName();
13896 lPort = pAttach->i_getPort();
13897 lDevice = pAttach->i_getDevice();
13898 fTempEject = pAttach->i_getTempEject();
13899 }
13900
13901 if (!fTempEject)
13902 {
13903 /* Remember previously mounted medium. The medium before taking the
13904 * backup is not necessarily the same thing. */
13905 ComObjPtr<Medium> oldmedium;
13906 oldmedium = pAttach->i_getMedium();
13907
13908 i_setModified(IsModified_Storage);
13909 mMediumAttachments.backup();
13910
13911 // The backup operation makes the pAttach reference point to the
13912 // old settings. Re-get the correct reference.
13913 pAttach = i_findAttachment(*mMediumAttachments.data(),
13914 ctrlName,
13915 lPort,
13916 lDevice);
13917
13918 {
13919 AutoCaller autoAttachCaller(this);
13920 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13921
13922 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13923 if (!oldmedium.isNull())
13924 oldmedium->i_removeBackReference(mData->mUuid);
13925
13926 pAttach->i_updateMedium(NULL);
13927 pAttach->i_updateEjected();
13928 }
13929
13930 i_setModified(IsModified_Storage);
13931 }
13932 else
13933 {
13934 {
13935 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13936 pAttach->i_updateEjected();
13937 }
13938 }
13939
13940 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13941
13942 return S_OK;
13943}
13944
13945HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13946 com::Utf8Str &aResult)
13947{
13948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13949
13950 HRESULT hr = S_OK;
13951
13952 if (!mAuthLibCtx.hAuthLibrary)
13953 {
13954 /* Load the external authentication library. */
13955 Bstr authLibrary;
13956 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13957
13958 Utf8Str filename = authLibrary;
13959
13960 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13961 if (RT_FAILURE(vrc))
13962 hr = setErrorBoth(E_FAIL, vrc,
13963 tr("Could not load the external authentication library '%s' (%Rrc)"),
13964 filename.c_str(), vrc);
13965 }
13966
13967 /* The auth library might need the machine lock. */
13968 alock.release();
13969
13970 if (FAILED(hr))
13971 return hr;
13972
13973 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13974 {
13975 enum VRDEAuthParams
13976 {
13977 parmUuid = 1,
13978 parmGuestJudgement,
13979 parmUser,
13980 parmPassword,
13981 parmDomain,
13982 parmClientId
13983 };
13984
13985 AuthResult result = AuthResultAccessDenied;
13986
13987 Guid uuid(aAuthParams[parmUuid]);
13988 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13989 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13990
13991 result = AuthLibAuthenticate(&mAuthLibCtx,
13992 uuid.raw(), guestJudgement,
13993 aAuthParams[parmUser].c_str(),
13994 aAuthParams[parmPassword].c_str(),
13995 aAuthParams[parmDomain].c_str(),
13996 u32ClientId);
13997
13998 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13999 size_t cbPassword = aAuthParams[parmPassword].length();
14000 if (cbPassword)
14001 {
14002 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14003 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14004 }
14005
14006 if (result == AuthResultAccessGranted)
14007 aResult = "granted";
14008 else
14009 aResult = "denied";
14010
14011 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14012 aAuthParams[parmUser].c_str(), aResult.c_str()));
14013 }
14014 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14015 {
14016 enum VRDEAuthDisconnectParams
14017 {
14018 parmUuid = 1,
14019 parmClientId
14020 };
14021
14022 Guid uuid(aAuthParams[parmUuid]);
14023 uint32_t u32ClientId = 0;
14024 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14025 }
14026 else
14027 {
14028 hr = E_INVALIDARG;
14029 }
14030
14031 return hr;
14032}
14033
14034// public methods only for internal purposes
14035/////////////////////////////////////////////////////////////////////////////
14036
14037#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14038/**
14039 * Called from the client watcher thread to check for expected or unexpected
14040 * death of the client process that has a direct session to this machine.
14041 *
14042 * On Win32 and on OS/2, this method is called only when we've got the
14043 * mutex (i.e. the client has either died or terminated normally) so it always
14044 * returns @c true (the client is terminated, the session machine is
14045 * uninitialized).
14046 *
14047 * On other platforms, the method returns @c true if the client process has
14048 * terminated normally or abnormally and the session machine was uninitialized,
14049 * and @c false if the client process is still alive.
14050 *
14051 * @note Locks this object for writing.
14052 */
14053bool SessionMachine::i_checkForDeath()
14054{
14055 Uninit::Reason reason;
14056 bool terminated = false;
14057
14058 /* Enclose autoCaller with a block because calling uninit() from under it
14059 * will deadlock. */
14060 {
14061 AutoCaller autoCaller(this);
14062 if (!autoCaller.isOk())
14063 {
14064 /* return true if not ready, to cause the client watcher to exclude
14065 * the corresponding session from watching */
14066 LogFlowThisFunc(("Already uninitialized!\n"));
14067 return true;
14068 }
14069
14070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14071
14072 /* Determine the reason of death: if the session state is Closing here,
14073 * everything is fine. Otherwise it means that the client did not call
14074 * OnSessionEnd() before it released the IPC semaphore. This may happen
14075 * either because the client process has abnormally terminated, or
14076 * because it simply forgot to call ISession::Close() before exiting. We
14077 * threat the latter also as an abnormal termination (see
14078 * Session::uninit() for details). */
14079 reason = mData->mSession.mState == SessionState_Unlocking ?
14080 Uninit::Normal :
14081 Uninit::Abnormal;
14082
14083 if (mClientToken)
14084 terminated = mClientToken->release();
14085 } /* AutoCaller block */
14086
14087 if (terminated)
14088 uninit(reason);
14089
14090 return terminated;
14091}
14092
14093void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14094{
14095 LogFlowThisFunc(("\n"));
14096
14097 strTokenId.setNull();
14098
14099 AutoCaller autoCaller(this);
14100 AssertComRCReturnVoid(autoCaller.rc());
14101
14102 Assert(mClientToken);
14103 if (mClientToken)
14104 mClientToken->getId(strTokenId);
14105}
14106#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14107IToken *SessionMachine::i_getToken()
14108{
14109 LogFlowThisFunc(("\n"));
14110
14111 AutoCaller autoCaller(this);
14112 AssertComRCReturn(autoCaller.rc(), NULL);
14113
14114 Assert(mClientToken);
14115 if (mClientToken)
14116 return mClientToken->getToken();
14117 else
14118 return NULL;
14119}
14120#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14121
14122Machine::ClientToken *SessionMachine::i_getClientToken()
14123{
14124 LogFlowThisFunc(("\n"));
14125
14126 AutoCaller autoCaller(this);
14127 AssertComRCReturn(autoCaller.rc(), NULL);
14128
14129 return mClientToken;
14130}
14131
14132
14133/**
14134 * @note Locks this object for reading.
14135 */
14136HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14137{
14138 LogFlowThisFunc(("\n"));
14139
14140 AutoCaller autoCaller(this);
14141 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14142
14143 ComPtr<IInternalSessionControl> directControl;
14144 {
14145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14146 if (mData->mSession.mLockType == LockType_VM)
14147 directControl = mData->mSession.mDirectControl;
14148 }
14149
14150 /* ignore notifications sent after #OnSessionEnd() is called */
14151 if (!directControl)
14152 return S_OK;
14153
14154 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14155}
14156
14157/**
14158 * @note Locks this object for reading.
14159 */
14160HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14161 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14162 IN_BSTR aGuestIp, LONG aGuestPort)
14163{
14164 LogFlowThisFunc(("\n"));
14165
14166 AutoCaller autoCaller(this);
14167 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14168
14169 ComPtr<IInternalSessionControl> directControl;
14170 {
14171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14172 if (mData->mSession.mLockType == LockType_VM)
14173 directControl = mData->mSession.mDirectControl;
14174 }
14175
14176 /* ignore notifications sent after #OnSessionEnd() is called */
14177 if (!directControl)
14178 return S_OK;
14179 /*
14180 * instead acting like callback we ask IVirtualBox deliver corresponding event
14181 */
14182
14183 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14184 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14185 return S_OK;
14186}
14187
14188/**
14189 * @note Locks this object for reading.
14190 */
14191HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14192{
14193 LogFlowThisFunc(("\n"));
14194
14195 AutoCaller autoCaller(this);
14196 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14197
14198 ComPtr<IInternalSessionControl> directControl;
14199 {
14200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14201 if (mData->mSession.mLockType == LockType_VM)
14202 directControl = mData->mSession.mDirectControl;
14203 }
14204
14205 /* ignore notifications sent after #OnSessionEnd() is called */
14206 if (!directControl)
14207 return S_OK;
14208
14209 return directControl->OnAudioAdapterChange(audioAdapter);
14210}
14211
14212/**
14213 * @note Locks this object for reading.
14214 */
14215HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14216{
14217 LogFlowThisFunc(("\n"));
14218
14219 AutoCaller autoCaller(this);
14220 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14221
14222 ComPtr<IInternalSessionControl> directControl;
14223 {
14224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14225 if (mData->mSession.mLockType == LockType_VM)
14226 directControl = mData->mSession.mDirectControl;
14227 }
14228
14229 /* ignore notifications sent after #OnSessionEnd() is called */
14230 if (!directControl)
14231 return S_OK;
14232
14233 return directControl->OnSerialPortChange(serialPort);
14234}
14235
14236/**
14237 * @note Locks this object for reading.
14238 */
14239HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14240{
14241 LogFlowThisFunc(("\n"));
14242
14243 AutoCaller autoCaller(this);
14244 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14245
14246 ComPtr<IInternalSessionControl> directControl;
14247 {
14248 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14249 if (mData->mSession.mLockType == LockType_VM)
14250 directControl = mData->mSession.mDirectControl;
14251 }
14252
14253 /* ignore notifications sent after #OnSessionEnd() is called */
14254 if (!directControl)
14255 return S_OK;
14256
14257 return directControl->OnParallelPortChange(parallelPort);
14258}
14259
14260/**
14261 * @note Locks this object for reading.
14262 */
14263HRESULT SessionMachine::i_onStorageControllerChange()
14264{
14265 LogFlowThisFunc(("\n"));
14266
14267 AutoCaller autoCaller(this);
14268 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14269
14270 ComPtr<IInternalSessionControl> directControl;
14271 {
14272 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14273 if (mData->mSession.mLockType == LockType_VM)
14274 directControl = mData->mSession.mDirectControl;
14275 }
14276
14277 /* ignore notifications sent after #OnSessionEnd() is called */
14278 if (!directControl)
14279 return S_OK;
14280
14281 return directControl->OnStorageControllerChange();
14282}
14283
14284/**
14285 * @note Locks this object for reading.
14286 */
14287HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14288{
14289 LogFlowThisFunc(("\n"));
14290
14291 AutoCaller autoCaller(this);
14292 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14293
14294 ComPtr<IInternalSessionControl> directControl;
14295 {
14296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14297 if (mData->mSession.mLockType == LockType_VM)
14298 directControl = mData->mSession.mDirectControl;
14299 }
14300
14301 /* ignore notifications sent after #OnSessionEnd() is called */
14302 if (!directControl)
14303 return S_OK;
14304
14305 return directControl->OnMediumChange(aAttachment, aForce);
14306}
14307
14308/**
14309 * @note Locks this object for reading.
14310 */
14311HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14312{
14313 LogFlowThisFunc(("\n"));
14314
14315 AutoCaller autoCaller(this);
14316 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14317
14318 ComPtr<IInternalSessionControl> directControl;
14319 {
14320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14321 if (mData->mSession.mLockType == LockType_VM)
14322 directControl = mData->mSession.mDirectControl;
14323 }
14324
14325 /* ignore notifications sent after #OnSessionEnd() is called */
14326 if (!directControl)
14327 return S_OK;
14328
14329 return directControl->OnCPUChange(aCPU, aRemove);
14330}
14331
14332HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14333{
14334 LogFlowThisFunc(("\n"));
14335
14336 AutoCaller autoCaller(this);
14337 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14338
14339 ComPtr<IInternalSessionControl> directControl;
14340 {
14341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14342 if (mData->mSession.mLockType == LockType_VM)
14343 directControl = mData->mSession.mDirectControl;
14344 }
14345
14346 /* ignore notifications sent after #OnSessionEnd() is called */
14347 if (!directControl)
14348 return S_OK;
14349
14350 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14351}
14352
14353/**
14354 * @note Locks this object for reading.
14355 */
14356HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14357{
14358 LogFlowThisFunc(("\n"));
14359
14360 AutoCaller autoCaller(this);
14361 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14362
14363 ComPtr<IInternalSessionControl> directControl;
14364 {
14365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14366 if (mData->mSession.mLockType == LockType_VM)
14367 directControl = mData->mSession.mDirectControl;
14368 }
14369
14370 /* ignore notifications sent after #OnSessionEnd() is called */
14371 if (!directControl)
14372 return S_OK;
14373
14374 return directControl->OnVRDEServerChange(aRestart);
14375}
14376
14377/**
14378 * @note Locks this object for reading.
14379 */
14380HRESULT SessionMachine::i_onVideoCaptureChange()
14381{
14382 LogFlowThisFunc(("\n"));
14383
14384 AutoCaller autoCaller(this);
14385 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14386
14387 ComPtr<IInternalSessionControl> directControl;
14388 {
14389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14390 if (mData->mSession.mLockType == LockType_VM)
14391 directControl = mData->mSession.mDirectControl;
14392 }
14393
14394 /* ignore notifications sent after #OnSessionEnd() is called */
14395 if (!directControl)
14396 return S_OK;
14397
14398 return directControl->OnVideoCaptureChange();
14399}
14400
14401/**
14402 * @note Locks this object for reading.
14403 */
14404HRESULT SessionMachine::i_onUSBControllerChange()
14405{
14406 LogFlowThisFunc(("\n"));
14407
14408 AutoCaller autoCaller(this);
14409 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14410
14411 ComPtr<IInternalSessionControl> directControl;
14412 {
14413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14414 if (mData->mSession.mLockType == LockType_VM)
14415 directControl = mData->mSession.mDirectControl;
14416 }
14417
14418 /* ignore notifications sent after #OnSessionEnd() is called */
14419 if (!directControl)
14420 return S_OK;
14421
14422 return directControl->OnUSBControllerChange();
14423}
14424
14425/**
14426 * @note Locks this object for reading.
14427 */
14428HRESULT SessionMachine::i_onSharedFolderChange()
14429{
14430 LogFlowThisFunc(("\n"));
14431
14432 AutoCaller autoCaller(this);
14433 AssertComRCReturnRC(autoCaller.rc());
14434
14435 ComPtr<IInternalSessionControl> directControl;
14436 {
14437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14438 if (mData->mSession.mLockType == LockType_VM)
14439 directControl = mData->mSession.mDirectControl;
14440 }
14441
14442 /* ignore notifications sent after #OnSessionEnd() is called */
14443 if (!directControl)
14444 return S_OK;
14445
14446 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14447}
14448
14449/**
14450 * @note Locks this object for reading.
14451 */
14452HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14453{
14454 LogFlowThisFunc(("\n"));
14455
14456 AutoCaller autoCaller(this);
14457 AssertComRCReturnRC(autoCaller.rc());
14458
14459 ComPtr<IInternalSessionControl> directControl;
14460 {
14461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14462 if (mData->mSession.mLockType == LockType_VM)
14463 directControl = mData->mSession.mDirectControl;
14464 }
14465
14466 /* ignore notifications sent after #OnSessionEnd() is called */
14467 if (!directControl)
14468 return S_OK;
14469
14470 return directControl->OnClipboardModeChange(aClipboardMode);
14471}
14472
14473/**
14474 * @note Locks this object for reading.
14475 */
14476HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14477{
14478 LogFlowThisFunc(("\n"));
14479
14480 AutoCaller autoCaller(this);
14481 AssertComRCReturnRC(autoCaller.rc());
14482
14483 ComPtr<IInternalSessionControl> directControl;
14484 {
14485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14486 if (mData->mSession.mLockType == LockType_VM)
14487 directControl = mData->mSession.mDirectControl;
14488 }
14489
14490 /* ignore notifications sent after #OnSessionEnd() is called */
14491 if (!directControl)
14492 return S_OK;
14493
14494 return directControl->OnDnDModeChange(aDnDMode);
14495}
14496
14497/**
14498 * @note Locks this object for reading.
14499 */
14500HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14501{
14502 LogFlowThisFunc(("\n"));
14503
14504 AutoCaller autoCaller(this);
14505 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14506
14507 ComPtr<IInternalSessionControl> directControl;
14508 {
14509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14510 if (mData->mSession.mLockType == LockType_VM)
14511 directControl = mData->mSession.mDirectControl;
14512 }
14513
14514 /* ignore notifications sent after #OnSessionEnd() is called */
14515 if (!directControl)
14516 return S_OK;
14517
14518 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14519}
14520
14521/**
14522 * @note Locks this object for reading.
14523 */
14524HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14525{
14526 LogFlowThisFunc(("\n"));
14527
14528 AutoCaller autoCaller(this);
14529 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14530
14531 ComPtr<IInternalSessionControl> directControl;
14532 {
14533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14534 if (mData->mSession.mLockType == LockType_VM)
14535 directControl = mData->mSession.mDirectControl;
14536 }
14537
14538 /* ignore notifications sent after #OnSessionEnd() is called */
14539 if (!directControl)
14540 return S_OK;
14541
14542 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14543}
14544
14545/**
14546 * Returns @c true if this machine's USB controller reports it has a matching
14547 * filter for the given USB device and @c false otherwise.
14548 *
14549 * @note locks this object for reading.
14550 */
14551bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14552{
14553 AutoCaller autoCaller(this);
14554 /* silently return if not ready -- this method may be called after the
14555 * direct machine session has been called */
14556 if (!autoCaller.isOk())
14557 return false;
14558
14559#ifdef VBOX_WITH_USB
14560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14561
14562 switch (mData->mMachineState)
14563 {
14564 case MachineState_Starting:
14565 case MachineState_Restoring:
14566 case MachineState_TeleportingIn:
14567 case MachineState_Paused:
14568 case MachineState_Running:
14569 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14570 * elsewhere... */
14571 alock.release();
14572 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14573 default: break;
14574 }
14575#else
14576 NOREF(aDevice);
14577 NOREF(aMaskedIfs);
14578#endif
14579 return false;
14580}
14581
14582/**
14583 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14584 */
14585HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14586 IVirtualBoxErrorInfo *aError,
14587 ULONG aMaskedIfs,
14588 const com::Utf8Str &aCaptureFilename)
14589{
14590 LogFlowThisFunc(("\n"));
14591
14592 AutoCaller autoCaller(this);
14593
14594 /* This notification may happen after the machine object has been
14595 * uninitialized (the session was closed), so don't assert. */
14596 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14597
14598 ComPtr<IInternalSessionControl> directControl;
14599 {
14600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14601 if (mData->mSession.mLockType == LockType_VM)
14602 directControl = mData->mSession.mDirectControl;
14603 }
14604
14605 /* fail on notifications sent after #OnSessionEnd() is called, it is
14606 * expected by the caller */
14607 if (!directControl)
14608 return E_FAIL;
14609
14610 /* No locks should be held at this point. */
14611 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14612 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14613
14614 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14615}
14616
14617/**
14618 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14619 */
14620HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14621 IVirtualBoxErrorInfo *aError)
14622{
14623 LogFlowThisFunc(("\n"));
14624
14625 AutoCaller autoCaller(this);
14626
14627 /* This notification may happen after the machine object has been
14628 * uninitialized (the session was closed), so don't assert. */
14629 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14630
14631 ComPtr<IInternalSessionControl> directControl;
14632 {
14633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14634 if (mData->mSession.mLockType == LockType_VM)
14635 directControl = mData->mSession.mDirectControl;
14636 }
14637
14638 /* fail on notifications sent after #OnSessionEnd() is called, it is
14639 * expected by the caller */
14640 if (!directControl)
14641 return E_FAIL;
14642
14643 /* No locks should be held at this point. */
14644 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14645 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14646
14647 return directControl->OnUSBDeviceDetach(aId, aError);
14648}
14649
14650// protected methods
14651/////////////////////////////////////////////////////////////////////////////
14652
14653/**
14654 * Deletes the given file if it is no longer in use by either the current machine state
14655 * (if the machine is "saved") or any of the machine's snapshots.
14656 *
14657 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14658 * but is different for each SnapshotMachine. When calling this, the order of calling this
14659 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14660 * is therefore critical. I know, it's all rather messy.
14661 *
14662 * @param strStateFile
14663 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14664 * the test for whether the saved state file is in use.
14665 */
14666void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14667 Snapshot *pSnapshotToIgnore)
14668{
14669 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14670 if ( (strStateFile.isNotEmpty())
14671 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14672 )
14673 // ... and it must also not be shared with other snapshots
14674 if ( !mData->mFirstSnapshot
14675 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14676 // this checks the SnapshotMachine's state file paths
14677 )
14678 RTFileDelete(strStateFile.c_str());
14679}
14680
14681/**
14682 * Locks the attached media.
14683 *
14684 * All attached hard disks are locked for writing and DVD/floppy are locked for
14685 * reading. Parents of attached hard disks (if any) are locked for reading.
14686 *
14687 * This method also performs accessibility check of all media it locks: if some
14688 * media is inaccessible, the method will return a failure and a bunch of
14689 * extended error info objects per each inaccessible medium.
14690 *
14691 * Note that this method is atomic: if it returns a success, all media are
14692 * locked as described above; on failure no media is locked at all (all
14693 * succeeded individual locks will be undone).
14694 *
14695 * The caller is responsible for doing the necessary state sanity checks.
14696 *
14697 * The locks made by this method must be undone by calling #unlockMedia() when
14698 * no more needed.
14699 */
14700HRESULT SessionMachine::i_lockMedia()
14701{
14702 AutoCaller autoCaller(this);
14703 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14704
14705 AutoMultiWriteLock2 alock(this->lockHandle(),
14706 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14707
14708 /* bail out if trying to lock things with already set up locking */
14709 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14710
14711 MultiResult mrc(S_OK);
14712
14713 /* Collect locking information for all medium objects attached to the VM. */
14714 for (MediumAttachmentList::const_iterator
14715 it = mMediumAttachments->begin();
14716 it != mMediumAttachments->end();
14717 ++it)
14718 {
14719 MediumAttachment *pAtt = *it;
14720 DeviceType_T devType = pAtt->i_getType();
14721 Medium *pMedium = pAtt->i_getMedium();
14722
14723 MediumLockList *pMediumLockList(new MediumLockList());
14724 // There can be attachments without a medium (floppy/dvd), and thus
14725 // it's impossible to create a medium lock list. It still makes sense
14726 // to have the empty medium lock list in the map in case a medium is
14727 // attached later.
14728 if (pMedium != NULL)
14729 {
14730 MediumType_T mediumType = pMedium->i_getType();
14731 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14732 || mediumType == MediumType_Shareable;
14733 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14734
14735 alock.release();
14736 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14737 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14738 false /* fMediumLockWriteAll */,
14739 NULL,
14740 *pMediumLockList);
14741 alock.acquire();
14742 if (FAILED(mrc))
14743 {
14744 delete pMediumLockList;
14745 mData->mSession.mLockedMedia.Clear();
14746 break;
14747 }
14748 }
14749
14750 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14751 if (FAILED(rc))
14752 {
14753 mData->mSession.mLockedMedia.Clear();
14754 mrc = setError(rc,
14755 tr("Collecting locking information for all attached media failed"));
14756 break;
14757 }
14758 }
14759
14760 if (SUCCEEDED(mrc))
14761 {
14762 /* Now lock all media. If this fails, nothing is locked. */
14763 alock.release();
14764 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14765 alock.acquire();
14766 if (FAILED(rc))
14767 {
14768 mrc = setError(rc,
14769 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14770 }
14771 }
14772
14773 return mrc;
14774}
14775
14776/**
14777 * Undoes the locks made by by #lockMedia().
14778 */
14779HRESULT SessionMachine::i_unlockMedia()
14780{
14781 AutoCaller autoCaller(this);
14782 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14783
14784 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14785
14786 /* we may be holding important error info on the current thread;
14787 * preserve it */
14788 ErrorInfoKeeper eik;
14789
14790 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14791 AssertComRC(rc);
14792 return rc;
14793}
14794
14795/**
14796 * Helper to change the machine state (reimplementation).
14797 *
14798 * @note Locks this object for writing.
14799 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14800 * it can cause crashes in random places due to unexpectedly committing
14801 * the current settings. The caller is responsible for that. The call
14802 * to saveStateSettings is fine, because this method does not commit.
14803 */
14804HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14805{
14806 LogFlowThisFuncEnter();
14807 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14808
14809 AutoCaller autoCaller(this);
14810 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14811
14812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14813
14814 MachineState_T oldMachineState = mData->mMachineState;
14815
14816 AssertMsgReturn(oldMachineState != aMachineState,
14817 ("oldMachineState=%s, aMachineState=%s\n",
14818 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14819 E_FAIL);
14820
14821 HRESULT rc = S_OK;
14822
14823 int stsFlags = 0;
14824 bool deleteSavedState = false;
14825
14826 /* detect some state transitions */
14827
14828 if ( ( oldMachineState == MachineState_Saved
14829 && aMachineState == MachineState_Restoring)
14830 || ( ( oldMachineState == MachineState_PoweredOff
14831 || oldMachineState == MachineState_Teleported
14832 || oldMachineState == MachineState_Aborted
14833 )
14834 && ( aMachineState == MachineState_TeleportingIn
14835 || aMachineState == MachineState_Starting
14836 )
14837 )
14838 )
14839 {
14840 /* The EMT thread is about to start */
14841
14842 /* Nothing to do here for now... */
14843
14844 /// @todo NEWMEDIA don't let mDVDDrive and other children
14845 /// change anything when in the Starting/Restoring state
14846 }
14847 else if ( ( oldMachineState == MachineState_Running
14848 || oldMachineState == MachineState_Paused
14849 || oldMachineState == MachineState_Teleporting
14850 || oldMachineState == MachineState_OnlineSnapshotting
14851 || oldMachineState == MachineState_LiveSnapshotting
14852 || oldMachineState == MachineState_Stuck
14853 || oldMachineState == MachineState_Starting
14854 || oldMachineState == MachineState_Stopping
14855 || oldMachineState == MachineState_Saving
14856 || oldMachineState == MachineState_Restoring
14857 || oldMachineState == MachineState_TeleportingPausedVM
14858 || oldMachineState == MachineState_TeleportingIn
14859 )
14860 && ( aMachineState == MachineState_PoweredOff
14861 || aMachineState == MachineState_Saved
14862 || aMachineState == MachineState_Teleported
14863 || aMachineState == MachineState_Aborted
14864 )
14865 )
14866 {
14867 /* The EMT thread has just stopped, unlock attached media. Note that as
14868 * opposed to locking that is done from Console, we do unlocking here
14869 * because the VM process may have aborted before having a chance to
14870 * properly unlock all media it locked. */
14871
14872 unlockMedia();
14873 }
14874
14875 if (oldMachineState == MachineState_Restoring)
14876 {
14877 if (aMachineState != MachineState_Saved)
14878 {
14879 /*
14880 * delete the saved state file once the machine has finished
14881 * restoring from it (note that Console sets the state from
14882 * Restoring to Saved if the VM couldn't restore successfully,
14883 * to give the user an ability to fix an error and retry --
14884 * we keep the saved state file in this case)
14885 */
14886 deleteSavedState = true;
14887 }
14888 }
14889 else if ( oldMachineState == MachineState_Saved
14890 && ( aMachineState == MachineState_PoweredOff
14891 || aMachineState == MachineState_Aborted
14892 || aMachineState == MachineState_Teleported
14893 )
14894 )
14895 {
14896 /*
14897 * delete the saved state after SessionMachine::ForgetSavedState() is called
14898 * or if the VM process (owning a direct VM session) crashed while the
14899 * VM was Saved
14900 */
14901
14902 /// @todo (dmik)
14903 // Not sure that deleting the saved state file just because of the
14904 // client death before it attempted to restore the VM is a good
14905 // thing. But when it crashes we need to go to the Aborted state
14906 // which cannot have the saved state file associated... The only
14907 // way to fix this is to make the Aborted condition not a VM state
14908 // but a bool flag: i.e., when a crash occurs, set it to true and
14909 // change the state to PoweredOff or Saved depending on the
14910 // saved state presence.
14911
14912 deleteSavedState = true;
14913 mData->mCurrentStateModified = TRUE;
14914 stsFlags |= SaveSTS_CurStateModified;
14915 }
14916
14917 if ( aMachineState == MachineState_Starting
14918 || aMachineState == MachineState_Restoring
14919 || aMachineState == MachineState_TeleportingIn
14920 )
14921 {
14922 /* set the current state modified flag to indicate that the current
14923 * state is no more identical to the state in the
14924 * current snapshot */
14925 if (!mData->mCurrentSnapshot.isNull())
14926 {
14927 mData->mCurrentStateModified = TRUE;
14928 stsFlags |= SaveSTS_CurStateModified;
14929 }
14930 }
14931
14932 if (deleteSavedState)
14933 {
14934 if (mRemoveSavedState)
14935 {
14936 Assert(!mSSData->strStateFilePath.isEmpty());
14937
14938 // it is safe to delete the saved state file if ...
14939 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14940 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14941 // ... none of the snapshots share the saved state file
14942 )
14943 RTFileDelete(mSSData->strStateFilePath.c_str());
14944 }
14945
14946 mSSData->strStateFilePath.setNull();
14947 stsFlags |= SaveSTS_StateFilePath;
14948 }
14949
14950 /* redirect to the underlying peer machine */
14951 mPeer->i_setMachineState(aMachineState);
14952
14953 if ( oldMachineState != MachineState_RestoringSnapshot
14954 && ( aMachineState == MachineState_PoweredOff
14955 || aMachineState == MachineState_Teleported
14956 || aMachineState == MachineState_Aborted
14957 || aMachineState == MachineState_Saved))
14958 {
14959 /* the machine has stopped execution
14960 * (or the saved state file was adopted) */
14961 stsFlags |= SaveSTS_StateTimeStamp;
14962 }
14963
14964 if ( ( oldMachineState == MachineState_PoweredOff
14965 || oldMachineState == MachineState_Aborted
14966 || oldMachineState == MachineState_Teleported
14967 )
14968 && aMachineState == MachineState_Saved)
14969 {
14970 /* the saved state file was adopted */
14971 Assert(!mSSData->strStateFilePath.isEmpty());
14972 stsFlags |= SaveSTS_StateFilePath;
14973 }
14974
14975#ifdef VBOX_WITH_GUEST_PROPS
14976 if ( aMachineState == MachineState_PoweredOff
14977 || aMachineState == MachineState_Aborted
14978 || aMachineState == MachineState_Teleported)
14979 {
14980 /* Make sure any transient guest properties get removed from the
14981 * property store on shutdown. */
14982 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14983
14984 /* remove it from the settings representation */
14985 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14986 for (settings::GuestPropertiesList::iterator
14987 it = llGuestProperties.begin();
14988 it != llGuestProperties.end();
14989 /*nothing*/)
14990 {
14991 const settings::GuestProperty &prop = *it;
14992 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14993 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14994 {
14995 it = llGuestProperties.erase(it);
14996 fNeedsSaving = true;
14997 }
14998 else
14999 {
15000 ++it;
15001 }
15002 }
15003
15004 /* Additionally remove it from the HWData representation. Required to
15005 * keep everything in sync, as this is what the API keeps using. */
15006 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15007 for (HWData::GuestPropertyMap::iterator
15008 it = llHWGuestProperties.begin();
15009 it != llHWGuestProperties.end();
15010 /*nothing*/)
15011 {
15012 uint32_t fFlags = it->second.mFlags;
15013 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15014 {
15015 /* iterator where we need to continue after the erase call
15016 * (C++03 is a fact still, and it doesn't return the iterator
15017 * which would allow continuing) */
15018 HWData::GuestPropertyMap::iterator it2 = it;
15019 ++it2;
15020 llHWGuestProperties.erase(it);
15021 it = it2;
15022 fNeedsSaving = true;
15023 }
15024 else
15025 {
15026 ++it;
15027 }
15028 }
15029
15030 if (fNeedsSaving)
15031 {
15032 mData->mCurrentStateModified = TRUE;
15033 stsFlags |= SaveSTS_CurStateModified;
15034 }
15035 }
15036#endif /* VBOX_WITH_GUEST_PROPS */
15037
15038 rc = i_saveStateSettings(stsFlags);
15039
15040 if ( ( oldMachineState != MachineState_PoweredOff
15041 && oldMachineState != MachineState_Aborted
15042 && oldMachineState != MachineState_Teleported
15043 )
15044 && ( aMachineState == MachineState_PoweredOff
15045 || aMachineState == MachineState_Aborted
15046 || aMachineState == MachineState_Teleported
15047 )
15048 )
15049 {
15050 /* we've been shut down for any reason */
15051 /* no special action so far */
15052 }
15053
15054 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15055 LogFlowThisFuncLeave();
15056 return rc;
15057}
15058
15059/**
15060 * Sends the current machine state value to the VM process.
15061 *
15062 * @note Locks this object for reading, then calls a client process.
15063 */
15064HRESULT SessionMachine::i_updateMachineStateOnClient()
15065{
15066 AutoCaller autoCaller(this);
15067 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15068
15069 ComPtr<IInternalSessionControl> directControl;
15070 {
15071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15072 AssertReturn(!!mData, E_FAIL);
15073 if (mData->mSession.mLockType == LockType_VM)
15074 directControl = mData->mSession.mDirectControl;
15075
15076 /* directControl may be already set to NULL here in #OnSessionEnd()
15077 * called too early by the direct session process while there is still
15078 * some operation (like deleting the snapshot) in progress. The client
15079 * process in this case is waiting inside Session::close() for the
15080 * "end session" process object to complete, while #uninit() called by
15081 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15082 * operation to complete. For now, we accept this inconsistent behavior
15083 * and simply do nothing here. */
15084
15085 if (mData->mSession.mState == SessionState_Unlocking)
15086 return S_OK;
15087 }
15088
15089 /* ignore notifications sent after #OnSessionEnd() is called */
15090 if (!directControl)
15091 return S_OK;
15092
15093 return directControl->UpdateMachineState(mData->mMachineState);
15094}
15095
15096
15097/*static*/
15098HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15099{
15100 va_list args;
15101 va_start(args, pcszMsg);
15102 HRESULT rc = setErrorInternal(aResultCode,
15103 getStaticClassIID(),
15104 getStaticComponentName(),
15105 Utf8Str(pcszMsg, args),
15106 false /* aWarning */,
15107 true /* aLogIt */);
15108 va_end(args);
15109 return rc;
15110}
15111
15112
15113HRESULT Machine::updateState(MachineState_T aState)
15114{
15115 NOREF(aState);
15116 ReturnComNotImplemented();
15117}
15118
15119HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15120{
15121 NOREF(aProgress);
15122 ReturnComNotImplemented();
15123}
15124
15125HRESULT Machine::endPowerUp(LONG aResult)
15126{
15127 NOREF(aResult);
15128 ReturnComNotImplemented();
15129}
15130
15131HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15132{
15133 NOREF(aProgress);
15134 ReturnComNotImplemented();
15135}
15136
15137HRESULT Machine::endPoweringDown(LONG aResult,
15138 const com::Utf8Str &aErrMsg)
15139{
15140 NOREF(aResult);
15141 NOREF(aErrMsg);
15142 ReturnComNotImplemented();
15143}
15144
15145HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15146 BOOL *aMatched,
15147 ULONG *aMaskedInterfaces)
15148{
15149 NOREF(aDevice);
15150 NOREF(aMatched);
15151 NOREF(aMaskedInterfaces);
15152 ReturnComNotImplemented();
15153
15154}
15155
15156HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15157{
15158 NOREF(aId); NOREF(aCaptureFilename);
15159 ReturnComNotImplemented();
15160}
15161
15162HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15163 BOOL aDone)
15164{
15165 NOREF(aId);
15166 NOREF(aDone);
15167 ReturnComNotImplemented();
15168}
15169
15170HRESULT Machine::autoCaptureUSBDevices()
15171{
15172 ReturnComNotImplemented();
15173}
15174
15175HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15176{
15177 NOREF(aDone);
15178 ReturnComNotImplemented();
15179}
15180
15181HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15182 ComPtr<IProgress> &aProgress)
15183{
15184 NOREF(aSession);
15185 NOREF(aProgress);
15186 ReturnComNotImplemented();
15187}
15188
15189HRESULT Machine::finishOnlineMergeMedium()
15190{
15191 ReturnComNotImplemented();
15192}
15193
15194HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15195 std::vector<com::Utf8Str> &aValues,
15196 std::vector<LONG64> &aTimestamps,
15197 std::vector<com::Utf8Str> &aFlags)
15198{
15199 NOREF(aNames);
15200 NOREF(aValues);
15201 NOREF(aTimestamps);
15202 NOREF(aFlags);
15203 ReturnComNotImplemented();
15204}
15205
15206HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15207 const com::Utf8Str &aValue,
15208 LONG64 aTimestamp,
15209 const com::Utf8Str &aFlags)
15210{
15211 NOREF(aName);
15212 NOREF(aValue);
15213 NOREF(aTimestamp);
15214 NOREF(aFlags);
15215 ReturnComNotImplemented();
15216}
15217
15218HRESULT Machine::lockMedia()
15219{
15220 ReturnComNotImplemented();
15221}
15222
15223HRESULT Machine::unlockMedia()
15224{
15225 ReturnComNotImplemented();
15226}
15227
15228HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15229 ComPtr<IMediumAttachment> &aNewAttachment)
15230{
15231 NOREF(aAttachment);
15232 NOREF(aNewAttachment);
15233 ReturnComNotImplemented();
15234}
15235
15236HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15237 ULONG aCpuUser,
15238 ULONG aCpuKernel,
15239 ULONG aCpuIdle,
15240 ULONG aMemTotal,
15241 ULONG aMemFree,
15242 ULONG aMemBalloon,
15243 ULONG aMemShared,
15244 ULONG aMemCache,
15245 ULONG aPagedTotal,
15246 ULONG aMemAllocTotal,
15247 ULONG aMemFreeTotal,
15248 ULONG aMemBalloonTotal,
15249 ULONG aMemSharedTotal,
15250 ULONG aVmNetRx,
15251 ULONG aVmNetTx)
15252{
15253 NOREF(aValidStats);
15254 NOREF(aCpuUser);
15255 NOREF(aCpuKernel);
15256 NOREF(aCpuIdle);
15257 NOREF(aMemTotal);
15258 NOREF(aMemFree);
15259 NOREF(aMemBalloon);
15260 NOREF(aMemShared);
15261 NOREF(aMemCache);
15262 NOREF(aPagedTotal);
15263 NOREF(aMemAllocTotal);
15264 NOREF(aMemFreeTotal);
15265 NOREF(aMemBalloonTotal);
15266 NOREF(aMemSharedTotal);
15267 NOREF(aVmNetRx);
15268 NOREF(aVmNetTx);
15269 ReturnComNotImplemented();
15270}
15271
15272HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15273 com::Utf8Str &aResult)
15274{
15275 NOREF(aAuthParams);
15276 NOREF(aResult);
15277 ReturnComNotImplemented();
15278}
15279
15280HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15281{
15282 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15283
15284 AutoCaller autoCaller(this);
15285 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15286
15287 HRESULT rc = S_OK;
15288
15289 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15290 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15291 rc = getUSBDeviceFilters(usbDeviceFilters);
15292 if (FAILED(rc)) return rc;
15293
15294 NOREF(aFlags);
15295 com::Utf8Str osTypeId;
15296 ComObjPtr<GuestOSType> osType = NULL;
15297
15298 /* Get the guest os type as a string from the VB. */
15299 rc = getOSTypeId(osTypeId);
15300 if (FAILED(rc)) return rc;
15301
15302 /* Get the os type obj that coresponds, can be used to get
15303 * the defaults for this guest OS. */
15304 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15305 if (FAILED(rc)) return rc;
15306
15307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15308
15309 /* Let the OS type select 64-bit ness. */
15310 mHWData->mLongMode = osType->i_is64Bit()
15311 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15312
15313 /* Apply network adapters defaults */
15314 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15315 mNetworkAdapters[slot]->i_applyDefaults(osType);
15316
15317 /* Apply serial port defaults */
15318 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15319 mSerialPorts[slot]->i_applyDefaults(osType);
15320
15321 /* Apply parallel port defaults - not OS dependent*/
15322 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15323 mParallelPorts[slot]->i_applyDefaults();
15324
15325
15326 /* Let the OS type enable the X2APIC */
15327 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15328
15329 /* This one covers IOAPICEnabled. */
15330 mBIOSSettings->i_applyDefaults(osType);
15331
15332 /* Initialize default BIOS settings here */
15333 mHWData->mAPIC = osType->i_recommendedIOAPIC();
15334 mHWData->mHWVirtExEnabled = osType->i_recommendedVirtEx();
15335
15336 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15337 if (FAILED(rc)) return rc;
15338
15339 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15340 if (FAILED(rc)) return rc;
15341
15342 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15343 if (FAILED(rc)) return rc;
15344
15345 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15346 if (FAILED(rc)) return rc;
15347
15348 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15349 if (FAILED(rc)) return rc;
15350
15351 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15352 if (FAILED(rc)) return rc;
15353
15354 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15355 if (FAILED(rc)) return rc;
15356
15357 BOOL mRTCUseUTC;
15358 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15359 if (FAILED(rc)) return rc;
15360
15361 setRTCUseUTC(mRTCUseUTC);
15362 if (FAILED(rc)) return rc;
15363
15364 rc = osType->COMGETTER(RecommendedChipset)(&mHWData->mChipsetType);
15365 if (FAILED(rc)) return rc;
15366
15367 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15368 if (FAILED(rc)) return rc;
15369
15370 /* Audio stuff. */
15371 AudioCodecType_T audioCodec;
15372 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15373 if (FAILED(rc)) return rc;
15374
15375 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15376 if (FAILED(rc)) return rc;
15377
15378 AudioControllerType_T audioController;
15379 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15380 if (FAILED(rc)) return rc;
15381
15382 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15383 if (FAILED(rc)) return rc;
15384
15385 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15386 if (FAILED(rc)) return rc;
15387
15388 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15389 if (FAILED(rc)) return rc;
15390
15391 /* Storage Controllers */
15392 StorageControllerType_T hdStorageControllerType;
15393 StorageBus_T hdStorageBusType;
15394 StorageControllerType_T dvdStorageControllerType;
15395 StorageBus_T dvdStorageBusType;
15396 BOOL recommendedFloppy;
15397 ComPtr<IStorageController> floppyController;
15398 ComPtr<IStorageController> hdController;
15399 ComPtr<IStorageController> dvdController;
15400 Utf8Str strFloppyName, strDVDName, strHDName;
15401
15402 /* GUI auto generates these - not accesible here - so hardware, at least for now. */
15403 strFloppyName = Bstr("Floppy 1").raw();
15404 strDVDName = Bstr("DVD 1").raw();
15405 strHDName = Bstr("HDD 1").raw();
15406
15407 /* Floppy recommended? add one. */
15408 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15409 if (FAILED(rc)) return rc;
15410 if (recommendedFloppy)
15411 {
15412 rc = addStorageController(strFloppyName,
15413 StorageBus_Floppy,
15414 floppyController);
15415 if (FAILED(rc)) return rc;
15416 }
15417
15418 /* Setup one DVD storage controller. */
15419 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15420 if (FAILED(rc)) return rc;
15421
15422 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15423 if (FAILED(rc)) return rc;
15424
15425 rc = addStorageController(strDVDName,
15426 dvdStorageBusType,
15427 dvdController);
15428 if (FAILED(rc)) return rc;
15429
15430 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15431 if (FAILED(rc)) return rc;
15432
15433 /* Setup one HDD storage controller. */
15434 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15435 if (FAILED(rc)) return rc;
15436
15437 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15438 if (FAILED(rc)) return rc;
15439
15440 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15441 {
15442 rc = addStorageController(strHDName,
15443 hdStorageBusType,
15444 hdController);
15445 if (FAILED(rc)) return rc;
15446
15447 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15448 if (FAILED(rc)) return rc;
15449 }
15450 else
15451 {
15452 /* The HD controller is the same as DVD: */
15453 hdController = dvdController;
15454 strHDName = Bstr("DVD 1").raw();
15455 }
15456
15457 /* Limit the AHCI port count if it's used because windows has trouble with
15458 * too many ports and other guest (OS X in particular) may take extra long
15459 * boot: */
15460
15461 // pParent = static_cast<Medium*>(aP)
15462 IStorageController *temp = hdController;
15463 ComObjPtr<StorageController> storageController;
15464 storageController = static_cast<StorageController *>(temp);
15465
15466 // tempHDController = aHDController;
15467 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15468 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15469 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15470 storageController->COMSETTER(PortCount)(1);
15471
15472 /* USB stuff */
15473
15474 bool ohciEnabled = false;
15475
15476 ComPtr<IUSBController> usbController;
15477 BOOL recommendedUSB3;
15478 BOOL recommendedUSB;
15479 BOOL usbProxyAvailable;
15480
15481 getUSBProxyAvailable(&usbProxyAvailable);
15482 if (FAILED(rc)) return rc;
15483
15484 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15485 if (FAILED(rc)) return rc;
15486 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15487 if (FAILED(rc)) return rc;
15488
15489 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15490 {
15491 /* USB 3.0 is only available if the proper ExtPack is installed. */
15492 ExtPackManager *aManager = mParent->i_getExtPackManager();
15493 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15494 {
15495 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15496 if (FAILED(rc)) return rc;
15497
15498 /* xHci includes OHCI */
15499 ohciEnabled = true;
15500 }
15501 }
15502 if ( !ohciEnabled
15503 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15504 {
15505 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15506 if (FAILED(rc)) return rc;
15507 ohciEnabled = true;
15508
15509 /* USB 2.0 is only available if the proper ExtPack is installed.
15510 * Note. Configuring EHCI here and providing messages about
15511 * the missing extpack isn't exactly clean, but it is a
15512 * necessary evil to patch over legacy compatability issues
15513 * introduced by the new distribution model. */
15514 ExtPackManager *manager = mParent->i_getExtPackManager();
15515 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15516 {
15517 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15518 if (FAILED(rc)) return rc;
15519 }
15520 }
15521
15522 /* Set recommended human interface device types: */
15523 BOOL recommendedUSBHID;
15524 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15525 if (FAILED(rc)) return rc;
15526
15527 if (recommendedUSBHID)
15528 {
15529 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15530 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15531 if (!ohciEnabled && !usbDeviceFilters.isNull())
15532 {
15533 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15534 if (FAILED(rc)) return rc;
15535 }
15536 }
15537
15538 BOOL recommendedUSBTablet;
15539 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15540 if (FAILED(rc)) return rc;
15541
15542 if (recommendedUSBTablet)
15543 {
15544 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15545 if (!ohciEnabled && !usbDeviceFilters.isNull())
15546 {
15547 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15548 if (FAILED(rc)) return rc;
15549 }
15550 }
15551 return S_OK;
15552}
15553
15554/* This isn't handled entirely by the wrapper generator yet. */
15555#ifdef VBOX_WITH_XPCOM
15556NS_DECL_CLASSINFO(SessionMachine)
15557NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15558
15559NS_DECL_CLASSINFO(SnapshotMachine)
15560NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15561#endif
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