VirtualBox

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

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

Main: Use setErrorBoth when we've got a VBox status code handy. (The COM status codes aren't too specfic and this may help us decode error messages and provide an alternative to strstr for API clients. setErrorBoth isn't new, btw.)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 527.9 KB
Line 
1/* $Id: MachineImpl.cpp 73003 2018-07-09 11:09:32Z 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.virtualbox.org. 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
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70
71#include <VBox/com/array.h>
72#include <VBox/com/list.h>
73
74#include <VBox/err.h>
75#include <VBox/param.h>
76#include <VBox/settings.h>
77#include <VBox/vmm/ssm.h>
78
79#ifdef VBOX_WITH_GUEST_PROPS
80# include <VBox/HostServices/GuestPropertySvc.h>
81# include <VBox/com/array.h>
82#endif
83
84#include "VBox/com/MultiResult.h"
85
86#include <algorithm>
87
88#ifdef VBOX_WITH_DTRACE_R3_MAIN
89# include "dtrace/VBoxAPI.h"
90#endif
91
92#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
93# define HOSTSUFF_EXE ".exe"
94#else /* !RT_OS_WINDOWS */
95# define HOSTSUFF_EXE ""
96#endif /* !RT_OS_WINDOWS */
97
98// defines / prototypes
99/////////////////////////////////////////////////////////////////////////////
100
101/////////////////////////////////////////////////////////////////////////////
102// Machine::Data structure
103/////////////////////////////////////////////////////////////////////////////
104
105Machine::Data::Data()
106{
107 mRegistered = FALSE;
108 pMachineConfigFile = NULL;
109 /* Contains hints on what has changed when the user is using the VM (config
110 * changes, running the VM, ...). This is used to decide if a config needs
111 * to be written to disk. */
112 flModifications = 0;
113 /* VM modification usually also trigger setting the current state to
114 * "Modified". Although this is not always the case. An e.g. is the VM
115 * initialization phase or when snapshot related data is changed. The
116 * actually behavior is controlled by the following flag. */
117 m_fAllowStateModification = false;
118 mAccessible = FALSE;
119 /* mUuid is initialized in Machine::init() */
120
121 mMachineState = MachineState_PoweredOff;
122 RTTimeNow(&mLastStateChange);
123
124 mMachineStateDeps = 0;
125 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
126 mMachineStateChangePending = 0;
127
128 mCurrentStateModified = TRUE;
129 mGuestPropertiesModified = FALSE;
130
131 mSession.mPID = NIL_RTPROCESS;
132 mSession.mLockType = LockType_Null;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189 mHWVirtExUseNativeApi = false;
190#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
191 mPAEEnabled = true;
192#else
193 mPAEEnabled = false;
194#endif
195 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
196 mTripleFaultReset = false;
197 mAPIC = true;
198 mX2APIC = false;
199 mIBPBOnVMExit = false;
200 mIBPBOnVMEntry = false;
201 mSpecCtrl = false;
202 mSpecCtrlByHost = false;
203 mNestedHWVirt = false;
204 mHPETEnabled = false;
205 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
206 mCpuIdPortabilityLevel = 0;
207 mCpuProfile = "host";
208
209 /* default boot order: floppy - DVD - HDD */
210 mBootOrder[0] = DeviceType_Floppy;
211 mBootOrder[1] = DeviceType_DVD;
212 mBootOrder[2] = DeviceType_HardDisk;
213 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
214 mBootOrder[i] = DeviceType_Null;
215
216 mClipboardMode = ClipboardMode_Disabled;
217 mDnDMode = DnDMode_Disabled;
218
219 mFirmwareType = FirmwareType_BIOS;
220 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
221 mPointingHIDType = PointingHIDType_PS2Mouse;
222 mChipsetType = ChipsetType_PIIX3;
223 mParavirtProvider = ParavirtProvider_Default;
224 mEmulatedUSBCardReaderEnabled = FALSE;
225
226 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
227 mCPUAttached[i] = false;
228
229 mIOCacheEnabled = true;
230 mIOCacheSize = 5; /* 5MB */
231}
232
233Machine::HWData::~HWData()
234{
235}
236
237/////////////////////////////////////////////////////////////////////////////
238// Machine class
239/////////////////////////////////////////////////////////////////////////////
240
241// constructor / destructor
242/////////////////////////////////////////////////////////////////////////////
243
244Machine::Machine() :
245#ifdef VBOX_WITH_RESOURCE_USAGE_API
246 mCollectorGuest(NULL),
247#endif
248 mPeer(NULL),
249 mParent(NULL),
250 mSerialPorts(),
251 mParallelPorts(),
252 uRegistryNeedsSaving(0)
253{}
254
255Machine::~Machine()
256{}
257
258HRESULT Machine::FinalConstruct()
259{
260 LogFlowThisFunc(("\n"));
261 return BaseFinalConstruct();
262}
263
264void Machine::FinalRelease()
265{
266 LogFlowThisFunc(("\n"));
267 uninit();
268 BaseFinalRelease();
269}
270
271/**
272 * Initializes a new machine instance; this init() variant creates a new, empty machine.
273 * This gets called from VirtualBox::CreateMachine().
274 *
275 * @param aParent Associated parent object
276 * @param strConfigFile Local file system path to the VM settings file (can
277 * be relative to the VirtualBox config directory).
278 * @param strName name for the machine
279 * @param llGroups list of groups for the machine
280 * @param strOsType OS Type string (stored as is if aOsType is NULL).
281 * @param aOsType OS Type of this machine or NULL.
282 * @param aId UUID for the new machine.
283 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
284 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
285 * scheme (includes the UUID).
286 *
287 * @return Success indicator. if not S_OK, the machine object is invalid
288 */
289HRESULT Machine::init(VirtualBox *aParent,
290 const Utf8Str &strConfigFile,
291 const Utf8Str &strName,
292 const StringsList &llGroups,
293 const Utf8Str &strOsType,
294 GuestOSType *aOsType,
295 const Guid &aId,
296 bool fForceOverwrite,
297 bool fDirectoryIncludesUUID)
298{
299 LogFlowThisFuncEnter();
300 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
301
302 /* Enclose the state transition NotReady->InInit->Ready */
303 AutoInitSpan autoInitSpan(this);
304 AssertReturn(autoInitSpan.isOk(), E_FAIL);
305
306 HRESULT rc = initImpl(aParent, strConfigFile);
307 if (FAILED(rc)) return rc;
308
309 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
310 if (FAILED(rc)) return rc;
311
312 if (SUCCEEDED(rc))
313 {
314 // create an empty machine config
315 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
316
317 rc = initDataAndChildObjects();
318 }
319
320 if (SUCCEEDED(rc))
321 {
322 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
323 mData->mAccessible = TRUE;
324
325 unconst(mData->mUuid) = aId;
326
327 mUserData->s.strName = strName;
328
329 if (llGroups.size())
330 mUserData->s.llGroups = llGroups;
331
332 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
333 // the "name sync" flag determines whether the machine directory gets renamed along
334 // with the machine file; say so if the settings file name is the same as the
335 // settings file parent directory (machine directory)
336 mUserData->s.fNameSync = i_isInOwnDir();
337
338 // initialize the default snapshots folder
339 rc = COMSETTER(SnapshotFolder)(NULL);
340 AssertComRC(rc);
341
342 if (aOsType)
343 {
344 /* Store OS type */
345 mUserData->s.strOsType = aOsType->i_id();
346
347 /* Let the OS type select 64-bit ness. */
348 mHWData->mLongMode = aOsType->i_is64Bit()
349 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
350
351 /* Let the OS type enable the X2APIC */
352 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
353 }
354 else if (!strOsType.isEmpty())
355 {
356 /* Store OS type */
357 mUserData->s.strOsType = strOsType;
358
359 /* No guest OS type object. Pick some plausible defaults which the
360 * host can handle. There's no way to know or validate anything. */
361 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
362 mHWData->mX2APIC = false;
363 }
364
365 /* Apply BIOS defaults */
366 mBIOSSettings->i_applyDefaults(aOsType);
367
368 /* Apply network adapters defaults */
369 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
370 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
371
372 /* Apply serial port defaults */
373 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
374 mSerialPorts[slot]->i_applyDefaults(aOsType);
375
376 /* Apply parallel port defaults */
377 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
378 mParallelPorts[slot]->i_applyDefaults();
379
380 /* At this point the changing of the current state modification
381 * flag is allowed. */
382 i_allowStateModification();
383
384 /* commit all changes made during the initialization */
385 i_commit();
386 }
387
388 /* Confirm a successful initialization when it's the case */
389 if (SUCCEEDED(rc))
390 {
391 if (mData->mAccessible)
392 autoInitSpan.setSucceeded();
393 else
394 autoInitSpan.setLimited();
395 }
396
397 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
398 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
399 mData->mRegistered,
400 mData->mAccessible,
401 rc));
402
403 LogFlowThisFuncLeave();
404
405 return rc;
406}
407
408/**
409 * Initializes a new instance with data from machine XML (formerly Init_Registered).
410 * Gets called in two modes:
411 *
412 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
413 * UUID is specified and we mark the machine as "registered";
414 *
415 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
416 * and the machine remains unregistered until RegisterMachine() is called.
417 *
418 * @param aParent Associated parent object
419 * @param strConfigFile Local file system path to the VM settings file (can
420 * be relative to the VirtualBox config directory).
421 * @param aId UUID of the machine or NULL (see above).
422 *
423 * @return Success indicator. if not S_OK, the machine object is invalid
424 */
425HRESULT Machine::initFromSettings(VirtualBox *aParent,
426 const Utf8Str &strConfigFile,
427 const Guid *aId)
428{
429 LogFlowThisFuncEnter();
430 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
431
432 /* Enclose the state transition NotReady->InInit->Ready */
433 AutoInitSpan autoInitSpan(this);
434 AssertReturn(autoInitSpan.isOk(), E_FAIL);
435
436 HRESULT rc = initImpl(aParent, strConfigFile);
437 if (FAILED(rc)) return rc;
438
439 if (aId)
440 {
441 // loading a registered VM:
442 unconst(mData->mUuid) = *aId;
443 mData->mRegistered = TRUE;
444 // now load the settings from XML:
445 rc = i_registeredInit();
446 // this calls initDataAndChildObjects() and loadSettings()
447 }
448 else
449 {
450 // opening an unregistered VM (VirtualBox::OpenMachine()):
451 rc = initDataAndChildObjects();
452
453 if (SUCCEEDED(rc))
454 {
455 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
456 mData->mAccessible = TRUE;
457
458 try
459 {
460 // load and parse machine XML; this will throw on XML or logic errors
461 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
462
463 // reject VM UUID duplicates, they can happen if someone
464 // tries to register an already known VM config again
465 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
466 true /* fPermitInaccessible */,
467 false /* aDoSetError */,
468 NULL) != VBOX_E_OBJECT_NOT_FOUND)
469 {
470 throw setError(E_FAIL,
471 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
472 mData->m_strConfigFile.c_str());
473 }
474
475 // use UUID from machine config
476 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
477
478 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
479 NULL /* puuidRegistry */);
480 if (FAILED(rc)) throw rc;
481
482 /* At this point the changing of the current state modification
483 * flag is allowed. */
484 i_allowStateModification();
485
486 i_commit();
487 }
488 catch (HRESULT err)
489 {
490 /* we assume that error info is set by the thrower */
491 rc = err;
492 }
493 catch (...)
494 {
495 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
496 }
497 }
498 }
499
500 /* Confirm a successful initialization when it's the case */
501 if (SUCCEEDED(rc))
502 {
503 if (mData->mAccessible)
504 autoInitSpan.setSucceeded();
505 else
506 {
507 autoInitSpan.setLimited();
508
509 // uninit media from this machine's media registry, or else
510 // reloading the settings will fail
511 mParent->i_unregisterMachineMedia(i_getId());
512 }
513 }
514
515 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
516 "rc=%08X\n",
517 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
518 mData->mRegistered, mData->mAccessible, rc));
519
520 LogFlowThisFuncLeave();
521
522 return rc;
523}
524
525/**
526 * Initializes a new instance from a machine config that is already in memory
527 * (import OVF case). Since we are importing, the UUID in the machine
528 * config is ignored and we always generate a fresh one.
529 *
530 * @param aParent Associated parent object.
531 * @param strName Name for the new machine; this overrides what is specified in config.
532 * @param strSettingsFilename File name of .vbox file.
533 * @param config Machine configuration loaded and parsed from XML.
534 *
535 * @return Success indicator. if not S_OK, the machine object is invalid
536 */
537HRESULT Machine::init(VirtualBox *aParent,
538 const Utf8Str &strName,
539 const Utf8Str &strSettingsFilename,
540 const settings::MachineConfigFile &config)
541{
542 LogFlowThisFuncEnter();
543
544 /* Enclose the state transition NotReady->InInit->Ready */
545 AutoInitSpan autoInitSpan(this);
546 AssertReturn(autoInitSpan.isOk(), E_FAIL);
547
548 HRESULT rc = initImpl(aParent, strSettingsFilename);
549 if (FAILED(rc)) return rc;
550
551 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
552 if (FAILED(rc)) return rc;
553
554 rc = initDataAndChildObjects();
555
556 if (SUCCEEDED(rc))
557 {
558 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
559 mData->mAccessible = TRUE;
560
561 // create empty machine config for instance data
562 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
563
564 // generate fresh UUID, ignore machine config
565 unconst(mData->mUuid).create();
566
567 rc = i_loadMachineDataFromSettings(config,
568 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
569
570 // override VM name as well, it may be different
571 mUserData->s.strName = strName;
572
573 if (SUCCEEDED(rc))
574 {
575 /* At this point the changing of the current state modification
576 * flag is allowed. */
577 i_allowStateModification();
578
579 /* commit all changes made during the initialization */
580 i_commit();
581 }
582 }
583
584 /* Confirm a successful initialization when it's the case */
585 if (SUCCEEDED(rc))
586 {
587 if (mData->mAccessible)
588 autoInitSpan.setSucceeded();
589 else
590 {
591 /* Ignore all errors from unregistering, they would destroy
592- * the more interesting error information we already have,
593- * pinpointing the issue with the VM config. */
594 ErrorInfoKeeper eik;
595
596 autoInitSpan.setLimited();
597
598 // uninit media from this machine's media registry, or else
599 // reloading the settings will fail
600 mParent->i_unregisterMachineMedia(i_getId());
601 }
602 }
603
604 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
605 "rc=%08X\n",
606 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
607 mData->mRegistered, mData->mAccessible, rc));
608
609 LogFlowThisFuncLeave();
610
611 return rc;
612}
613
614/**
615 * Shared code between the various init() implementations.
616 * @param aParent The VirtualBox object.
617 * @param strConfigFile Settings file.
618 * @return
619 */
620HRESULT Machine::initImpl(VirtualBox *aParent,
621 const Utf8Str &strConfigFile)
622{
623 LogFlowThisFuncEnter();
624
625 AssertReturn(aParent, E_INVALIDARG);
626 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
627
628 HRESULT rc = S_OK;
629
630 /* share the parent weakly */
631 unconst(mParent) = aParent;
632
633 /* allocate the essential machine data structure (the rest will be
634 * allocated later by initDataAndChildObjects() */
635 mData.allocate();
636
637 /* memorize the config file name (as provided) */
638 mData->m_strConfigFile = strConfigFile;
639
640 /* get the full file name */
641 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
642 if (RT_FAILURE(vrc1))
643 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
644 tr("Invalid machine settings file name '%s' (%Rrc)"),
645 strConfigFile.c_str(),
646 vrc1);
647
648 LogFlowThisFuncLeave();
649
650 return rc;
651}
652
653/**
654 * Tries to create a machine settings file in the path stored in the machine
655 * instance data. Used when a new machine is created to fail gracefully if
656 * the settings file could not be written (e.g. because machine dir is read-only).
657 * @return
658 */
659HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
660{
661 HRESULT rc = S_OK;
662
663 // when we create a new machine, we must be able to create the settings file
664 RTFILE f = NIL_RTFILE;
665 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
666 if ( RT_SUCCESS(vrc)
667 || vrc == VERR_SHARING_VIOLATION
668 )
669 {
670 if (RT_SUCCESS(vrc))
671 RTFileClose(f);
672 if (!fForceOverwrite)
673 rc = setError(VBOX_E_FILE_ERROR,
674 tr("Machine settings file '%s' already exists"),
675 mData->m_strConfigFileFull.c_str());
676 else
677 {
678 /* try to delete the config file, as otherwise the creation
679 * of a new settings file will fail. */
680 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
681 if (RT_FAILURE(vrc2))
682 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
683 tr("Could not delete the existing settings file '%s' (%Rrc)"),
684 mData->m_strConfigFileFull.c_str(), vrc2);
685 }
686 }
687 else if ( vrc != VERR_FILE_NOT_FOUND
688 && vrc != VERR_PATH_NOT_FOUND
689 )
690 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
691 tr("Invalid machine settings file name '%s' (%Rrc)"),
692 mData->m_strConfigFileFull.c_str(),
693 vrc);
694 return rc;
695}
696
697/**
698 * Initializes the registered machine by loading the settings file.
699 * This method is separated from #init() in order to make it possible to
700 * retry the operation after VirtualBox startup instead of refusing to
701 * startup the whole VirtualBox server in case if the settings file of some
702 * registered VM is invalid or inaccessible.
703 *
704 * @note Must be always called from this object's write lock
705 * (unless called from #init() that doesn't need any locking).
706 * @note Locks the mUSBController method for writing.
707 * @note Subclasses must not call this method.
708 */
709HRESULT Machine::i_registeredInit()
710{
711 AssertReturn(!i_isSessionMachine(), E_FAIL);
712 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
713 AssertReturn(mData->mUuid.isValid(), E_FAIL);
714 AssertReturn(!mData->mAccessible, E_FAIL);
715
716 HRESULT rc = initDataAndChildObjects();
717
718 if (SUCCEEDED(rc))
719 {
720 /* Temporarily reset the registered flag in order to let setters
721 * potentially called from loadSettings() succeed (isMutable() used in
722 * all setters will return FALSE for a Machine instance if mRegistered
723 * is TRUE). */
724 mData->mRegistered = FALSE;
725
726 try
727 {
728 // load and parse machine XML; this will throw on XML or logic errors
729 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
730
731 if (mData->mUuid != mData->pMachineConfigFile->uuid)
732 throw setError(E_FAIL,
733 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
734 mData->pMachineConfigFile->uuid.raw(),
735 mData->m_strConfigFileFull.c_str(),
736 mData->mUuid.toString().c_str(),
737 mParent->i_settingsFilePath().c_str());
738
739 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
740 NULL /* const Guid *puuidRegistry */);
741 if (FAILED(rc)) throw rc;
742 }
743 catch (HRESULT err)
744 {
745 /* we assume that error info is set by the thrower */
746 rc = err;
747 }
748 catch (...)
749 {
750 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
751 }
752
753 /* Restore the registered flag (even on failure) */
754 mData->mRegistered = TRUE;
755 }
756
757 if (SUCCEEDED(rc))
758 {
759 /* Set mAccessible to TRUE only if we successfully locked and loaded
760 * the settings file */
761 mData->mAccessible = TRUE;
762
763 /* commit all changes made during loading the settings file */
764 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
765 /// @todo r=klaus for some reason the settings loading logic backs up
766 // the settings, and therefore a commit is needed. Should probably be changed.
767 }
768 else
769 {
770 /* If the machine is registered, then, instead of returning a
771 * failure, we mark it as inaccessible and set the result to
772 * success to give it a try later */
773
774 /* fetch the current error info */
775 mData->mAccessError = com::ErrorInfo();
776 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
777
778 /* rollback all changes */
779 i_rollback(false /* aNotify */);
780
781 // uninit media from this machine's media registry, or else
782 // reloading the settings will fail
783 mParent->i_unregisterMachineMedia(i_getId());
784
785 /* uninitialize the common part to make sure all data is reset to
786 * default (null) values */
787 uninitDataAndChildObjects();
788
789 rc = S_OK;
790 }
791
792 return rc;
793}
794
795/**
796 * Uninitializes the instance.
797 * Called either from FinalRelease() or by the parent when it gets destroyed.
798 *
799 * @note The caller of this method must make sure that this object
800 * a) doesn't have active callers on the current thread and b) is not locked
801 * by the current thread; otherwise uninit() will hang either a) due to
802 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
803 * a dead-lock caused by this thread waiting for all callers on the other
804 * threads are done but preventing them from doing so by holding a lock.
805 */
806void Machine::uninit()
807{
808 LogFlowThisFuncEnter();
809
810 Assert(!isWriteLockOnCurrentThread());
811
812 Assert(!uRegistryNeedsSaving);
813 if (uRegistryNeedsSaving)
814 {
815 AutoCaller autoCaller(this);
816 if (SUCCEEDED(autoCaller.rc()))
817 {
818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
819 i_saveSettings(NULL, Machine::SaveS_Force);
820 }
821 }
822
823 /* Enclose the state transition Ready->InUninit->NotReady */
824 AutoUninitSpan autoUninitSpan(this);
825 if (autoUninitSpan.uninitDone())
826 return;
827
828 Assert(!i_isSnapshotMachine());
829 Assert(!i_isSessionMachine());
830 Assert(!!mData);
831
832 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
833 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
834
835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
836
837 if (!mData->mSession.mMachine.isNull())
838 {
839 /* Theoretically, this can only happen if the VirtualBox server has been
840 * terminated while there were clients running that owned open direct
841 * sessions. Since in this case we are definitely called by
842 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
843 * won't happen on the client watcher thread (because it has a
844 * VirtualBox caller for the duration of the
845 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
846 * cannot happen until the VirtualBox caller is released). This is
847 * important, because SessionMachine::uninit() cannot correctly operate
848 * after we return from this method (it expects the Machine instance is
849 * still valid). We'll call it ourselves below.
850 */
851 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
852 (SessionMachine*)mData->mSession.mMachine));
853
854 if (Global::IsOnlineOrTransient(mData->mMachineState))
855 {
856 Log1WarningThisFunc(("Setting state to Aborted!\n"));
857 /* set machine state using SessionMachine reimplementation */
858 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
859 }
860
861 /*
862 * Uninitialize SessionMachine using public uninit() to indicate
863 * an unexpected uninitialization.
864 */
865 mData->mSession.mMachine->uninit();
866 /* SessionMachine::uninit() must set mSession.mMachine to null */
867 Assert(mData->mSession.mMachine.isNull());
868 }
869
870 // uninit media from this machine's media registry, if they're still there
871 Guid uuidMachine(i_getId());
872
873 /* the lock is no more necessary (SessionMachine is uninitialized) */
874 alock.release();
875
876 /* XXX This will fail with
877 * "cannot be closed because it is still attached to 1 virtual machines"
878 * because at this point we did not call uninitDataAndChildObjects() yet
879 * and therefore also removeBackReference() for all these mediums was not called! */
880
881 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
882 mParent->i_unregisterMachineMedia(uuidMachine);
883
884 // has machine been modified?
885 if (mData->flModifications)
886 {
887 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
888 i_rollback(false /* aNotify */);
889 }
890
891 if (mData->mAccessible)
892 uninitDataAndChildObjects();
893
894 /* free the essential data structure last */
895 mData.free();
896
897 LogFlowThisFuncLeave();
898}
899
900// Wrapped IMachine properties
901/////////////////////////////////////////////////////////////////////////////
902HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
903{
904 /* mParent is constant during life time, no need to lock */
905 ComObjPtr<VirtualBox> pVirtualBox(mParent);
906 aParent = pVirtualBox;
907
908 return S_OK;
909}
910
911
912HRESULT Machine::getAccessible(BOOL *aAccessible)
913{
914 /* In some cases (medium registry related), it is necessary to be able to
915 * go through the list of all machines. Happens when an inaccessible VM
916 * has a sensible medium registry. */
917 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
918 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
919
920 HRESULT rc = S_OK;
921
922 if (!mData->mAccessible)
923 {
924 /* try to initialize the VM once more if not accessible */
925
926 AutoReinitSpan autoReinitSpan(this);
927 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
928
929#ifdef DEBUG
930 LogFlowThisFunc(("Dumping media backreferences\n"));
931 mParent->i_dumpAllBackRefs();
932#endif
933
934 if (mData->pMachineConfigFile)
935 {
936 // reset the XML file to force loadSettings() (called from i_registeredInit())
937 // to parse it again; the file might have changed
938 delete mData->pMachineConfigFile;
939 mData->pMachineConfigFile = NULL;
940 }
941
942 rc = i_registeredInit();
943
944 if (SUCCEEDED(rc) && mData->mAccessible)
945 {
946 autoReinitSpan.setSucceeded();
947
948 /* make sure interesting parties will notice the accessibility
949 * state change */
950 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
951 mParent->i_onMachineDataChange(mData->mUuid);
952 }
953 }
954
955 if (SUCCEEDED(rc))
956 *aAccessible = mData->mAccessible;
957
958 LogFlowThisFuncLeave();
959
960 return rc;
961}
962
963HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
964{
965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
966
967 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
968 {
969 /* return shortly */
970 aAccessError = NULL;
971 return S_OK;
972 }
973
974 HRESULT rc = S_OK;
975
976 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
977 rc = errorInfo.createObject();
978 if (SUCCEEDED(rc))
979 {
980 errorInfo->init(mData->mAccessError.getResultCode(),
981 mData->mAccessError.getInterfaceID().ref(),
982 Utf8Str(mData->mAccessError.getComponent()).c_str(),
983 Utf8Str(mData->mAccessError.getText()));
984 aAccessError = errorInfo;
985 }
986
987 return rc;
988}
989
990HRESULT Machine::getName(com::Utf8Str &aName)
991{
992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
993
994 aName = mUserData->s.strName;
995
996 return S_OK;
997}
998
999HRESULT Machine::setName(const com::Utf8Str &aName)
1000{
1001 // prohibit setting a UUID only as the machine name, or else it can
1002 // never be found by findMachine()
1003 Guid test(aName);
1004
1005 if (test.isValid())
1006 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1007
1008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1009
1010 HRESULT rc = i_checkStateDependency(MutableStateDep);
1011 if (FAILED(rc)) return rc;
1012
1013 i_setModified(IsModified_MachineData);
1014 mUserData.backup();
1015 mUserData->s.strName = aName;
1016
1017 return S_OK;
1018}
1019
1020HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1021{
1022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 aDescription = mUserData->s.strDescription;
1025
1026 return S_OK;
1027}
1028
1029HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1030{
1031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1032
1033 // this can be done in principle in any state as it doesn't affect the VM
1034 // significantly, but play safe by not messing around while complex
1035 // activities are going on
1036 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1037 if (FAILED(rc)) return rc;
1038
1039 i_setModified(IsModified_MachineData);
1040 mUserData.backup();
1041 mUserData->s.strDescription = aDescription;
1042
1043 return S_OK;
1044}
1045
1046HRESULT Machine::getId(com::Guid &aId)
1047{
1048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1049
1050 aId = mData->mUuid;
1051
1052 return S_OK;
1053}
1054
1055HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1056{
1057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1058 aGroups.resize(mUserData->s.llGroups.size());
1059 size_t i = 0;
1060 for (StringsList::const_iterator
1061 it = mUserData->s.llGroups.begin();
1062 it != mUserData->s.llGroups.end();
1063 ++it, ++i)
1064 aGroups[i] = (*it);
1065
1066 return S_OK;
1067}
1068
1069HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1070{
1071 StringsList llGroups;
1072 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1073 if (FAILED(rc))
1074 return rc;
1075
1076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1077
1078 rc = i_checkStateDependency(MutableOrSavedStateDep);
1079 if (FAILED(rc)) return rc;
1080
1081 i_setModified(IsModified_MachineData);
1082 mUserData.backup();
1083 mUserData->s.llGroups = llGroups;
1084
1085 return S_OK;
1086}
1087
1088HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1089{
1090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1091
1092 aOSTypeId = mUserData->s.strOsType;
1093
1094 return S_OK;
1095}
1096
1097HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1098{
1099 /* look up the object by Id to check it is valid */
1100 ComObjPtr<GuestOSType> pGuestOSType;
1101 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1102
1103 /* when setting, always use the "etalon" value for consistency -- lookup
1104 * by ID is case-insensitive and the input value may have different case */
1105 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1106
1107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1108
1109 HRESULT rc = i_checkStateDependency(MutableStateDep);
1110 if (FAILED(rc)) return rc;
1111
1112 i_setModified(IsModified_MachineData);
1113 mUserData.backup();
1114 mUserData->s.strOsType = osTypeId;
1115
1116 return S_OK;
1117}
1118
1119HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1120{
1121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1122
1123 *aFirmwareType = mHWData->mFirmwareType;
1124
1125 return S_OK;
1126}
1127
1128HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1129{
1130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1131
1132 HRESULT rc = i_checkStateDependency(MutableStateDep);
1133 if (FAILED(rc)) return rc;
1134
1135 i_setModified(IsModified_MachineData);
1136 mHWData.backup();
1137 mHWData->mFirmwareType = aFirmwareType;
1138
1139 return S_OK;
1140}
1141
1142HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1143{
1144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1145
1146 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1147
1148 return S_OK;
1149}
1150
1151HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1152{
1153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1154
1155 HRESULT rc = i_checkStateDependency(MutableStateDep);
1156 if (FAILED(rc)) return rc;
1157
1158 i_setModified(IsModified_MachineData);
1159 mHWData.backup();
1160 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1161
1162 return S_OK;
1163}
1164
1165HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1166{
1167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1168
1169 *aPointingHIDType = mHWData->mPointingHIDType;
1170
1171 return S_OK;
1172}
1173
1174HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1175{
1176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1177
1178 HRESULT rc = i_checkStateDependency(MutableStateDep);
1179 if (FAILED(rc)) return rc;
1180
1181 i_setModified(IsModified_MachineData);
1182 mHWData.backup();
1183 mHWData->mPointingHIDType = aPointingHIDType;
1184
1185 return S_OK;
1186}
1187
1188HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1189{
1190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1191
1192 *aChipsetType = mHWData->mChipsetType;
1193
1194 return S_OK;
1195}
1196
1197HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1198{
1199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1200
1201 HRESULT rc = i_checkStateDependency(MutableStateDep);
1202 if (FAILED(rc)) return rc;
1203
1204 if (aChipsetType != mHWData->mChipsetType)
1205 {
1206 i_setModified(IsModified_MachineData);
1207 mHWData.backup();
1208 mHWData->mChipsetType = aChipsetType;
1209
1210 // Resize network adapter array, to be finalized on commit/rollback.
1211 // We must not throw away entries yet, otherwise settings are lost
1212 // without a way to roll back.
1213 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1214 size_t oldCount = mNetworkAdapters.size();
1215 if (newCount > oldCount)
1216 {
1217 mNetworkAdapters.resize(newCount);
1218 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1219 {
1220 unconst(mNetworkAdapters[slot]).createObject();
1221 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1222 }
1223 }
1224 }
1225
1226 return S_OK;
1227}
1228
1229HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1230{
1231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 aParavirtDebug = mHWData->mParavirtDebug;
1234 return S_OK;
1235}
1236
1237HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1238{
1239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1240
1241 HRESULT rc = i_checkStateDependency(MutableStateDep);
1242 if (FAILED(rc)) return rc;
1243
1244 /** @todo Parse/validate options? */
1245 if (aParavirtDebug != mHWData->mParavirtDebug)
1246 {
1247 i_setModified(IsModified_MachineData);
1248 mHWData.backup();
1249 mHWData->mParavirtDebug = aParavirtDebug;
1250 }
1251
1252 return S_OK;
1253}
1254
1255HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1256{
1257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1258
1259 *aParavirtProvider = mHWData->mParavirtProvider;
1260
1261 return S_OK;
1262}
1263
1264HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1265{
1266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 HRESULT rc = i_checkStateDependency(MutableStateDep);
1269 if (FAILED(rc)) return rc;
1270
1271 if (aParavirtProvider != mHWData->mParavirtProvider)
1272 {
1273 i_setModified(IsModified_MachineData);
1274 mHWData.backup();
1275 mHWData->mParavirtProvider = aParavirtProvider;
1276 }
1277
1278 return S_OK;
1279}
1280
1281HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1282{
1283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1284
1285 *aParavirtProvider = mHWData->mParavirtProvider;
1286 switch (mHWData->mParavirtProvider)
1287 {
1288 case ParavirtProvider_None:
1289 case ParavirtProvider_HyperV:
1290 case ParavirtProvider_KVM:
1291 case ParavirtProvider_Minimal:
1292 break;
1293
1294 /* Resolve dynamic provider types to the effective types. */
1295 default:
1296 {
1297 ComObjPtr<GuestOSType> pGuestOSType;
1298 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1299 pGuestOSType);
1300 if (FAILED(hrc2) || pGuestOSType.isNull())
1301 {
1302 *aParavirtProvider = ParavirtProvider_None;
1303 break;
1304 }
1305
1306 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1307 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1308
1309 switch (mHWData->mParavirtProvider)
1310 {
1311 case ParavirtProvider_Legacy:
1312 {
1313 if (fOsXGuest)
1314 *aParavirtProvider = ParavirtProvider_Minimal;
1315 else
1316 *aParavirtProvider = ParavirtProvider_None;
1317 break;
1318 }
1319
1320 case ParavirtProvider_Default:
1321 {
1322 if (fOsXGuest)
1323 *aParavirtProvider = ParavirtProvider_Minimal;
1324 else if ( mUserData->s.strOsType == "Windows10"
1325 || mUserData->s.strOsType == "Windows10_64"
1326 || mUserData->s.strOsType == "Windows81"
1327 || mUserData->s.strOsType == "Windows81_64"
1328 || mUserData->s.strOsType == "Windows8"
1329 || mUserData->s.strOsType == "Windows8_64"
1330 || mUserData->s.strOsType == "Windows7"
1331 || mUserData->s.strOsType == "Windows7_64"
1332 || mUserData->s.strOsType == "WindowsVista"
1333 || mUserData->s.strOsType == "WindowsVista_64"
1334 || mUserData->s.strOsType == "Windows2012"
1335 || mUserData->s.strOsType == "Windows2012_64"
1336 || mUserData->s.strOsType == "Windows2008"
1337 || mUserData->s.strOsType == "Windows2008_64")
1338 {
1339 *aParavirtProvider = ParavirtProvider_HyperV;
1340 }
1341 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1342 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1343 || mUserData->s.strOsType == "Linux"
1344 || mUserData->s.strOsType == "Linux_64"
1345 || mUserData->s.strOsType == "ArchLinux"
1346 || mUserData->s.strOsType == "ArchLinux_64"
1347 || mUserData->s.strOsType == "Debian"
1348 || mUserData->s.strOsType == "Debian_64"
1349 || mUserData->s.strOsType == "Fedora"
1350 || mUserData->s.strOsType == "Fedora_64"
1351 || mUserData->s.strOsType == "Gentoo"
1352 || mUserData->s.strOsType == "Gentoo_64"
1353 || mUserData->s.strOsType == "Mandriva"
1354 || mUserData->s.strOsType == "Mandriva_64"
1355 || mUserData->s.strOsType == "OpenSUSE"
1356 || mUserData->s.strOsType == "OpenSUSE_64"
1357 || mUserData->s.strOsType == "Oracle"
1358 || mUserData->s.strOsType == "Oracle_64"
1359 || mUserData->s.strOsType == "RedHat"
1360 || mUserData->s.strOsType == "RedHat_64"
1361 || mUserData->s.strOsType == "Turbolinux"
1362 || mUserData->s.strOsType == "Turbolinux_64"
1363 || mUserData->s.strOsType == "Ubuntu"
1364 || mUserData->s.strOsType == "Ubuntu_64"
1365 || mUserData->s.strOsType == "Xandros"
1366 || mUserData->s.strOsType == "Xandros_64")
1367 {
1368 *aParavirtProvider = ParavirtProvider_KVM;
1369 }
1370 else
1371 *aParavirtProvider = ParavirtProvider_None;
1372 break;
1373 }
1374
1375 default: AssertFailedBreak(); /* Shut up MSC. */
1376 }
1377 break;
1378 }
1379 }
1380
1381 Assert( *aParavirtProvider == ParavirtProvider_None
1382 || *aParavirtProvider == ParavirtProvider_Minimal
1383 || *aParavirtProvider == ParavirtProvider_HyperV
1384 || *aParavirtProvider == ParavirtProvider_KVM);
1385 return S_OK;
1386}
1387
1388HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1389{
1390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1391
1392 aHardwareVersion = mHWData->mHWVersion;
1393
1394 return S_OK;
1395}
1396
1397HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1398{
1399 /* check known version */
1400 Utf8Str hwVersion = aHardwareVersion;
1401 if ( hwVersion.compare("1") != 0
1402 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1403 return setError(E_INVALIDARG,
1404 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1405
1406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1407
1408 HRESULT rc = i_checkStateDependency(MutableStateDep);
1409 if (FAILED(rc)) return rc;
1410
1411 i_setModified(IsModified_MachineData);
1412 mHWData.backup();
1413 mHWData->mHWVersion = aHardwareVersion;
1414
1415 return S_OK;
1416}
1417
1418HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1419{
1420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1421
1422 if (!mHWData->mHardwareUUID.isZero())
1423 aHardwareUUID = mHWData->mHardwareUUID;
1424 else
1425 aHardwareUUID = mData->mUuid;
1426
1427 return S_OK;
1428}
1429
1430HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1431{
1432 if (!aHardwareUUID.isValid())
1433 return E_INVALIDARG;
1434
1435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1436
1437 HRESULT rc = i_checkStateDependency(MutableStateDep);
1438 if (FAILED(rc)) return rc;
1439
1440 i_setModified(IsModified_MachineData);
1441 mHWData.backup();
1442 if (aHardwareUUID == mData->mUuid)
1443 mHWData->mHardwareUUID.clear();
1444 else
1445 mHWData->mHardwareUUID = aHardwareUUID;
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1451{
1452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 *aMemorySize = mHWData->mMemorySize;
1455
1456 return S_OK;
1457}
1458
1459HRESULT Machine::setMemorySize(ULONG aMemorySize)
1460{
1461 /* check RAM limits */
1462 if ( aMemorySize < MM_RAM_MIN_IN_MB
1463 || aMemorySize > MM_RAM_MAX_IN_MB
1464 )
1465 return setError(E_INVALIDARG,
1466 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1467 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1468
1469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1470
1471 HRESULT rc = i_checkStateDependency(MutableStateDep);
1472 if (FAILED(rc)) return rc;
1473
1474 i_setModified(IsModified_MachineData);
1475 mHWData.backup();
1476 mHWData->mMemorySize = aMemorySize;
1477
1478 return S_OK;
1479}
1480
1481HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1482{
1483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1484
1485 *aCPUCount = mHWData->mCPUCount;
1486
1487 return S_OK;
1488}
1489
1490HRESULT Machine::setCPUCount(ULONG aCPUCount)
1491{
1492 /* check CPU limits */
1493 if ( aCPUCount < SchemaDefs::MinCPUCount
1494 || aCPUCount > SchemaDefs::MaxCPUCount
1495 )
1496 return setError(E_INVALIDARG,
1497 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1498 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1499
1500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1501
1502 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1503 if (mHWData->mCPUHotPlugEnabled)
1504 {
1505 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1506 {
1507 if (mHWData->mCPUAttached[idx])
1508 return setError(E_INVALIDARG,
1509 tr("There is still a CPU attached to socket %lu."
1510 "Detach the CPU before removing the socket"),
1511 aCPUCount, idx+1);
1512 }
1513 }
1514
1515 HRESULT rc = i_checkStateDependency(MutableStateDep);
1516 if (FAILED(rc)) return rc;
1517
1518 i_setModified(IsModified_MachineData);
1519 mHWData.backup();
1520 mHWData->mCPUCount = aCPUCount;
1521
1522 return S_OK;
1523}
1524
1525HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1526{
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1530
1531 return S_OK;
1532}
1533
1534HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1535{
1536 HRESULT rc = S_OK;
1537
1538 /* check throttle limits */
1539 if ( aCPUExecutionCap < 1
1540 || aCPUExecutionCap > 100
1541 )
1542 return setError(E_INVALIDARG,
1543 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1544 aCPUExecutionCap, 1, 100);
1545
1546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1547
1548 alock.release();
1549 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1550 alock.acquire();
1551 if (FAILED(rc)) return rc;
1552
1553 i_setModified(IsModified_MachineData);
1554 mHWData.backup();
1555 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1556
1557 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1558 if (Global::IsOnline(mData->mMachineState))
1559 i_saveSettings(NULL);
1560
1561 return S_OK;
1562}
1563
1564HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1565{
1566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1567
1568 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1569
1570 return S_OK;
1571}
1572
1573HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1574{
1575 HRESULT rc = S_OK;
1576
1577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1578
1579 rc = i_checkStateDependency(MutableStateDep);
1580 if (FAILED(rc)) return rc;
1581
1582 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1583 {
1584 if (aCPUHotPlugEnabled)
1585 {
1586 i_setModified(IsModified_MachineData);
1587 mHWData.backup();
1588
1589 /* Add the amount of CPUs currently attached */
1590 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1591 mHWData->mCPUAttached[i] = true;
1592 }
1593 else
1594 {
1595 /*
1596 * We can disable hotplug only if the amount of maximum CPUs is equal
1597 * to the amount of attached CPUs
1598 */
1599 unsigned cCpusAttached = 0;
1600 unsigned iHighestId = 0;
1601
1602 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1603 {
1604 if (mHWData->mCPUAttached[i])
1605 {
1606 cCpusAttached++;
1607 iHighestId = i;
1608 }
1609 }
1610
1611 if ( (cCpusAttached != mHWData->mCPUCount)
1612 || (iHighestId >= mHWData->mCPUCount))
1613 return setError(E_INVALIDARG,
1614 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1615
1616 i_setModified(IsModified_MachineData);
1617 mHWData.backup();
1618 }
1619 }
1620
1621 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1622
1623 return rc;
1624}
1625
1626HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1627{
1628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1629
1630 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1631
1632 return S_OK;
1633}
1634
1635HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1636{
1637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1638
1639 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1640 if (SUCCEEDED(hrc))
1641 {
1642 i_setModified(IsModified_MachineData);
1643 mHWData.backup();
1644 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1645 }
1646 return hrc;
1647}
1648
1649HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1650{
1651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1652 aCPUProfile = mHWData->mCpuProfile;
1653 return S_OK;
1654}
1655
1656HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1657{
1658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1659 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1660 if (SUCCEEDED(hrc))
1661 {
1662 i_setModified(IsModified_MachineData);
1663 mHWData.backup();
1664 /* Empty equals 'host'. */
1665 if (aCPUProfile.isNotEmpty())
1666 mHWData->mCpuProfile = aCPUProfile;
1667 else
1668 mHWData->mCpuProfile = "host";
1669 }
1670 return hrc;
1671}
1672
1673HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1674{
1675#ifdef VBOX_WITH_USB_CARDREADER
1676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1677
1678 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1679
1680 return S_OK;
1681#else
1682 NOREF(aEmulatedUSBCardReaderEnabled);
1683 return E_NOTIMPL;
1684#endif
1685}
1686
1687HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1688{
1689#ifdef VBOX_WITH_USB_CARDREADER
1690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1691
1692 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1693 if (FAILED(rc)) return rc;
1694
1695 i_setModified(IsModified_MachineData);
1696 mHWData.backup();
1697 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1698
1699 return S_OK;
1700#else
1701 NOREF(aEmulatedUSBCardReaderEnabled);
1702 return E_NOTIMPL;
1703#endif
1704}
1705
1706HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1707{
1708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1709
1710 *aHPETEnabled = mHWData->mHPETEnabled;
1711
1712 return S_OK;
1713}
1714
1715HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1716{
1717 HRESULT rc = S_OK;
1718
1719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1720
1721 rc = i_checkStateDependency(MutableStateDep);
1722 if (FAILED(rc)) return rc;
1723
1724 i_setModified(IsModified_MachineData);
1725 mHWData.backup();
1726
1727 mHWData->mHPETEnabled = aHPETEnabled;
1728
1729 return rc;
1730}
1731
1732HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1733{
1734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1735
1736 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1737 return S_OK;
1738}
1739
1740HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1741{
1742 HRESULT rc = S_OK;
1743
1744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1745
1746 i_setModified(IsModified_MachineData);
1747 mHWData.backup();
1748 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1749
1750 alock.release();
1751 rc = i_onVideoCaptureChange();
1752 alock.acquire();
1753 if (FAILED(rc))
1754 {
1755 /*
1756 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1757 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1758 * determine if it should start or stop capturing. Therefore we need to manually
1759 * undo change.
1760 */
1761 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1762 return rc;
1763 }
1764
1765 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1766 if (Global::IsOnline(mData->mMachineState))
1767 i_saveSettings(NULL);
1768
1769 return rc;
1770}
1771
1772HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1773{
1774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1775 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1776 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1777 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1778 return S_OK;
1779}
1780
1781HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1782{
1783 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1784 bool fChanged = false;
1785
1786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1787
1788 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1789 {
1790 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1791 {
1792 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1793 fChanged = true;
1794 }
1795 }
1796 if (fChanged)
1797 {
1798 alock.release();
1799 HRESULT rc = i_onVideoCaptureChange();
1800 alock.acquire();
1801 if (FAILED(rc)) return rc;
1802 i_setModified(IsModified_MachineData);
1803
1804 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1805 if (Global::IsOnline(mData->mMachineState))
1806 i_saveSettings(NULL);
1807 }
1808
1809 return S_OK;
1810}
1811
1812HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1813{
1814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1815 if (mHWData->mVideoCaptureFile.isEmpty())
1816 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1817 else
1818 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1819 return S_OK;
1820}
1821
1822HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1823{
1824 Utf8Str strFile(aVideoCaptureFile);
1825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1826
1827 if ( Global::IsOnline(mData->mMachineState)
1828 && mHWData->mVideoCaptureEnabled)
1829 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1830
1831 if (!RTPathStartsWithRoot(strFile.c_str()))
1832 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1833
1834 if (!strFile.isEmpty())
1835 {
1836 Utf8Str defaultFile;
1837 i_getDefaultVideoCaptureFile(defaultFile);
1838 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1839 strFile.setNull();
1840 }
1841
1842 i_setModified(IsModified_MachineData);
1843 mHWData.backup();
1844 mHWData->mVideoCaptureFile = strFile;
1845
1846 return S_OK;
1847}
1848
1849HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1850{
1851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1852 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1853 return S_OK;
1854}
1855
1856HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1857{
1858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1859
1860 if ( Global::IsOnline(mData->mMachineState)
1861 && mHWData->mVideoCaptureEnabled)
1862 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1863
1864 i_setModified(IsModified_MachineData);
1865 mHWData.backup();
1866 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1867
1868 return S_OK;
1869}
1870
1871HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1872{
1873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1874 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1875 return S_OK;
1876}
1877
1878HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1879{
1880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1881
1882 if ( Global::IsOnline(mData->mMachineState)
1883 && mHWData->mVideoCaptureEnabled)
1884 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1885
1886 i_setModified(IsModified_MachineData);
1887 mHWData.backup();
1888 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1889
1890 return S_OK;
1891}
1892
1893HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1894{
1895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1896 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1897 return S_OK;
1898}
1899
1900HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1901{
1902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1903
1904 if ( Global::IsOnline(mData->mMachineState)
1905 && mHWData->mVideoCaptureEnabled)
1906 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1907
1908 i_setModified(IsModified_MachineData);
1909 mHWData.backup();
1910 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1911
1912 return S_OK;
1913}
1914
1915HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1916{
1917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1918 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1919 return S_OK;
1920}
1921
1922HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1923{
1924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1925
1926 if ( Global::IsOnline(mData->mMachineState)
1927 && mHWData->mVideoCaptureEnabled)
1928 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1929
1930 i_setModified(IsModified_MachineData);
1931 mHWData.backup();
1932 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1933
1934 return S_OK;
1935}
1936
1937HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1938{
1939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1940 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1941 return S_OK;
1942}
1943
1944HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1945{
1946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1947
1948 if ( Global::IsOnline(mData->mMachineState)
1949 && mHWData->mVideoCaptureEnabled)
1950 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1951
1952 i_setModified(IsModified_MachineData);
1953 mHWData.backup();
1954 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1955
1956 return S_OK;
1957}
1958
1959HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1960{
1961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1962 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1963 return S_OK;
1964}
1965
1966HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1967{
1968 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1969
1970 if ( Global::IsOnline(mData->mMachineState)
1971 && mHWData->mVideoCaptureEnabled)
1972 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1973
1974 i_setModified(IsModified_MachineData);
1975 mHWData.backup();
1976 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1977
1978 return S_OK;
1979}
1980
1981HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1982{
1983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1984
1985 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1986 return S_OK;
1987}
1988
1989HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1990{
1991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1992
1993 if ( Global::IsOnline(mData->mMachineState)
1994 && mHWData->mVideoCaptureEnabled)
1995 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1996
1997 i_setModified(IsModified_MachineData);
1998 mHWData.backup();
1999 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
2000
2001 return S_OK;
2002}
2003
2004HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
2005{
2006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2007
2008 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
2009
2010 return S_OK;
2011}
2012
2013HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2014{
2015 switch (aGraphicsControllerType)
2016 {
2017 case GraphicsControllerType_Null:
2018 case GraphicsControllerType_VBoxVGA:
2019#ifdef VBOX_WITH_VMSVGA
2020 case GraphicsControllerType_VMSVGA:
2021#endif
2022 break;
2023 default:
2024 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2025 }
2026
2027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2028
2029 HRESULT rc = i_checkStateDependency(MutableStateDep);
2030 if (FAILED(rc)) return rc;
2031
2032 i_setModified(IsModified_MachineData);
2033 mHWData.backup();
2034 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2035
2036 return S_OK;
2037}
2038
2039HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2040{
2041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2042
2043 *aVRAMSize = mHWData->mVRAMSize;
2044
2045 return S_OK;
2046}
2047
2048HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2049{
2050 /* check VRAM limits */
2051 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2052 return setError(E_INVALIDARG,
2053 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2054 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2055
2056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2057
2058 HRESULT rc = i_checkStateDependency(MutableStateDep);
2059 if (FAILED(rc)) return rc;
2060
2061 i_setModified(IsModified_MachineData);
2062 mHWData.backup();
2063 mHWData->mVRAMSize = aVRAMSize;
2064
2065 return S_OK;
2066}
2067
2068/** @todo this method should not be public */
2069HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2070{
2071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2072
2073 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2074
2075 return S_OK;
2076}
2077
2078/**
2079 * Set the memory balloon size.
2080 *
2081 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2082 * we have to make sure that we never call IGuest from here.
2083 */
2084HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2085{
2086 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2087#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2088 /* check limits */
2089 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2090 return setError(E_INVALIDARG,
2091 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2092 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2093
2094 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2095
2096 i_setModified(IsModified_MachineData);
2097 mHWData.backup();
2098 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2099
2100 return S_OK;
2101#else
2102 NOREF(aMemoryBalloonSize);
2103 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2104#endif
2105}
2106
2107HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2108{
2109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2110
2111 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2112 return S_OK;
2113}
2114
2115HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2116{
2117#ifdef VBOX_WITH_PAGE_SHARING
2118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2119
2120 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2121 i_setModified(IsModified_MachineData);
2122 mHWData.backup();
2123 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2124 return S_OK;
2125#else
2126 NOREF(aPageFusionEnabled);
2127 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2128#endif
2129}
2130
2131HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2132{
2133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2134
2135 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2136
2137 return S_OK;
2138}
2139
2140HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2141{
2142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2143
2144 HRESULT rc = i_checkStateDependency(MutableStateDep);
2145 if (FAILED(rc)) return rc;
2146
2147 /** @todo check validity! */
2148
2149 i_setModified(IsModified_MachineData);
2150 mHWData.backup();
2151 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2152
2153 return S_OK;
2154}
2155
2156
2157HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2158{
2159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2160
2161 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2162
2163 return S_OK;
2164}
2165
2166HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2167{
2168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2169
2170 HRESULT rc = i_checkStateDependency(MutableStateDep);
2171 if (FAILED(rc)) return rc;
2172
2173 /** @todo check validity! */
2174 i_setModified(IsModified_MachineData);
2175 mHWData.backup();
2176 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2177
2178 return S_OK;
2179}
2180
2181HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2182{
2183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2184
2185 *aMonitorCount = mHWData->mMonitorCount;
2186
2187 return S_OK;
2188}
2189
2190HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2191{
2192 /* make sure monitor count is a sensible number */
2193 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2194 return setError(E_INVALIDARG,
2195 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2196 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2197
2198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2199
2200 HRESULT rc = i_checkStateDependency(MutableStateDep);
2201 if (FAILED(rc)) return rc;
2202
2203 i_setModified(IsModified_MachineData);
2204 mHWData.backup();
2205 mHWData->mMonitorCount = aMonitorCount;
2206
2207 return S_OK;
2208}
2209
2210HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2211{
2212 /* mBIOSSettings is constant during life time, no need to lock */
2213 aBIOSSettings = mBIOSSettings;
2214
2215 return S_OK;
2216}
2217
2218HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2219{
2220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2221
2222 switch (aProperty)
2223 {
2224 case CPUPropertyType_PAE:
2225 *aValue = mHWData->mPAEEnabled;
2226 break;
2227
2228 case CPUPropertyType_LongMode:
2229 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2230 *aValue = TRUE;
2231 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2232 *aValue = FALSE;
2233#if HC_ARCH_BITS == 64
2234 else
2235 *aValue = TRUE;
2236#else
2237 else
2238 {
2239 *aValue = FALSE;
2240
2241 ComObjPtr<GuestOSType> pGuestOSType;
2242 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2243 pGuestOSType);
2244 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2245 {
2246 if (pGuestOSType->i_is64Bit())
2247 {
2248 ComObjPtr<Host> pHost = mParent->i_host();
2249 alock.release();
2250
2251 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2252 if (FAILED(hrc2))
2253 *aValue = FALSE;
2254 }
2255 }
2256 }
2257#endif
2258 break;
2259
2260 case CPUPropertyType_TripleFaultReset:
2261 *aValue = mHWData->mTripleFaultReset;
2262 break;
2263
2264 case CPUPropertyType_APIC:
2265 *aValue = mHWData->mAPIC;
2266 break;
2267
2268 case CPUPropertyType_X2APIC:
2269 *aValue = mHWData->mX2APIC;
2270 break;
2271
2272 case CPUPropertyType_IBPBOnVMExit:
2273 *aValue = mHWData->mIBPBOnVMExit;
2274 break;
2275
2276 case CPUPropertyType_IBPBOnVMEntry:
2277 *aValue = mHWData->mIBPBOnVMEntry;
2278 break;
2279
2280 case CPUPropertyType_SpecCtrl:
2281 *aValue = mHWData->mSpecCtrl;
2282 break;
2283
2284 case CPUPropertyType_SpecCtrlByHost:
2285 *aValue = mHWData->mSpecCtrlByHost;
2286 break;
2287
2288 case CPUPropertyType_HWVirt:
2289 *aValue = mHWData->mNestedHWVirt;
2290 break;
2291
2292 default:
2293 return E_INVALIDARG;
2294 }
2295 return S_OK;
2296}
2297
2298HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2299{
2300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2301
2302 HRESULT rc = i_checkStateDependency(MutableStateDep);
2303 if (FAILED(rc)) return rc;
2304
2305 switch (aProperty)
2306 {
2307 case CPUPropertyType_PAE:
2308 i_setModified(IsModified_MachineData);
2309 mHWData.backup();
2310 mHWData->mPAEEnabled = !!aValue;
2311 break;
2312
2313 case CPUPropertyType_LongMode:
2314 i_setModified(IsModified_MachineData);
2315 mHWData.backup();
2316 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2317 break;
2318
2319 case CPUPropertyType_TripleFaultReset:
2320 i_setModified(IsModified_MachineData);
2321 mHWData.backup();
2322 mHWData->mTripleFaultReset = !!aValue;
2323 break;
2324
2325 case CPUPropertyType_APIC:
2326 if (mHWData->mX2APIC)
2327 aValue = TRUE;
2328 i_setModified(IsModified_MachineData);
2329 mHWData.backup();
2330 mHWData->mAPIC = !!aValue;
2331 break;
2332
2333 case CPUPropertyType_X2APIC:
2334 i_setModified(IsModified_MachineData);
2335 mHWData.backup();
2336 mHWData->mX2APIC = !!aValue;
2337 if (aValue)
2338 mHWData->mAPIC = !!aValue;
2339 break;
2340
2341 case CPUPropertyType_IBPBOnVMExit:
2342 i_setModified(IsModified_MachineData);
2343 mHWData.backup();
2344 mHWData->mIBPBOnVMExit = !!aValue;
2345 break;
2346
2347 case CPUPropertyType_IBPBOnVMEntry:
2348 i_setModified(IsModified_MachineData);
2349 mHWData.backup();
2350 mHWData->mIBPBOnVMEntry = !!aValue;
2351 break;
2352
2353 case CPUPropertyType_SpecCtrl:
2354 i_setModified(IsModified_MachineData);
2355 mHWData.backup();
2356 mHWData->mSpecCtrl = !!aValue;
2357 break;
2358
2359 case CPUPropertyType_SpecCtrlByHost:
2360 i_setModified(IsModified_MachineData);
2361 mHWData.backup();
2362 mHWData->mSpecCtrlByHost = !!aValue;
2363 break;
2364
2365 case CPUPropertyType_HWVirt:
2366 i_setModified(IsModified_MachineData);
2367 mHWData.backup();
2368 mHWData->mNestedHWVirt = !!aValue;
2369 break;
2370
2371 default:
2372 return E_INVALIDARG;
2373 }
2374 return S_OK;
2375}
2376
2377HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2378 ULONG *aValEcx, ULONG *aValEdx)
2379{
2380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2381 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2382 {
2383 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2384 it != mHWData->mCpuIdLeafList.end();
2385 ++it)
2386 {
2387 if (aOrdinal == 0)
2388 {
2389 const settings::CpuIdLeaf &rLeaf= *it;
2390 *aIdx = rLeaf.idx;
2391 *aSubIdx = rLeaf.idxSub;
2392 *aValEax = rLeaf.uEax;
2393 *aValEbx = rLeaf.uEbx;
2394 *aValEcx = rLeaf.uEcx;
2395 *aValEdx = rLeaf.uEdx;
2396 return S_OK;
2397 }
2398 aOrdinal--;
2399 }
2400 }
2401 return E_INVALIDARG;
2402}
2403
2404HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2405{
2406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2407
2408 /*
2409 * Search the list.
2410 */
2411 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2412 {
2413 const settings::CpuIdLeaf &rLeaf= *it;
2414 if ( rLeaf.idx == aIdx
2415 && ( aSubIdx == UINT32_MAX
2416 || rLeaf.idxSub == aSubIdx) )
2417 {
2418 *aValEax = rLeaf.uEax;
2419 *aValEbx = rLeaf.uEbx;
2420 *aValEcx = rLeaf.uEcx;
2421 *aValEdx = rLeaf.uEdx;
2422 return S_OK;
2423 }
2424 }
2425
2426 return E_INVALIDARG;
2427}
2428
2429
2430HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2431{
2432 /*
2433 * Validate input before taking locks and checking state.
2434 */
2435 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2436 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2437 if ( aIdx >= UINT32_C(0x20)
2438 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2439 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2440 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2441
2442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2443 HRESULT rc = i_checkStateDependency(MutableStateDep);
2444 if (FAILED(rc)) return rc;
2445
2446 /*
2447 * Impose a maximum number of leaves.
2448 */
2449 if (mHWData->mCpuIdLeafList.size() > 256)
2450 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2451
2452 /*
2453 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2454 */
2455 i_setModified(IsModified_MachineData);
2456 mHWData.backup();
2457
2458 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2459 {
2460 settings::CpuIdLeaf &rLeaf= *it;
2461 if ( rLeaf.idx == aIdx
2462 && ( aSubIdx == UINT32_MAX
2463 || rLeaf.idxSub == aSubIdx) )
2464 it = mHWData->mCpuIdLeafList.erase(it);
2465 else
2466 ++it;
2467 }
2468
2469 settings::CpuIdLeaf NewLeaf;
2470 NewLeaf.idx = aIdx;
2471 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2472 NewLeaf.uEax = aValEax;
2473 NewLeaf.uEbx = aValEbx;
2474 NewLeaf.uEcx = aValEcx;
2475 NewLeaf.uEdx = aValEdx;
2476 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2477 return S_OK;
2478}
2479
2480HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2481{
2482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2483
2484 HRESULT rc = i_checkStateDependency(MutableStateDep);
2485 if (FAILED(rc)) return rc;
2486
2487 /*
2488 * Do the removal.
2489 */
2490 bool fModified = false;
2491 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2492 {
2493 settings::CpuIdLeaf &rLeaf= *it;
2494 if ( rLeaf.idx == aIdx
2495 && ( aSubIdx == UINT32_MAX
2496 || rLeaf.idxSub == aSubIdx) )
2497 {
2498 if (!fModified)
2499 {
2500 fModified = true;
2501 i_setModified(IsModified_MachineData);
2502 mHWData.backup();
2503 }
2504 it = mHWData->mCpuIdLeafList.erase(it);
2505 }
2506 else
2507 ++it;
2508 }
2509
2510 return S_OK;
2511}
2512
2513HRESULT Machine::removeAllCPUIDLeaves()
2514{
2515 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2516
2517 HRESULT rc = i_checkStateDependency(MutableStateDep);
2518 if (FAILED(rc)) return rc;
2519
2520 if (mHWData->mCpuIdLeafList.size() > 0)
2521 {
2522 i_setModified(IsModified_MachineData);
2523 mHWData.backup();
2524
2525 mHWData->mCpuIdLeafList.clear();
2526 }
2527
2528 return S_OK;
2529}
2530HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2531{
2532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2533
2534 switch(aProperty)
2535 {
2536 case HWVirtExPropertyType_Enabled:
2537 *aValue = mHWData->mHWVirtExEnabled;
2538 break;
2539
2540 case HWVirtExPropertyType_VPID:
2541 *aValue = mHWData->mHWVirtExVPIDEnabled;
2542 break;
2543
2544 case HWVirtExPropertyType_NestedPaging:
2545 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2546 break;
2547
2548 case HWVirtExPropertyType_UnrestrictedExecution:
2549 *aValue = mHWData->mHWVirtExUXEnabled;
2550 break;
2551
2552 case HWVirtExPropertyType_LargePages:
2553 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2554#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2555 *aValue = FALSE;
2556#endif
2557 break;
2558
2559 case HWVirtExPropertyType_Force:
2560 *aValue = mHWData->mHWVirtExForceEnabled;
2561 break;
2562
2563 case HWVirtExPropertyType_UseNativeApi:
2564 *aValue = mHWData->mHWVirtExUseNativeApi;
2565 break;
2566
2567 default:
2568 return E_INVALIDARG;
2569 }
2570 return S_OK;
2571}
2572
2573HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2574{
2575 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2576
2577 HRESULT rc = i_checkStateDependency(MutableStateDep);
2578 if (FAILED(rc)) return rc;
2579
2580 switch (aProperty)
2581 {
2582 case HWVirtExPropertyType_Enabled:
2583 i_setModified(IsModified_MachineData);
2584 mHWData.backup();
2585 mHWData->mHWVirtExEnabled = !!aValue;
2586 break;
2587
2588 case HWVirtExPropertyType_VPID:
2589 i_setModified(IsModified_MachineData);
2590 mHWData.backup();
2591 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2592 break;
2593
2594 case HWVirtExPropertyType_NestedPaging:
2595 i_setModified(IsModified_MachineData);
2596 mHWData.backup();
2597 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2598 break;
2599
2600 case HWVirtExPropertyType_UnrestrictedExecution:
2601 i_setModified(IsModified_MachineData);
2602 mHWData.backup();
2603 mHWData->mHWVirtExUXEnabled = !!aValue;
2604 break;
2605
2606 case HWVirtExPropertyType_LargePages:
2607 i_setModified(IsModified_MachineData);
2608 mHWData.backup();
2609 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2610 break;
2611
2612 case HWVirtExPropertyType_Force:
2613 i_setModified(IsModified_MachineData);
2614 mHWData.backup();
2615 mHWData->mHWVirtExForceEnabled = !!aValue;
2616 break;
2617
2618 case HWVirtExPropertyType_UseNativeApi:
2619 i_setModified(IsModified_MachineData);
2620 mHWData.backup();
2621 mHWData->mHWVirtExUseNativeApi = !!aValue;
2622 break;
2623
2624 default:
2625 return E_INVALIDARG;
2626 }
2627
2628 return S_OK;
2629}
2630
2631HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2632{
2633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2634
2635 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2636
2637 return S_OK;
2638}
2639
2640HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2641{
2642 /** @todo (r=dmik):
2643 * 1. Allow to change the name of the snapshot folder containing snapshots
2644 * 2. Rename the folder on disk instead of just changing the property
2645 * value (to be smart and not to leave garbage). Note that it cannot be
2646 * done here because the change may be rolled back. Thus, the right
2647 * place is #saveSettings().
2648 */
2649
2650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2651
2652 HRESULT rc = i_checkStateDependency(MutableStateDep);
2653 if (FAILED(rc)) return rc;
2654
2655 if (!mData->mCurrentSnapshot.isNull())
2656 return setError(E_FAIL,
2657 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2658
2659 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2660
2661 if (strSnapshotFolder.isEmpty())
2662 strSnapshotFolder = "Snapshots";
2663 int vrc = i_calculateFullPath(strSnapshotFolder,
2664 strSnapshotFolder);
2665 if (RT_FAILURE(vrc))
2666 return setErrorBoth(E_FAIL, vrc,
2667 tr("Invalid snapshot folder '%s' (%Rrc)"),
2668 strSnapshotFolder.c_str(), vrc);
2669
2670 i_setModified(IsModified_MachineData);
2671 mUserData.backup();
2672
2673 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2674
2675 return S_OK;
2676}
2677
2678HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2679{
2680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2681
2682 aMediumAttachments.resize(mMediumAttachments->size());
2683 size_t i = 0;
2684 for (MediumAttachmentList::const_iterator
2685 it = mMediumAttachments->begin();
2686 it != mMediumAttachments->end();
2687 ++it, ++i)
2688 aMediumAttachments[i] = *it;
2689
2690 return S_OK;
2691}
2692
2693HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2694{
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696
2697 Assert(!!mVRDEServer);
2698
2699 aVRDEServer = mVRDEServer;
2700
2701 return S_OK;
2702}
2703
2704HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2705{
2706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2707
2708 aAudioAdapter = mAudioAdapter;
2709
2710 return S_OK;
2711}
2712
2713HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2714{
2715#ifdef VBOX_WITH_VUSB
2716 clearError();
2717 MultiResult rc(S_OK);
2718
2719# ifdef VBOX_WITH_USB
2720 rc = mParent->i_host()->i_checkUSBProxyService();
2721 if (FAILED(rc)) return rc;
2722# endif
2723
2724 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2725
2726 aUSBControllers.resize(mUSBControllers->size());
2727 size_t i = 0;
2728 for (USBControllerList::const_iterator
2729 it = mUSBControllers->begin();
2730 it != mUSBControllers->end();
2731 ++it, ++i)
2732 aUSBControllers[i] = *it;
2733
2734 return S_OK;
2735#else
2736 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2737 * extended error info to indicate that USB is simply not available
2738 * (w/o treating it as a failure), for example, as in OSE */
2739 NOREF(aUSBControllers);
2740 ReturnComNotImplemented();
2741#endif /* VBOX_WITH_VUSB */
2742}
2743
2744HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2745{
2746#ifdef VBOX_WITH_VUSB
2747 clearError();
2748 MultiResult rc(S_OK);
2749
2750# ifdef VBOX_WITH_USB
2751 rc = mParent->i_host()->i_checkUSBProxyService();
2752 if (FAILED(rc)) return rc;
2753# endif
2754
2755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2756
2757 aUSBDeviceFilters = mUSBDeviceFilters;
2758 return rc;
2759#else
2760 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2761 * extended error info to indicate that USB is simply not available
2762 * (w/o treating it as a failure), for example, as in OSE */
2763 NOREF(aUSBDeviceFilters);
2764 ReturnComNotImplemented();
2765#endif /* VBOX_WITH_VUSB */
2766}
2767
2768HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2769{
2770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2771
2772 aSettingsFilePath = mData->m_strConfigFileFull;
2773
2774 return S_OK;
2775}
2776
2777HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2778{
2779 RT_NOREF(aSettingsFilePath);
2780 ReturnComNotImplemented();
2781}
2782
2783HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2784{
2785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2786
2787 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2788 if (FAILED(rc)) return rc;
2789
2790 if (!mData->pMachineConfigFile->fileExists())
2791 // this is a new machine, and no config file exists yet:
2792 *aSettingsModified = TRUE;
2793 else
2794 *aSettingsModified = (mData->flModifications != 0);
2795
2796 return S_OK;
2797}
2798
2799HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2800{
2801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2802
2803 *aSessionState = mData->mSession.mState;
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2809{
2810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 aSessionName = mData->mSession.mName;
2813
2814 return S_OK;
2815}
2816
2817HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2818{
2819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 *aSessionPID = mData->mSession.mPID;
2822
2823 return S_OK;
2824}
2825
2826HRESULT Machine::getState(MachineState_T *aState)
2827{
2828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2829
2830 *aState = mData->mMachineState;
2831 Assert(mData->mMachineState != MachineState_Null);
2832
2833 return S_OK;
2834}
2835
2836HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2837{
2838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2839
2840 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2841
2842 return S_OK;
2843}
2844
2845HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2846{
2847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2848
2849 aStateFilePath = mSSData->strStateFilePath;
2850
2851 return S_OK;
2852}
2853
2854HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2855{
2856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2857
2858 i_getLogFolder(aLogFolder);
2859
2860 return S_OK;
2861}
2862
2863HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2864{
2865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2866
2867 aCurrentSnapshot = mData->mCurrentSnapshot;
2868
2869 return S_OK;
2870}
2871
2872HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2873{
2874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2875
2876 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2877 ? 0
2878 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2879
2880 return S_OK;
2881}
2882
2883HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2884{
2885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2886
2887 /* Note: for machines with no snapshots, we always return FALSE
2888 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2889 * reasons :) */
2890
2891 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2892 ? FALSE
2893 : mData->mCurrentStateModified;
2894
2895 return S_OK;
2896}
2897
2898HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2899{
2900 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2901
2902 aSharedFolders.resize(mHWData->mSharedFolders.size());
2903 size_t i = 0;
2904 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2905 it = mHWData->mSharedFolders.begin();
2906 it != mHWData->mSharedFolders.end();
2907 ++it, ++i)
2908 aSharedFolders[i] = *it;
2909
2910 return S_OK;
2911}
2912
2913HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2914{
2915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2916
2917 *aClipboardMode = mHWData->mClipboardMode;
2918
2919 return S_OK;
2920}
2921
2922HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2923{
2924 HRESULT rc = S_OK;
2925
2926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2927
2928 alock.release();
2929 rc = i_onClipboardModeChange(aClipboardMode);
2930 alock.acquire();
2931 if (FAILED(rc)) return rc;
2932
2933 i_setModified(IsModified_MachineData);
2934 mHWData.backup();
2935 mHWData->mClipboardMode = aClipboardMode;
2936
2937 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2938 if (Global::IsOnline(mData->mMachineState))
2939 i_saveSettings(NULL);
2940
2941 return S_OK;
2942}
2943
2944HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2945{
2946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2947
2948 *aDnDMode = mHWData->mDnDMode;
2949
2950 return S_OK;
2951}
2952
2953HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2954{
2955 HRESULT rc = S_OK;
2956
2957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2958
2959 alock.release();
2960 rc = i_onDnDModeChange(aDnDMode);
2961
2962 alock.acquire();
2963 if (FAILED(rc)) return rc;
2964
2965 i_setModified(IsModified_MachineData);
2966 mHWData.backup();
2967 mHWData->mDnDMode = aDnDMode;
2968
2969 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2970 if (Global::IsOnline(mData->mMachineState))
2971 i_saveSettings(NULL);
2972
2973 return S_OK;
2974}
2975
2976HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2977{
2978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2979
2980 aStorageControllers.resize(mStorageControllers->size());
2981 size_t i = 0;
2982 for (StorageControllerList::const_iterator
2983 it = mStorageControllers->begin();
2984 it != mStorageControllers->end();
2985 ++it, ++i)
2986 aStorageControllers[i] = *it;
2987
2988 return S_OK;
2989}
2990
2991HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2992{
2993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2994
2995 *aEnabled = mUserData->s.fTeleporterEnabled;
2996
2997 return S_OK;
2998}
2999
3000HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3001{
3002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3003
3004 /* Only allow it to be set to true when PoweredOff or Aborted.
3005 (Clearing it is always permitted.) */
3006 if ( aTeleporterEnabled
3007 && mData->mRegistered
3008 && ( !i_isSessionMachine()
3009 || ( mData->mMachineState != MachineState_PoweredOff
3010 && mData->mMachineState != MachineState_Teleported
3011 && mData->mMachineState != MachineState_Aborted
3012 )
3013 )
3014 )
3015 return setError(VBOX_E_INVALID_VM_STATE,
3016 tr("The machine is not powered off (state is %s)"),
3017 Global::stringifyMachineState(mData->mMachineState));
3018
3019 i_setModified(IsModified_MachineData);
3020 mUserData.backup();
3021 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3022
3023 return S_OK;
3024}
3025
3026HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3027{
3028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3029
3030 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3031
3032 return S_OK;
3033}
3034
3035HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3036{
3037 if (aTeleporterPort >= _64K)
3038 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3039
3040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3041
3042 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3043 if (FAILED(rc)) return rc;
3044
3045 i_setModified(IsModified_MachineData);
3046 mUserData.backup();
3047 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3048
3049 return S_OK;
3050}
3051
3052HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3053{
3054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3055
3056 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3057
3058 return S_OK;
3059}
3060
3061HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3062{
3063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3064
3065 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3066 if (FAILED(rc)) return rc;
3067
3068 i_setModified(IsModified_MachineData);
3069 mUserData.backup();
3070 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3071
3072 return S_OK;
3073}
3074
3075HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3076{
3077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3078 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3079
3080 return S_OK;
3081}
3082
3083HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3084{
3085 /*
3086 * Hash the password first.
3087 */
3088 com::Utf8Str aT = aTeleporterPassword;
3089
3090 if (!aT.isEmpty())
3091 {
3092 if (VBoxIsPasswordHashed(&aT))
3093 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3094 VBoxHashPassword(&aT);
3095 }
3096
3097 /*
3098 * Do the update.
3099 */
3100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3101 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3102 if (SUCCEEDED(hrc))
3103 {
3104 i_setModified(IsModified_MachineData);
3105 mUserData.backup();
3106 mUserData->s.strTeleporterPassword = aT;
3107 }
3108
3109 return hrc;
3110}
3111
3112HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3113{
3114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3115
3116 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3117 return S_OK;
3118}
3119
3120HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3121{
3122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3123
3124 /** @todo deal with running state change. */
3125 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3126 if (FAILED(rc)) return rc;
3127
3128 i_setModified(IsModified_MachineData);
3129 mUserData.backup();
3130 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3131 return S_OK;
3132}
3133
3134HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3135{
3136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3137
3138 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3139 return S_OK;
3140}
3141
3142HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3143{
3144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3145
3146 /** @todo deal with running state change. */
3147 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3148 if (FAILED(rc)) return rc;
3149
3150 i_setModified(IsModified_MachineData);
3151 mUserData.backup();
3152 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3153 return S_OK;
3154}
3155
3156HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3157{
3158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3159
3160 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3161 return S_OK;
3162}
3163
3164HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3165{
3166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3167
3168 /** @todo deal with running state change. */
3169 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3170 if (FAILED(rc)) return rc;
3171
3172 i_setModified(IsModified_MachineData);
3173 mUserData.backup();
3174 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3175 return S_OK;
3176}
3177
3178HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3179{
3180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3183
3184 return S_OK;
3185}
3186
3187HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3188{
3189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3190
3191 /** @todo deal with running state change. */
3192 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3193 if (FAILED(rc)) return rc;
3194
3195 i_setModified(IsModified_MachineData);
3196 mUserData.backup();
3197 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3198
3199 return S_OK;
3200}
3201
3202HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3203{
3204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3205
3206 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3207 return S_OK;
3208}
3209
3210HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3211{
3212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3213
3214 /** @todo deal with running state change. */
3215 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3216 if (FAILED(rc)) return rc;
3217
3218 i_setModified(IsModified_MachineData);
3219 mUserData.backup();
3220 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3221 return S_OK;
3222}
3223
3224HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3225{
3226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3227
3228 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3229
3230 return S_OK;
3231}
3232
3233HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3234{
3235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3236
3237 /* Only allow it to be set to true when PoweredOff or Aborted.
3238 (Clearing it is always permitted.) */
3239 if ( aRTCUseUTC
3240 && mData->mRegistered
3241 && ( !i_isSessionMachine()
3242 || ( mData->mMachineState != MachineState_PoweredOff
3243 && mData->mMachineState != MachineState_Teleported
3244 && mData->mMachineState != MachineState_Aborted
3245 )
3246 )
3247 )
3248 return setError(VBOX_E_INVALID_VM_STATE,
3249 tr("The machine is not powered off (state is %s)"),
3250 Global::stringifyMachineState(mData->mMachineState));
3251
3252 i_setModified(IsModified_MachineData);
3253 mUserData.backup();
3254 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3255
3256 return S_OK;
3257}
3258
3259HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3260{
3261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3262
3263 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3264
3265 return S_OK;
3266}
3267
3268HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3269{
3270 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3271
3272 HRESULT rc = i_checkStateDependency(MutableStateDep);
3273 if (FAILED(rc)) return rc;
3274
3275 i_setModified(IsModified_MachineData);
3276 mHWData.backup();
3277 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3278
3279 return S_OK;
3280}
3281
3282HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3283{
3284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3285
3286 *aIOCacheSize = mHWData->mIOCacheSize;
3287
3288 return S_OK;
3289}
3290
3291HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3292{
3293 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3294
3295 HRESULT rc = i_checkStateDependency(MutableStateDep);
3296 if (FAILED(rc)) return rc;
3297
3298 i_setModified(IsModified_MachineData);
3299 mHWData.backup();
3300 mHWData->mIOCacheSize = aIOCacheSize;
3301
3302 return S_OK;
3303}
3304
3305
3306/**
3307 * @note Locks objects!
3308 */
3309HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3310 LockType_T aLockType)
3311{
3312 /* check the session state */
3313 SessionState_T state;
3314 HRESULT rc = aSession->COMGETTER(State)(&state);
3315 if (FAILED(rc)) return rc;
3316
3317 if (state != SessionState_Unlocked)
3318 return setError(VBOX_E_INVALID_OBJECT_STATE,
3319 tr("The given session is busy"));
3320
3321 // get the client's IInternalSessionControl interface
3322 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3323 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3324 E_INVALIDARG);
3325
3326 // session name (only used in some code paths)
3327 Utf8Str strSessionName;
3328
3329 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3330
3331 if (!mData->mRegistered)
3332 return setError(E_UNEXPECTED,
3333 tr("The machine '%s' is not registered"),
3334 mUserData->s.strName.c_str());
3335
3336 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3337
3338 SessionState_T oldState = mData->mSession.mState;
3339 /* Hack: in case the session is closing and there is a progress object
3340 * which allows waiting for the session to be closed, take the opportunity
3341 * and do a limited wait (max. 1 second). This helps a lot when the system
3342 * is busy and thus session closing can take a little while. */
3343 if ( mData->mSession.mState == SessionState_Unlocking
3344 && mData->mSession.mProgress)
3345 {
3346 alock.release();
3347 mData->mSession.mProgress->WaitForCompletion(1000);
3348 alock.acquire();
3349 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3350 }
3351
3352 // try again now
3353 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3354 // (i.e. session machine exists)
3355 && (aLockType == LockType_Shared) // caller wants a shared link to the
3356 // existing session that holds the write lock:
3357 )
3358 {
3359 // OK, share the session... we are now dealing with three processes:
3360 // 1) VBoxSVC (where this code runs);
3361 // 2) process C: the caller's client process (who wants a shared session);
3362 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3363
3364 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3365 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3366 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3367 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3368 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3369
3370 /*
3371 * Release the lock before calling the client process. It's safe here
3372 * since the only thing to do after we get the lock again is to add
3373 * the remote control to the list (which doesn't directly influence
3374 * anything).
3375 */
3376 alock.release();
3377
3378 // get the console of the session holding the write lock (this is a remote call)
3379 ComPtr<IConsole> pConsoleW;
3380 if (mData->mSession.mLockType == LockType_VM)
3381 {
3382 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3383 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3384 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3385 if (FAILED(rc))
3386 // the failure may occur w/o any error info (from RPC), so provide one
3387 return setError(VBOX_E_VM_ERROR,
3388 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3389 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3390 }
3391
3392 // share the session machine and W's console with the caller's session
3393 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3394 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3395 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3396
3397 if (FAILED(rc))
3398 // the failure may occur w/o any error info (from RPC), so provide one
3399 return setError(VBOX_E_VM_ERROR,
3400 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3401 alock.acquire();
3402
3403 // need to revalidate the state after acquiring the lock again
3404 if (mData->mSession.mState != SessionState_Locked)
3405 {
3406 pSessionControl->Uninitialize();
3407 return setError(VBOX_E_INVALID_SESSION_STATE,
3408 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3409 mUserData->s.strName.c_str());
3410 }
3411
3412 // add the caller's session to the list
3413 mData->mSession.mRemoteControls.push_back(pSessionControl);
3414 }
3415 else if ( mData->mSession.mState == SessionState_Locked
3416 || mData->mSession.mState == SessionState_Unlocking
3417 )
3418 {
3419 // sharing not permitted, or machine still unlocking:
3420 return setError(VBOX_E_INVALID_OBJECT_STATE,
3421 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3422 mUserData->s.strName.c_str());
3423 }
3424 else
3425 {
3426 // machine is not locked: then write-lock the machine (create the session machine)
3427
3428 // must not be busy
3429 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3430
3431 // get the caller's session PID
3432 RTPROCESS pid = NIL_RTPROCESS;
3433 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3434 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3435 Assert(pid != NIL_RTPROCESS);
3436
3437 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3438
3439 if (fLaunchingVMProcess)
3440 {
3441 if (mData->mSession.mPID == NIL_RTPROCESS)
3442 {
3443 // two or more clients racing for a lock, the one which set the
3444 // session state to Spawning will win, the others will get an
3445 // error as we can't decide here if waiting a little would help
3446 // (only for shared locks this would avoid an error)
3447 return setError(VBOX_E_INVALID_OBJECT_STATE,
3448 tr("The machine '%s' already has a lock request pending"),
3449 mUserData->s.strName.c_str());
3450 }
3451
3452 // this machine is awaiting for a spawning session to be opened:
3453 // then the calling process must be the one that got started by
3454 // LaunchVMProcess()
3455
3456 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3457 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3458
3459#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3460 /* Hardened windows builds spawns three processes when a VM is
3461 launched, the 3rd one is the one that will end up here. */
3462 RTPROCESS ppid;
3463 int rc = RTProcQueryParent(pid, &ppid);
3464 if (RT_SUCCESS(rc))
3465 rc = RTProcQueryParent(ppid, &ppid);
3466 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3467 || rc == VERR_ACCESS_DENIED)
3468 {
3469 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3470 mData->mSession.mPID = pid;
3471 }
3472#endif
3473
3474 if (mData->mSession.mPID != pid)
3475 return setError(E_ACCESSDENIED,
3476 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3477 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3478 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3479 }
3480
3481 // create the mutable SessionMachine from the current machine
3482 ComObjPtr<SessionMachine> sessionMachine;
3483 sessionMachine.createObject();
3484 rc = sessionMachine->init(this);
3485 AssertComRC(rc);
3486
3487 /* NOTE: doing return from this function after this point but
3488 * before the end is forbidden since it may call SessionMachine::uninit()
3489 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3490 * lock while still holding the Machine lock in alock so that a deadlock
3491 * is possible due to the wrong lock order. */
3492
3493 if (SUCCEEDED(rc))
3494 {
3495 /*
3496 * Set the session state to Spawning to protect against subsequent
3497 * attempts to open a session and to unregister the machine after
3498 * we release the lock.
3499 */
3500 SessionState_T origState = mData->mSession.mState;
3501 mData->mSession.mState = SessionState_Spawning;
3502
3503#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3504 /* Get the client token ID to be passed to the client process */
3505 Utf8Str strTokenId;
3506 sessionMachine->i_getTokenId(strTokenId);
3507 Assert(!strTokenId.isEmpty());
3508#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3509 /* Get the client token to be passed to the client process */
3510 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3511 /* The token is now "owned" by pToken, fix refcount */
3512 if (!pToken.isNull())
3513 pToken->Release();
3514#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3515
3516 /*
3517 * Release the lock before calling the client process -- it will call
3518 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3519 * because the state is Spawning, so that LaunchVMProcess() and
3520 * LockMachine() calls will fail. This method, called before we
3521 * acquire the lock again, will fail because of the wrong PID.
3522 *
3523 * Note that mData->mSession.mRemoteControls accessed outside
3524 * the lock may not be modified when state is Spawning, so it's safe.
3525 */
3526 alock.release();
3527
3528 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3529#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3530 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3531#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3532 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3533 /* Now the token is owned by the client process. */
3534 pToken.setNull();
3535#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3536 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3537
3538 /* The failure may occur w/o any error info (from RPC), so provide one */
3539 if (FAILED(rc))
3540 setError(VBOX_E_VM_ERROR,
3541 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3542
3543 // get session name, either to remember or to compare against
3544 // the already known session name.
3545 {
3546 Bstr bstrSessionName;
3547 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3548 if (SUCCEEDED(rc2))
3549 strSessionName = bstrSessionName;
3550 }
3551
3552 if ( SUCCEEDED(rc)
3553 && fLaunchingVMProcess
3554 )
3555 {
3556 /* complete the remote session initialization */
3557
3558 /* get the console from the direct session */
3559 ComPtr<IConsole> console;
3560 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3561 ComAssertComRC(rc);
3562
3563 if (SUCCEEDED(rc) && !console)
3564 {
3565 ComAssert(!!console);
3566 rc = E_FAIL;
3567 }
3568
3569 /* assign machine & console to the remote session */
3570 if (SUCCEEDED(rc))
3571 {
3572 /*
3573 * after LaunchVMProcess(), the first and the only
3574 * entry in remoteControls is that remote session
3575 */
3576 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3577 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3578 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3579
3580 /* The failure may occur w/o any error info (from RPC), so provide one */
3581 if (FAILED(rc))
3582 setError(VBOX_E_VM_ERROR,
3583 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3584 }
3585
3586 if (FAILED(rc))
3587 pSessionControl->Uninitialize();
3588 }
3589
3590 /* acquire the lock again */
3591 alock.acquire();
3592
3593 /* Restore the session state */
3594 mData->mSession.mState = origState;
3595 }
3596
3597 // finalize spawning anyway (this is why we don't return on errors above)
3598 if (fLaunchingVMProcess)
3599 {
3600 Assert(mData->mSession.mName == strSessionName);
3601 /* Note that the progress object is finalized later */
3602 /** @todo Consider checking mData->mSession.mProgress for cancellation
3603 * around here. */
3604
3605 /* We don't reset mSession.mPID here because it is necessary for
3606 * SessionMachine::uninit() to reap the child process later. */
3607
3608 if (FAILED(rc))
3609 {
3610 /* Close the remote session, remove the remote control from the list
3611 * and reset session state to Closed (@note keep the code in sync
3612 * with the relevant part in checkForSpawnFailure()). */
3613
3614 Assert(mData->mSession.mRemoteControls.size() == 1);
3615 if (mData->mSession.mRemoteControls.size() == 1)
3616 {
3617 ErrorInfoKeeper eik;
3618 mData->mSession.mRemoteControls.front()->Uninitialize();
3619 }
3620
3621 mData->mSession.mRemoteControls.clear();
3622 mData->mSession.mState = SessionState_Unlocked;
3623 }
3624 }
3625 else
3626 {
3627 /* memorize PID of the directly opened session */
3628 if (SUCCEEDED(rc))
3629 mData->mSession.mPID = pid;
3630 }
3631
3632 if (SUCCEEDED(rc))
3633 {
3634 mData->mSession.mLockType = aLockType;
3635 /* memorize the direct session control and cache IUnknown for it */
3636 mData->mSession.mDirectControl = pSessionControl;
3637 mData->mSession.mState = SessionState_Locked;
3638 if (!fLaunchingVMProcess)
3639 mData->mSession.mName = strSessionName;
3640 /* associate the SessionMachine with this Machine */
3641 mData->mSession.mMachine = sessionMachine;
3642
3643 /* request an IUnknown pointer early from the remote party for later
3644 * identity checks (it will be internally cached within mDirectControl
3645 * at least on XPCOM) */
3646 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3647 NOREF(unk);
3648 }
3649
3650 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3651 * would break the lock order */
3652 alock.release();
3653
3654 /* uninitialize the created session machine on failure */
3655 if (FAILED(rc))
3656 sessionMachine->uninit();
3657 }
3658
3659 if (SUCCEEDED(rc))
3660 {
3661 /*
3662 * tell the client watcher thread to update the set of
3663 * machines that have open sessions
3664 */
3665 mParent->i_updateClientWatcher();
3666
3667 if (oldState != SessionState_Locked)
3668 /* fire an event */
3669 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3670 }
3671
3672 return rc;
3673}
3674
3675/**
3676 * @note Locks objects!
3677 */
3678HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3679 const com::Utf8Str &aName,
3680 const com::Utf8Str &aEnvironment,
3681 ComPtr<IProgress> &aProgress)
3682{
3683 Utf8Str strFrontend(aName);
3684 /* "emergencystop" doesn't need the session, so skip the checks/interface
3685 * retrieval. This code doesn't quite fit in here, but introducing a
3686 * special API method would be even more effort, and would require explicit
3687 * support by every API client. It's better to hide the feature a bit. */
3688 if (strFrontend != "emergencystop")
3689 CheckComArgNotNull(aSession);
3690
3691 HRESULT rc = S_OK;
3692 if (strFrontend.isEmpty())
3693 {
3694 Bstr bstrFrontend;
3695 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3696 if (FAILED(rc))
3697 return rc;
3698 strFrontend = bstrFrontend;
3699 if (strFrontend.isEmpty())
3700 {
3701 ComPtr<ISystemProperties> systemProperties;
3702 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3703 if (FAILED(rc))
3704 return rc;
3705 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3706 if (FAILED(rc))
3707 return rc;
3708 strFrontend = bstrFrontend;
3709 }
3710 /* paranoia - emergencystop is not a valid default */
3711 if (strFrontend == "emergencystop")
3712 strFrontend = Utf8Str::Empty;
3713 }
3714 /* default frontend: Qt GUI */
3715 if (strFrontend.isEmpty())
3716 strFrontend = "GUI/Qt";
3717
3718 if (strFrontend != "emergencystop")
3719 {
3720 /* check the session state */
3721 SessionState_T state;
3722 rc = aSession->COMGETTER(State)(&state);
3723 if (FAILED(rc))
3724 return rc;
3725
3726 if (state != SessionState_Unlocked)
3727 return setError(VBOX_E_INVALID_OBJECT_STATE,
3728 tr("The given session is busy"));
3729
3730 /* get the IInternalSessionControl interface */
3731 ComPtr<IInternalSessionControl> control(aSession);
3732 ComAssertMsgRet(!control.isNull(),
3733 ("No IInternalSessionControl interface"),
3734 E_INVALIDARG);
3735
3736 /* get the teleporter enable state for the progress object init. */
3737 BOOL fTeleporterEnabled;
3738 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3739 if (FAILED(rc))
3740 return rc;
3741
3742 /* create a progress object */
3743 ComObjPtr<ProgressProxy> progress;
3744 progress.createObject();
3745 rc = progress->init(mParent,
3746 static_cast<IMachine*>(this),
3747 Bstr(tr("Starting VM")).raw(),
3748 TRUE /* aCancelable */,
3749 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3750 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3751 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3752 2 /* uFirstOperationWeight */,
3753 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3754
3755 if (SUCCEEDED(rc))
3756 {
3757 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3758 if (SUCCEEDED(rc))
3759 {
3760 aProgress = progress;
3761
3762 /* signal the client watcher thread */
3763 mParent->i_updateClientWatcher();
3764
3765 /* fire an event */
3766 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3767 }
3768 }
3769 }
3770 else
3771 {
3772 /* no progress object - either instant success or failure */
3773 aProgress = NULL;
3774
3775 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3776
3777 if (mData->mSession.mState != SessionState_Locked)
3778 return setError(VBOX_E_INVALID_OBJECT_STATE,
3779 tr("The machine '%s' is not locked by a session"),
3780 mUserData->s.strName.c_str());
3781
3782 /* must have a VM process associated - do not kill normal API clients
3783 * with an open session */
3784 if (!Global::IsOnline(mData->mMachineState))
3785 return setError(VBOX_E_INVALID_OBJECT_STATE,
3786 tr("The machine '%s' does not have a VM process"),
3787 mUserData->s.strName.c_str());
3788
3789 /* forcibly terminate the VM process */
3790 if (mData->mSession.mPID != NIL_RTPROCESS)
3791 RTProcTerminate(mData->mSession.mPID);
3792
3793 /* signal the client watcher thread, as most likely the client has
3794 * been terminated */
3795 mParent->i_updateClientWatcher();
3796 }
3797
3798 return rc;
3799}
3800
3801HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3802{
3803 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3804 return setError(E_INVALIDARG,
3805 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3806 aPosition, SchemaDefs::MaxBootPosition);
3807
3808 if (aDevice == DeviceType_USB)
3809 return setError(E_NOTIMPL,
3810 tr("Booting from USB device is currently not supported"));
3811
3812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3813
3814 HRESULT rc = i_checkStateDependency(MutableStateDep);
3815 if (FAILED(rc)) return rc;
3816
3817 i_setModified(IsModified_MachineData);
3818 mHWData.backup();
3819 mHWData->mBootOrder[aPosition - 1] = aDevice;
3820
3821 return S_OK;
3822}
3823
3824HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3825{
3826 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3827 return setError(E_INVALIDARG,
3828 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3829 aPosition, SchemaDefs::MaxBootPosition);
3830
3831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3832
3833 *aDevice = mHWData->mBootOrder[aPosition - 1];
3834
3835 return S_OK;
3836}
3837
3838HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3839 LONG aControllerPort,
3840 LONG aDevice,
3841 DeviceType_T aType,
3842 const ComPtr<IMedium> &aMedium)
3843{
3844 IMedium *aM = aMedium;
3845 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3846 aName.c_str(), aControllerPort, aDevice, aType, aM));
3847
3848 // request the host lock first, since might be calling Host methods for getting host drives;
3849 // next, protect the media tree all the while we're in here, as well as our member variables
3850 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3851 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3852
3853 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3854 if (FAILED(rc)) return rc;
3855
3856 /// @todo NEWMEDIA implicit machine registration
3857 if (!mData->mRegistered)
3858 return setError(VBOX_E_INVALID_OBJECT_STATE,
3859 tr("Cannot attach storage devices to an unregistered machine"));
3860
3861 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3862
3863 /* Check for an existing controller. */
3864 ComObjPtr<StorageController> ctl;
3865 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3866 if (FAILED(rc)) return rc;
3867
3868 StorageControllerType_T ctrlType;
3869 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3870 if (FAILED(rc))
3871 return setError(E_FAIL,
3872 tr("Could not get type of controller '%s'"),
3873 aName.c_str());
3874
3875 bool fSilent = false;
3876 Utf8Str strReconfig;
3877
3878 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3879 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3880 if ( mData->mMachineState == MachineState_Paused
3881 && strReconfig == "1")
3882 fSilent = true;
3883
3884 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3885 bool fHotplug = false;
3886 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3887 fHotplug = true;
3888
3889 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3890 return setError(VBOX_E_INVALID_VM_STATE,
3891 tr("Controller '%s' does not support hotplugging"),
3892 aName.c_str());
3893
3894 // check that the port and device are not out of range
3895 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3896 if (FAILED(rc)) return rc;
3897
3898 /* check if the device slot is already busy */
3899 MediumAttachment *pAttachTemp;
3900 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3901 aName,
3902 aControllerPort,
3903 aDevice)))
3904 {
3905 Medium *pMedium = pAttachTemp->i_getMedium();
3906 if (pMedium)
3907 {
3908 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3909 return setError(VBOX_E_OBJECT_IN_USE,
3910 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3911 pMedium->i_getLocationFull().c_str(),
3912 aControllerPort,
3913 aDevice,
3914 aName.c_str());
3915 }
3916 else
3917 return setError(VBOX_E_OBJECT_IN_USE,
3918 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3919 aControllerPort, aDevice, aName.c_str());
3920 }
3921
3922 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3923 if (aMedium && medium.isNull())
3924 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3925
3926 AutoCaller mediumCaller(medium);
3927 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3928
3929 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3930
3931 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3932 && !medium.isNull()
3933 )
3934 return setError(VBOX_E_OBJECT_IN_USE,
3935 tr("Medium '%s' is already attached to this virtual machine"),
3936 medium->i_getLocationFull().c_str());
3937
3938 if (!medium.isNull())
3939 {
3940 MediumType_T mtype = medium->i_getType();
3941 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3942 // For DVDs it's not written to the config file, so needs no global config
3943 // version bump. For floppies it's a new attribute "type", which is ignored
3944 // by older VirtualBox version, so needs no global config version bump either.
3945 // For hard disks this type is not accepted.
3946 if (mtype == MediumType_MultiAttach)
3947 {
3948 // This type is new with VirtualBox 4.0 and therefore requires settings
3949 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3950 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3951 // two reasons: The medium type is a property of the media registry tree, which
3952 // can reside in the global config file (for pre-4.0 media); we would therefore
3953 // possibly need to bump the global config version. We don't want to do that though
3954 // because that might make downgrading to pre-4.0 impossible.
3955 // As a result, we can only use these two new types if the medium is NOT in the
3956 // global registry:
3957 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3958 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3959 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3960 )
3961 return setError(VBOX_E_INVALID_OBJECT_STATE,
3962 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3963 "to machines that were created with VirtualBox 4.0 or later"),
3964 medium->i_getLocationFull().c_str());
3965 }
3966 }
3967
3968 bool fIndirect = false;
3969 if (!medium.isNull())
3970 fIndirect = medium->i_isReadOnly();
3971 bool associate = true;
3972
3973 do
3974 {
3975 if ( aType == DeviceType_HardDisk
3976 && mMediumAttachments.isBackedUp())
3977 {
3978 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3979
3980 /* check if the medium was attached to the VM before we started
3981 * changing attachments in which case the attachment just needs to
3982 * be restored */
3983 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3984 {
3985 AssertReturn(!fIndirect, E_FAIL);
3986
3987 /* see if it's the same bus/channel/device */
3988 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3989 {
3990 /* the simplest case: restore the whole attachment
3991 * and return, nothing else to do */
3992 mMediumAttachments->push_back(pAttachTemp);
3993
3994 /* Reattach the medium to the VM. */
3995 if (fHotplug || fSilent)
3996 {
3997 mediumLock.release();
3998 treeLock.release();
3999 alock.release();
4000
4001 MediumLockList *pMediumLockList(new MediumLockList());
4002
4003 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4004 medium /* pToLockWrite */,
4005 false /* fMediumLockWriteAll */,
4006 NULL,
4007 *pMediumLockList);
4008 alock.acquire();
4009 if (FAILED(rc))
4010 delete pMediumLockList;
4011 else
4012 {
4013 mData->mSession.mLockedMedia.Unlock();
4014 alock.release();
4015 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4016 mData->mSession.mLockedMedia.Lock();
4017 alock.acquire();
4018 }
4019 alock.release();
4020
4021 if (SUCCEEDED(rc))
4022 {
4023 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4024 /* Remove lock list in case of error. */
4025 if (FAILED(rc))
4026 {
4027 mData->mSession.mLockedMedia.Unlock();
4028 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4029 mData->mSession.mLockedMedia.Lock();
4030 }
4031 }
4032 }
4033
4034 return S_OK;
4035 }
4036
4037 /* bus/channel/device differ; we need a new attachment object,
4038 * but don't try to associate it again */
4039 associate = false;
4040 break;
4041 }
4042 }
4043
4044 /* go further only if the attachment is to be indirect */
4045 if (!fIndirect)
4046 break;
4047
4048 /* perform the so called smart attachment logic for indirect
4049 * attachments. Note that smart attachment is only applicable to base
4050 * hard disks. */
4051
4052 if (medium->i_getParent().isNull())
4053 {
4054 /* first, investigate the backup copy of the current hard disk
4055 * attachments to make it possible to re-attach existing diffs to
4056 * another device slot w/o losing their contents */
4057 if (mMediumAttachments.isBackedUp())
4058 {
4059 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4060
4061 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4062 uint32_t foundLevel = 0;
4063
4064 for (MediumAttachmentList::const_iterator
4065 it = oldAtts.begin();
4066 it != oldAtts.end();
4067 ++it)
4068 {
4069 uint32_t level = 0;
4070 MediumAttachment *pAttach = *it;
4071 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4072 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4073 if (pMedium.isNull())
4074 continue;
4075
4076 if (pMedium->i_getBase(&level) == medium)
4077 {
4078 /* skip the hard disk if its currently attached (we
4079 * cannot attach the same hard disk twice) */
4080 if (i_findAttachment(*mMediumAttachments.data(),
4081 pMedium))
4082 continue;
4083
4084 /* matched device, channel and bus (i.e. attached to the
4085 * same place) will win and immediately stop the search;
4086 * otherwise the attachment that has the youngest
4087 * descendant of medium will be used
4088 */
4089 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4090 {
4091 /* the simplest case: restore the whole attachment
4092 * and return, nothing else to do */
4093 mMediumAttachments->push_back(*it);
4094
4095 /* Reattach the medium to the VM. */
4096 if (fHotplug || fSilent)
4097 {
4098 mediumLock.release();
4099 treeLock.release();
4100 alock.release();
4101
4102 MediumLockList *pMediumLockList(new MediumLockList());
4103
4104 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4105 medium /* pToLockWrite */,
4106 false /* fMediumLockWriteAll */,
4107 NULL,
4108 *pMediumLockList);
4109 alock.acquire();
4110 if (FAILED(rc))
4111 delete pMediumLockList;
4112 else
4113 {
4114 mData->mSession.mLockedMedia.Unlock();
4115 alock.release();
4116 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4117 mData->mSession.mLockedMedia.Lock();
4118 alock.acquire();
4119 }
4120 alock.release();
4121
4122 if (SUCCEEDED(rc))
4123 {
4124 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4125 /* Remove lock list in case of error. */
4126 if (FAILED(rc))
4127 {
4128 mData->mSession.mLockedMedia.Unlock();
4129 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4130 mData->mSession.mLockedMedia.Lock();
4131 }
4132 }
4133 }
4134
4135 return S_OK;
4136 }
4137 else if ( foundIt == oldAtts.end()
4138 || level > foundLevel /* prefer younger */
4139 )
4140 {
4141 foundIt = it;
4142 foundLevel = level;
4143 }
4144 }
4145 }
4146
4147 if (foundIt != oldAtts.end())
4148 {
4149 /* use the previously attached hard disk */
4150 medium = (*foundIt)->i_getMedium();
4151 mediumCaller.attach(medium);
4152 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4153 mediumLock.attach(medium);
4154 /* not implicit, doesn't require association with this VM */
4155 fIndirect = false;
4156 associate = false;
4157 /* go right to the MediumAttachment creation */
4158 break;
4159 }
4160 }
4161
4162 /* must give up the medium lock and medium tree lock as below we
4163 * go over snapshots, which needs a lock with higher lock order. */
4164 mediumLock.release();
4165 treeLock.release();
4166
4167 /* then, search through snapshots for the best diff in the given
4168 * hard disk's chain to base the new diff on */
4169
4170 ComObjPtr<Medium> base;
4171 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4172 while (snap)
4173 {
4174 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4175
4176 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4177
4178 MediumAttachment *pAttachFound = NULL;
4179 uint32_t foundLevel = 0;
4180
4181 for (MediumAttachmentList::const_iterator
4182 it = snapAtts.begin();
4183 it != snapAtts.end();
4184 ++it)
4185 {
4186 MediumAttachment *pAttach = *it;
4187 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4188 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4189 if (pMedium.isNull())
4190 continue;
4191
4192 uint32_t level = 0;
4193 if (pMedium->i_getBase(&level) == medium)
4194 {
4195 /* matched device, channel and bus (i.e. attached to the
4196 * same place) will win and immediately stop the search;
4197 * otherwise the attachment that has the youngest
4198 * descendant of medium will be used
4199 */
4200 if ( pAttach->i_getDevice() == aDevice
4201 && pAttach->i_getPort() == aControllerPort
4202 && pAttach->i_getControllerName() == aName
4203 )
4204 {
4205 pAttachFound = pAttach;
4206 break;
4207 }
4208 else if ( !pAttachFound
4209 || level > foundLevel /* prefer younger */
4210 )
4211 {
4212 pAttachFound = pAttach;
4213 foundLevel = level;
4214 }
4215 }
4216 }
4217
4218 if (pAttachFound)
4219 {
4220 base = pAttachFound->i_getMedium();
4221 break;
4222 }
4223
4224 snap = snap->i_getParent();
4225 }
4226
4227 /* re-lock medium tree and the medium, as we need it below */
4228 treeLock.acquire();
4229 mediumLock.acquire();
4230
4231 /* found a suitable diff, use it as a base */
4232 if (!base.isNull())
4233 {
4234 medium = base;
4235 mediumCaller.attach(medium);
4236 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4237 mediumLock.attach(medium);
4238 }
4239 }
4240
4241 Utf8Str strFullSnapshotFolder;
4242 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4243
4244 ComObjPtr<Medium> diff;
4245 diff.createObject();
4246 // store this diff in the same registry as the parent
4247 Guid uuidRegistryParent;
4248 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4249 {
4250 // parent image has no registry: this can happen if we're attaching a new immutable
4251 // image that has not yet been attached (medium then points to the base and we're
4252 // creating the diff image for the immutable, and the parent is not yet registered);
4253 // put the parent in the machine registry then
4254 mediumLock.release();
4255 treeLock.release();
4256 alock.release();
4257 i_addMediumToRegistry(medium);
4258 alock.acquire();
4259 treeLock.acquire();
4260 mediumLock.acquire();
4261 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4262 }
4263 rc = diff->init(mParent,
4264 medium->i_getPreferredDiffFormat(),
4265 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4266 uuidRegistryParent,
4267 DeviceType_HardDisk);
4268 if (FAILED(rc)) return rc;
4269
4270 /* Apply the normal locking logic to the entire chain. */
4271 MediumLockList *pMediumLockList(new MediumLockList());
4272 mediumLock.release();
4273 treeLock.release();
4274 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4275 diff /* pToLockWrite */,
4276 false /* fMediumLockWriteAll */,
4277 medium,
4278 *pMediumLockList);
4279 treeLock.acquire();
4280 mediumLock.acquire();
4281 if (SUCCEEDED(rc))
4282 {
4283 mediumLock.release();
4284 treeLock.release();
4285 rc = pMediumLockList->Lock();
4286 treeLock.acquire();
4287 mediumLock.acquire();
4288 if (FAILED(rc))
4289 setError(rc,
4290 tr("Could not lock medium when creating diff '%s'"),
4291 diff->i_getLocationFull().c_str());
4292 else
4293 {
4294 /* will release the lock before the potentially lengthy
4295 * operation, so protect with the special state */
4296 MachineState_T oldState = mData->mMachineState;
4297 i_setMachineState(MachineState_SettingUp);
4298
4299 mediumLock.release();
4300 treeLock.release();
4301 alock.release();
4302
4303 rc = medium->i_createDiffStorage(diff,
4304 medium->i_getPreferredDiffVariant(),
4305 pMediumLockList,
4306 NULL /* aProgress */,
4307 true /* aWait */);
4308
4309 alock.acquire();
4310 treeLock.acquire();
4311 mediumLock.acquire();
4312
4313 i_setMachineState(oldState);
4314 }
4315 }
4316
4317 /* Unlock the media and free the associated memory. */
4318 delete pMediumLockList;
4319
4320 if (FAILED(rc)) return rc;
4321
4322 /* use the created diff for the actual attachment */
4323 medium = diff;
4324 mediumCaller.attach(medium);
4325 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4326 mediumLock.attach(medium);
4327 }
4328 while (0);
4329
4330 ComObjPtr<MediumAttachment> attachment;
4331 attachment.createObject();
4332 rc = attachment->init(this,
4333 medium,
4334 aName,
4335 aControllerPort,
4336 aDevice,
4337 aType,
4338 fIndirect,
4339 false /* fPassthrough */,
4340 false /* fTempEject */,
4341 false /* fNonRotational */,
4342 false /* fDiscard */,
4343 fHotplug /* fHotPluggable */,
4344 Utf8Str::Empty);
4345 if (FAILED(rc)) return rc;
4346
4347 if (associate && !medium.isNull())
4348 {
4349 // as the last step, associate the medium to the VM
4350 rc = medium->i_addBackReference(mData->mUuid);
4351 // here we can fail because of Deleting, or being in process of creating a Diff
4352 if (FAILED(rc)) return rc;
4353
4354 mediumLock.release();
4355 treeLock.release();
4356 alock.release();
4357 i_addMediumToRegistry(medium);
4358 alock.acquire();
4359 treeLock.acquire();
4360 mediumLock.acquire();
4361 }
4362
4363 /* success: finally remember the attachment */
4364 i_setModified(IsModified_Storage);
4365 mMediumAttachments.backup();
4366 mMediumAttachments->push_back(attachment);
4367
4368 mediumLock.release();
4369 treeLock.release();
4370 alock.release();
4371
4372 if (fHotplug || fSilent)
4373 {
4374 if (!medium.isNull())
4375 {
4376 MediumLockList *pMediumLockList(new MediumLockList());
4377
4378 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4379 medium /* pToLockWrite */,
4380 false /* fMediumLockWriteAll */,
4381 NULL,
4382 *pMediumLockList);
4383 alock.acquire();
4384 if (FAILED(rc))
4385 delete pMediumLockList;
4386 else
4387 {
4388 mData->mSession.mLockedMedia.Unlock();
4389 alock.release();
4390 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4391 mData->mSession.mLockedMedia.Lock();
4392 alock.acquire();
4393 }
4394 alock.release();
4395 }
4396
4397 if (SUCCEEDED(rc))
4398 {
4399 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4400 /* Remove lock list in case of error. */
4401 if (FAILED(rc))
4402 {
4403 mData->mSession.mLockedMedia.Unlock();
4404 mData->mSession.mLockedMedia.Remove(attachment);
4405 mData->mSession.mLockedMedia.Lock();
4406 }
4407 }
4408 }
4409
4410 /* Save modified registries, but skip this machine as it's the caller's
4411 * job to save its settings like all other settings changes. */
4412 mParent->i_unmarkRegistryModified(i_getId());
4413 mParent->i_saveModifiedRegistries();
4414
4415 return rc;
4416}
4417
4418HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4419 LONG aDevice)
4420{
4421 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4422 aName.c_str(), aControllerPort, aDevice));
4423
4424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4425
4426 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4427 if (FAILED(rc)) return rc;
4428
4429 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4430
4431 /* Check for an existing controller. */
4432 ComObjPtr<StorageController> ctl;
4433 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4434 if (FAILED(rc)) return rc;
4435
4436 StorageControllerType_T ctrlType;
4437 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4438 if (FAILED(rc))
4439 return setError(E_FAIL,
4440 tr("Could not get type of controller '%s'"),
4441 aName.c_str());
4442
4443 bool fSilent = false;
4444 Utf8Str strReconfig;
4445
4446 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4447 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4448 if ( mData->mMachineState == MachineState_Paused
4449 && strReconfig == "1")
4450 fSilent = true;
4451
4452 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4453 bool fHotplug = false;
4454 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4455 fHotplug = true;
4456
4457 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4458 return setError(VBOX_E_INVALID_VM_STATE,
4459 tr("Controller '%s' does not support hotplugging"),
4460 aName.c_str());
4461
4462 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4463 aName,
4464 aControllerPort,
4465 aDevice);
4466 if (!pAttach)
4467 return setError(VBOX_E_OBJECT_NOT_FOUND,
4468 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4469 aDevice, aControllerPort, aName.c_str());
4470
4471 if (fHotplug && !pAttach->i_getHotPluggable())
4472 return setError(VBOX_E_NOT_SUPPORTED,
4473 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4474 aDevice, aControllerPort, aName.c_str());
4475
4476 /*
4477 * The VM has to detach the device before we delete any implicit diffs.
4478 * If this fails we can roll back without loosing data.
4479 */
4480 if (fHotplug || fSilent)
4481 {
4482 alock.release();
4483 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4484 alock.acquire();
4485 }
4486 if (FAILED(rc)) return rc;
4487
4488 /* If we are here everything went well and we can delete the implicit now. */
4489 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4490
4491 alock.release();
4492
4493 /* Save modified registries, but skip this machine as it's the caller's
4494 * job to save its settings like all other settings changes. */
4495 mParent->i_unmarkRegistryModified(i_getId());
4496 mParent->i_saveModifiedRegistries();
4497
4498 return rc;
4499}
4500
4501HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4502 LONG aDevice, BOOL aPassthrough)
4503{
4504 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4505 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4506
4507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4508
4509 HRESULT rc = i_checkStateDependency(MutableStateDep);
4510 if (FAILED(rc)) return rc;
4511
4512 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4513
4514 if (Global::IsOnlineOrTransient(mData->mMachineState))
4515 return setError(VBOX_E_INVALID_VM_STATE,
4516 tr("Invalid machine state: %s"),
4517 Global::stringifyMachineState(mData->mMachineState));
4518
4519 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4520 aName,
4521 aControllerPort,
4522 aDevice);
4523 if (!pAttach)
4524 return setError(VBOX_E_OBJECT_NOT_FOUND,
4525 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4526 aDevice, aControllerPort, aName.c_str());
4527
4528
4529 i_setModified(IsModified_Storage);
4530 mMediumAttachments.backup();
4531
4532 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4533
4534 if (pAttach->i_getType() != DeviceType_DVD)
4535 return setError(E_INVALIDARG,
4536 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4537 aDevice, aControllerPort, aName.c_str());
4538 pAttach->i_updatePassthrough(!!aPassthrough);
4539
4540 return S_OK;
4541}
4542
4543HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4544 LONG aDevice, BOOL aTemporaryEject)
4545{
4546
4547 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4548 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4549
4550 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4551
4552 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4553 if (FAILED(rc)) return rc;
4554
4555 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4556 aName,
4557 aControllerPort,
4558 aDevice);
4559 if (!pAttach)
4560 return setError(VBOX_E_OBJECT_NOT_FOUND,
4561 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4562 aDevice, aControllerPort, aName.c_str());
4563
4564
4565 i_setModified(IsModified_Storage);
4566 mMediumAttachments.backup();
4567
4568 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4569
4570 if (pAttach->i_getType() != DeviceType_DVD)
4571 return setError(E_INVALIDARG,
4572 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4573 aDevice, aControllerPort, aName.c_str());
4574 pAttach->i_updateTempEject(!!aTemporaryEject);
4575
4576 return S_OK;
4577}
4578
4579HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4580 LONG aDevice, BOOL aNonRotational)
4581{
4582
4583 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4584 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4585
4586 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4587
4588 HRESULT rc = i_checkStateDependency(MutableStateDep);
4589 if (FAILED(rc)) return rc;
4590
4591 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4592
4593 if (Global::IsOnlineOrTransient(mData->mMachineState))
4594 return setError(VBOX_E_INVALID_VM_STATE,
4595 tr("Invalid machine state: %s"),
4596 Global::stringifyMachineState(mData->mMachineState));
4597
4598 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4599 aName,
4600 aControllerPort,
4601 aDevice);
4602 if (!pAttach)
4603 return setError(VBOX_E_OBJECT_NOT_FOUND,
4604 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4605 aDevice, aControllerPort, aName.c_str());
4606
4607
4608 i_setModified(IsModified_Storage);
4609 mMediumAttachments.backup();
4610
4611 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4612
4613 if (pAttach->i_getType() != DeviceType_HardDisk)
4614 return setError(E_INVALIDARG,
4615 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"),
4616 aDevice, aControllerPort, aName.c_str());
4617 pAttach->i_updateNonRotational(!!aNonRotational);
4618
4619 return S_OK;
4620}
4621
4622HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4623 LONG aDevice, BOOL aDiscard)
4624{
4625
4626 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4627 aName.c_str(), aControllerPort, aDevice, aDiscard));
4628
4629 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4630
4631 HRESULT rc = i_checkStateDependency(MutableStateDep);
4632 if (FAILED(rc)) return rc;
4633
4634 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4635
4636 if (Global::IsOnlineOrTransient(mData->mMachineState))
4637 return setError(VBOX_E_INVALID_VM_STATE,
4638 tr("Invalid machine state: %s"),
4639 Global::stringifyMachineState(mData->mMachineState));
4640
4641 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4642 aName,
4643 aControllerPort,
4644 aDevice);
4645 if (!pAttach)
4646 return setError(VBOX_E_OBJECT_NOT_FOUND,
4647 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4648 aDevice, aControllerPort, aName.c_str());
4649
4650
4651 i_setModified(IsModified_Storage);
4652 mMediumAttachments.backup();
4653
4654 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4655
4656 if (pAttach->i_getType() != DeviceType_HardDisk)
4657 return setError(E_INVALIDARG,
4658 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"),
4659 aDevice, aControllerPort, aName.c_str());
4660 pAttach->i_updateDiscard(!!aDiscard);
4661
4662 return S_OK;
4663}
4664
4665HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4666 LONG aDevice, BOOL aHotPluggable)
4667{
4668 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4669 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4670
4671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4672
4673 HRESULT rc = i_checkStateDependency(MutableStateDep);
4674 if (FAILED(rc)) return rc;
4675
4676 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4677
4678 if (Global::IsOnlineOrTransient(mData->mMachineState))
4679 return setError(VBOX_E_INVALID_VM_STATE,
4680 tr("Invalid machine state: %s"),
4681 Global::stringifyMachineState(mData->mMachineState));
4682
4683 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4684 aName,
4685 aControllerPort,
4686 aDevice);
4687 if (!pAttach)
4688 return setError(VBOX_E_OBJECT_NOT_FOUND,
4689 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4690 aDevice, aControllerPort, aName.c_str());
4691
4692 /* Check for an existing controller. */
4693 ComObjPtr<StorageController> ctl;
4694 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4695 if (FAILED(rc)) return rc;
4696
4697 StorageControllerType_T ctrlType;
4698 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4699 if (FAILED(rc))
4700 return setError(E_FAIL,
4701 tr("Could not get type of controller '%s'"),
4702 aName.c_str());
4703
4704 if (!i_isControllerHotplugCapable(ctrlType))
4705 return setError(VBOX_E_NOT_SUPPORTED,
4706 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4707 aName.c_str());
4708
4709 i_setModified(IsModified_Storage);
4710 mMediumAttachments.backup();
4711
4712 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4713
4714 if (pAttach->i_getType() == DeviceType_Floppy)
4715 return setError(E_INVALIDARG,
4716 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"),
4717 aDevice, aControllerPort, aName.c_str());
4718 pAttach->i_updateHotPluggable(!!aHotPluggable);
4719
4720 return S_OK;
4721}
4722
4723HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4724 LONG aDevice)
4725{
4726 int rc = S_OK;
4727 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4728 aName.c_str(), aControllerPort, aDevice));
4729
4730 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4731
4732 return rc;
4733}
4734
4735HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4736 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4737{
4738 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4739 aName.c_str(), aControllerPort, aDevice));
4740
4741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4742
4743 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4744 if (FAILED(rc)) return rc;
4745
4746 if (Global::IsOnlineOrTransient(mData->mMachineState))
4747 return setError(VBOX_E_INVALID_VM_STATE,
4748 tr("Invalid machine state: %s"),
4749 Global::stringifyMachineState(mData->mMachineState));
4750
4751 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4752 aName,
4753 aControllerPort,
4754 aDevice);
4755 if (!pAttach)
4756 return setError(VBOX_E_OBJECT_NOT_FOUND,
4757 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4758 aDevice, aControllerPort, aName.c_str());
4759
4760
4761 i_setModified(IsModified_Storage);
4762 mMediumAttachments.backup();
4763
4764 IBandwidthGroup *iB = aBandwidthGroup;
4765 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4766 if (aBandwidthGroup && group.isNull())
4767 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4768
4769 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4770
4771 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4772 if (strBandwidthGroupOld.isNotEmpty())
4773 {
4774 /* Get the bandwidth group object and release it - this must not fail. */
4775 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4776 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4777 Assert(SUCCEEDED(rc));
4778
4779 pBandwidthGroupOld->i_release();
4780 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4781 }
4782
4783 if (!group.isNull())
4784 {
4785 group->i_reference();
4786 pAttach->i_updateBandwidthGroup(group->i_getName());
4787 }
4788
4789 return S_OK;
4790}
4791
4792HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4793 LONG aControllerPort,
4794 LONG aDevice,
4795 DeviceType_T aType)
4796{
4797 HRESULT rc = S_OK;
4798
4799 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4800 aName.c_str(), aControllerPort, aDevice, aType));
4801
4802 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4803
4804 return rc;
4805}
4806
4807
4808HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4809 LONG aControllerPort,
4810 LONG aDevice,
4811 BOOL aForce)
4812{
4813 int rc = S_OK;
4814 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4815 aName.c_str(), aControllerPort, aForce));
4816
4817 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4818
4819 return rc;
4820}
4821
4822HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4823 LONG aControllerPort,
4824 LONG aDevice,
4825 const ComPtr<IMedium> &aMedium,
4826 BOOL aForce)
4827{
4828 int rc = S_OK;
4829 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4830 aName.c_str(), aControllerPort, aDevice, aForce));
4831
4832 // request the host lock first, since might be calling Host methods for getting host drives;
4833 // next, protect the media tree all the while we're in here, as well as our member variables
4834 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4835 this->lockHandle(),
4836 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4837
4838 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4839 aName,
4840 aControllerPort,
4841 aDevice);
4842 if (pAttach.isNull())
4843 return setError(VBOX_E_OBJECT_NOT_FOUND,
4844 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4845 aDevice, aControllerPort, aName.c_str());
4846
4847 /* Remember previously mounted medium. The medium before taking the
4848 * backup is not necessarily the same thing. */
4849 ComObjPtr<Medium> oldmedium;
4850 oldmedium = pAttach->i_getMedium();
4851
4852 IMedium *iM = aMedium;
4853 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4854 if (aMedium && pMedium.isNull())
4855 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4856
4857 AutoCaller mediumCaller(pMedium);
4858 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4859
4860 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4861 if (pMedium)
4862 {
4863 DeviceType_T mediumType = pAttach->i_getType();
4864 switch (mediumType)
4865 {
4866 case DeviceType_DVD:
4867 case DeviceType_Floppy:
4868 break;
4869
4870 default:
4871 return setError(VBOX_E_INVALID_OBJECT_STATE,
4872 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4873 aControllerPort,
4874 aDevice,
4875 aName.c_str());
4876 }
4877 }
4878
4879 i_setModified(IsModified_Storage);
4880 mMediumAttachments.backup();
4881
4882 {
4883 // The backup operation makes the pAttach reference point to the
4884 // old settings. Re-get the correct reference.
4885 pAttach = i_findAttachment(*mMediumAttachments.data(),
4886 aName,
4887 aControllerPort,
4888 aDevice);
4889 if (!oldmedium.isNull())
4890 oldmedium->i_removeBackReference(mData->mUuid);
4891 if (!pMedium.isNull())
4892 {
4893 pMedium->i_addBackReference(mData->mUuid);
4894
4895 mediumLock.release();
4896 multiLock.release();
4897 i_addMediumToRegistry(pMedium);
4898 multiLock.acquire();
4899 mediumLock.acquire();
4900 }
4901
4902 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4903 pAttach->i_updateMedium(pMedium);
4904 }
4905
4906 i_setModified(IsModified_Storage);
4907
4908 mediumLock.release();
4909 multiLock.release();
4910 rc = i_onMediumChange(pAttach, aForce);
4911 multiLock.acquire();
4912 mediumLock.acquire();
4913
4914 /* On error roll back this change only. */
4915 if (FAILED(rc))
4916 {
4917 if (!pMedium.isNull())
4918 pMedium->i_removeBackReference(mData->mUuid);
4919 pAttach = i_findAttachment(*mMediumAttachments.data(),
4920 aName,
4921 aControllerPort,
4922 aDevice);
4923 /* If the attachment is gone in the meantime, bail out. */
4924 if (pAttach.isNull())
4925 return rc;
4926 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4927 if (!oldmedium.isNull())
4928 oldmedium->i_addBackReference(mData->mUuid);
4929 pAttach->i_updateMedium(oldmedium);
4930 }
4931
4932 mediumLock.release();
4933 multiLock.release();
4934
4935 /* Save modified registries, but skip this machine as it's the caller's
4936 * job to save its settings like all other settings changes. */
4937 mParent->i_unmarkRegistryModified(i_getId());
4938 mParent->i_saveModifiedRegistries();
4939
4940 return rc;
4941}
4942HRESULT Machine::getMedium(const com::Utf8Str &aName,
4943 LONG aControllerPort,
4944 LONG aDevice,
4945 ComPtr<IMedium> &aMedium)
4946{
4947 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4948 aName.c_str(), aControllerPort, aDevice));
4949
4950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4951
4952 aMedium = NULL;
4953
4954 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4955 aName,
4956 aControllerPort,
4957 aDevice);
4958 if (pAttach.isNull())
4959 return setError(VBOX_E_OBJECT_NOT_FOUND,
4960 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4961 aDevice, aControllerPort, aName.c_str());
4962
4963 aMedium = pAttach->i_getMedium();
4964
4965 return S_OK;
4966}
4967
4968HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4969{
4970
4971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4972
4973 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4974
4975 return S_OK;
4976}
4977
4978HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4979{
4980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4981
4982 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4983
4984 return S_OK;
4985}
4986
4987HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4988{
4989 /* Do not assert if slot is out of range, just return the advertised
4990 status. testdriver/vbox.py triggers this in logVmInfo. */
4991 if (aSlot >= mNetworkAdapters.size())
4992 return setError(E_INVALIDARG,
4993 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4994 aSlot, mNetworkAdapters.size());
4995
4996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4997
4998 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4999
5000 return S_OK;
5001}
5002
5003HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5004{
5005 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5006
5007 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5008 size_t i = 0;
5009 for (settings::StringsMap::const_iterator
5010 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5011 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5012 ++it, ++i)
5013 aKeys[i] = it->first;
5014
5015 return S_OK;
5016}
5017
5018 /**
5019 * @note Locks this object for reading.
5020 */
5021HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5022 com::Utf8Str &aValue)
5023{
5024 /* start with nothing found */
5025 aValue = "";
5026
5027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5028
5029 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5030 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5031 // found:
5032 aValue = it->second; // source is a Utf8Str
5033
5034 /* return the result to caller (may be empty) */
5035 return S_OK;
5036}
5037
5038 /**
5039 * @note Locks mParent for writing + this object for writing.
5040 */
5041HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5042{
5043 Utf8Str strOldValue; // empty
5044
5045 // locking note: we only hold the read lock briefly to look up the old value,
5046 // then release it and call the onExtraCanChange callbacks. There is a small
5047 // chance of a race insofar as the callback might be called twice if two callers
5048 // change the same key at the same time, but that's a much better solution
5049 // than the deadlock we had here before. The actual changing of the extradata
5050 // is then performed under the write lock and race-free.
5051
5052 // look up the old value first; if nothing has changed then we need not do anything
5053 {
5054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5055
5056 // For snapshots don't even think about allowing changes, extradata
5057 // is global for a machine, so there is nothing snapshot specific.
5058 if (i_isSnapshotMachine())
5059 return setError(VBOX_E_INVALID_VM_STATE,
5060 tr("Cannot set extradata for a snapshot"));
5061
5062 // check if the right IMachine instance is used
5063 if (mData->mRegistered && !i_isSessionMachine())
5064 return setError(VBOX_E_INVALID_VM_STATE,
5065 tr("Cannot set extradata for an immutable machine"));
5066
5067 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5068 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5069 strOldValue = it->second;
5070 }
5071
5072 bool fChanged;
5073 if ((fChanged = (strOldValue != aValue)))
5074 {
5075 // ask for permission from all listeners outside the locks;
5076 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5077 // lock to copy the list of callbacks to invoke
5078 Bstr error;
5079 Bstr bstrValue(aValue);
5080
5081 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5082 {
5083 const char *sep = error.isEmpty() ? "" : ": ";
5084 CBSTR err = error.raw();
5085 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5086 return setError(E_ACCESSDENIED,
5087 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5088 aKey.c_str(),
5089 aValue.c_str(),
5090 sep,
5091 err);
5092 }
5093
5094 // data is changing and change not vetoed: then write it out under the lock
5095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5096
5097 if (aValue.isEmpty())
5098 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5099 else
5100 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5101 // creates a new key if needed
5102
5103 bool fNeedsGlobalSaveSettings = false;
5104 // This saving of settings is tricky: there is no "old state" for the
5105 // extradata items at all (unlike all other settings), so the old/new
5106 // settings comparison would give a wrong result!
5107 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5108
5109 if (fNeedsGlobalSaveSettings)
5110 {
5111 // save the global settings; for that we should hold only the VirtualBox lock
5112 alock.release();
5113 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5114 mParent->i_saveSettings();
5115 }
5116 }
5117
5118 // fire notification outside the lock
5119 if (fChanged)
5120 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5121
5122 return S_OK;
5123}
5124
5125HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5126{
5127 aProgress = NULL;
5128 NOREF(aSettingsFilePath);
5129 ReturnComNotImplemented();
5130}
5131
5132HRESULT Machine::saveSettings()
5133{
5134 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5135
5136 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5137 if (FAILED(rc)) return rc;
5138
5139 /* the settings file path may never be null */
5140 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5141
5142 /* save all VM data excluding snapshots */
5143 bool fNeedsGlobalSaveSettings = false;
5144 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5145 mlock.release();
5146
5147 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5148 {
5149 // save the global settings; for that we should hold only the VirtualBox lock
5150 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5151 rc = mParent->i_saveSettings();
5152 }
5153
5154 return rc;
5155}
5156
5157
5158HRESULT Machine::discardSettings()
5159{
5160 /*
5161 * We need to take the machine list lock here as well as the machine one
5162 * or we'll get into trouble should any media stuff require rolling back.
5163 *
5164 * Details:
5165 *
5166 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5167 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5168 * 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]
5169 * 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
5170 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5171 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5172 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5173 * 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
5174 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5175 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5176 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5177 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5178 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5179 * 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]
5180 * 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] (*)
5181 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5182 * 0:005> k
5183 * # Child-SP RetAddr Call Site
5184 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5185 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5186 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5187 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5188 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5189 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5190 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5191 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5192 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5193 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5194 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5195 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5196 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5197 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5198 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5199 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5200 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5201 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5202 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5203 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5204 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5205 *
5206 */
5207 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5209
5210 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5211 if (FAILED(rc)) return rc;
5212
5213 /*
5214 * during this rollback, the session will be notified if data has
5215 * been actually changed
5216 */
5217 i_rollback(true /* aNotify */);
5218
5219 return S_OK;
5220}
5221
5222/** @note Locks objects! */
5223HRESULT Machine::unregister(AutoCaller &autoCaller,
5224 CleanupMode_T aCleanupMode,
5225 std::vector<ComPtr<IMedium> > &aMedia)
5226{
5227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5228
5229 Guid id(i_getId());
5230
5231 if (mData->mSession.mState != SessionState_Unlocked)
5232 return setError(VBOX_E_INVALID_OBJECT_STATE,
5233 tr("Cannot unregister the machine '%s' while it is locked"),
5234 mUserData->s.strName.c_str());
5235
5236 // wait for state dependents to drop to zero
5237 i_ensureNoStateDependencies();
5238
5239 if (!mData->mAccessible)
5240 {
5241 // inaccessible maschines can only be unregistered; uninitialize ourselves
5242 // here because currently there may be no unregistered that are inaccessible
5243 // (this state combination is not supported). Note releasing the caller and
5244 // leaving the lock before calling uninit()
5245 alock.release();
5246 autoCaller.release();
5247
5248 uninit();
5249
5250 mParent->i_unregisterMachine(this, id);
5251 // calls VirtualBox::i_saveSettings()
5252
5253 return S_OK;
5254 }
5255
5256 HRESULT rc = S_OK;
5257
5258 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5259 // discard saved state
5260 if (mData->mMachineState == MachineState_Saved)
5261 {
5262 // add the saved state file to the list of files the caller should delete
5263 Assert(!mSSData->strStateFilePath.isEmpty());
5264 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5265
5266 mSSData->strStateFilePath.setNull();
5267
5268 // unconditionally set the machine state to powered off, we now
5269 // know no session has locked the machine
5270 mData->mMachineState = MachineState_PoweredOff;
5271 }
5272
5273 size_t cSnapshots = 0;
5274 if (mData->mFirstSnapshot)
5275 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5276 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5277 // fail now before we start detaching media
5278 return setError(VBOX_E_INVALID_OBJECT_STATE,
5279 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5280 mUserData->s.strName.c_str(), cSnapshots);
5281
5282 // This list collects the medium objects from all medium attachments
5283 // which we will detach from the machine and its snapshots, in a specific
5284 // order which allows for closing all media without getting "media in use"
5285 // errors, simply by going through the list from the front to the back:
5286 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5287 // and must be closed before the parent media from the snapshots, or closing the parents
5288 // will fail because they still have children);
5289 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5290 // the root ("first") snapshot of the machine.
5291 MediaList llMedia;
5292
5293 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5294 && mMediumAttachments->size()
5295 )
5296 {
5297 // we have media attachments: detach them all and add the Medium objects to our list
5298 if (aCleanupMode != CleanupMode_UnregisterOnly)
5299 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5300 else
5301 return setError(VBOX_E_INVALID_OBJECT_STATE,
5302 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5303 mUserData->s.strName.c_str(), mMediumAttachments->size());
5304 }
5305
5306 if (cSnapshots)
5307 {
5308 // add the media from the medium attachments of the snapshots to llMedia
5309 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5310 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5311 // into the children first
5312
5313 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5314 MachineState_T oldState = mData->mMachineState;
5315 mData->mMachineState = MachineState_DeletingSnapshot;
5316
5317 // make a copy of the first snapshot so the refcount does not drop to 0
5318 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5319 // because of the AutoCaller voodoo)
5320 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5321
5322 // GO!
5323 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5324
5325 mData->mMachineState = oldState;
5326 }
5327
5328 if (FAILED(rc))
5329 {
5330 i_rollbackMedia();
5331 return rc;
5332 }
5333
5334 // commit all the media changes made above
5335 i_commitMedia();
5336
5337 mData->mRegistered = false;
5338
5339 // machine lock no longer needed
5340 alock.release();
5341
5342 // return media to caller
5343 aMedia.resize(llMedia.size());
5344 size_t i = 0;
5345 for (MediaList::const_iterator
5346 it = llMedia.begin();
5347 it != llMedia.end();
5348 ++it, ++i)
5349 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5350
5351 mParent->i_unregisterMachine(this, id);
5352 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5353
5354 return S_OK;
5355}
5356
5357/**
5358 * Task record for deleting a machine config.
5359 */
5360class Machine::DeleteConfigTask
5361 : public Machine::Task
5362{
5363public:
5364 DeleteConfigTask(Machine *m,
5365 Progress *p,
5366 const Utf8Str &t,
5367 const RTCList<ComPtr<IMedium> > &llMediums,
5368 const StringsList &llFilesToDelete)
5369 : Task(m, p, t),
5370 m_llMediums(llMediums),
5371 m_llFilesToDelete(llFilesToDelete)
5372 {}
5373
5374private:
5375 void handler()
5376 {
5377 try
5378 {
5379 m_pMachine->i_deleteConfigHandler(*this);
5380 }
5381 catch (...)
5382 {
5383 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5384 }
5385 }
5386
5387 RTCList<ComPtr<IMedium> > m_llMediums;
5388 StringsList m_llFilesToDelete;
5389
5390 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5391};
5392
5393/**
5394 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5395 * SessionMachine::taskHandler().
5396 *
5397 * @note Locks this object for writing.
5398 *
5399 * @param task
5400 * @return
5401 */
5402void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5403{
5404 LogFlowThisFuncEnter();
5405
5406 AutoCaller autoCaller(this);
5407 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5408 if (FAILED(autoCaller.rc()))
5409 {
5410 /* we might have been uninitialized because the session was accidentally
5411 * closed by the client, so don't assert */
5412 HRESULT rc = setError(E_FAIL,
5413 tr("The session has been accidentally closed"));
5414 task.m_pProgress->i_notifyComplete(rc);
5415 LogFlowThisFuncLeave();
5416 return;
5417 }
5418
5419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5420
5421 HRESULT rc = S_OK;
5422
5423 try
5424 {
5425 ULONG uLogHistoryCount = 3;
5426 ComPtr<ISystemProperties> systemProperties;
5427 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5428 if (FAILED(rc)) throw rc;
5429
5430 if (!systemProperties.isNull())
5431 {
5432 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5433 if (FAILED(rc)) throw rc;
5434 }
5435
5436 MachineState_T oldState = mData->mMachineState;
5437 i_setMachineState(MachineState_SettingUp);
5438 alock.release();
5439 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5440 {
5441 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5442 {
5443 AutoCaller mac(pMedium);
5444 if (FAILED(mac.rc())) throw mac.rc();
5445 Utf8Str strLocation = pMedium->i_getLocationFull();
5446 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5447 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5448 if (FAILED(rc)) throw rc;
5449 }
5450 if (pMedium->i_isMediumFormatFile())
5451 {
5452 ComPtr<IProgress> pProgress2;
5453 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5454 if (FAILED(rc)) throw rc;
5455 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5456 if (FAILED(rc)) throw rc;
5457 /* Check the result of the asynchronous process. */
5458 LONG iRc;
5459 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5460 if (FAILED(rc)) throw rc;
5461 /* If the thread of the progress object has an error, then
5462 * retrieve the error info from there, or it'll be lost. */
5463 if (FAILED(iRc))
5464 throw setError(ProgressErrorInfo(pProgress2));
5465 }
5466
5467 /* Close the medium, deliberately without checking the return
5468 * code, and without leaving any trace in the error info, as
5469 * a failure here is a very minor issue, which shouldn't happen
5470 * as above we even managed to delete the medium. */
5471 {
5472 ErrorInfoKeeper eik;
5473 pMedium->Close();
5474 }
5475 }
5476 i_setMachineState(oldState);
5477 alock.acquire();
5478
5479 // delete the files pushed on the task list by Machine::Delete()
5480 // (this includes saved states of the machine and snapshots and
5481 // medium storage files from the IMedium list passed in, and the
5482 // machine XML file)
5483 for (StringsList::const_iterator
5484 it = task.m_llFilesToDelete.begin();
5485 it != task.m_llFilesToDelete.end();
5486 ++it)
5487 {
5488 const Utf8Str &strFile = *it;
5489 LogFunc(("Deleting file %s\n", strFile.c_str()));
5490 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5491 if (FAILED(rc)) throw rc;
5492
5493 int vrc = RTFileDelete(strFile.c_str());
5494 if (RT_FAILURE(vrc))
5495 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5496 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5497 }
5498
5499 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5500 if (FAILED(rc)) throw rc;
5501
5502 /* delete the settings only when the file actually exists */
5503 if (mData->pMachineConfigFile->fileExists())
5504 {
5505 /* Delete any backup or uncommitted XML files. Ignore failures.
5506 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5507 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5508 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5509 RTFileDelete(otherXml.c_str());
5510 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5511 RTFileDelete(otherXml.c_str());
5512
5513 /* delete the Logs folder, nothing important should be left
5514 * there (we don't check for errors because the user might have
5515 * some private files there that we don't want to delete) */
5516 Utf8Str logFolder;
5517 getLogFolder(logFolder);
5518 Assert(logFolder.length());
5519 if (RTDirExists(logFolder.c_str()))
5520 {
5521 /* Delete all VBox.log[.N] files from the Logs folder
5522 * (this must be in sync with the rotation logic in
5523 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5524 * files that may have been created by the GUI. */
5525 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5526 logFolder.c_str(), RTPATH_DELIMITER);
5527 RTFileDelete(log.c_str());
5528 log = Utf8StrFmt("%s%cVBox.png",
5529 logFolder.c_str(), RTPATH_DELIMITER);
5530 RTFileDelete(log.c_str());
5531 for (int i = uLogHistoryCount; i > 0; i--)
5532 {
5533 log = Utf8StrFmt("%s%cVBox.log.%d",
5534 logFolder.c_str(), RTPATH_DELIMITER, i);
5535 RTFileDelete(log.c_str());
5536 log = Utf8StrFmt("%s%cVBox.png.%d",
5537 logFolder.c_str(), RTPATH_DELIMITER, i);
5538 RTFileDelete(log.c_str());
5539 }
5540#if defined(RT_OS_WINDOWS)
5541 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5542 RTFileDelete(log.c_str());
5543 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5544 RTFileDelete(log.c_str());
5545#endif
5546
5547 RTDirRemove(logFolder.c_str());
5548 }
5549
5550 /* delete the Snapshots folder, nothing important should be left
5551 * there (we don't check for errors because the user might have
5552 * some private files there that we don't want to delete) */
5553 Utf8Str strFullSnapshotFolder;
5554 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5555 Assert(!strFullSnapshotFolder.isEmpty());
5556 if (RTDirExists(strFullSnapshotFolder.c_str()))
5557 RTDirRemove(strFullSnapshotFolder.c_str());
5558
5559 // delete the directory that contains the settings file, but only
5560 // if it matches the VM name
5561 Utf8Str settingsDir;
5562 if (i_isInOwnDir(&settingsDir))
5563 RTDirRemove(settingsDir.c_str());
5564 }
5565
5566 alock.release();
5567
5568 mParent->i_saveModifiedRegistries();
5569 }
5570 catch (HRESULT aRC) { rc = aRC; }
5571
5572 task.m_pProgress->i_notifyComplete(rc);
5573
5574 LogFlowThisFuncLeave();
5575}
5576
5577HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5578{
5579 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5580
5581 HRESULT rc = i_checkStateDependency(MutableStateDep);
5582 if (FAILED(rc)) return rc;
5583
5584 if (mData->mRegistered)
5585 return setError(VBOX_E_INVALID_VM_STATE,
5586 tr("Cannot delete settings of a registered machine"));
5587
5588 // collect files to delete
5589 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5590 if (mData->pMachineConfigFile->fileExists())
5591 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5592
5593 RTCList<ComPtr<IMedium> > llMediums;
5594 for (size_t i = 0; i < aMedia.size(); ++i)
5595 {
5596 IMedium *pIMedium(aMedia[i]);
5597 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5598 if (pMedium.isNull())
5599 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5600 SafeArray<BSTR> ids;
5601 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5602 if (FAILED(rc)) return rc;
5603 /* At this point the medium should not have any back references
5604 * anymore. If it has it is attached to another VM and *must* not
5605 * deleted. */
5606 if (ids.size() < 1)
5607 llMediums.append(pMedium);
5608 }
5609
5610 ComObjPtr<Progress> pProgress;
5611 pProgress.createObject();
5612 rc = pProgress->init(i_getVirtualBox(),
5613 static_cast<IMachine*>(this) /* aInitiator */,
5614 tr("Deleting files"),
5615 true /* fCancellable */,
5616 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5617 tr("Collecting file inventory"));
5618 if (FAILED(rc))
5619 return rc;
5620
5621 /* create and start the task on a separate thread (note that it will not
5622 * start working until we release alock) */
5623 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5624 rc = pTask->createThread();
5625 if (FAILED(rc))
5626 return rc;
5627
5628 pProgress.queryInterfaceTo(aProgress.asOutParam());
5629
5630 LogFlowFuncLeave();
5631
5632 return S_OK;
5633}
5634
5635HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5636{
5637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5638
5639 ComObjPtr<Snapshot> pSnapshot;
5640 HRESULT rc;
5641
5642 if (aNameOrId.isEmpty())
5643 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5644 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5645 else
5646 {
5647 Guid uuid(aNameOrId);
5648 if (uuid.isValid())
5649 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5650 else
5651 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5652 }
5653 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5654
5655 return rc;
5656}
5657
5658HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5659{
5660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5661
5662 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5663 if (FAILED(rc)) return rc;
5664
5665 ComObjPtr<SharedFolder> sharedFolder;
5666 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5667 if (SUCCEEDED(rc))
5668 return setError(VBOX_E_OBJECT_IN_USE,
5669 tr("Shared folder named '%s' already exists"),
5670 aName.c_str());
5671
5672 sharedFolder.createObject();
5673 rc = sharedFolder->init(i_getMachine(),
5674 aName,
5675 aHostPath,
5676 !!aWritable,
5677 !!aAutomount,
5678 true /* fFailOnError */);
5679 if (FAILED(rc)) return rc;
5680
5681 i_setModified(IsModified_SharedFolders);
5682 mHWData.backup();
5683 mHWData->mSharedFolders.push_back(sharedFolder);
5684
5685 /* inform the direct session if any */
5686 alock.release();
5687 i_onSharedFolderChange();
5688
5689 return S_OK;
5690}
5691
5692HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5693{
5694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5695
5696 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5697 if (FAILED(rc)) return rc;
5698
5699 ComObjPtr<SharedFolder> sharedFolder;
5700 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5701 if (FAILED(rc)) return rc;
5702
5703 i_setModified(IsModified_SharedFolders);
5704 mHWData.backup();
5705 mHWData->mSharedFolders.remove(sharedFolder);
5706
5707 /* inform the direct session if any */
5708 alock.release();
5709 i_onSharedFolderChange();
5710
5711 return S_OK;
5712}
5713
5714HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5715{
5716 /* start with No */
5717 *aCanShow = FALSE;
5718
5719 ComPtr<IInternalSessionControl> directControl;
5720 {
5721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5722
5723 if (mData->mSession.mState != SessionState_Locked)
5724 return setError(VBOX_E_INVALID_VM_STATE,
5725 tr("Machine is not locked for session (session state: %s)"),
5726 Global::stringifySessionState(mData->mSession.mState));
5727
5728 if (mData->mSession.mLockType == LockType_VM)
5729 directControl = mData->mSession.mDirectControl;
5730 }
5731
5732 /* ignore calls made after #OnSessionEnd() is called */
5733 if (!directControl)
5734 return S_OK;
5735
5736 LONG64 dummy;
5737 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5738}
5739
5740HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5741{
5742 ComPtr<IInternalSessionControl> directControl;
5743 {
5744 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5745
5746 if (mData->mSession.mState != SessionState_Locked)
5747 return setError(E_FAIL,
5748 tr("Machine is not locked for session (session state: %s)"),
5749 Global::stringifySessionState(mData->mSession.mState));
5750
5751 if (mData->mSession.mLockType == LockType_VM)
5752 directControl = mData->mSession.mDirectControl;
5753 }
5754
5755 /* ignore calls made after #OnSessionEnd() is called */
5756 if (!directControl)
5757 return S_OK;
5758
5759 BOOL dummy;
5760 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5761}
5762
5763#ifdef VBOX_WITH_GUEST_PROPS
5764/**
5765 * Look up a guest property in VBoxSVC's internal structures.
5766 */
5767HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5768 com::Utf8Str &aValue,
5769 LONG64 *aTimestamp,
5770 com::Utf8Str &aFlags) const
5771{
5772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5773
5774 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5775 if (it != mHWData->mGuestProperties.end())
5776 {
5777 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5778 aValue = it->second.strValue;
5779 *aTimestamp = it->second.mTimestamp;
5780 GuestPropWriteFlags(it->second.mFlags, szFlags);
5781 aFlags = Utf8Str(szFlags);
5782 }
5783
5784 return S_OK;
5785}
5786
5787/**
5788 * Query the VM that a guest property belongs to for the property.
5789 * @returns E_ACCESSDENIED if the VM process is not available or not
5790 * currently handling queries and the lookup should then be done in
5791 * VBoxSVC.
5792 */
5793HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5794 com::Utf8Str &aValue,
5795 LONG64 *aTimestamp,
5796 com::Utf8Str &aFlags) const
5797{
5798 HRESULT rc = S_OK;
5799 BSTR bValue = NULL;
5800 BSTR bFlags = NULL;
5801
5802 ComPtr<IInternalSessionControl> directControl;
5803 {
5804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5805 if (mData->mSession.mLockType == LockType_VM)
5806 directControl = mData->mSession.mDirectControl;
5807 }
5808
5809 /* ignore calls made after #OnSessionEnd() is called */
5810 if (!directControl)
5811 rc = E_ACCESSDENIED;
5812 else
5813 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5814 0 /* accessMode */,
5815 &bValue, aTimestamp, &bFlags);
5816
5817 aValue = bValue;
5818 aFlags = bFlags;
5819
5820 return rc;
5821}
5822#endif // VBOX_WITH_GUEST_PROPS
5823
5824HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5825 com::Utf8Str &aValue,
5826 LONG64 *aTimestamp,
5827 com::Utf8Str &aFlags)
5828{
5829#ifndef VBOX_WITH_GUEST_PROPS
5830 ReturnComNotImplemented();
5831#else // VBOX_WITH_GUEST_PROPS
5832
5833 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5834
5835 if (rc == E_ACCESSDENIED)
5836 /* The VM is not running or the service is not (yet) accessible */
5837 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5838 return rc;
5839#endif // VBOX_WITH_GUEST_PROPS
5840}
5841
5842HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5843{
5844 LONG64 dummyTimestamp;
5845 com::Utf8Str dummyFlags;
5846 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5847 return rc;
5848
5849}
5850HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5851{
5852 com::Utf8Str dummyFlags;
5853 com::Utf8Str dummyValue;
5854 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5855 return rc;
5856}
5857
5858#ifdef VBOX_WITH_GUEST_PROPS
5859/**
5860 * Set a guest property in VBoxSVC's internal structures.
5861 */
5862HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5863 const com::Utf8Str &aFlags, bool fDelete)
5864{
5865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5866 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5867 if (FAILED(rc)) return rc;
5868
5869 try
5870 {
5871 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5872 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5873 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5874
5875 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5876 if (it == mHWData->mGuestProperties.end())
5877 {
5878 if (!fDelete)
5879 {
5880 i_setModified(IsModified_MachineData);
5881 mHWData.backupEx();
5882
5883 RTTIMESPEC time;
5884 HWData::GuestProperty prop;
5885 prop.strValue = Bstr(aValue).raw();
5886 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5887 prop.mFlags = fFlags;
5888 mHWData->mGuestProperties[aName] = prop;
5889 }
5890 }
5891 else
5892 {
5893 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5894 {
5895 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5896 }
5897 else
5898 {
5899 i_setModified(IsModified_MachineData);
5900 mHWData.backupEx();
5901
5902 /* The backupEx() operation invalidates our iterator,
5903 * so get a new one. */
5904 it = mHWData->mGuestProperties.find(aName);
5905 Assert(it != mHWData->mGuestProperties.end());
5906
5907 if (!fDelete)
5908 {
5909 RTTIMESPEC time;
5910 it->second.strValue = aValue;
5911 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5912 it->second.mFlags = fFlags;
5913 }
5914 else
5915 mHWData->mGuestProperties.erase(it);
5916 }
5917 }
5918
5919 if (SUCCEEDED(rc))
5920 {
5921 alock.release();
5922
5923 mParent->i_onGuestPropertyChange(mData->mUuid,
5924 Bstr(aName).raw(),
5925 Bstr(aValue).raw(),
5926 Bstr(aFlags).raw());
5927 }
5928 }
5929 catch (std::bad_alloc &)
5930 {
5931 rc = E_OUTOFMEMORY;
5932 }
5933
5934 return rc;
5935}
5936
5937/**
5938 * Set a property on the VM that that property belongs to.
5939 * @returns E_ACCESSDENIED if the VM process is not available or not
5940 * currently handling queries and the setting should then be done in
5941 * VBoxSVC.
5942 */
5943HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5944 const com::Utf8Str &aFlags, bool fDelete)
5945{
5946 HRESULT rc;
5947
5948 try
5949 {
5950 ComPtr<IInternalSessionControl> directControl;
5951 {
5952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5953 if (mData->mSession.mLockType == LockType_VM)
5954 directControl = mData->mSession.mDirectControl;
5955 }
5956
5957 BSTR dummy = NULL; /* will not be changed (setter) */
5958 LONG64 dummy64;
5959 if (!directControl)
5960 rc = E_ACCESSDENIED;
5961 else
5962 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5963 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5964 fDelete? 2: 1 /* accessMode */,
5965 &dummy, &dummy64, &dummy);
5966 }
5967 catch (std::bad_alloc &)
5968 {
5969 rc = E_OUTOFMEMORY;
5970 }
5971
5972 return rc;
5973}
5974#endif // VBOX_WITH_GUEST_PROPS
5975
5976HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5977 const com::Utf8Str &aFlags)
5978{
5979#ifndef VBOX_WITH_GUEST_PROPS
5980 ReturnComNotImplemented();
5981#else // VBOX_WITH_GUEST_PROPS
5982 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5983 if (rc == E_ACCESSDENIED)
5984 /* The VM is not running or the service is not (yet) accessible */
5985 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5986 return rc;
5987#endif // VBOX_WITH_GUEST_PROPS
5988}
5989
5990HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5991{
5992 return setGuestProperty(aProperty, aValue, "");
5993}
5994
5995HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5996{
5997#ifndef VBOX_WITH_GUEST_PROPS
5998 ReturnComNotImplemented();
5999#else // VBOX_WITH_GUEST_PROPS
6000 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6001 if (rc == E_ACCESSDENIED)
6002 /* The VM is not running or the service is not (yet) accessible */
6003 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6004 return rc;
6005#endif // VBOX_WITH_GUEST_PROPS
6006}
6007
6008#ifdef VBOX_WITH_GUEST_PROPS
6009/**
6010 * Enumerate the guest properties in VBoxSVC's internal structures.
6011 */
6012HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6013 std::vector<com::Utf8Str> &aNames,
6014 std::vector<com::Utf8Str> &aValues,
6015 std::vector<LONG64> &aTimestamps,
6016 std::vector<com::Utf8Str> &aFlags)
6017{
6018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6019 Utf8Str strPatterns(aPatterns);
6020
6021 /*
6022 * Look for matching patterns and build up a list.
6023 */
6024 HWData::GuestPropertyMap propMap;
6025 for (HWData::GuestPropertyMap::const_iterator
6026 it = mHWData->mGuestProperties.begin();
6027 it != mHWData->mGuestProperties.end();
6028 ++it)
6029 {
6030 if ( strPatterns.isEmpty()
6031 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6032 RTSTR_MAX,
6033 it->first.c_str(),
6034 RTSTR_MAX,
6035 NULL)
6036 )
6037 propMap.insert(*it);
6038 }
6039
6040 alock.release();
6041
6042 /*
6043 * And build up the arrays for returning the property information.
6044 */
6045 size_t cEntries = propMap.size();
6046
6047 aNames.resize(cEntries);
6048 aValues.resize(cEntries);
6049 aTimestamps.resize(cEntries);
6050 aFlags.resize(cEntries);
6051
6052 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6053 size_t i = 0;
6054 for (HWData::GuestPropertyMap::const_iterator
6055 it = propMap.begin();
6056 it != propMap.end();
6057 ++it, ++i)
6058 {
6059 aNames[i] = it->first;
6060 aValues[i] = it->second.strValue;
6061 aTimestamps[i] = it->second.mTimestamp;
6062 GuestPropWriteFlags(it->second.mFlags, szFlags);
6063 aFlags[i] = Utf8Str(szFlags);
6064 }
6065
6066 return S_OK;
6067}
6068
6069/**
6070 * Enumerate the properties managed by a VM.
6071 * @returns E_ACCESSDENIED if the VM process is not available or not
6072 * currently handling queries and the setting should then be done in
6073 * VBoxSVC.
6074 */
6075HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6076 std::vector<com::Utf8Str> &aNames,
6077 std::vector<com::Utf8Str> &aValues,
6078 std::vector<LONG64> &aTimestamps,
6079 std::vector<com::Utf8Str> &aFlags)
6080{
6081 HRESULT rc;
6082 ComPtr<IInternalSessionControl> directControl;
6083 {
6084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6085 if (mData->mSession.mLockType == LockType_VM)
6086 directControl = mData->mSession.mDirectControl;
6087 }
6088
6089 com::SafeArray<BSTR> bNames;
6090 com::SafeArray<BSTR> bValues;
6091 com::SafeArray<LONG64> bTimestamps;
6092 com::SafeArray<BSTR> bFlags;
6093
6094 if (!directControl)
6095 rc = E_ACCESSDENIED;
6096 else
6097 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6098 ComSafeArrayAsOutParam(bNames),
6099 ComSafeArrayAsOutParam(bValues),
6100 ComSafeArrayAsOutParam(bTimestamps),
6101 ComSafeArrayAsOutParam(bFlags));
6102 size_t i;
6103 aNames.resize(bNames.size());
6104 for (i = 0; i < bNames.size(); ++i)
6105 aNames[i] = Utf8Str(bNames[i]);
6106 aValues.resize(bValues.size());
6107 for (i = 0; i < bValues.size(); ++i)
6108 aValues[i] = Utf8Str(bValues[i]);
6109 aTimestamps.resize(bTimestamps.size());
6110 for (i = 0; i < bTimestamps.size(); ++i)
6111 aTimestamps[i] = bTimestamps[i];
6112 aFlags.resize(bFlags.size());
6113 for (i = 0; i < bFlags.size(); ++i)
6114 aFlags[i] = Utf8Str(bFlags[i]);
6115
6116 return rc;
6117}
6118#endif // VBOX_WITH_GUEST_PROPS
6119HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6120 std::vector<com::Utf8Str> &aNames,
6121 std::vector<com::Utf8Str> &aValues,
6122 std::vector<LONG64> &aTimestamps,
6123 std::vector<com::Utf8Str> &aFlags)
6124{
6125#ifndef VBOX_WITH_GUEST_PROPS
6126 ReturnComNotImplemented();
6127#else // VBOX_WITH_GUEST_PROPS
6128
6129 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6130
6131 if (rc == E_ACCESSDENIED)
6132 /* The VM is not running or the service is not (yet) accessible */
6133 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6134 return rc;
6135#endif // VBOX_WITH_GUEST_PROPS
6136}
6137
6138HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6139 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6140{
6141 MediumAttachmentList atts;
6142
6143 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6144 if (FAILED(rc)) return rc;
6145
6146 aMediumAttachments.resize(atts.size());
6147 size_t i = 0;
6148 for (MediumAttachmentList::const_iterator
6149 it = atts.begin();
6150 it != atts.end();
6151 ++it, ++i)
6152 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6153
6154 return S_OK;
6155}
6156
6157HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6158 LONG aControllerPort,
6159 LONG aDevice,
6160 ComPtr<IMediumAttachment> &aAttachment)
6161{
6162 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6163 aName.c_str(), aControllerPort, aDevice));
6164
6165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6166
6167 aAttachment = NULL;
6168
6169 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6170 aName,
6171 aControllerPort,
6172 aDevice);
6173 if (pAttach.isNull())
6174 return setError(VBOX_E_OBJECT_NOT_FOUND,
6175 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6176 aDevice, aControllerPort, aName.c_str());
6177
6178 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6179
6180 return S_OK;
6181}
6182
6183
6184HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6185 StorageBus_T aConnectionType,
6186 ComPtr<IStorageController> &aController)
6187{
6188 if ( (aConnectionType <= StorageBus_Null)
6189 || (aConnectionType > StorageBus_PCIe))
6190 return setError(E_INVALIDARG,
6191 tr("Invalid connection type: %d"),
6192 aConnectionType);
6193
6194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6195
6196 HRESULT rc = i_checkStateDependency(MutableStateDep);
6197 if (FAILED(rc)) return rc;
6198
6199 /* try to find one with the name first. */
6200 ComObjPtr<StorageController> ctrl;
6201
6202 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6203 if (SUCCEEDED(rc))
6204 return setError(VBOX_E_OBJECT_IN_USE,
6205 tr("Storage controller named '%s' already exists"),
6206 aName.c_str());
6207
6208 ctrl.createObject();
6209
6210 /* get a new instance number for the storage controller */
6211 ULONG ulInstance = 0;
6212 bool fBootable = true;
6213 for (StorageControllerList::const_iterator
6214 it = mStorageControllers->begin();
6215 it != mStorageControllers->end();
6216 ++it)
6217 {
6218 if ((*it)->i_getStorageBus() == aConnectionType)
6219 {
6220 ULONG ulCurInst = (*it)->i_getInstance();
6221
6222 if (ulCurInst >= ulInstance)
6223 ulInstance = ulCurInst + 1;
6224
6225 /* Only one controller of each type can be marked as bootable. */
6226 if ((*it)->i_getBootable())
6227 fBootable = false;
6228 }
6229 }
6230
6231 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6232 if (FAILED(rc)) return rc;
6233
6234 i_setModified(IsModified_Storage);
6235 mStorageControllers.backup();
6236 mStorageControllers->push_back(ctrl);
6237
6238 ctrl.queryInterfaceTo(aController.asOutParam());
6239
6240 /* inform the direct session if any */
6241 alock.release();
6242 i_onStorageControllerChange();
6243
6244 return S_OK;
6245}
6246
6247HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6248 ComPtr<IStorageController> &aStorageController)
6249{
6250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6251
6252 ComObjPtr<StorageController> ctrl;
6253
6254 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6255 if (SUCCEEDED(rc))
6256 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6257
6258 return rc;
6259}
6260
6261HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6262 ULONG aInstance,
6263 ComPtr<IStorageController> &aStorageController)
6264{
6265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6266
6267 for (StorageControllerList::const_iterator
6268 it = mStorageControllers->begin();
6269 it != mStorageControllers->end();
6270 ++it)
6271 {
6272 if ( (*it)->i_getStorageBus() == aConnectionType
6273 && (*it)->i_getInstance() == aInstance)
6274 {
6275 (*it).queryInterfaceTo(aStorageController.asOutParam());
6276 return S_OK;
6277 }
6278 }
6279
6280 return setError(VBOX_E_OBJECT_NOT_FOUND,
6281 tr("Could not find a storage controller with instance number '%lu'"),
6282 aInstance);
6283}
6284
6285HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6286{
6287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6288
6289 HRESULT rc = i_checkStateDependency(MutableStateDep);
6290 if (FAILED(rc)) return rc;
6291
6292 ComObjPtr<StorageController> ctrl;
6293
6294 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6295 if (SUCCEEDED(rc))
6296 {
6297 /* Ensure that only one controller of each type is marked as bootable. */
6298 if (aBootable == TRUE)
6299 {
6300 for (StorageControllerList::const_iterator
6301 it = mStorageControllers->begin();
6302 it != mStorageControllers->end();
6303 ++it)
6304 {
6305 ComObjPtr<StorageController> aCtrl = (*it);
6306
6307 if ( (aCtrl->i_getName() != aName)
6308 && aCtrl->i_getBootable() == TRUE
6309 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6310 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6311 {
6312 aCtrl->i_setBootable(FALSE);
6313 break;
6314 }
6315 }
6316 }
6317
6318 if (SUCCEEDED(rc))
6319 {
6320 ctrl->i_setBootable(aBootable);
6321 i_setModified(IsModified_Storage);
6322 }
6323 }
6324
6325 if (SUCCEEDED(rc))
6326 {
6327 /* inform the direct session if any */
6328 alock.release();
6329 i_onStorageControllerChange();
6330 }
6331
6332 return rc;
6333}
6334
6335HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6336{
6337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6338
6339 HRESULT rc = i_checkStateDependency(MutableStateDep);
6340 if (FAILED(rc)) return rc;
6341
6342 ComObjPtr<StorageController> ctrl;
6343 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6344 if (FAILED(rc)) return rc;
6345
6346 {
6347 /* find all attached devices to the appropriate storage controller and detach them all */
6348 // make a temporary list because detachDevice invalidates iterators into
6349 // mMediumAttachments
6350 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6351
6352 for (MediumAttachmentList::const_iterator
6353 it = llAttachments2.begin();
6354 it != llAttachments2.end();
6355 ++it)
6356 {
6357 MediumAttachment *pAttachTemp = *it;
6358
6359 AutoCaller localAutoCaller(pAttachTemp);
6360 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6361
6362 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6363
6364 if (pAttachTemp->i_getControllerName() == aName)
6365 {
6366 rc = i_detachDevice(pAttachTemp, alock, NULL);
6367 if (FAILED(rc)) return rc;
6368 }
6369 }
6370 }
6371
6372 /* We can remove it now. */
6373 i_setModified(IsModified_Storage);
6374 mStorageControllers.backup();
6375
6376 ctrl->i_unshare();
6377
6378 mStorageControllers->remove(ctrl);
6379
6380 /* inform the direct session if any */
6381 alock.release();
6382 i_onStorageControllerChange();
6383
6384 return S_OK;
6385}
6386
6387HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6388 ComPtr<IUSBController> &aController)
6389{
6390 if ( (aType <= USBControllerType_Null)
6391 || (aType >= USBControllerType_Last))
6392 return setError(E_INVALIDARG,
6393 tr("Invalid USB controller type: %d"),
6394 aType);
6395
6396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6397
6398 HRESULT rc = i_checkStateDependency(MutableStateDep);
6399 if (FAILED(rc)) return rc;
6400
6401 /* try to find one with the same type first. */
6402 ComObjPtr<USBController> ctrl;
6403
6404 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6405 if (SUCCEEDED(rc))
6406 return setError(VBOX_E_OBJECT_IN_USE,
6407 tr("USB controller named '%s' already exists"),
6408 aName.c_str());
6409
6410 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6411 ULONG maxInstances;
6412 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6413 if (FAILED(rc))
6414 return rc;
6415
6416 ULONG cInstances = i_getUSBControllerCountByType(aType);
6417 if (cInstances >= maxInstances)
6418 return setError(E_INVALIDARG,
6419 tr("Too many USB controllers of this type"));
6420
6421 ctrl.createObject();
6422
6423 rc = ctrl->init(this, aName, aType);
6424 if (FAILED(rc)) return rc;
6425
6426 i_setModified(IsModified_USB);
6427 mUSBControllers.backup();
6428 mUSBControllers->push_back(ctrl);
6429
6430 ctrl.queryInterfaceTo(aController.asOutParam());
6431
6432 /* inform the direct session if any */
6433 alock.release();
6434 i_onUSBControllerChange();
6435
6436 return S_OK;
6437}
6438
6439HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6440{
6441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6442
6443 ComObjPtr<USBController> ctrl;
6444
6445 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6446 if (SUCCEEDED(rc))
6447 ctrl.queryInterfaceTo(aController.asOutParam());
6448
6449 return rc;
6450}
6451
6452HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6453 ULONG *aControllers)
6454{
6455 if ( (aType <= USBControllerType_Null)
6456 || (aType >= USBControllerType_Last))
6457 return setError(E_INVALIDARG,
6458 tr("Invalid USB controller type: %d"),
6459 aType);
6460
6461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6462
6463 ComObjPtr<USBController> ctrl;
6464
6465 *aControllers = i_getUSBControllerCountByType(aType);
6466
6467 return S_OK;
6468}
6469
6470HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6471{
6472
6473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6474
6475 HRESULT rc = i_checkStateDependency(MutableStateDep);
6476 if (FAILED(rc)) return rc;
6477
6478 ComObjPtr<USBController> ctrl;
6479 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6480 if (FAILED(rc)) return rc;
6481
6482 i_setModified(IsModified_USB);
6483 mUSBControllers.backup();
6484
6485 ctrl->i_unshare();
6486
6487 mUSBControllers->remove(ctrl);
6488
6489 /* inform the direct session if any */
6490 alock.release();
6491 i_onUSBControllerChange();
6492
6493 return S_OK;
6494}
6495
6496HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6497 ULONG *aOriginX,
6498 ULONG *aOriginY,
6499 ULONG *aWidth,
6500 ULONG *aHeight,
6501 BOOL *aEnabled)
6502{
6503 uint32_t u32OriginX= 0;
6504 uint32_t u32OriginY= 0;
6505 uint32_t u32Width = 0;
6506 uint32_t u32Height = 0;
6507 uint16_t u16Flags = 0;
6508
6509 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6510 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6511 if (RT_FAILURE(vrc))
6512 {
6513#ifdef RT_OS_WINDOWS
6514 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6515 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6516 * So just assign fEnable to TRUE again.
6517 * The right fix would be to change GUI API wrappers to make sure that parameters
6518 * are changed only if API succeeds.
6519 */
6520 *aEnabled = TRUE;
6521#endif
6522 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6523 tr("Saved guest size is not available (%Rrc)"),
6524 vrc);
6525 }
6526
6527 *aOriginX = u32OriginX;
6528 *aOriginY = u32OriginY;
6529 *aWidth = u32Width;
6530 *aHeight = u32Height;
6531 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6532
6533 return S_OK;
6534}
6535
6536HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6537 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6538{
6539 if (aScreenId != 0)
6540 return E_NOTIMPL;
6541
6542 if ( aBitmapFormat != BitmapFormat_BGR0
6543 && aBitmapFormat != BitmapFormat_BGRA
6544 && aBitmapFormat != BitmapFormat_RGBA
6545 && aBitmapFormat != BitmapFormat_PNG)
6546 return setError(E_NOTIMPL,
6547 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6548
6549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6550
6551 uint8_t *pu8Data = NULL;
6552 uint32_t cbData = 0;
6553 uint32_t u32Width = 0;
6554 uint32_t u32Height = 0;
6555
6556 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6557
6558 if (RT_FAILURE(vrc))
6559 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6560 tr("Saved thumbnail data is not available (%Rrc)"),
6561 vrc);
6562
6563 HRESULT hr = S_OK;
6564
6565 *aWidth = u32Width;
6566 *aHeight = u32Height;
6567
6568 if (cbData > 0)
6569 {
6570 /* Convert pixels to the format expected by the API caller. */
6571 if (aBitmapFormat == BitmapFormat_BGR0)
6572 {
6573 /* [0] B, [1] G, [2] R, [3] 0. */
6574 aData.resize(cbData);
6575 memcpy(&aData.front(), pu8Data, cbData);
6576 }
6577 else if (aBitmapFormat == BitmapFormat_BGRA)
6578 {
6579 /* [0] B, [1] G, [2] R, [3] A. */
6580 aData.resize(cbData);
6581 for (uint32_t i = 0; i < cbData; i += 4)
6582 {
6583 aData[i] = pu8Data[i];
6584 aData[i + 1] = pu8Data[i + 1];
6585 aData[i + 2] = pu8Data[i + 2];
6586 aData[i + 3] = 0xff;
6587 }
6588 }
6589 else if (aBitmapFormat == BitmapFormat_RGBA)
6590 {
6591 /* [0] R, [1] G, [2] B, [3] A. */
6592 aData.resize(cbData);
6593 for (uint32_t i = 0; i < cbData; i += 4)
6594 {
6595 aData[i] = pu8Data[i + 2];
6596 aData[i + 1] = pu8Data[i + 1];
6597 aData[i + 2] = pu8Data[i];
6598 aData[i + 3] = 0xff;
6599 }
6600 }
6601 else if (aBitmapFormat == BitmapFormat_PNG)
6602 {
6603 uint8_t *pu8PNG = NULL;
6604 uint32_t cbPNG = 0;
6605 uint32_t cxPNG = 0;
6606 uint32_t cyPNG = 0;
6607
6608 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6609
6610 if (RT_SUCCESS(vrc))
6611 {
6612 aData.resize(cbPNG);
6613 if (cbPNG)
6614 memcpy(&aData.front(), pu8PNG, cbPNG);
6615 }
6616 else
6617 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6618 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6619 vrc);
6620
6621 RTMemFree(pu8PNG);
6622 }
6623 }
6624
6625 freeSavedDisplayScreenshot(pu8Data);
6626
6627 return hr;
6628}
6629
6630HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6631 ULONG *aWidth,
6632 ULONG *aHeight,
6633 std::vector<BitmapFormat_T> &aBitmapFormats)
6634{
6635 if (aScreenId != 0)
6636 return E_NOTIMPL;
6637
6638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6639
6640 uint8_t *pu8Data = NULL;
6641 uint32_t cbData = 0;
6642 uint32_t u32Width = 0;
6643 uint32_t u32Height = 0;
6644
6645 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6646
6647 if (RT_FAILURE(vrc))
6648 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6649 tr("Saved screenshot data is not available (%Rrc)"),
6650 vrc);
6651
6652 *aWidth = u32Width;
6653 *aHeight = u32Height;
6654 aBitmapFormats.resize(1);
6655 aBitmapFormats[0] = BitmapFormat_PNG;
6656
6657 freeSavedDisplayScreenshot(pu8Data);
6658
6659 return S_OK;
6660}
6661
6662HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6663 BitmapFormat_T aBitmapFormat,
6664 ULONG *aWidth,
6665 ULONG *aHeight,
6666 std::vector<BYTE> &aData)
6667{
6668 if (aScreenId != 0)
6669 return E_NOTIMPL;
6670
6671 if (aBitmapFormat != BitmapFormat_PNG)
6672 return E_NOTIMPL;
6673
6674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6675
6676 uint8_t *pu8Data = NULL;
6677 uint32_t cbData = 0;
6678 uint32_t u32Width = 0;
6679 uint32_t u32Height = 0;
6680
6681 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6682
6683 if (RT_FAILURE(vrc))
6684 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6685 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6686 vrc);
6687
6688 *aWidth = u32Width;
6689 *aHeight = u32Height;
6690
6691 aData.resize(cbData);
6692 if (cbData)
6693 memcpy(&aData.front(), pu8Data, cbData);
6694
6695 freeSavedDisplayScreenshot(pu8Data);
6696
6697 return S_OK;
6698}
6699
6700HRESULT Machine::hotPlugCPU(ULONG aCpu)
6701{
6702 HRESULT rc = S_OK;
6703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6704
6705 if (!mHWData->mCPUHotPlugEnabled)
6706 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6707
6708 if (aCpu >= mHWData->mCPUCount)
6709 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6710
6711 if (mHWData->mCPUAttached[aCpu])
6712 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6713
6714 alock.release();
6715 rc = i_onCPUChange(aCpu, false);
6716 alock.acquire();
6717 if (FAILED(rc)) return rc;
6718
6719 i_setModified(IsModified_MachineData);
6720 mHWData.backup();
6721 mHWData->mCPUAttached[aCpu] = true;
6722
6723 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6724 if (Global::IsOnline(mData->mMachineState))
6725 i_saveSettings(NULL);
6726
6727 return S_OK;
6728}
6729
6730HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6731{
6732 HRESULT rc = S_OK;
6733
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 >= SchemaDefs::MaxCPUCount)
6740 return setError(E_INVALIDARG,
6741 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6742 SchemaDefs::MaxCPUCount);
6743
6744 if (!mHWData->mCPUAttached[aCpu])
6745 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6746
6747 /* CPU 0 can't be detached */
6748 if (aCpu == 0)
6749 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6750
6751 alock.release();
6752 rc = i_onCPUChange(aCpu, true);
6753 alock.acquire();
6754 if (FAILED(rc)) return rc;
6755
6756 i_setModified(IsModified_MachineData);
6757 mHWData.backup();
6758 mHWData->mCPUAttached[aCpu] = false;
6759
6760 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6761 if (Global::IsOnline(mData->mMachineState))
6762 i_saveSettings(NULL);
6763
6764 return S_OK;
6765}
6766
6767HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6768{
6769 *aAttached = false;
6770
6771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6772
6773 /* If hotplug is enabled the CPU is always enabled. */
6774 if (!mHWData->mCPUHotPlugEnabled)
6775 {
6776 if (aCpu < mHWData->mCPUCount)
6777 *aAttached = true;
6778 }
6779 else
6780 {
6781 if (aCpu < SchemaDefs::MaxCPUCount)
6782 *aAttached = mHWData->mCPUAttached[aCpu];
6783 }
6784
6785 return S_OK;
6786}
6787
6788HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6789{
6790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6791
6792 Utf8Str log = i_getLogFilename(aIdx);
6793 if (!RTFileExists(log.c_str()))
6794 log.setNull();
6795 aFilename = log;
6796
6797 return S_OK;
6798}
6799
6800HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6801{
6802 if (aSize < 0)
6803 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6804
6805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6806
6807 HRESULT rc = S_OK;
6808 Utf8Str log = i_getLogFilename(aIdx);
6809
6810 /* do not unnecessarily hold the lock while doing something which does
6811 * not need the lock and potentially takes a long time. */
6812 alock.release();
6813
6814 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6815 * keeps the SOAP reply size under 1M for the webservice (we're using
6816 * base64 encoded strings for binary data for years now, avoiding the
6817 * expansion of each byte array element to approx. 25 bytes of XML. */
6818 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6819 aData.resize(cbData);
6820
6821 RTFILE LogFile;
6822 int vrc = RTFileOpen(&LogFile, log.c_str(),
6823 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6824 if (RT_SUCCESS(vrc))
6825 {
6826 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6827 if (RT_SUCCESS(vrc))
6828 aData.resize(cbData);
6829 else
6830 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6831 tr("Could not read log file '%s' (%Rrc)"),
6832 log.c_str(), vrc);
6833 RTFileClose(LogFile);
6834 }
6835 else
6836 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6837 tr("Could not open log file '%s' (%Rrc)"),
6838 log.c_str(), vrc);
6839
6840 if (FAILED(rc))
6841 aData.resize(0);
6842
6843 return rc;
6844}
6845
6846
6847/**
6848 * Currently this method doesn't attach device to the running VM,
6849 * just makes sure it's plugged on next VM start.
6850 */
6851HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6852{
6853 // lock scope
6854 {
6855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6856
6857 HRESULT rc = i_checkStateDependency(MutableStateDep);
6858 if (FAILED(rc)) return rc;
6859
6860 ChipsetType_T aChipset = ChipsetType_PIIX3;
6861 COMGETTER(ChipsetType)(&aChipset);
6862
6863 if (aChipset != ChipsetType_ICH9)
6864 {
6865 return setError(E_INVALIDARG,
6866 tr("Host PCI attachment only supported with ICH9 chipset"));
6867 }
6868
6869 // check if device with this host PCI address already attached
6870 for (HWData::PCIDeviceAssignmentList::const_iterator
6871 it = mHWData->mPCIDeviceAssignments.begin();
6872 it != mHWData->mPCIDeviceAssignments.end();
6873 ++it)
6874 {
6875 LONG iHostAddress = -1;
6876 ComPtr<PCIDeviceAttachment> pAttach;
6877 pAttach = *it;
6878 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6879 if (iHostAddress == aHostAddress)
6880 return setError(E_INVALIDARG,
6881 tr("Device with host PCI address already attached to this VM"));
6882 }
6883
6884 ComObjPtr<PCIDeviceAttachment> pda;
6885 char name[32];
6886
6887 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6888 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6889 pda.createObject();
6890 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6891 i_setModified(IsModified_MachineData);
6892 mHWData.backup();
6893 mHWData->mPCIDeviceAssignments.push_back(pda);
6894 }
6895
6896 return S_OK;
6897}
6898
6899/**
6900 * Currently this method doesn't detach device from the running VM,
6901 * just makes sure it's not plugged on next VM start.
6902 */
6903HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6904{
6905 ComObjPtr<PCIDeviceAttachment> pAttach;
6906 bool fRemoved = false;
6907 HRESULT rc;
6908
6909 // lock scope
6910 {
6911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6912
6913 rc = i_checkStateDependency(MutableStateDep);
6914 if (FAILED(rc)) return rc;
6915
6916 for (HWData::PCIDeviceAssignmentList::const_iterator
6917 it = mHWData->mPCIDeviceAssignments.begin();
6918 it != mHWData->mPCIDeviceAssignments.end();
6919 ++it)
6920 {
6921 LONG iHostAddress = -1;
6922 pAttach = *it;
6923 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6924 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6925 {
6926 i_setModified(IsModified_MachineData);
6927 mHWData.backup();
6928 mHWData->mPCIDeviceAssignments.remove(pAttach);
6929 fRemoved = true;
6930 break;
6931 }
6932 }
6933 }
6934
6935
6936 /* Fire event outside of the lock */
6937 if (fRemoved)
6938 {
6939 Assert(!pAttach.isNull());
6940 ComPtr<IEventSource> es;
6941 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6942 Assert(SUCCEEDED(rc));
6943 Bstr mid;
6944 rc = this->COMGETTER(Id)(mid.asOutParam());
6945 Assert(SUCCEEDED(rc));
6946 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6947 }
6948
6949 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6950 tr("No host PCI device %08x attached"),
6951 aHostAddress
6952 );
6953}
6954
6955HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6956{
6957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6958
6959 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6960 size_t i = 0;
6961 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6962 it = mHWData->mPCIDeviceAssignments.begin();
6963 it != mHWData->mPCIDeviceAssignments.end();
6964 ++it, ++i)
6965 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6966
6967 return S_OK;
6968}
6969
6970HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6971{
6972 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6973
6974 return S_OK;
6975}
6976
6977HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6978{
6979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6980
6981 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6982
6983 return S_OK;
6984}
6985
6986HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6987{
6988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6989 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6990 if (SUCCEEDED(hrc))
6991 {
6992 hrc = mHWData.backupEx();
6993 if (SUCCEEDED(hrc))
6994 {
6995 i_setModified(IsModified_MachineData);
6996 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6997 }
6998 }
6999 return hrc;
7000}
7001
7002HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7003{
7004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7005 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7006 return S_OK;
7007}
7008
7009HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7010{
7011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7012 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7013 if (SUCCEEDED(hrc))
7014 {
7015 hrc = mHWData.backupEx();
7016 if (SUCCEEDED(hrc))
7017 {
7018 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7019 if (SUCCEEDED(hrc))
7020 i_setModified(IsModified_MachineData);
7021 }
7022 }
7023 return hrc;
7024}
7025
7026HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7027{
7028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7029
7030 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7031
7032 return S_OK;
7033}
7034
7035HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7036{
7037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7038 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7039 if (SUCCEEDED(hrc))
7040 {
7041 hrc = mHWData.backupEx();
7042 if (SUCCEEDED(hrc))
7043 {
7044 i_setModified(IsModified_MachineData);
7045 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7046 }
7047 }
7048 return hrc;
7049}
7050
7051HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7052{
7053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7054
7055 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7056
7057 return S_OK;
7058}
7059
7060HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7061{
7062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7063
7064 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7065 if ( SUCCEEDED(hrc)
7066 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7067 {
7068 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7069 int vrc;
7070
7071 if (aAutostartEnabled)
7072 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7073 else
7074 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7075
7076 if (RT_SUCCESS(vrc))
7077 {
7078 hrc = mHWData.backupEx();
7079 if (SUCCEEDED(hrc))
7080 {
7081 i_setModified(IsModified_MachineData);
7082 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7083 }
7084 }
7085 else if (vrc == VERR_NOT_SUPPORTED)
7086 hrc = setError(VBOX_E_NOT_SUPPORTED,
7087 tr("The VM autostart feature is not supported on this platform"));
7088 else if (vrc == VERR_PATH_NOT_FOUND)
7089 hrc = setError(E_FAIL,
7090 tr("The path to the autostart database is not set"));
7091 else
7092 hrc = setError(E_UNEXPECTED,
7093 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7094 aAutostartEnabled ? "Adding" : "Removing",
7095 mUserData->s.strName.c_str(), vrc);
7096 }
7097 return hrc;
7098}
7099
7100HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7101{
7102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7103
7104 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7105
7106 return S_OK;
7107}
7108
7109HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7110{
7111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7112 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7113 if (SUCCEEDED(hrc))
7114 {
7115 hrc = mHWData.backupEx();
7116 if (SUCCEEDED(hrc))
7117 {
7118 i_setModified(IsModified_MachineData);
7119 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7120 }
7121 }
7122 return hrc;
7123}
7124
7125HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7126{
7127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7128
7129 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7130
7131 return S_OK;
7132}
7133
7134HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7135{
7136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7137 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7138 if ( SUCCEEDED(hrc)
7139 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7140 {
7141 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7142 int vrc;
7143
7144 if (aAutostopType != AutostopType_Disabled)
7145 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7146 else
7147 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7148
7149 if (RT_SUCCESS(vrc))
7150 {
7151 hrc = mHWData.backupEx();
7152 if (SUCCEEDED(hrc))
7153 {
7154 i_setModified(IsModified_MachineData);
7155 mHWData->mAutostart.enmAutostopType = aAutostopType;
7156 }
7157 }
7158 else if (vrc == VERR_NOT_SUPPORTED)
7159 hrc = setError(VBOX_E_NOT_SUPPORTED,
7160 tr("The VM autostop feature is not supported on this platform"));
7161 else if (vrc == VERR_PATH_NOT_FOUND)
7162 hrc = setError(E_FAIL,
7163 tr("The path to the autostart database is not set"));
7164 else
7165 hrc = setError(E_UNEXPECTED,
7166 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7167 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7168 mUserData->s.strName.c_str(), vrc);
7169 }
7170 return hrc;
7171}
7172
7173HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7174{
7175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7176
7177 aDefaultFrontend = mHWData->mDefaultFrontend;
7178
7179 return S_OK;
7180}
7181
7182HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7183{
7184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7185 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7186 if (SUCCEEDED(hrc))
7187 {
7188 hrc = mHWData.backupEx();
7189 if (SUCCEEDED(hrc))
7190 {
7191 i_setModified(IsModified_MachineData);
7192 mHWData->mDefaultFrontend = aDefaultFrontend;
7193 }
7194 }
7195 return hrc;
7196}
7197
7198HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7199{
7200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7201 size_t cbIcon = mUserData->s.ovIcon.size();
7202 aIcon.resize(cbIcon);
7203 if (cbIcon)
7204 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7205 return S_OK;
7206}
7207
7208HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7209{
7210 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7211 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7212 if (SUCCEEDED(hrc))
7213 {
7214 i_setModified(IsModified_MachineData);
7215 mUserData.backup();
7216 size_t cbIcon = aIcon.size();
7217 mUserData->s.ovIcon.resize(cbIcon);
7218 if (cbIcon)
7219 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7220 }
7221 return hrc;
7222}
7223
7224HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7225{
7226#ifdef VBOX_WITH_USB
7227 *aUSBProxyAvailable = true;
7228#else
7229 *aUSBProxyAvailable = false;
7230#endif
7231 return S_OK;
7232}
7233
7234HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7235{
7236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7237
7238 aVMProcessPriority = mUserData->s.strVMPriority;
7239
7240 return S_OK;
7241}
7242
7243HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7244{
7245 RT_NOREF(aVMProcessPriority);
7246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7247 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7248 if (SUCCEEDED(hrc))
7249 {
7250 /** @todo r=klaus: currently this is marked as not implemented, as
7251 * the code for setting the priority of the process is not there
7252 * (neither when starting the VM nor at runtime). */
7253 ReturnComNotImplemented();
7254#if 0
7255 hrc = mUserData.backupEx();
7256 if (SUCCEEDED(hrc))
7257 {
7258 i_setModified(IsModified_MachineData);
7259 mUserData->s.strVMPriority = aVMProcessPriority;
7260 }
7261#endif
7262 }
7263 return hrc;
7264}
7265
7266HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7267 ComPtr<IProgress> &aProgress)
7268{
7269 ComObjPtr<Progress> pP;
7270 Progress *ppP = pP;
7271 IProgress *iP = static_cast<IProgress *>(ppP);
7272 IProgress **pProgress = &iP;
7273
7274 IMachine *pTarget = aTarget;
7275
7276 /* Convert the options. */
7277 RTCList<CloneOptions_T> optList;
7278 if (aOptions.size())
7279 for (size_t i = 0; i < aOptions.size(); ++i)
7280 optList.append(aOptions[i]);
7281
7282 if (optList.contains(CloneOptions_Link))
7283 {
7284 if (!i_isSnapshotMachine())
7285 return setError(E_INVALIDARG,
7286 tr("Linked clone can only be created from a snapshot"));
7287 if (aMode != CloneMode_MachineState)
7288 return setError(E_INVALIDARG,
7289 tr("Linked clone can only be created for a single machine state"));
7290 }
7291 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7292
7293 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7294
7295 HRESULT rc = pWorker->start(pProgress);
7296
7297 pP = static_cast<Progress *>(*pProgress);
7298 pP.queryInterfaceTo(aProgress.asOutParam());
7299
7300 return rc;
7301
7302}
7303
7304HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7305 const com::Utf8Str &aType,
7306 ComPtr<IProgress> &aProgress)
7307{
7308 LogFlowThisFuncEnter();
7309
7310 ComObjPtr<Progress> progress;
7311
7312 progress.createObject();
7313
7314 HRESULT rc = S_OK;
7315 Utf8Str targetPath = aTargetPath;
7316 Utf8Str type = aType;
7317
7318 /* Initialize our worker task */
7319 MachineMoveVM* task = NULL;
7320 try
7321 {
7322 task = new MachineMoveVM(this, targetPath, type, progress);
7323 }
7324 catch(...)
7325 {
7326 delete task;
7327 return rc;
7328 }
7329
7330 /*
7331 * task pointer will be owned by the ThreadTask class.
7332 * There is no need to call operator "delete" in the end.
7333 */
7334 rc = task->init();
7335 if (SUCCEEDED(rc))
7336 {
7337 rc = task->createThread();
7338 if (FAILED(rc))
7339 {
7340 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7341 }
7342
7343 /* Return progress to the caller */
7344 progress.queryInterfaceTo(aProgress.asOutParam());
7345 }
7346
7347 LogFlowThisFuncLeave();
7348 return rc;
7349
7350}
7351
7352HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7353{
7354 NOREF(aProgress);
7355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7356
7357 // This check should always fail.
7358 HRESULT rc = i_checkStateDependency(MutableStateDep);
7359 if (FAILED(rc)) return rc;
7360
7361 AssertFailedReturn(E_NOTIMPL);
7362}
7363
7364HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7365{
7366 NOREF(aSavedStateFile);
7367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7368
7369 // This check should always fail.
7370 HRESULT rc = i_checkStateDependency(MutableStateDep);
7371 if (FAILED(rc)) return rc;
7372
7373 AssertFailedReturn(E_NOTIMPL);
7374}
7375
7376HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7377{
7378 NOREF(aFRemoveFile);
7379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7380
7381 // This check should always fail.
7382 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7383 if (FAILED(rc)) return rc;
7384
7385 AssertFailedReturn(E_NOTIMPL);
7386}
7387
7388// public methods for internal purposes
7389/////////////////////////////////////////////////////////////////////////////
7390
7391/**
7392 * Adds the given IsModified_* flag to the dirty flags of the machine.
7393 * This must be called either during i_loadSettings or under the machine write lock.
7394 * @param fl Flag
7395 * @param fAllowStateModification If state modifications are allowed.
7396 */
7397void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7398{
7399 mData->flModifications |= fl;
7400 if (fAllowStateModification && i_isStateModificationAllowed())
7401 mData->mCurrentStateModified = true;
7402}
7403
7404/**
7405 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7406 * care of the write locking.
7407 *
7408 * @param fModification The flag to add.
7409 * @param fAllowStateModification If state modifications are allowed.
7410 */
7411void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7412{
7413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7414 i_setModified(fModification, fAllowStateModification);
7415}
7416
7417/**
7418 * Saves the registry entry of this machine to the given configuration node.
7419 *
7420 * @param data Machine registry data.
7421 *
7422 * @note locks this object for reading.
7423 */
7424HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7425{
7426 AutoLimitedCaller autoCaller(this);
7427 AssertComRCReturnRC(autoCaller.rc());
7428
7429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7430
7431 data.uuid = mData->mUuid;
7432 data.strSettingsFile = mData->m_strConfigFile;
7433
7434 return S_OK;
7435}
7436
7437/**
7438 * Calculates the absolute path of the given path taking the directory of the
7439 * machine settings file as the current directory.
7440 *
7441 * @param strPath Path to calculate the absolute path for.
7442 * @param aResult Where to put the result (used only on success, can be the
7443 * same Utf8Str instance as passed in @a aPath).
7444 * @return IPRT result.
7445 *
7446 * @note Locks this object for reading.
7447 */
7448int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7449{
7450 AutoCaller autoCaller(this);
7451 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7452
7453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7454
7455 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7456
7457 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7458
7459 strSettingsDir.stripFilename();
7460 char folder[RTPATH_MAX];
7461 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7462 if (RT_SUCCESS(vrc))
7463 aResult = folder;
7464
7465 return vrc;
7466}
7467
7468/**
7469 * Copies strSource to strTarget, making it relative to the machine folder
7470 * if it is a subdirectory thereof, or simply copying it otherwise.
7471 *
7472 * @param strSource Path to evaluate and copy.
7473 * @param strTarget Buffer to receive target path.
7474 *
7475 * @note Locks this object for reading.
7476 */
7477void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7478 Utf8Str &strTarget)
7479{
7480 AutoCaller autoCaller(this);
7481 AssertComRCReturn(autoCaller.rc(), (void)0);
7482
7483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7484
7485 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7486 // use strTarget as a temporary buffer to hold the machine settings dir
7487 strTarget = mData->m_strConfigFileFull;
7488 strTarget.stripFilename();
7489 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7490 {
7491 // is relative: then append what's left
7492 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7493 // for empty paths (only possible for subdirs) use "." to avoid
7494 // triggering default settings for not present config attributes.
7495 if (strTarget.isEmpty())
7496 strTarget = ".";
7497 }
7498 else
7499 // is not relative: then overwrite
7500 strTarget = strSource;
7501}
7502
7503/**
7504 * Returns the full path to the machine's log folder in the
7505 * \a aLogFolder argument.
7506 */
7507void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7508{
7509 AutoCaller autoCaller(this);
7510 AssertComRCReturnVoid(autoCaller.rc());
7511
7512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7513
7514 char szTmp[RTPATH_MAX];
7515 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7516 if (RT_SUCCESS(vrc))
7517 {
7518 if (szTmp[0] && !mUserData.isNull())
7519 {
7520 char szTmp2[RTPATH_MAX];
7521 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7522 if (RT_SUCCESS(vrc))
7523 aLogFolder = Utf8StrFmt("%s%c%s",
7524 szTmp2,
7525 RTPATH_DELIMITER,
7526 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7527 }
7528 else
7529 vrc = VERR_PATH_IS_RELATIVE;
7530 }
7531
7532 if (RT_FAILURE(vrc))
7533 {
7534 // fallback if VBOX_USER_LOGHOME is not set or invalid
7535 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7536 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7537 aLogFolder.append(RTPATH_DELIMITER);
7538 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7539 }
7540}
7541
7542/**
7543 * Returns the full path to the machine's log file for an given index.
7544 */
7545Utf8Str Machine::i_getLogFilename(ULONG idx)
7546{
7547 Utf8Str logFolder;
7548 getLogFolder(logFolder);
7549 Assert(logFolder.length());
7550
7551 Utf8Str log;
7552 if (idx == 0)
7553 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7554#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7555 else if (idx == 1)
7556 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7557 else
7558 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7559#else
7560 else
7561 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7562#endif
7563 return log;
7564}
7565
7566/**
7567 * Returns the full path to the machine's hardened log file.
7568 */
7569Utf8Str Machine::i_getHardeningLogFilename(void)
7570{
7571 Utf8Str strFilename;
7572 getLogFolder(strFilename);
7573 Assert(strFilename.length());
7574 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7575 return strFilename;
7576}
7577
7578
7579/**
7580 * Composes a unique saved state filename based on the current system time. The filename is
7581 * granular to the second so this will work so long as no more than one snapshot is taken on
7582 * a machine per second.
7583 *
7584 * Before version 4.1, we used this formula for saved state files:
7585 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7586 * which no longer works because saved state files can now be shared between the saved state of the
7587 * "saved" machine and an online snapshot, and the following would cause problems:
7588 * 1) save machine
7589 * 2) create online snapshot from that machine state --> reusing saved state file
7590 * 3) save machine again --> filename would be reused, breaking the online snapshot
7591 *
7592 * So instead we now use a timestamp.
7593 *
7594 * @param strStateFilePath
7595 */
7596
7597void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7598{
7599 AutoCaller autoCaller(this);
7600 AssertComRCReturnVoid(autoCaller.rc());
7601
7602 {
7603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7604 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7605 }
7606
7607 RTTIMESPEC ts;
7608 RTTimeNow(&ts);
7609 RTTIME time;
7610 RTTimeExplode(&time, &ts);
7611
7612 strStateFilePath += RTPATH_DELIMITER;
7613 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7614 time.i32Year, time.u8Month, time.u8MonthDay,
7615 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7616}
7617
7618/**
7619 * Returns the full path to the default video capture file.
7620 */
7621void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7622{
7623 AutoCaller autoCaller(this);
7624 AssertComRCReturnVoid(autoCaller.rc());
7625
7626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7627
7628 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7629 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7630 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7631}
7632
7633/**
7634 * Returns whether at least one USB controller is present for the VM.
7635 */
7636bool Machine::i_isUSBControllerPresent()
7637{
7638 AutoCaller autoCaller(this);
7639 AssertComRCReturn(autoCaller.rc(), false);
7640
7641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7642
7643 return (mUSBControllers->size() > 0);
7644}
7645
7646/**
7647 * @note Locks this object for writing, calls the client process
7648 * (inside the lock).
7649 */
7650HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7651 const Utf8Str &strFrontend,
7652 const Utf8Str &strEnvironment,
7653 ProgressProxy *aProgress)
7654{
7655 LogFlowThisFuncEnter();
7656
7657 AssertReturn(aControl, E_FAIL);
7658 AssertReturn(aProgress, E_FAIL);
7659 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7660
7661 AutoCaller autoCaller(this);
7662 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7663
7664 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7665
7666 if (!mData->mRegistered)
7667 return setError(E_UNEXPECTED,
7668 tr("The machine '%s' is not registered"),
7669 mUserData->s.strName.c_str());
7670
7671 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7672
7673 /* The process started when launching a VM with separate UI/VM processes is always
7674 * the UI process, i.e. needs special handling as it won't claim the session. */
7675 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7676
7677 if (fSeparate)
7678 {
7679 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7680 return setError(VBOX_E_INVALID_OBJECT_STATE,
7681 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7682 mUserData->s.strName.c_str());
7683 }
7684 else
7685 {
7686 if ( mData->mSession.mState == SessionState_Locked
7687 || mData->mSession.mState == SessionState_Spawning
7688 || mData->mSession.mState == SessionState_Unlocking)
7689 return setError(VBOX_E_INVALID_OBJECT_STATE,
7690 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7691 mUserData->s.strName.c_str());
7692
7693 /* may not be busy */
7694 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7695 }
7696
7697 /* get the path to the executable */
7698 char szPath[RTPATH_MAX];
7699 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7700 size_t cchBufLeft = strlen(szPath);
7701 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7702 szPath[cchBufLeft] = 0;
7703 char *pszNamePart = szPath + cchBufLeft;
7704 cchBufLeft = sizeof(szPath) - cchBufLeft;
7705
7706 int vrc = VINF_SUCCESS;
7707 RTPROCESS pid = NIL_RTPROCESS;
7708
7709 RTENV env = RTENV_DEFAULT;
7710
7711 if (!strEnvironment.isEmpty())
7712 {
7713 char *newEnvStr = NULL;
7714
7715 do
7716 {
7717 /* clone the current environment */
7718 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7719 AssertRCBreakStmt(vrc2, vrc = vrc2);
7720
7721 newEnvStr = RTStrDup(strEnvironment.c_str());
7722 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7723
7724 /* put new variables to the environment
7725 * (ignore empty variable names here since RTEnv API
7726 * intentionally doesn't do that) */
7727 char *var = newEnvStr;
7728 for (char *p = newEnvStr; *p; ++p)
7729 {
7730 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7731 {
7732 *p = '\0';
7733 if (*var)
7734 {
7735 char *val = strchr(var, '=');
7736 if (val)
7737 {
7738 *val++ = '\0';
7739 vrc2 = RTEnvSetEx(env, var, val);
7740 }
7741 else
7742 vrc2 = RTEnvUnsetEx(env, var);
7743 if (RT_FAILURE(vrc2))
7744 break;
7745 }
7746 var = p + 1;
7747 }
7748 }
7749 if (RT_SUCCESS(vrc2) && *var)
7750 vrc2 = RTEnvPutEx(env, var);
7751
7752 AssertRCBreakStmt(vrc2, vrc = vrc2);
7753 }
7754 while (0);
7755
7756 if (newEnvStr != NULL)
7757 RTStrFree(newEnvStr);
7758 }
7759
7760 /* Hardening logging */
7761#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7762 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7763 {
7764 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7765 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7766 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7767 {
7768 Utf8Str strStartupLogDir = strHardeningLogFile;
7769 strStartupLogDir.stripFilename();
7770 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7771 file without stripping the file. */
7772 }
7773 strSupHardeningLogArg.append(strHardeningLogFile);
7774
7775 /* Remove legacy log filename to avoid confusion. */
7776 Utf8Str strOldStartupLogFile;
7777 getLogFolder(strOldStartupLogFile);
7778 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7779 RTFileDelete(strOldStartupLogFile.c_str());
7780 }
7781 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7782#else
7783 const char *pszSupHardeningLogArg = NULL;
7784#endif
7785
7786 Utf8Str strCanonicalName;
7787
7788#ifdef VBOX_WITH_QTGUI
7789 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7790 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7791 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7792 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7793 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7794 {
7795 strCanonicalName = "GUI/Qt";
7796# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7797 /* Modify the base path so that we don't need to use ".." below. */
7798 RTPathStripTrailingSlash(szPath);
7799 RTPathStripFilename(szPath);
7800 cchBufLeft = strlen(szPath);
7801 pszNamePart = szPath + cchBufLeft;
7802 cchBufLeft = sizeof(szPath) - cchBufLeft;
7803
7804# define OSX_APP_NAME "VirtualBoxVM"
7805# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7806
7807 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7808 if ( strAppOverride.contains(".")
7809 || strAppOverride.contains("/")
7810 || strAppOverride.contains("\\")
7811 || strAppOverride.contains(":"))
7812 strAppOverride.setNull();
7813 Utf8Str strAppPath;
7814 if (!strAppOverride.isEmpty())
7815 {
7816 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7817 Utf8Str strFullPath(szPath);
7818 strFullPath.append(strAppPath);
7819 /* there is a race, but people using this deserve the failure */
7820 if (!RTFileExists(strFullPath.c_str()))
7821 strAppOverride.setNull();
7822 }
7823 if (strAppOverride.isEmpty())
7824 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7825 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7826 strcpy(pszNamePart, strAppPath.c_str());
7827# else
7828# ifndef VBOX_GUI_WITH_SHARED_LIBRARY
7829 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7830# else
7831 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7832# endif
7833 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7834 strcpy(pszNamePart, s_szVirtualBox_exe);
7835# endif
7836
7837 Utf8Str idStr = mData->mUuid.toString();
7838 const char *apszArgs[] =
7839 {
7840 szPath,
7841 "--comment", mUserData->s.strName.c_str(),
7842 "--startvm", idStr.c_str(),
7843 "--no-startvm-errormsgbox",
7844 NULL, /* For "--separate". */
7845 NULL, /* For "--sup-startup-log". */
7846 NULL
7847 };
7848 unsigned iArg = 6;
7849 if (fSeparate)
7850 apszArgs[iArg++] = "--separate";
7851 apszArgs[iArg++] = pszSupHardeningLogArg;
7852
7853 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7854 }
7855#else /* !VBOX_WITH_QTGUI */
7856 if (0)
7857 ;
7858#endif /* VBOX_WITH_QTGUI */
7859
7860 else
7861
7862#ifdef VBOX_WITH_VBOXSDL
7863 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7864 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7865 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7866 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7867 {
7868 strCanonicalName = "GUI/SDL";
7869 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7870 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7871 strcpy(pszNamePart, s_szVBoxSDL_exe);
7872
7873 Utf8Str idStr = mData->mUuid.toString();
7874 const char *apszArgs[] =
7875 {
7876 szPath,
7877 "--comment", mUserData->s.strName.c_str(),
7878 "--startvm", idStr.c_str(),
7879 NULL, /* For "--separate". */
7880 NULL, /* For "--sup-startup-log". */
7881 NULL
7882 };
7883 unsigned iArg = 5;
7884 if (fSeparate)
7885 apszArgs[iArg++] = "--separate";
7886 apszArgs[iArg++] = pszSupHardeningLogArg;
7887
7888 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7889 }
7890#else /* !VBOX_WITH_VBOXSDL */
7891 if (0)
7892 ;
7893#endif /* !VBOX_WITH_VBOXSDL */
7894
7895 else
7896
7897#ifdef VBOX_WITH_HEADLESS
7898 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7899 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7900 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7901 )
7902 {
7903 strCanonicalName = "headless";
7904 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7905 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7906 * and a VM works even if the server has not been installed.
7907 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7908 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7909 * differently in 4.0 and 3.x.
7910 */
7911 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7912 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7913 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7914
7915 Utf8Str idStr = mData->mUuid.toString();
7916 const char *apszArgs[] =
7917 {
7918 szPath,
7919 "--comment", mUserData->s.strName.c_str(),
7920 "--startvm", idStr.c_str(),
7921 "--vrde", "config",
7922 NULL, /* For "--capture". */
7923 NULL, /* For "--sup-startup-log". */
7924 NULL
7925 };
7926 unsigned iArg = 7;
7927 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7928 apszArgs[iArg++] = "--capture";
7929 apszArgs[iArg++] = pszSupHardeningLogArg;
7930
7931# ifdef RT_OS_WINDOWS
7932 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7933# else
7934 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7935# endif
7936 }
7937#else /* !VBOX_WITH_HEADLESS */
7938 if (0)
7939 ;
7940#endif /* !VBOX_WITH_HEADLESS */
7941 else
7942 {
7943 RTEnvDestroy(env);
7944 return setError(E_INVALIDARG,
7945 tr("Invalid frontend name: '%s'"),
7946 strFrontend.c_str());
7947 }
7948
7949 RTEnvDestroy(env);
7950
7951 if (RT_FAILURE(vrc))
7952 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7953 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7954 mUserData->s.strName.c_str(), vrc);
7955
7956 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7957
7958 if (!fSeparate)
7959 {
7960 /*
7961 * Note that we don't release the lock here before calling the client,
7962 * because it doesn't need to call us back if called with a NULL argument.
7963 * Releasing the lock here is dangerous because we didn't prepare the
7964 * launch data yet, but the client we've just started may happen to be
7965 * too fast and call LockMachine() that will fail (because of PID, etc.),
7966 * so that the Machine will never get out of the Spawning session state.
7967 */
7968
7969 /* inform the session that it will be a remote one */
7970 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7971#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7972 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7973#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7974 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7975#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7976 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7977
7978 if (FAILED(rc))
7979 {
7980 /* restore the session state */
7981 mData->mSession.mState = SessionState_Unlocked;
7982 alock.release();
7983 mParent->i_addProcessToReap(pid);
7984 /* The failure may occur w/o any error info (from RPC), so provide one */
7985 return setError(VBOX_E_VM_ERROR,
7986 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7987 }
7988
7989 /* attach launch data to the machine */
7990 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7991 mData->mSession.mRemoteControls.push_back(aControl);
7992 mData->mSession.mProgress = aProgress;
7993 mData->mSession.mPID = pid;
7994 mData->mSession.mState = SessionState_Spawning;
7995 Assert(strCanonicalName.isNotEmpty());
7996 mData->mSession.mName = strCanonicalName;
7997 }
7998 else
7999 {
8000 /* For separate UI process we declare the launch as completed instantly, as the
8001 * actual headless VM start may or may not come. No point in remembering anything
8002 * yet, as what matters for us is when the headless VM gets started. */
8003 aProgress->i_notifyComplete(S_OK);
8004 }
8005
8006 alock.release();
8007 mParent->i_addProcessToReap(pid);
8008
8009 LogFlowThisFuncLeave();
8010 return S_OK;
8011}
8012
8013/**
8014 * Returns @c true if the given session machine instance has an open direct
8015 * session (and optionally also for direct sessions which are closing) and
8016 * returns the session control machine instance if so.
8017 *
8018 * Note that when the method returns @c false, the arguments remain unchanged.
8019 *
8020 * @param aMachine Session machine object.
8021 * @param aControl Direct session control object (optional).
8022 * @param aRequireVM If true then only allow VM sessions.
8023 * @param aAllowClosing If true then additionally a session which is currently
8024 * being closed will also be allowed.
8025 *
8026 * @note locks this object for reading.
8027 */
8028bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8029 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8030 bool aRequireVM /*= false*/,
8031 bool aAllowClosing /*= false*/)
8032{
8033 AutoLimitedCaller autoCaller(this);
8034 AssertComRCReturn(autoCaller.rc(), false);
8035
8036 /* just return false for inaccessible machines */
8037 if (getObjectState().getState() != ObjectState::Ready)
8038 return false;
8039
8040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8041
8042 if ( ( mData->mSession.mState == SessionState_Locked
8043 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8044 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8045 )
8046 {
8047 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8048
8049 aMachine = mData->mSession.mMachine;
8050
8051 if (aControl != NULL)
8052 *aControl = mData->mSession.mDirectControl;
8053
8054 return true;
8055 }
8056
8057 return false;
8058}
8059
8060/**
8061 * Returns @c true if the given machine has an spawning direct session.
8062 *
8063 * @note locks this object for reading.
8064 */
8065bool Machine::i_isSessionSpawning()
8066{
8067 AutoLimitedCaller autoCaller(this);
8068 AssertComRCReturn(autoCaller.rc(), false);
8069
8070 /* just return false for inaccessible machines */
8071 if (getObjectState().getState() != ObjectState::Ready)
8072 return false;
8073
8074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8075
8076 if (mData->mSession.mState == SessionState_Spawning)
8077 return true;
8078
8079 return false;
8080}
8081
8082/**
8083 * Called from the client watcher thread to check for unexpected client process
8084 * death during Session_Spawning state (e.g. before it successfully opened a
8085 * direct session).
8086 *
8087 * On Win32 and on OS/2, this method is called only when we've got the
8088 * direct client's process termination notification, so it always returns @c
8089 * true.
8090 *
8091 * On other platforms, this method returns @c true if the client process is
8092 * terminated and @c false if it's still alive.
8093 *
8094 * @note Locks this object for writing.
8095 */
8096bool Machine::i_checkForSpawnFailure()
8097{
8098 AutoCaller autoCaller(this);
8099 if (!autoCaller.isOk())
8100 {
8101 /* nothing to do */
8102 LogFlowThisFunc(("Already uninitialized!\n"));
8103 return true;
8104 }
8105
8106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8107
8108 if (mData->mSession.mState != SessionState_Spawning)
8109 {
8110 /* nothing to do */
8111 LogFlowThisFunc(("Not spawning any more!\n"));
8112 return true;
8113 }
8114
8115 HRESULT rc = S_OK;
8116
8117 /* PID not yet initialized, skip check. */
8118 if (mData->mSession.mPID == NIL_RTPROCESS)
8119 return false;
8120
8121 RTPROCSTATUS status;
8122 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8123
8124 if (vrc != VERR_PROCESS_RUNNING)
8125 {
8126 Utf8Str strExtraInfo;
8127
8128#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8129 /* If the startup logfile exists and is of non-zero length, tell the
8130 user to look there for more details to encourage them to attach it
8131 when reporting startup issues. */
8132 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8133 uint64_t cbStartupLogFile = 0;
8134 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
8135 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8136 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8137#endif
8138
8139 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8140 rc = setError(E_FAIL,
8141 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8142 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8143 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8144 rc = setError(E_FAIL,
8145 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8146 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8147 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8148 rc = setError(E_FAIL,
8149 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8150 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8151 else
8152 rc = setErrorBoth(E_FAIL, vrc,
8153 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8154 i_getName().c_str(), vrc, strExtraInfo.c_str());
8155 }
8156
8157 if (FAILED(rc))
8158 {
8159 /* Close the remote session, remove the remote control from the list
8160 * and reset session state to Closed (@note keep the code in sync with
8161 * the relevant part in LockMachine()). */
8162
8163 Assert(mData->mSession.mRemoteControls.size() == 1);
8164 if (mData->mSession.mRemoteControls.size() == 1)
8165 {
8166 ErrorInfoKeeper eik;
8167 mData->mSession.mRemoteControls.front()->Uninitialize();
8168 }
8169
8170 mData->mSession.mRemoteControls.clear();
8171 mData->mSession.mState = SessionState_Unlocked;
8172
8173 /* finalize the progress after setting the state */
8174 if (!mData->mSession.mProgress.isNull())
8175 {
8176 mData->mSession.mProgress->notifyComplete(rc);
8177 mData->mSession.mProgress.setNull();
8178 }
8179
8180 mData->mSession.mPID = NIL_RTPROCESS;
8181
8182 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8183 return true;
8184 }
8185
8186 return false;
8187}
8188
8189/**
8190 * Checks whether the machine can be registered. If so, commits and saves
8191 * all settings.
8192 *
8193 * @note Must be called from mParent's write lock. Locks this object and
8194 * children for writing.
8195 */
8196HRESULT Machine::i_prepareRegister()
8197{
8198 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8199
8200 AutoLimitedCaller autoCaller(this);
8201 AssertComRCReturnRC(autoCaller.rc());
8202
8203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8204
8205 /* wait for state dependents to drop to zero */
8206 i_ensureNoStateDependencies();
8207
8208 if (!mData->mAccessible)
8209 return setError(VBOX_E_INVALID_OBJECT_STATE,
8210 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8211 mUserData->s.strName.c_str(),
8212 mData->mUuid.toString().c_str());
8213
8214 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8215
8216 if (mData->mRegistered)
8217 return setError(VBOX_E_INVALID_OBJECT_STATE,
8218 tr("The machine '%s' with UUID {%s} is already registered"),
8219 mUserData->s.strName.c_str(),
8220 mData->mUuid.toString().c_str());
8221
8222 HRESULT rc = S_OK;
8223
8224 // Ensure the settings are saved. If we are going to be registered and
8225 // no config file exists yet, create it by calling i_saveSettings() too.
8226 if ( (mData->flModifications)
8227 || (!mData->pMachineConfigFile->fileExists())
8228 )
8229 {
8230 rc = i_saveSettings(NULL);
8231 // no need to check whether VirtualBox.xml needs saving too since
8232 // we can't have a machine XML file rename pending
8233 if (FAILED(rc)) return rc;
8234 }
8235
8236 /* more config checking goes here */
8237
8238 if (SUCCEEDED(rc))
8239 {
8240 /* we may have had implicit modifications we want to fix on success */
8241 i_commit();
8242
8243 mData->mRegistered = true;
8244 }
8245 else
8246 {
8247 /* we may have had implicit modifications we want to cancel on failure*/
8248 i_rollback(false /* aNotify */);
8249 }
8250
8251 return rc;
8252}
8253
8254/**
8255 * Increases the number of objects dependent on the machine state or on the
8256 * registered state. Guarantees that these two states will not change at least
8257 * until #i_releaseStateDependency() is called.
8258 *
8259 * Depending on the @a aDepType value, additional state checks may be made.
8260 * These checks will set extended error info on failure. See
8261 * #i_checkStateDependency() for more info.
8262 *
8263 * If this method returns a failure, the dependency is not added and the caller
8264 * is not allowed to rely on any particular machine state or registration state
8265 * value and may return the failed result code to the upper level.
8266 *
8267 * @param aDepType Dependency type to add.
8268 * @param aState Current machine state (NULL if not interested).
8269 * @param aRegistered Current registered state (NULL if not interested).
8270 *
8271 * @note Locks this object for writing.
8272 */
8273HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8274 MachineState_T *aState /* = NULL */,
8275 BOOL *aRegistered /* = NULL */)
8276{
8277 AutoCaller autoCaller(this);
8278 AssertComRCReturnRC(autoCaller.rc());
8279
8280 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8281
8282 HRESULT rc = i_checkStateDependency(aDepType);
8283 if (FAILED(rc)) return rc;
8284
8285 {
8286 if (mData->mMachineStateChangePending != 0)
8287 {
8288 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8289 * drop to zero so don't add more. It may make sense to wait a bit
8290 * and retry before reporting an error (since the pending state
8291 * transition should be really quick) but let's just assert for
8292 * now to see if it ever happens on practice. */
8293
8294 AssertFailed();
8295
8296 return setError(E_ACCESSDENIED,
8297 tr("Machine state change is in progress. Please retry the operation later."));
8298 }
8299
8300 ++mData->mMachineStateDeps;
8301 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8302 }
8303
8304 if (aState)
8305 *aState = mData->mMachineState;
8306 if (aRegistered)
8307 *aRegistered = mData->mRegistered;
8308
8309 return S_OK;
8310}
8311
8312/**
8313 * Decreases the number of objects dependent on the machine state.
8314 * Must always complete the #i_addStateDependency() call after the state
8315 * dependency is no more necessary.
8316 */
8317void Machine::i_releaseStateDependency()
8318{
8319 AutoCaller autoCaller(this);
8320 AssertComRCReturnVoid(autoCaller.rc());
8321
8322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8323
8324 /* releaseStateDependency() w/o addStateDependency()? */
8325 AssertReturnVoid(mData->mMachineStateDeps != 0);
8326 -- mData->mMachineStateDeps;
8327
8328 if (mData->mMachineStateDeps == 0)
8329 {
8330 /* inform i_ensureNoStateDependencies() that there are no more deps */
8331 if (mData->mMachineStateChangePending != 0)
8332 {
8333 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8334 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8335 }
8336 }
8337}
8338
8339Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8340{
8341 /* start with nothing found */
8342 Utf8Str strResult("");
8343
8344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8345
8346 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8347 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8348 // found:
8349 strResult = it->second; // source is a Utf8Str
8350
8351 return strResult;
8352}
8353
8354// protected methods
8355/////////////////////////////////////////////////////////////////////////////
8356
8357/**
8358 * Performs machine state checks based on the @a aDepType value. If a check
8359 * fails, this method will set extended error info, otherwise it will return
8360 * S_OK. It is supposed, that on failure, the caller will immediately return
8361 * the return value of this method to the upper level.
8362 *
8363 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8364 *
8365 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8366 * current state of this machine object allows to change settings of the
8367 * machine (i.e. the machine is not registered, or registered but not running
8368 * and not saved). It is useful to call this method from Machine setters
8369 * before performing any change.
8370 *
8371 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8372 * as for MutableStateDep except that if the machine is saved, S_OK is also
8373 * returned. This is useful in setters which allow changing machine
8374 * properties when it is in the saved state.
8375 *
8376 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8377 * if the current state of this machine object allows to change runtime
8378 * changeable settings of the machine (i.e. the machine is not registered, or
8379 * registered but either running or not running and not saved). It is useful
8380 * to call this method from Machine setters before performing any changes to
8381 * runtime changeable settings.
8382 *
8383 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8384 * the same as for MutableOrRunningStateDep except that if the machine is
8385 * saved, S_OK is also returned. This is useful in setters which allow
8386 * changing runtime and saved state changeable machine properties.
8387 *
8388 * @param aDepType Dependency type to check.
8389 *
8390 * @note Non Machine based classes should use #i_addStateDependency() and
8391 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8392 * template.
8393 *
8394 * @note This method must be called from under this object's read or write
8395 * lock.
8396 */
8397HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8398{
8399 switch (aDepType)
8400 {
8401 case AnyStateDep:
8402 {
8403 break;
8404 }
8405 case MutableStateDep:
8406 {
8407 if ( mData->mRegistered
8408 && ( !i_isSessionMachine()
8409 || ( mData->mMachineState != MachineState_Aborted
8410 && mData->mMachineState != MachineState_Teleported
8411 && mData->mMachineState != MachineState_PoweredOff
8412 )
8413 )
8414 )
8415 return setError(VBOX_E_INVALID_VM_STATE,
8416 tr("The machine is not mutable (state is %s)"),
8417 Global::stringifyMachineState(mData->mMachineState));
8418 break;
8419 }
8420 case MutableOrSavedStateDep:
8421 {
8422 if ( mData->mRegistered
8423 && ( !i_isSessionMachine()
8424 || ( mData->mMachineState != MachineState_Aborted
8425 && mData->mMachineState != MachineState_Teleported
8426 && mData->mMachineState != MachineState_Saved
8427 && mData->mMachineState != MachineState_PoweredOff
8428 )
8429 )
8430 )
8431 return setError(VBOX_E_INVALID_VM_STATE,
8432 tr("The machine is not mutable or saved (state is %s)"),
8433 Global::stringifyMachineState(mData->mMachineState));
8434 break;
8435 }
8436 case MutableOrRunningStateDep:
8437 {
8438 if ( mData->mRegistered
8439 && ( !i_isSessionMachine()
8440 || ( mData->mMachineState != MachineState_Aborted
8441 && mData->mMachineState != MachineState_Teleported
8442 && mData->mMachineState != MachineState_PoweredOff
8443 && !Global::IsOnline(mData->mMachineState)
8444 )
8445 )
8446 )
8447 return setError(VBOX_E_INVALID_VM_STATE,
8448 tr("The machine is not mutable or running (state is %s)"),
8449 Global::stringifyMachineState(mData->mMachineState));
8450 break;
8451 }
8452 case MutableOrSavedOrRunningStateDep:
8453 {
8454 if ( mData->mRegistered
8455 && ( !i_isSessionMachine()
8456 || ( mData->mMachineState != MachineState_Aborted
8457 && mData->mMachineState != MachineState_Teleported
8458 && mData->mMachineState != MachineState_Saved
8459 && mData->mMachineState != MachineState_PoweredOff
8460 && !Global::IsOnline(mData->mMachineState)
8461 )
8462 )
8463 )
8464 return setError(VBOX_E_INVALID_VM_STATE,
8465 tr("The machine is not mutable, saved or running (state is %s)"),
8466 Global::stringifyMachineState(mData->mMachineState));
8467 break;
8468 }
8469 }
8470
8471 return S_OK;
8472}
8473
8474/**
8475 * Helper to initialize all associated child objects and allocate data
8476 * structures.
8477 *
8478 * This method must be called as a part of the object's initialization procedure
8479 * (usually done in the #init() method).
8480 *
8481 * @note Must be called only from #init() or from #i_registeredInit().
8482 */
8483HRESULT Machine::initDataAndChildObjects()
8484{
8485 AutoCaller autoCaller(this);
8486 AssertComRCReturnRC(autoCaller.rc());
8487 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8488 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8489
8490 AssertReturn(!mData->mAccessible, E_FAIL);
8491
8492 /* allocate data structures */
8493 mSSData.allocate();
8494 mUserData.allocate();
8495 mHWData.allocate();
8496 mMediumAttachments.allocate();
8497 mStorageControllers.allocate();
8498 mUSBControllers.allocate();
8499
8500 /* initialize mOSTypeId */
8501 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8502
8503/** @todo r=bird: init() methods never fails, right? Why don't we make them
8504 * return void then! */
8505
8506 /* create associated BIOS settings object */
8507 unconst(mBIOSSettings).createObject();
8508 mBIOSSettings->init(this);
8509
8510 /* create an associated VRDE object (default is disabled) */
8511 unconst(mVRDEServer).createObject();
8512 mVRDEServer->init(this);
8513
8514 /* create associated serial port objects */
8515 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8516 {
8517 unconst(mSerialPorts[slot]).createObject();
8518 mSerialPorts[slot]->init(this, slot);
8519 }
8520
8521 /* create associated parallel port objects */
8522 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8523 {
8524 unconst(mParallelPorts[slot]).createObject();
8525 mParallelPorts[slot]->init(this, slot);
8526 }
8527
8528 /* create the audio adapter object (always present, default is disabled) */
8529 unconst(mAudioAdapter).createObject();
8530 mAudioAdapter->init(this);
8531
8532 /* create the USB device filters object (always present) */
8533 unconst(mUSBDeviceFilters).createObject();
8534 mUSBDeviceFilters->init(this);
8535
8536 /* create associated network adapter objects */
8537 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8538 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8539 {
8540 unconst(mNetworkAdapters[slot]).createObject();
8541 mNetworkAdapters[slot]->init(this, slot);
8542 }
8543
8544 /* create the bandwidth control */
8545 unconst(mBandwidthControl).createObject();
8546 mBandwidthControl->init(this);
8547
8548 return S_OK;
8549}
8550
8551/**
8552 * Helper to uninitialize all associated child objects and to free all data
8553 * structures.
8554 *
8555 * This method must be called as a part of the object's uninitialization
8556 * procedure (usually done in the #uninit() method).
8557 *
8558 * @note Must be called only from #uninit() or from #i_registeredInit().
8559 */
8560void Machine::uninitDataAndChildObjects()
8561{
8562 AutoCaller autoCaller(this);
8563 AssertComRCReturnVoid(autoCaller.rc());
8564 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8565 || getObjectState().getState() == ObjectState::Limited);
8566
8567 /* tell all our other child objects we've been uninitialized */
8568 if (mBandwidthControl)
8569 {
8570 mBandwidthControl->uninit();
8571 unconst(mBandwidthControl).setNull();
8572 }
8573
8574 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8575 {
8576 if (mNetworkAdapters[slot])
8577 {
8578 mNetworkAdapters[slot]->uninit();
8579 unconst(mNetworkAdapters[slot]).setNull();
8580 }
8581 }
8582
8583 if (mUSBDeviceFilters)
8584 {
8585 mUSBDeviceFilters->uninit();
8586 unconst(mUSBDeviceFilters).setNull();
8587 }
8588
8589 if (mAudioAdapter)
8590 {
8591 mAudioAdapter->uninit();
8592 unconst(mAudioAdapter).setNull();
8593 }
8594
8595 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8596 {
8597 if (mParallelPorts[slot])
8598 {
8599 mParallelPorts[slot]->uninit();
8600 unconst(mParallelPorts[slot]).setNull();
8601 }
8602 }
8603
8604 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8605 {
8606 if (mSerialPorts[slot])
8607 {
8608 mSerialPorts[slot]->uninit();
8609 unconst(mSerialPorts[slot]).setNull();
8610 }
8611 }
8612
8613 if (mVRDEServer)
8614 {
8615 mVRDEServer->uninit();
8616 unconst(mVRDEServer).setNull();
8617 }
8618
8619 if (mBIOSSettings)
8620 {
8621 mBIOSSettings->uninit();
8622 unconst(mBIOSSettings).setNull();
8623 }
8624
8625 /* Deassociate media (only when a real Machine or a SnapshotMachine
8626 * instance is uninitialized; SessionMachine instances refer to real
8627 * Machine media). This is necessary for a clean re-initialization of
8628 * the VM after successfully re-checking the accessibility state. Note
8629 * that in case of normal Machine or SnapshotMachine uninitialization (as
8630 * a result of unregistering or deleting the snapshot), outdated media
8631 * attachments will already be uninitialized and deleted, so this
8632 * code will not affect them. */
8633 if ( !mMediumAttachments.isNull()
8634 && !i_isSessionMachine()
8635 )
8636 {
8637 for (MediumAttachmentList::const_iterator
8638 it = mMediumAttachments->begin();
8639 it != mMediumAttachments->end();
8640 ++it)
8641 {
8642 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8643 if (pMedium.isNull())
8644 continue;
8645 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8646 AssertComRC(rc);
8647 }
8648 }
8649
8650 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8651 {
8652 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8653 if (mData->mFirstSnapshot)
8654 {
8655 // snapshots tree is protected by machine write lock; strictly
8656 // this isn't necessary here since we're deleting the entire
8657 // machine, but otherwise we assert in Snapshot::uninit()
8658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8659 mData->mFirstSnapshot->uninit();
8660 mData->mFirstSnapshot.setNull();
8661 }
8662
8663 mData->mCurrentSnapshot.setNull();
8664 }
8665
8666 /* free data structures (the essential mData structure is not freed here
8667 * since it may be still in use) */
8668 mMediumAttachments.free();
8669 mStorageControllers.free();
8670 mUSBControllers.free();
8671 mHWData.free();
8672 mUserData.free();
8673 mSSData.free();
8674}
8675
8676/**
8677 * Returns a pointer to the Machine object for this machine that acts like a
8678 * parent for complex machine data objects such as shared folders, etc.
8679 *
8680 * For primary Machine objects and for SnapshotMachine objects, returns this
8681 * object's pointer itself. For SessionMachine objects, returns the peer
8682 * (primary) machine pointer.
8683 */
8684Machine *Machine::i_getMachine()
8685{
8686 if (i_isSessionMachine())
8687 return (Machine*)mPeer;
8688 return this;
8689}
8690
8691/**
8692 * Makes sure that there are no machine state dependents. If necessary, waits
8693 * for the number of dependents to drop to zero.
8694 *
8695 * Make sure this method is called from under this object's write lock to
8696 * guarantee that no new dependents may be added when this method returns
8697 * control to the caller.
8698 *
8699 * @note Locks this object for writing. The lock will be released while waiting
8700 * (if necessary).
8701 *
8702 * @warning To be used only in methods that change the machine state!
8703 */
8704void Machine::i_ensureNoStateDependencies()
8705{
8706 AssertReturnVoid(isWriteLockOnCurrentThread());
8707
8708 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8709
8710 /* Wait for all state dependents if necessary */
8711 if (mData->mMachineStateDeps != 0)
8712 {
8713 /* lazy semaphore creation */
8714 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8715 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8716
8717 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8718 mData->mMachineStateDeps));
8719
8720 ++mData->mMachineStateChangePending;
8721
8722 /* reset the semaphore before waiting, the last dependent will signal
8723 * it */
8724 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8725
8726 alock.release();
8727
8728 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8729
8730 alock.acquire();
8731
8732 -- mData->mMachineStateChangePending;
8733 }
8734}
8735
8736/**
8737 * Changes the machine state and informs callbacks.
8738 *
8739 * This method is not intended to fail so it either returns S_OK or asserts (and
8740 * returns a failure).
8741 *
8742 * @note Locks this object for writing.
8743 */
8744HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8745{
8746 LogFlowThisFuncEnter();
8747 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8748 Assert(aMachineState != MachineState_Null);
8749
8750 AutoCaller autoCaller(this);
8751 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8752
8753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8754
8755 /* wait for state dependents to drop to zero */
8756 i_ensureNoStateDependencies();
8757
8758 MachineState_T const enmOldState = mData->mMachineState;
8759 if (enmOldState != aMachineState)
8760 {
8761 mData->mMachineState = aMachineState;
8762 RTTimeNow(&mData->mLastStateChange);
8763
8764#ifdef VBOX_WITH_DTRACE_R3_MAIN
8765 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8766#endif
8767 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8768 }
8769
8770 LogFlowThisFuncLeave();
8771 return S_OK;
8772}
8773
8774/**
8775 * Searches for a shared folder with the given logical name
8776 * in the collection of shared folders.
8777 *
8778 * @param aName logical name of the shared folder
8779 * @param aSharedFolder where to return the found object
8780 * @param aSetError whether to set the error info if the folder is
8781 * not found
8782 * @return
8783 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8784 *
8785 * @note
8786 * must be called from under the object's lock!
8787 */
8788HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8789 ComObjPtr<SharedFolder> &aSharedFolder,
8790 bool aSetError /* = false */)
8791{
8792 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8793 for (HWData::SharedFolderList::const_iterator
8794 it = mHWData->mSharedFolders.begin();
8795 it != mHWData->mSharedFolders.end();
8796 ++it)
8797 {
8798 SharedFolder *pSF = *it;
8799 AutoCaller autoCaller(pSF);
8800 if (pSF->i_getName() == aName)
8801 {
8802 aSharedFolder = pSF;
8803 rc = S_OK;
8804 break;
8805 }
8806 }
8807
8808 if (aSetError && FAILED(rc))
8809 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8810
8811 return rc;
8812}
8813
8814/**
8815 * Initializes all machine instance data from the given settings structures
8816 * from XML. The exception is the machine UUID which needs special handling
8817 * depending on the caller's use case, so the caller needs to set that herself.
8818 *
8819 * This gets called in several contexts during machine initialization:
8820 *
8821 * -- When machine XML exists on disk already and needs to be loaded into memory,
8822 * for example, from #i_registeredInit() to load all registered machines on
8823 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8824 * attached to the machine should be part of some media registry already.
8825 *
8826 * -- During OVF import, when a machine config has been constructed from an
8827 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8828 * ensure that the media listed as attachments in the config (which have
8829 * been imported from the OVF) receive the correct registry ID.
8830 *
8831 * -- During VM cloning.
8832 *
8833 * @param config Machine settings from XML.
8834 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8835 * for each attached medium in the config.
8836 * @return
8837 */
8838HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8839 const Guid *puuidRegistry)
8840{
8841 // copy name, description, OS type, teleporter, UTC etc.
8842 mUserData->s = config.machineUserData;
8843
8844 // look up the object by Id to check it is valid
8845 ComObjPtr<GuestOSType> pGuestOSType;
8846 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8847 if (!pGuestOSType.isNull())
8848 mUserData->s.strOsType = pGuestOSType->i_id();
8849
8850 // stateFile (optional)
8851 if (config.strStateFile.isEmpty())
8852 mSSData->strStateFilePath.setNull();
8853 else
8854 {
8855 Utf8Str stateFilePathFull(config.strStateFile);
8856 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8857 if (RT_FAILURE(vrc))
8858 return setErrorBoth(E_FAIL, vrc,
8859 tr("Invalid saved state file path '%s' (%Rrc)"),
8860 config.strStateFile.c_str(),
8861 vrc);
8862 mSSData->strStateFilePath = stateFilePathFull;
8863 }
8864
8865 // snapshot folder needs special processing so set it again
8866 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8867 if (FAILED(rc)) return rc;
8868
8869 /* Copy the extra data items (config may or may not be the same as
8870 * mData->pMachineConfigFile) if necessary. When loading the XML files
8871 * from disk they are the same, but not for OVF import. */
8872 if (mData->pMachineConfigFile != &config)
8873 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8874
8875 /* currentStateModified (optional, default is true) */
8876 mData->mCurrentStateModified = config.fCurrentStateModified;
8877
8878 mData->mLastStateChange = config.timeLastStateChange;
8879
8880 /*
8881 * note: all mUserData members must be assigned prior this point because
8882 * we need to commit changes in order to let mUserData be shared by all
8883 * snapshot machine instances.
8884 */
8885 mUserData.commitCopy();
8886
8887 // machine registry, if present (must be loaded before snapshots)
8888 if (config.canHaveOwnMediaRegistry())
8889 {
8890 // determine machine folder
8891 Utf8Str strMachineFolder = i_getSettingsFileFull();
8892 strMachineFolder.stripFilename();
8893 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8894 config.mediaRegistry,
8895 strMachineFolder);
8896 if (FAILED(rc)) return rc;
8897 }
8898
8899 /* Snapshot node (optional) */
8900 size_t cRootSnapshots;
8901 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8902 {
8903 // there must be only one root snapshot
8904 Assert(cRootSnapshots == 1);
8905
8906 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8907
8908 rc = i_loadSnapshot(snap,
8909 config.uuidCurrentSnapshot,
8910 NULL); // no parent == first snapshot
8911 if (FAILED(rc)) return rc;
8912 }
8913
8914 // hardware data
8915 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8916 if (FAILED(rc)) return rc;
8917
8918 /*
8919 * NOTE: the assignment below must be the last thing to do,
8920 * otherwise it will be not possible to change the settings
8921 * somewhere in the code above because all setters will be
8922 * blocked by i_checkStateDependency(MutableStateDep).
8923 */
8924
8925 /* set the machine state to Aborted or Saved when appropriate */
8926 if (config.fAborted)
8927 {
8928 mSSData->strStateFilePath.setNull();
8929
8930 /* no need to use i_setMachineState() during init() */
8931 mData->mMachineState = MachineState_Aborted;
8932 }
8933 else if (!mSSData->strStateFilePath.isEmpty())
8934 {
8935 /* no need to use i_setMachineState() during init() */
8936 mData->mMachineState = MachineState_Saved;
8937 }
8938
8939 // after loading settings, we are no longer different from the XML on disk
8940 mData->flModifications = 0;
8941
8942 return S_OK;
8943}
8944
8945/**
8946 * Recursively loads all snapshots starting from the given.
8947 *
8948 * @param data snapshot settings.
8949 * @param aCurSnapshotId Current snapshot ID from the settings file.
8950 * @param aParentSnapshot Parent snapshot.
8951 */
8952HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8953 const Guid &aCurSnapshotId,
8954 Snapshot *aParentSnapshot)
8955{
8956 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8957 AssertReturn(!i_isSessionMachine(), E_FAIL);
8958
8959 HRESULT rc = S_OK;
8960
8961 Utf8Str strStateFile;
8962 if (!data.strStateFile.isEmpty())
8963 {
8964 /* optional */
8965 strStateFile = data.strStateFile;
8966 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8967 if (RT_FAILURE(vrc))
8968 return setErrorBoth(E_FAIL, vrc,
8969 tr("Invalid saved state file path '%s' (%Rrc)"),
8970 strStateFile.c_str(),
8971 vrc);
8972 }
8973
8974 /* create a snapshot machine object */
8975 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8976 pSnapshotMachine.createObject();
8977 rc = pSnapshotMachine->initFromSettings(this,
8978 data.hardware,
8979 &data.debugging,
8980 &data.autostart,
8981 data.uuid.ref(),
8982 strStateFile);
8983 if (FAILED(rc)) return rc;
8984
8985 /* create a snapshot object */
8986 ComObjPtr<Snapshot> pSnapshot;
8987 pSnapshot.createObject();
8988 /* initialize the snapshot */
8989 rc = pSnapshot->init(mParent, // VirtualBox object
8990 data.uuid,
8991 data.strName,
8992 data.strDescription,
8993 data.timestamp,
8994 pSnapshotMachine,
8995 aParentSnapshot);
8996 if (FAILED(rc)) return rc;
8997
8998 /* memorize the first snapshot if necessary */
8999 if (!mData->mFirstSnapshot)
9000 mData->mFirstSnapshot = pSnapshot;
9001
9002 /* memorize the current snapshot when appropriate */
9003 if ( !mData->mCurrentSnapshot
9004 && pSnapshot->i_getId() == aCurSnapshotId
9005 )
9006 mData->mCurrentSnapshot = pSnapshot;
9007
9008 // now create the children
9009 for (settings::SnapshotsList::const_iterator
9010 it = data.llChildSnapshots.begin();
9011 it != data.llChildSnapshots.end();
9012 ++it)
9013 {
9014 const settings::Snapshot &childData = *it;
9015 // recurse
9016 rc = i_loadSnapshot(childData,
9017 aCurSnapshotId,
9018 pSnapshot); // parent = the one we created above
9019 if (FAILED(rc)) return rc;
9020 }
9021
9022 return rc;
9023}
9024
9025/**
9026 * Loads settings into mHWData.
9027 *
9028 * @param puuidRegistry Registry ID.
9029 * @param puuidSnapshot Snapshot ID
9030 * @param data Reference to the hardware settings.
9031 * @param pDbg Pointer to the debugging settings.
9032 * @param pAutostart Pointer to the autostart settings.
9033 */
9034HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9035 const Guid *puuidSnapshot,
9036 const settings::Hardware &data,
9037 const settings::Debugging *pDbg,
9038 const settings::Autostart *pAutostart)
9039{
9040 AssertReturn(!i_isSessionMachine(), E_FAIL);
9041
9042 HRESULT rc = S_OK;
9043
9044 try
9045 {
9046 ComObjPtr<GuestOSType> pGuestOSType;
9047 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9048
9049 /* The hardware version attribute (optional). */
9050 mHWData->mHWVersion = data.strVersion;
9051 mHWData->mHardwareUUID = data.uuid;
9052
9053 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9054 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9055 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9056 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9057 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9058 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9059 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9060 mHWData->mPAEEnabled = data.fPAE;
9061 mHWData->mLongMode = data.enmLongMode;
9062 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9063 mHWData->mAPIC = data.fAPIC;
9064 mHWData->mX2APIC = data.fX2APIC;
9065 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9066 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9067 mHWData->mSpecCtrl = data.fSpecCtrl;
9068 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9069 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9070 mHWData->mCPUCount = data.cCPUs;
9071 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9072 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9073 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9074 mHWData->mCpuProfile = data.strCpuProfile;
9075
9076 // cpu
9077 if (mHWData->mCPUHotPlugEnabled)
9078 {
9079 for (settings::CpuList::const_iterator
9080 it = data.llCpus.begin();
9081 it != data.llCpus.end();
9082 ++it)
9083 {
9084 const settings::Cpu &cpu = *it;
9085
9086 mHWData->mCPUAttached[cpu.ulId] = true;
9087 }
9088 }
9089
9090 // cpuid leafs
9091 for (settings::CpuIdLeafsList::const_iterator
9092 it = data.llCpuIdLeafs.begin();
9093 it != data.llCpuIdLeafs.end();
9094 ++it)
9095 {
9096 const settings::CpuIdLeaf &rLeaf= *it;
9097 if ( rLeaf.idx < UINT32_C(0x20)
9098 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9099 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9100 mHWData->mCpuIdLeafList.push_back(rLeaf);
9101 /* else: just ignore */
9102 }
9103
9104 mHWData->mMemorySize = data.ulMemorySizeMB;
9105 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9106
9107 // boot order
9108 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9109 {
9110 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9111 if (it == data.mapBootOrder.end())
9112 mHWData->mBootOrder[i] = DeviceType_Null;
9113 else
9114 mHWData->mBootOrder[i] = it->second;
9115 }
9116
9117 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9118 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9119 mHWData->mMonitorCount = data.cMonitors;
9120 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9121 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9122 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9123 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9124 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9125 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9126 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9127 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9128 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9129 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9130 if (!data.strVideoCaptureFile.isEmpty())
9131 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9132 else
9133 mHWData->mVideoCaptureFile.setNull();
9134 mHWData->mVideoCaptureOptions = data.strVideoCaptureOptions;
9135 mHWData->mFirmwareType = data.firmwareType;
9136 mHWData->mPointingHIDType = data.pointingHIDType;
9137 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9138 mHWData->mChipsetType = data.chipsetType;
9139 mHWData->mParavirtProvider = data.paravirtProvider;
9140 mHWData->mParavirtDebug = data.strParavirtDebug;
9141 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9142 mHWData->mHPETEnabled = data.fHPETEnabled;
9143
9144 /* VRDEServer */
9145 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9146 if (FAILED(rc)) return rc;
9147
9148 /* BIOS */
9149 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9150 if (FAILED(rc)) return rc;
9151
9152 // Bandwidth control (must come before network adapters)
9153 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9154 if (FAILED(rc)) return rc;
9155
9156 /* Shared folders */
9157 for (settings::USBControllerList::const_iterator
9158 it = data.usbSettings.llUSBControllers.begin();
9159 it != data.usbSettings.llUSBControllers.end();
9160 ++it)
9161 {
9162 const settings::USBController &settingsCtrl = *it;
9163 ComObjPtr<USBController> newCtrl;
9164
9165 newCtrl.createObject();
9166 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9167 mUSBControllers->push_back(newCtrl);
9168 }
9169
9170 /* USB device filters */
9171 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9172 if (FAILED(rc)) return rc;
9173
9174 // network adapters (establish array size first and apply defaults, to
9175 // ensure reading the same settings as we saved, since the list skips
9176 // adapters having defaults)
9177 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9178 size_t oldCount = mNetworkAdapters.size();
9179 if (newCount > oldCount)
9180 {
9181 mNetworkAdapters.resize(newCount);
9182 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9183 {
9184 unconst(mNetworkAdapters[slot]).createObject();
9185 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9186 }
9187 }
9188 else if (newCount < oldCount)
9189 mNetworkAdapters.resize(newCount);
9190 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9191 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9192 for (settings::NetworkAdaptersList::const_iterator
9193 it = data.llNetworkAdapters.begin();
9194 it != data.llNetworkAdapters.end();
9195 ++it)
9196 {
9197 const settings::NetworkAdapter &nic = *it;
9198
9199 /* slot uniqueness is guaranteed by XML Schema */
9200 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9201 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9202 if (FAILED(rc)) return rc;
9203 }
9204
9205 // serial ports (establish defaults first, to ensure reading the same
9206 // settings as we saved, since the list skips ports having defaults)
9207 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9208 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9209 for (settings::SerialPortsList::const_iterator
9210 it = data.llSerialPorts.begin();
9211 it != data.llSerialPorts.end();
9212 ++it)
9213 {
9214 const settings::SerialPort &s = *it;
9215
9216 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9217 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9218 if (FAILED(rc)) return rc;
9219 }
9220
9221 // parallel ports (establish defaults first, to ensure reading the same
9222 // settings as we saved, since the list skips ports having defaults)
9223 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9224 mParallelPorts[i]->i_applyDefaults();
9225 for (settings::ParallelPortsList::const_iterator
9226 it = data.llParallelPorts.begin();
9227 it != data.llParallelPorts.end();
9228 ++it)
9229 {
9230 const settings::ParallelPort &p = *it;
9231
9232 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9233 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9234 if (FAILED(rc)) return rc;
9235 }
9236
9237 /* AudioAdapter */
9238 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9239 if (FAILED(rc)) return rc;
9240
9241 /* storage controllers */
9242 rc = i_loadStorageControllers(data.storage,
9243 puuidRegistry,
9244 puuidSnapshot);
9245 if (FAILED(rc)) return rc;
9246
9247 /* Shared folders */
9248 for (settings::SharedFoldersList::const_iterator
9249 it = data.llSharedFolders.begin();
9250 it != data.llSharedFolders.end();
9251 ++it)
9252 {
9253 const settings::SharedFolder &sf = *it;
9254
9255 ComObjPtr<SharedFolder> sharedFolder;
9256 /* Check for double entries. Not allowed! */
9257 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9258 if (SUCCEEDED(rc))
9259 return setError(VBOX_E_OBJECT_IN_USE,
9260 tr("Shared folder named '%s' already exists"),
9261 sf.strName.c_str());
9262
9263 /* Create the new shared folder. Don't break on error. This will be
9264 * reported when the machine starts. */
9265 sharedFolder.createObject();
9266 rc = sharedFolder->init(i_getMachine(),
9267 sf.strName,
9268 sf.strHostPath,
9269 RT_BOOL(sf.fWritable),
9270 RT_BOOL(sf.fAutoMount),
9271 false /* fFailOnError */);
9272 if (FAILED(rc)) return rc;
9273 mHWData->mSharedFolders.push_back(sharedFolder);
9274 }
9275
9276 // Clipboard
9277 mHWData->mClipboardMode = data.clipboardMode;
9278
9279 // drag'n'drop
9280 mHWData->mDnDMode = data.dndMode;
9281
9282 // guest settings
9283 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9284
9285 // IO settings
9286 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9287 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9288
9289 // Host PCI devices
9290 for (settings::HostPCIDeviceAttachmentList::const_iterator
9291 it = data.pciAttachments.begin();
9292 it != data.pciAttachments.end();
9293 ++it)
9294 {
9295 const settings::HostPCIDeviceAttachment &hpda = *it;
9296 ComObjPtr<PCIDeviceAttachment> pda;
9297
9298 pda.createObject();
9299 pda->i_loadSettings(this, hpda);
9300 mHWData->mPCIDeviceAssignments.push_back(pda);
9301 }
9302
9303 /*
9304 * (The following isn't really real hardware, but it lives in HWData
9305 * for reasons of convenience.)
9306 */
9307
9308#ifdef VBOX_WITH_GUEST_PROPS
9309 /* Guest properties (optional) */
9310
9311 /* Only load transient guest properties for configs which have saved
9312 * state, because there shouldn't be any for powered off VMs. The same
9313 * logic applies for snapshots, as offline snapshots shouldn't have
9314 * any such properties. They confuse the code in various places.
9315 * Note: can't rely on the machine state, as it isn't set yet. */
9316 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9317 /* apologies for the hacky unconst() usage, but this needs hacking
9318 * actually inconsistent settings into consistency, otherwise there
9319 * will be some corner cases where the inconsistency survives
9320 * surprisingly long without getting fixed, especially for snapshots
9321 * as there are no config changes. */
9322 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9323 for (settings::GuestPropertiesList::iterator
9324 it = llGuestProperties.begin();
9325 it != llGuestProperties.end();
9326 /*nothing*/)
9327 {
9328 const settings::GuestProperty &prop = *it;
9329 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9330 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9331 if ( fSkipTransientGuestProperties
9332 && ( fFlags & GUEST_PROP_F_TRANSIENT
9333 || fFlags & GUEST_PROP_F_TRANSRESET))
9334 {
9335 it = llGuestProperties.erase(it);
9336 continue;
9337 }
9338 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9339 mHWData->mGuestProperties[prop.strName] = property;
9340 ++it;
9341 }
9342#endif /* VBOX_WITH_GUEST_PROPS defined */
9343
9344 rc = i_loadDebugging(pDbg);
9345 if (FAILED(rc))
9346 return rc;
9347
9348 mHWData->mAutostart = *pAutostart;
9349
9350 /* default frontend */
9351 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9352 }
9353 catch (std::bad_alloc &)
9354 {
9355 return E_OUTOFMEMORY;
9356 }
9357
9358 AssertComRC(rc);
9359 return rc;
9360}
9361
9362/**
9363 * Called from i_loadHardware() to load the debugging settings of the
9364 * machine.
9365 *
9366 * @param pDbg Pointer to the settings.
9367 */
9368HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9369{
9370 mHWData->mDebugging = *pDbg;
9371 /* no more processing currently required, this will probably change. */
9372 return S_OK;
9373}
9374
9375/**
9376 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9377 *
9378 * @param data storage settings.
9379 * @param puuidRegistry media registry ID to set media to or NULL;
9380 * see Machine::i_loadMachineDataFromSettings()
9381 * @param puuidSnapshot snapshot ID
9382 * @return
9383 */
9384HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9385 const Guid *puuidRegistry,
9386 const Guid *puuidSnapshot)
9387{
9388 AssertReturn(!i_isSessionMachine(), E_FAIL);
9389
9390 HRESULT rc = S_OK;
9391
9392 for (settings::StorageControllersList::const_iterator
9393 it = data.llStorageControllers.begin();
9394 it != data.llStorageControllers.end();
9395 ++it)
9396 {
9397 const settings::StorageController &ctlData = *it;
9398
9399 ComObjPtr<StorageController> pCtl;
9400 /* Try to find one with the name first. */
9401 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9402 if (SUCCEEDED(rc))
9403 return setError(VBOX_E_OBJECT_IN_USE,
9404 tr("Storage controller named '%s' already exists"),
9405 ctlData.strName.c_str());
9406
9407 pCtl.createObject();
9408 rc = pCtl->init(this,
9409 ctlData.strName,
9410 ctlData.storageBus,
9411 ctlData.ulInstance,
9412 ctlData.fBootable);
9413 if (FAILED(rc)) return rc;
9414
9415 mStorageControllers->push_back(pCtl);
9416
9417 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9418 if (FAILED(rc)) return rc;
9419
9420 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9421 if (FAILED(rc)) return rc;
9422
9423 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9424 if (FAILED(rc)) return rc;
9425
9426 /* Load the attached devices now. */
9427 rc = i_loadStorageDevices(pCtl,
9428 ctlData,
9429 puuidRegistry,
9430 puuidSnapshot);
9431 if (FAILED(rc)) return rc;
9432 }
9433
9434 return S_OK;
9435}
9436
9437/**
9438 * Called from i_loadStorageControllers for a controller's devices.
9439 *
9440 * @param aStorageController
9441 * @param data
9442 * @param puuidRegistry media registry ID to set media to or NULL; see
9443 * Machine::i_loadMachineDataFromSettings()
9444 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9445 * @return
9446 */
9447HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9448 const settings::StorageController &data,
9449 const Guid *puuidRegistry,
9450 const Guid *puuidSnapshot)
9451{
9452 HRESULT rc = S_OK;
9453
9454 /* paranoia: detect duplicate attachments */
9455 for (settings::AttachedDevicesList::const_iterator
9456 it = data.llAttachedDevices.begin();
9457 it != data.llAttachedDevices.end();
9458 ++it)
9459 {
9460 const settings::AttachedDevice &ad = *it;
9461
9462 for (settings::AttachedDevicesList::const_iterator it2 = it;
9463 it2 != data.llAttachedDevices.end();
9464 ++it2)
9465 {
9466 if (it == it2)
9467 continue;
9468
9469 const settings::AttachedDevice &ad2 = *it2;
9470
9471 if ( ad.lPort == ad2.lPort
9472 && ad.lDevice == ad2.lDevice)
9473 {
9474 return setError(E_FAIL,
9475 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9476 aStorageController->i_getName().c_str(),
9477 ad.lPort,
9478 ad.lDevice,
9479 mUserData->s.strName.c_str());
9480 }
9481 }
9482 }
9483
9484 for (settings::AttachedDevicesList::const_iterator
9485 it = data.llAttachedDevices.begin();
9486 it != data.llAttachedDevices.end();
9487 ++it)
9488 {
9489 const settings::AttachedDevice &dev = *it;
9490 ComObjPtr<Medium> medium;
9491
9492 switch (dev.deviceType)
9493 {
9494 case DeviceType_Floppy:
9495 case DeviceType_DVD:
9496 if (dev.strHostDriveSrc.isNotEmpty())
9497 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9498 false /* fRefresh */, medium);
9499 else
9500 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9501 dev.uuid,
9502 false /* fRefresh */,
9503 false /* aSetError */,
9504 medium);
9505 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9506 // This is not an error. The host drive or UUID might have vanished, so just go
9507 // ahead without this removeable medium attachment
9508 rc = S_OK;
9509 break;
9510
9511 case DeviceType_HardDisk:
9512 {
9513 /* find a hard disk by UUID */
9514 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9515 if (FAILED(rc))
9516 {
9517 if (i_isSnapshotMachine())
9518 {
9519 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9520 // so the user knows that the bad disk is in a snapshot somewhere
9521 com::ErrorInfo info;
9522 return setError(E_FAIL,
9523 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9524 puuidSnapshot->raw(),
9525 info.getText().raw());
9526 }
9527 else
9528 return rc;
9529 }
9530
9531 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9532
9533 if (medium->i_getType() == MediumType_Immutable)
9534 {
9535 if (i_isSnapshotMachine())
9536 return setError(E_FAIL,
9537 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9538 "of the virtual machine '%s' ('%s')"),
9539 medium->i_getLocationFull().c_str(),
9540 dev.uuid.raw(),
9541 puuidSnapshot->raw(),
9542 mUserData->s.strName.c_str(),
9543 mData->m_strConfigFileFull.c_str());
9544
9545 return setError(E_FAIL,
9546 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9547 medium->i_getLocationFull().c_str(),
9548 dev.uuid.raw(),
9549 mUserData->s.strName.c_str(),
9550 mData->m_strConfigFileFull.c_str());
9551 }
9552
9553 if (medium->i_getType() == MediumType_MultiAttach)
9554 {
9555 if (i_isSnapshotMachine())
9556 return setError(E_FAIL,
9557 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9558 "of the virtual machine '%s' ('%s')"),
9559 medium->i_getLocationFull().c_str(),
9560 dev.uuid.raw(),
9561 puuidSnapshot->raw(),
9562 mUserData->s.strName.c_str(),
9563 mData->m_strConfigFileFull.c_str());
9564
9565 return setError(E_FAIL,
9566 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9567 medium->i_getLocationFull().c_str(),
9568 dev.uuid.raw(),
9569 mUserData->s.strName.c_str(),
9570 mData->m_strConfigFileFull.c_str());
9571 }
9572
9573 if ( !i_isSnapshotMachine()
9574 && medium->i_getChildren().size() != 0
9575 )
9576 return setError(E_FAIL,
9577 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9578 "because it has %d differencing child hard disks"),
9579 medium->i_getLocationFull().c_str(),
9580 dev.uuid.raw(),
9581 mUserData->s.strName.c_str(),
9582 mData->m_strConfigFileFull.c_str(),
9583 medium->i_getChildren().size());
9584
9585 if (i_findAttachment(*mMediumAttachments.data(),
9586 medium))
9587 return setError(E_FAIL,
9588 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9589 medium->i_getLocationFull().c_str(),
9590 dev.uuid.raw(),
9591 mUserData->s.strName.c_str(),
9592 mData->m_strConfigFileFull.c_str());
9593
9594 break;
9595 }
9596
9597 default:
9598 return setError(E_FAIL,
9599 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9600 medium->i_getLocationFull().c_str(),
9601 mUserData->s.strName.c_str(),
9602 mData->m_strConfigFileFull.c_str());
9603 }
9604
9605 if (FAILED(rc))
9606 break;
9607
9608 /* Bandwidth groups are loaded at this point. */
9609 ComObjPtr<BandwidthGroup> pBwGroup;
9610
9611 if (!dev.strBwGroup.isEmpty())
9612 {
9613 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9614 if (FAILED(rc))
9615 return setError(E_FAIL,
9616 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9617 medium->i_getLocationFull().c_str(),
9618 dev.strBwGroup.c_str(),
9619 mUserData->s.strName.c_str(),
9620 mData->m_strConfigFileFull.c_str());
9621 pBwGroup->i_reference();
9622 }
9623
9624 const Utf8Str controllerName = aStorageController->i_getName();
9625 ComObjPtr<MediumAttachment> pAttachment;
9626 pAttachment.createObject();
9627 rc = pAttachment->init(this,
9628 medium,
9629 controllerName,
9630 dev.lPort,
9631 dev.lDevice,
9632 dev.deviceType,
9633 false,
9634 dev.fPassThrough,
9635 dev.fTempEject,
9636 dev.fNonRotational,
9637 dev.fDiscard,
9638 dev.fHotPluggable,
9639 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9640 if (FAILED(rc)) break;
9641
9642 /* associate the medium with this machine and snapshot */
9643 if (!medium.isNull())
9644 {
9645 AutoCaller medCaller(medium);
9646 if (FAILED(medCaller.rc())) return medCaller.rc();
9647 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9648
9649 if (i_isSnapshotMachine())
9650 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9651 else
9652 rc = medium->i_addBackReference(mData->mUuid);
9653 /* If the medium->addBackReference fails it sets an appropriate
9654 * error message, so no need to do any guesswork here. */
9655
9656 if (puuidRegistry)
9657 // caller wants registry ID to be set on all attached media (OVF import case)
9658 medium->i_addRegistry(*puuidRegistry);
9659 }
9660
9661 if (FAILED(rc))
9662 break;
9663
9664 /* back up mMediumAttachments to let registeredInit() properly rollback
9665 * on failure (= limited accessibility) */
9666 i_setModified(IsModified_Storage);
9667 mMediumAttachments.backup();
9668 mMediumAttachments->push_back(pAttachment);
9669 }
9670
9671 return rc;
9672}
9673
9674/**
9675 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9676 *
9677 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9678 * @param aSnapshot where to return the found snapshot
9679 * @param aSetError true to set extended error info on failure
9680 */
9681HRESULT Machine::i_findSnapshotById(const Guid &aId,
9682 ComObjPtr<Snapshot> &aSnapshot,
9683 bool aSetError /* = false */)
9684{
9685 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9686
9687 if (!mData->mFirstSnapshot)
9688 {
9689 if (aSetError)
9690 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9691 return E_FAIL;
9692 }
9693
9694 if (aId.isZero())
9695 aSnapshot = mData->mFirstSnapshot;
9696 else
9697 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9698
9699 if (!aSnapshot)
9700 {
9701 if (aSetError)
9702 return setError(E_FAIL,
9703 tr("Could not find a snapshot with UUID {%s}"),
9704 aId.toString().c_str());
9705 return E_FAIL;
9706 }
9707
9708 return S_OK;
9709}
9710
9711/**
9712 * Returns the snapshot with the given name or fails of no such snapshot.
9713 *
9714 * @param strName snapshot name to find
9715 * @param aSnapshot where to return the found snapshot
9716 * @param aSetError true to set extended error info on failure
9717 */
9718HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9719 ComObjPtr<Snapshot> &aSnapshot,
9720 bool aSetError /* = false */)
9721{
9722 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9723
9724 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9725
9726 if (!mData->mFirstSnapshot)
9727 {
9728 if (aSetError)
9729 return setError(VBOX_E_OBJECT_NOT_FOUND,
9730 tr("This machine does not have any snapshots"));
9731 return VBOX_E_OBJECT_NOT_FOUND;
9732 }
9733
9734 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9735
9736 if (!aSnapshot)
9737 {
9738 if (aSetError)
9739 return setError(VBOX_E_OBJECT_NOT_FOUND,
9740 tr("Could not find a snapshot named '%s'"), strName.c_str());
9741 return VBOX_E_OBJECT_NOT_FOUND;
9742 }
9743
9744 return S_OK;
9745}
9746
9747/**
9748 * Returns a storage controller object with the given name.
9749 *
9750 * @param aName storage controller name to find
9751 * @param aStorageController where to return the found storage controller
9752 * @param aSetError true to set extended error info on failure
9753 */
9754HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9755 ComObjPtr<StorageController> &aStorageController,
9756 bool aSetError /* = false */)
9757{
9758 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9759
9760 for (StorageControllerList::const_iterator
9761 it = mStorageControllers->begin();
9762 it != mStorageControllers->end();
9763 ++it)
9764 {
9765 if ((*it)->i_getName() == aName)
9766 {
9767 aStorageController = (*it);
9768 return S_OK;
9769 }
9770 }
9771
9772 if (aSetError)
9773 return setError(VBOX_E_OBJECT_NOT_FOUND,
9774 tr("Could not find a storage controller named '%s'"),
9775 aName.c_str());
9776 return VBOX_E_OBJECT_NOT_FOUND;
9777}
9778
9779/**
9780 * Returns a USB controller object with the given name.
9781 *
9782 * @param aName USB controller name to find
9783 * @param aUSBController where to return the found USB controller
9784 * @param aSetError true to set extended error info on failure
9785 */
9786HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9787 ComObjPtr<USBController> &aUSBController,
9788 bool aSetError /* = false */)
9789{
9790 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9791
9792 for (USBControllerList::const_iterator
9793 it = mUSBControllers->begin();
9794 it != mUSBControllers->end();
9795 ++it)
9796 {
9797 if ((*it)->i_getName() == aName)
9798 {
9799 aUSBController = (*it);
9800 return S_OK;
9801 }
9802 }
9803
9804 if (aSetError)
9805 return setError(VBOX_E_OBJECT_NOT_FOUND,
9806 tr("Could not find a storage controller named '%s'"),
9807 aName.c_str());
9808 return VBOX_E_OBJECT_NOT_FOUND;
9809}
9810
9811/**
9812 * Returns the number of USB controller instance of the given type.
9813 *
9814 * @param enmType USB controller type.
9815 */
9816ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9817{
9818 ULONG cCtrls = 0;
9819
9820 for (USBControllerList::const_iterator
9821 it = mUSBControllers->begin();
9822 it != mUSBControllers->end();
9823 ++it)
9824 {
9825 if ((*it)->i_getControllerType() == enmType)
9826 cCtrls++;
9827 }
9828
9829 return cCtrls;
9830}
9831
9832HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9833 MediumAttachmentList &atts)
9834{
9835 AutoCaller autoCaller(this);
9836 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9837
9838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9839
9840 for (MediumAttachmentList::const_iterator
9841 it = mMediumAttachments->begin();
9842 it != mMediumAttachments->end();
9843 ++it)
9844 {
9845 const ComObjPtr<MediumAttachment> &pAtt = *it;
9846 // should never happen, but deal with NULL pointers in the list.
9847 AssertContinue(!pAtt.isNull());
9848
9849 // getControllerName() needs caller+read lock
9850 AutoCaller autoAttCaller(pAtt);
9851 if (FAILED(autoAttCaller.rc()))
9852 {
9853 atts.clear();
9854 return autoAttCaller.rc();
9855 }
9856 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9857
9858 if (pAtt->i_getControllerName() == aName)
9859 atts.push_back(pAtt);
9860 }
9861
9862 return S_OK;
9863}
9864
9865
9866/**
9867 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9868 * file if the machine name was changed and about creating a new settings file
9869 * if this is a new machine.
9870 *
9871 * @note Must be never called directly but only from #saveSettings().
9872 */
9873HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9874{
9875 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9876
9877 HRESULT rc = S_OK;
9878
9879 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9880
9881 /// @todo need to handle primary group change, too
9882
9883 /* attempt to rename the settings file if machine name is changed */
9884 if ( mUserData->s.fNameSync
9885 && mUserData.isBackedUp()
9886 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9887 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9888 )
9889 {
9890 bool dirRenamed = false;
9891 bool fileRenamed = false;
9892
9893 Utf8Str configFile, newConfigFile;
9894 Utf8Str configFilePrev, newConfigFilePrev;
9895 Utf8Str configDir, newConfigDir;
9896
9897 do
9898 {
9899 int vrc = VINF_SUCCESS;
9900
9901 Utf8Str name = mUserData.backedUpData()->s.strName;
9902 Utf8Str newName = mUserData->s.strName;
9903 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9904 if (group == "/")
9905 group.setNull();
9906 Utf8Str newGroup = mUserData->s.llGroups.front();
9907 if (newGroup == "/")
9908 newGroup.setNull();
9909
9910 configFile = mData->m_strConfigFileFull;
9911
9912 /* first, rename the directory if it matches the group and machine name */
9913 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9914 group.c_str(), RTPATH_DELIMITER, name.c_str());
9915 /** @todo hack, make somehow use of ComposeMachineFilename */
9916 if (mUserData->s.fDirectoryIncludesUUID)
9917 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9918 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9919 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9920 /** @todo hack, make somehow use of ComposeMachineFilename */
9921 if (mUserData->s.fDirectoryIncludesUUID)
9922 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9923 configDir = configFile;
9924 configDir.stripFilename();
9925 newConfigDir = configDir;
9926 if ( configDir.length() >= groupPlusName.length()
9927 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9928 groupPlusName.c_str()))
9929 {
9930 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9931 Utf8Str newConfigBaseDir(newConfigDir);
9932 newConfigDir.append(newGroupPlusName);
9933 /* consistency: use \ if appropriate on the platform */
9934 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9935 /* new dir and old dir cannot be equal here because of 'if'
9936 * above and because name != newName */
9937 Assert(configDir != newConfigDir);
9938 if (!fSettingsFileIsNew)
9939 {
9940 /* perform real rename only if the machine is not new */
9941 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9942 if ( vrc == VERR_FILE_NOT_FOUND
9943 || vrc == VERR_PATH_NOT_FOUND)
9944 {
9945 /* create the parent directory, then retry renaming */
9946 Utf8Str parent(newConfigDir);
9947 parent.stripFilename();
9948 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9949 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9950 }
9951 if (RT_FAILURE(vrc))
9952 {
9953 rc = setErrorBoth(E_FAIL, vrc,
9954 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9955 configDir.c_str(),
9956 newConfigDir.c_str(),
9957 vrc);
9958 break;
9959 }
9960 /* delete subdirectories which are no longer needed */
9961 Utf8Str dir(configDir);
9962 dir.stripFilename();
9963 while (dir != newConfigBaseDir && dir != ".")
9964 {
9965 vrc = RTDirRemove(dir.c_str());
9966 if (RT_FAILURE(vrc))
9967 break;
9968 dir.stripFilename();
9969 }
9970 dirRenamed = true;
9971 }
9972 }
9973
9974 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9975 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9976
9977 /* then try to rename the settings file itself */
9978 if (newConfigFile != configFile)
9979 {
9980 /* get the path to old settings file in renamed directory */
9981 configFile = Utf8StrFmt("%s%c%s",
9982 newConfigDir.c_str(),
9983 RTPATH_DELIMITER,
9984 RTPathFilename(configFile.c_str()));
9985 if (!fSettingsFileIsNew)
9986 {
9987 /* perform real rename only if the machine is not new */
9988 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9989 if (RT_FAILURE(vrc))
9990 {
9991 rc = setErrorBoth(E_FAIL, vrc,
9992 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9993 configFile.c_str(),
9994 newConfigFile.c_str(),
9995 vrc);
9996 break;
9997 }
9998 fileRenamed = true;
9999 configFilePrev = configFile;
10000 configFilePrev += "-prev";
10001 newConfigFilePrev = newConfigFile;
10002 newConfigFilePrev += "-prev";
10003 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10004 }
10005 }
10006
10007 // update m_strConfigFileFull amd mConfigFile
10008 mData->m_strConfigFileFull = newConfigFile;
10009 // compute the relative path too
10010 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10011
10012 // store the old and new so that VirtualBox::i_saveSettings() can update
10013 // the media registry
10014 if ( mData->mRegistered
10015 && (configDir != newConfigDir || configFile != newConfigFile))
10016 {
10017 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10018
10019 if (pfNeedsGlobalSaveSettings)
10020 *pfNeedsGlobalSaveSettings = true;
10021 }
10022
10023 // in the saved state file path, replace the old directory with the new directory
10024 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10025 {
10026 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10027 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10028 }
10029
10030 // and do the same thing for the saved state file paths of all the online snapshots
10031 if (mData->mFirstSnapshot)
10032 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10033 newConfigDir.c_str());
10034 }
10035 while (0);
10036
10037 if (FAILED(rc))
10038 {
10039 /* silently try to rename everything back */
10040 if (fileRenamed)
10041 {
10042 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10043 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10044 }
10045 if (dirRenamed)
10046 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10047 }
10048
10049 if (FAILED(rc)) return rc;
10050 }
10051
10052 if (fSettingsFileIsNew)
10053 {
10054 /* create a virgin config file */
10055 int vrc = VINF_SUCCESS;
10056
10057 /* ensure the settings directory exists */
10058 Utf8Str path(mData->m_strConfigFileFull);
10059 path.stripFilename();
10060 if (!RTDirExists(path.c_str()))
10061 {
10062 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10063 if (RT_FAILURE(vrc))
10064 {
10065 return setErrorBoth(E_FAIL, vrc,
10066 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10067 path.c_str(),
10068 vrc);
10069 }
10070 }
10071
10072 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10073 path = Utf8Str(mData->m_strConfigFileFull);
10074 RTFILE f = NIL_RTFILE;
10075 vrc = RTFileOpen(&f, path.c_str(),
10076 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10077 if (RT_FAILURE(vrc))
10078 return setErrorBoth(E_FAIL, vrc,
10079 tr("Could not create the settings file '%s' (%Rrc)"),
10080 path.c_str(),
10081 vrc);
10082 RTFileClose(f);
10083 }
10084
10085 return rc;
10086}
10087
10088/**
10089 * Saves and commits machine data, user data and hardware data.
10090 *
10091 * Note that on failure, the data remains uncommitted.
10092 *
10093 * @a aFlags may combine the following flags:
10094 *
10095 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10096 * Used when saving settings after an operation that makes them 100%
10097 * correspond to the settings from the current snapshot.
10098 * - SaveS_Force: settings will be saved without doing a deep compare of the
10099 * settings structures. This is used when this is called because snapshots
10100 * have changed to avoid the overhead of the deep compare.
10101 *
10102 * @note Must be called from under this object's write lock. Locks children for
10103 * writing.
10104 *
10105 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10106 * initialized to false and that will be set to true by this function if
10107 * the caller must invoke VirtualBox::i_saveSettings() because the global
10108 * settings have changed. This will happen if a machine rename has been
10109 * saved and the global machine and media registries will therefore need
10110 * updating.
10111 * @param aFlags Flags.
10112 */
10113HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10114 int aFlags /*= 0*/)
10115{
10116 LogFlowThisFuncEnter();
10117
10118 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10119
10120 /* make sure child objects are unable to modify the settings while we are
10121 * saving them */
10122 i_ensureNoStateDependencies();
10123
10124 AssertReturn(!i_isSnapshotMachine(),
10125 E_FAIL);
10126
10127 HRESULT rc = S_OK;
10128 bool fNeedsWrite = false;
10129
10130 /* First, prepare to save settings. It will care about renaming the
10131 * settings directory and file if the machine name was changed and about
10132 * creating a new settings file if this is a new machine. */
10133 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10134 if (FAILED(rc)) return rc;
10135
10136 // keep a pointer to the current settings structures
10137 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10138 settings::MachineConfigFile *pNewConfig = NULL;
10139
10140 try
10141 {
10142 // make a fresh one to have everyone write stuff into
10143 pNewConfig = new settings::MachineConfigFile(NULL);
10144 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10145
10146 // now go and copy all the settings data from COM to the settings structures
10147 // (this calls i_saveSettings() on all the COM objects in the machine)
10148 i_copyMachineDataToSettings(*pNewConfig);
10149
10150 if (aFlags & SaveS_ResetCurStateModified)
10151 {
10152 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10153 mData->mCurrentStateModified = FALSE;
10154 fNeedsWrite = true; // always, no need to compare
10155 }
10156 else if (aFlags & SaveS_Force)
10157 {
10158 fNeedsWrite = true; // always, no need to compare
10159 }
10160 else
10161 {
10162 if (!mData->mCurrentStateModified)
10163 {
10164 // do a deep compare of the settings that we just saved with the settings
10165 // previously stored in the config file; this invokes MachineConfigFile::operator==
10166 // which does a deep compare of all the settings, which is expensive but less expensive
10167 // than writing out XML in vain
10168 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10169
10170 // could still be modified if any settings changed
10171 mData->mCurrentStateModified = fAnySettingsChanged;
10172
10173 fNeedsWrite = fAnySettingsChanged;
10174 }
10175 else
10176 fNeedsWrite = true;
10177 }
10178
10179 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10180
10181 if (fNeedsWrite)
10182 // now spit it all out!
10183 pNewConfig->write(mData->m_strConfigFileFull);
10184
10185 mData->pMachineConfigFile = pNewConfig;
10186 delete pOldConfig;
10187 i_commit();
10188
10189 // after saving settings, we are no longer different from the XML on disk
10190 mData->flModifications = 0;
10191 }
10192 catch (HRESULT err)
10193 {
10194 // we assume that error info is set by the thrower
10195 rc = err;
10196
10197 // restore old config
10198 delete pNewConfig;
10199 mData->pMachineConfigFile = pOldConfig;
10200 }
10201 catch (...)
10202 {
10203 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10204 }
10205
10206 if (fNeedsWrite)
10207 {
10208 /* Fire the data change event, even on failure (since we've already
10209 * committed all data). This is done only for SessionMachines because
10210 * mutable Machine instances are always not registered (i.e. private
10211 * to the client process that creates them) and thus don't need to
10212 * inform callbacks. */
10213 if (i_isSessionMachine())
10214 mParent->i_onMachineDataChange(mData->mUuid);
10215 }
10216
10217 LogFlowThisFunc(("rc=%08X\n", rc));
10218 LogFlowThisFuncLeave();
10219 return rc;
10220}
10221
10222/**
10223 * Implementation for saving the machine settings into the given
10224 * settings::MachineConfigFile instance. This copies machine extradata
10225 * from the previous machine config file in the instance data, if any.
10226 *
10227 * This gets called from two locations:
10228 *
10229 * -- Machine::i_saveSettings(), during the regular XML writing;
10230 *
10231 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10232 * exported to OVF and we write the VirtualBox proprietary XML
10233 * into a <vbox:Machine> tag.
10234 *
10235 * This routine fills all the fields in there, including snapshots, *except*
10236 * for the following:
10237 *
10238 * -- fCurrentStateModified. There is some special logic associated with that.
10239 *
10240 * The caller can then call MachineConfigFile::write() or do something else
10241 * with it.
10242 *
10243 * Caller must hold the machine lock!
10244 *
10245 * This throws XML errors and HRESULT, so the caller must have a catch block!
10246 */
10247void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10248{
10249 // deep copy extradata, being extra careful with self assignment (the STL
10250 // map assignment on Mac OS X clang based Xcode isn't checking)
10251 if (&config != mData->pMachineConfigFile)
10252 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10253
10254 config.uuid = mData->mUuid;
10255
10256 // copy name, description, OS type, teleport, UTC etc.
10257 config.machineUserData = mUserData->s;
10258
10259 if ( mData->mMachineState == MachineState_Saved
10260 || mData->mMachineState == MachineState_Restoring
10261 // when doing certain snapshot operations we may or may not have
10262 // a saved state in the current state, so keep everything as is
10263 || ( ( mData->mMachineState == MachineState_Snapshotting
10264 || mData->mMachineState == MachineState_DeletingSnapshot
10265 || mData->mMachineState == MachineState_RestoringSnapshot)
10266 && (!mSSData->strStateFilePath.isEmpty())
10267 )
10268 )
10269 {
10270 Assert(!mSSData->strStateFilePath.isEmpty());
10271 /* try to make the file name relative to the settings file dir */
10272 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10273 }
10274 else
10275 {
10276 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10277 config.strStateFile.setNull();
10278 }
10279
10280 if (mData->mCurrentSnapshot)
10281 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10282 else
10283 config.uuidCurrentSnapshot.clear();
10284
10285 config.timeLastStateChange = mData->mLastStateChange;
10286 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10287 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10288
10289 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10290 if (FAILED(rc)) throw rc;
10291
10292 // save machine's media registry if this is VirtualBox 4.0 or later
10293 if (config.canHaveOwnMediaRegistry())
10294 {
10295 // determine machine folder
10296 Utf8Str strMachineFolder = i_getSettingsFileFull();
10297 strMachineFolder.stripFilename();
10298 mParent->i_saveMediaRegistry(config.mediaRegistry,
10299 i_getId(), // only media with registry ID == machine UUID
10300 strMachineFolder);
10301 // this throws HRESULT
10302 }
10303
10304 // save snapshots
10305 rc = i_saveAllSnapshots(config);
10306 if (FAILED(rc)) throw rc;
10307}
10308
10309/**
10310 * Saves all snapshots of the machine into the given machine config file. Called
10311 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10312 * @param config
10313 * @return
10314 */
10315HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10316{
10317 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10318
10319 HRESULT rc = S_OK;
10320
10321 try
10322 {
10323 config.llFirstSnapshot.clear();
10324
10325 if (mData->mFirstSnapshot)
10326 {
10327 // the settings use a list for "the first snapshot"
10328 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10329
10330 // get reference to the snapshot on the list and work on that
10331 // element straight in the list to avoid excessive copying later
10332 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10333 if (FAILED(rc)) throw rc;
10334 }
10335
10336// if (mType == IsSessionMachine)
10337// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10338
10339 }
10340 catch (HRESULT err)
10341 {
10342 /* we assume that error info is set by the thrower */
10343 rc = err;
10344 }
10345 catch (...)
10346 {
10347 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10348 }
10349
10350 return rc;
10351}
10352
10353/**
10354 * Saves the VM hardware configuration. It is assumed that the
10355 * given node is empty.
10356 *
10357 * @param data Reference to the settings object for the hardware config.
10358 * @param pDbg Pointer to the settings object for the debugging config
10359 * which happens to live in mHWData.
10360 * @param pAutostart Pointer to the settings object for the autostart config
10361 * which happens to live in mHWData.
10362 */
10363HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10364 settings::Autostart *pAutostart)
10365{
10366 HRESULT rc = S_OK;
10367
10368 try
10369 {
10370 /* The hardware version attribute (optional).
10371 Automatically upgrade from 1 to current default hardware version
10372 when there is no saved state. (ugly!) */
10373 if ( mHWData->mHWVersion == "1"
10374 && mSSData->strStateFilePath.isEmpty()
10375 )
10376 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10377
10378 data.strVersion = mHWData->mHWVersion;
10379 data.uuid = mHWData->mHardwareUUID;
10380
10381 // CPU
10382 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10383 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10384 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10385 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10386 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10387 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10388 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10389 data.fPAE = !!mHWData->mPAEEnabled;
10390 data.enmLongMode = mHWData->mLongMode;
10391 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10392 data.fAPIC = !!mHWData->mAPIC;
10393 data.fX2APIC = !!mHWData->mX2APIC;
10394 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10395 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10396 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10397 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10398 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10399 data.cCPUs = mHWData->mCPUCount;
10400 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10401 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10402 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10403 data.strCpuProfile = mHWData->mCpuProfile;
10404
10405 data.llCpus.clear();
10406 if (data.fCpuHotPlug)
10407 {
10408 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10409 {
10410 if (mHWData->mCPUAttached[idx])
10411 {
10412 settings::Cpu cpu;
10413 cpu.ulId = idx;
10414 data.llCpus.push_back(cpu);
10415 }
10416 }
10417 }
10418
10419 /* Standard and Extended CPUID leafs. */
10420 data.llCpuIdLeafs.clear();
10421 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10422
10423 // memory
10424 data.ulMemorySizeMB = mHWData->mMemorySize;
10425 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10426
10427 // firmware
10428 data.firmwareType = mHWData->mFirmwareType;
10429
10430 // HID
10431 data.pointingHIDType = mHWData->mPointingHIDType;
10432 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10433
10434 // chipset
10435 data.chipsetType = mHWData->mChipsetType;
10436
10437 // paravirt
10438 data.paravirtProvider = mHWData->mParavirtProvider;
10439 data.strParavirtDebug = mHWData->mParavirtDebug;
10440
10441 // emulated USB card reader
10442 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10443
10444 // HPET
10445 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10446
10447 // boot order
10448 data.mapBootOrder.clear();
10449 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10450 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10451
10452 // display
10453 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10454 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10455 data.cMonitors = mHWData->mMonitorCount;
10456 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10457 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10458 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10459 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10460 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10461 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10462 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10463 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10464 {
10465 if (mHWData->maVideoCaptureScreens[i])
10466 ASMBitSet(&data.u64VideoCaptureScreens, i);
10467 else
10468 ASMBitClear(&data.u64VideoCaptureScreens, i);
10469 }
10470 /* store relative video capture file if possible */
10471 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10472 data.strVideoCaptureOptions = mHWData->mVideoCaptureOptions;
10473
10474 /* VRDEServer settings (optional) */
10475 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10476 if (FAILED(rc)) throw rc;
10477
10478 /* BIOS (required) */
10479 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10480 if (FAILED(rc)) throw rc;
10481
10482 /* USB Controller (required) */
10483 data.usbSettings.llUSBControllers.clear();
10484 for (USBControllerList::const_iterator
10485 it = mUSBControllers->begin();
10486 it != mUSBControllers->end();
10487 ++it)
10488 {
10489 ComObjPtr<USBController> ctrl = *it;
10490 settings::USBController settingsCtrl;
10491
10492 settingsCtrl.strName = ctrl->i_getName();
10493 settingsCtrl.enmType = ctrl->i_getControllerType();
10494
10495 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10496 }
10497
10498 /* USB device filters (required) */
10499 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10500 if (FAILED(rc)) throw rc;
10501
10502 /* Network adapters (required) */
10503 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10504 data.llNetworkAdapters.clear();
10505 /* Write out only the nominal number of network adapters for this
10506 * chipset type. Since Machine::commit() hasn't been called there
10507 * may be extra NIC settings in the vector. */
10508 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10509 {
10510 settings::NetworkAdapter nic;
10511 nic.ulSlot = (uint32_t)slot;
10512 /* paranoia check... must not be NULL, but must not crash either. */
10513 if (mNetworkAdapters[slot])
10514 {
10515 if (mNetworkAdapters[slot]->i_hasDefaults())
10516 continue;
10517
10518 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10519 if (FAILED(rc)) throw rc;
10520
10521 data.llNetworkAdapters.push_back(nic);
10522 }
10523 }
10524
10525 /* Serial ports */
10526 data.llSerialPorts.clear();
10527 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10528 {
10529 if (mSerialPorts[slot]->i_hasDefaults())
10530 continue;
10531
10532 settings::SerialPort s;
10533 s.ulSlot = slot;
10534 rc = mSerialPorts[slot]->i_saveSettings(s);
10535 if (FAILED(rc)) return rc;
10536
10537 data.llSerialPorts.push_back(s);
10538 }
10539
10540 /* Parallel ports */
10541 data.llParallelPorts.clear();
10542 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10543 {
10544 if (mParallelPorts[slot]->i_hasDefaults())
10545 continue;
10546
10547 settings::ParallelPort p;
10548 p.ulSlot = slot;
10549 rc = mParallelPorts[slot]->i_saveSettings(p);
10550 if (FAILED(rc)) return rc;
10551
10552 data.llParallelPorts.push_back(p);
10553 }
10554
10555 /* Audio adapter */
10556 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10557 if (FAILED(rc)) return rc;
10558
10559 rc = i_saveStorageControllers(data.storage);
10560 if (FAILED(rc)) return rc;
10561
10562 /* Shared folders */
10563 data.llSharedFolders.clear();
10564 for (HWData::SharedFolderList::const_iterator
10565 it = mHWData->mSharedFolders.begin();
10566 it != mHWData->mSharedFolders.end();
10567 ++it)
10568 {
10569 SharedFolder *pSF = *it;
10570 AutoCaller sfCaller(pSF);
10571 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10572 settings::SharedFolder sf;
10573 sf.strName = pSF->i_getName();
10574 sf.strHostPath = pSF->i_getHostPath();
10575 sf.fWritable = !!pSF->i_isWritable();
10576 sf.fAutoMount = !!pSF->i_isAutoMounted();
10577
10578 data.llSharedFolders.push_back(sf);
10579 }
10580
10581 // clipboard
10582 data.clipboardMode = mHWData->mClipboardMode;
10583
10584 // drag'n'drop
10585 data.dndMode = mHWData->mDnDMode;
10586
10587 /* Guest */
10588 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10589
10590 // IO settings
10591 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10592 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10593
10594 /* BandwidthControl (required) */
10595 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10596 if (FAILED(rc)) throw rc;
10597
10598 /* Host PCI devices */
10599 data.pciAttachments.clear();
10600 for (HWData::PCIDeviceAssignmentList::const_iterator
10601 it = mHWData->mPCIDeviceAssignments.begin();
10602 it != mHWData->mPCIDeviceAssignments.end();
10603 ++it)
10604 {
10605 ComObjPtr<PCIDeviceAttachment> pda = *it;
10606 settings::HostPCIDeviceAttachment hpda;
10607
10608 rc = pda->i_saveSettings(hpda);
10609 if (FAILED(rc)) throw rc;
10610
10611 data.pciAttachments.push_back(hpda);
10612 }
10613
10614 // guest properties
10615 data.llGuestProperties.clear();
10616#ifdef VBOX_WITH_GUEST_PROPS
10617 for (HWData::GuestPropertyMap::const_iterator
10618 it = mHWData->mGuestProperties.begin();
10619 it != mHWData->mGuestProperties.end();
10620 ++it)
10621 {
10622 HWData::GuestProperty property = it->second;
10623
10624 /* Remove transient guest properties at shutdown unless we
10625 * are saving state. Note that restoring snapshot intentionally
10626 * keeps them, they will be removed if appropriate once the final
10627 * machine state is set (as crashes etc. need to work). */
10628 if ( ( mData->mMachineState == MachineState_PoweredOff
10629 || mData->mMachineState == MachineState_Aborted
10630 || mData->mMachineState == MachineState_Teleported)
10631 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10632 continue;
10633 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10634 prop.strName = it->first;
10635 prop.strValue = property.strValue;
10636 prop.timestamp = property.mTimestamp;
10637 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10638 GuestPropWriteFlags(property.mFlags, szFlags);
10639 prop.strFlags = szFlags;
10640
10641 data.llGuestProperties.push_back(prop);
10642 }
10643
10644 /* I presume this doesn't require a backup(). */
10645 mData->mGuestPropertiesModified = FALSE;
10646#endif /* VBOX_WITH_GUEST_PROPS defined */
10647
10648 *pDbg = mHWData->mDebugging;
10649 *pAutostart = mHWData->mAutostart;
10650
10651 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10652 }
10653 catch (std::bad_alloc &)
10654 {
10655 return E_OUTOFMEMORY;
10656 }
10657
10658 AssertComRC(rc);
10659 return rc;
10660}
10661
10662/**
10663 * Saves the storage controller configuration.
10664 *
10665 * @param data storage settings.
10666 */
10667HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10668{
10669 data.llStorageControllers.clear();
10670
10671 for (StorageControllerList::const_iterator
10672 it = mStorageControllers->begin();
10673 it != mStorageControllers->end();
10674 ++it)
10675 {
10676 HRESULT rc;
10677 ComObjPtr<StorageController> pCtl = *it;
10678
10679 settings::StorageController ctl;
10680 ctl.strName = pCtl->i_getName();
10681 ctl.controllerType = pCtl->i_getControllerType();
10682 ctl.storageBus = pCtl->i_getStorageBus();
10683 ctl.ulInstance = pCtl->i_getInstance();
10684 ctl.fBootable = pCtl->i_getBootable();
10685
10686 /* Save the port count. */
10687 ULONG portCount;
10688 rc = pCtl->COMGETTER(PortCount)(&portCount);
10689 ComAssertComRCRet(rc, rc);
10690 ctl.ulPortCount = portCount;
10691
10692 /* Save fUseHostIOCache */
10693 BOOL fUseHostIOCache;
10694 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10695 ComAssertComRCRet(rc, rc);
10696 ctl.fUseHostIOCache = !!fUseHostIOCache;
10697
10698 /* save the devices now. */
10699 rc = i_saveStorageDevices(pCtl, ctl);
10700 ComAssertComRCRet(rc, rc);
10701
10702 data.llStorageControllers.push_back(ctl);
10703 }
10704
10705 return S_OK;
10706}
10707
10708/**
10709 * Saves the hard disk configuration.
10710 */
10711HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10712 settings::StorageController &data)
10713{
10714 MediumAttachmentList atts;
10715
10716 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10717 if (FAILED(rc)) return rc;
10718
10719 data.llAttachedDevices.clear();
10720 for (MediumAttachmentList::const_iterator
10721 it = atts.begin();
10722 it != atts.end();
10723 ++it)
10724 {
10725 settings::AttachedDevice dev;
10726 IMediumAttachment *iA = *it;
10727 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10728 Medium *pMedium = pAttach->i_getMedium();
10729
10730 dev.deviceType = pAttach->i_getType();
10731 dev.lPort = pAttach->i_getPort();
10732 dev.lDevice = pAttach->i_getDevice();
10733 dev.fPassThrough = pAttach->i_getPassthrough();
10734 dev.fHotPluggable = pAttach->i_getHotPluggable();
10735 if (pMedium)
10736 {
10737 if (pMedium->i_isHostDrive())
10738 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10739 else
10740 dev.uuid = pMedium->i_getId();
10741 dev.fTempEject = pAttach->i_getTempEject();
10742 dev.fNonRotational = pAttach->i_getNonRotational();
10743 dev.fDiscard = pAttach->i_getDiscard();
10744 }
10745
10746 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10747
10748 data.llAttachedDevices.push_back(dev);
10749 }
10750
10751 return S_OK;
10752}
10753
10754/**
10755 * Saves machine state settings as defined by aFlags
10756 * (SaveSTS_* values).
10757 *
10758 * @param aFlags Combination of SaveSTS_* flags.
10759 *
10760 * @note Locks objects for writing.
10761 */
10762HRESULT Machine::i_saveStateSettings(int aFlags)
10763{
10764 if (aFlags == 0)
10765 return S_OK;
10766
10767 AutoCaller autoCaller(this);
10768 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10769
10770 /* This object's write lock is also necessary to serialize file access
10771 * (prevent concurrent reads and writes) */
10772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10773
10774 HRESULT rc = S_OK;
10775
10776 Assert(mData->pMachineConfigFile);
10777
10778 try
10779 {
10780 if (aFlags & SaveSTS_CurStateModified)
10781 mData->pMachineConfigFile->fCurrentStateModified = true;
10782
10783 if (aFlags & SaveSTS_StateFilePath)
10784 {
10785 if (!mSSData->strStateFilePath.isEmpty())
10786 /* try to make the file name relative to the settings file dir */
10787 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10788 else
10789 mData->pMachineConfigFile->strStateFile.setNull();
10790 }
10791
10792 if (aFlags & SaveSTS_StateTimeStamp)
10793 {
10794 Assert( mData->mMachineState != MachineState_Aborted
10795 || mSSData->strStateFilePath.isEmpty());
10796
10797 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10798
10799 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10800/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10801 }
10802
10803 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10804 }
10805 catch (...)
10806 {
10807 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10808 }
10809
10810 return rc;
10811}
10812
10813/**
10814 * Ensures that the given medium is added to a media registry. If this machine
10815 * was created with 4.0 or later, then the machine registry is used. Otherwise
10816 * the global VirtualBox media registry is used.
10817 *
10818 * Caller must NOT hold machine lock, media tree or any medium locks!
10819 *
10820 * @param pMedium
10821 */
10822void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10823{
10824 /* Paranoia checks: do not hold machine or media tree locks. */
10825 AssertReturnVoid(!isWriteLockOnCurrentThread());
10826 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10827
10828 ComObjPtr<Medium> pBase;
10829 {
10830 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10831 pBase = pMedium->i_getBase();
10832 }
10833
10834 /* Paranoia checks: do not hold medium locks. */
10835 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10836 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10837
10838 // decide which medium registry to use now that the medium is attached:
10839 Guid uuid;
10840 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10841 if (fCanHaveOwnMediaRegistry)
10842 // machine XML is VirtualBox 4.0 or higher:
10843 uuid = i_getId(); // machine UUID
10844 else
10845 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10846
10847 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10848 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10849 if (pMedium->i_addRegistry(uuid))
10850 mParent->i_markRegistryModified(uuid);
10851
10852 /* For more complex hard disk structures it can happen that the base
10853 * medium isn't yet associated with any medium registry. Do that now. */
10854 if (pMedium != pBase)
10855 {
10856 /* Tree lock needed by Medium::addRegistry when recursing. */
10857 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10858 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10859 {
10860 treeLock.release();
10861 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10862 treeLock.acquire();
10863 }
10864 if (pBase->i_addRegistryRecursive(uuid))
10865 {
10866 treeLock.release();
10867 mParent->i_markRegistryModified(uuid);
10868 }
10869 }
10870}
10871
10872/**
10873 * Creates differencing hard disks for all normal hard disks attached to this
10874 * machine and a new set of attachments to refer to created disks.
10875 *
10876 * Used when taking a snapshot or when deleting the current state. Gets called
10877 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10878 *
10879 * This method assumes that mMediumAttachments contains the original hard disk
10880 * attachments it needs to create diffs for. On success, these attachments will
10881 * be replaced with the created diffs.
10882 *
10883 * Attachments with non-normal hard disks are left as is.
10884 *
10885 * If @a aOnline is @c false then the original hard disks that require implicit
10886 * diffs will be locked for reading. Otherwise it is assumed that they are
10887 * already locked for writing (when the VM was started). Note that in the latter
10888 * case it is responsibility of the caller to lock the newly created diffs for
10889 * writing if this method succeeds.
10890 *
10891 * @param aProgress Progress object to run (must contain at least as
10892 * many operations left as the number of hard disks
10893 * attached).
10894 * @param aWeight Weight of this operation.
10895 * @param aOnline Whether the VM was online prior to this operation.
10896 *
10897 * @note The progress object is not marked as completed, neither on success nor
10898 * on failure. This is a responsibility of the caller.
10899 *
10900 * @note Locks this object and the media tree for writing.
10901 */
10902HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10903 ULONG aWeight,
10904 bool aOnline)
10905{
10906 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10907
10908 AutoCaller autoCaller(this);
10909 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10910
10911 AutoMultiWriteLock2 alock(this->lockHandle(),
10912 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10913
10914 /* must be in a protective state because we release the lock below */
10915 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10916 || mData->mMachineState == MachineState_OnlineSnapshotting
10917 || mData->mMachineState == MachineState_LiveSnapshotting
10918 || mData->mMachineState == MachineState_RestoringSnapshot
10919 || mData->mMachineState == MachineState_DeletingSnapshot
10920 , E_FAIL);
10921
10922 HRESULT rc = S_OK;
10923
10924 // use appropriate locked media map (online or offline)
10925 MediumLockListMap lockedMediaOffline;
10926 MediumLockListMap *lockedMediaMap;
10927 if (aOnline)
10928 lockedMediaMap = &mData->mSession.mLockedMedia;
10929 else
10930 lockedMediaMap = &lockedMediaOffline;
10931
10932 try
10933 {
10934 if (!aOnline)
10935 {
10936 /* lock all attached hard disks early to detect "in use"
10937 * situations before creating actual diffs */
10938 for (MediumAttachmentList::const_iterator
10939 it = mMediumAttachments->begin();
10940 it != mMediumAttachments->end();
10941 ++it)
10942 {
10943 MediumAttachment *pAtt = *it;
10944 if (pAtt->i_getType() == DeviceType_HardDisk)
10945 {
10946 Medium *pMedium = pAtt->i_getMedium();
10947 Assert(pMedium);
10948
10949 MediumLockList *pMediumLockList(new MediumLockList());
10950 alock.release();
10951 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10952 NULL /* pToLockWrite */,
10953 false /* fMediumLockWriteAll */,
10954 NULL,
10955 *pMediumLockList);
10956 alock.acquire();
10957 if (FAILED(rc))
10958 {
10959 delete pMediumLockList;
10960 throw rc;
10961 }
10962 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10963 if (FAILED(rc))
10964 {
10965 throw setError(rc,
10966 tr("Collecting locking information for all attached media failed"));
10967 }
10968 }
10969 }
10970
10971 /* Now lock all media. If this fails, nothing is locked. */
10972 alock.release();
10973 rc = lockedMediaMap->Lock();
10974 alock.acquire();
10975 if (FAILED(rc))
10976 {
10977 throw setError(rc,
10978 tr("Locking of attached media failed"));
10979 }
10980 }
10981
10982 /* remember the current list (note that we don't use backup() since
10983 * mMediumAttachments may be already backed up) */
10984 MediumAttachmentList atts = *mMediumAttachments.data();
10985
10986 /* start from scratch */
10987 mMediumAttachments->clear();
10988
10989 /* go through remembered attachments and create diffs for normal hard
10990 * disks and attach them */
10991 for (MediumAttachmentList::const_iterator
10992 it = atts.begin();
10993 it != atts.end();
10994 ++it)
10995 {
10996 MediumAttachment *pAtt = *it;
10997
10998 DeviceType_T devType = pAtt->i_getType();
10999 Medium *pMedium = pAtt->i_getMedium();
11000
11001 if ( devType != DeviceType_HardDisk
11002 || pMedium == NULL
11003 || pMedium->i_getType() != MediumType_Normal)
11004 {
11005 /* copy the attachment as is */
11006
11007 /** @todo the progress object created in SessionMachine::TakeSnaphot
11008 * only expects operations for hard disks. Later other
11009 * device types need to show up in the progress as well. */
11010 if (devType == DeviceType_HardDisk)
11011 {
11012 if (pMedium == NULL)
11013 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11014 aWeight); // weight
11015 else
11016 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11017 pMedium->i_getBase()->i_getName().c_str()).raw(),
11018 aWeight); // weight
11019 }
11020
11021 mMediumAttachments->push_back(pAtt);
11022 continue;
11023 }
11024
11025 /* need a diff */
11026 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11027 pMedium->i_getBase()->i_getName().c_str()).raw(),
11028 aWeight); // weight
11029
11030 Utf8Str strFullSnapshotFolder;
11031 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11032
11033 ComObjPtr<Medium> diff;
11034 diff.createObject();
11035 // store the diff in the same registry as the parent
11036 // (this cannot fail here because we can't create implicit diffs for
11037 // unregistered images)
11038 Guid uuidRegistryParent;
11039 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11040 Assert(fInRegistry); NOREF(fInRegistry);
11041 rc = diff->init(mParent,
11042 pMedium->i_getPreferredDiffFormat(),
11043 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11044 uuidRegistryParent,
11045 DeviceType_HardDisk);
11046 if (FAILED(rc)) throw rc;
11047
11048 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11049 * the push_back? Looks like we're going to release medium with the
11050 * wrong kind of lock (general issue with if we fail anywhere at all)
11051 * and an orphaned VDI in the snapshots folder. */
11052
11053 /* update the appropriate lock list */
11054 MediumLockList *pMediumLockList;
11055 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11056 AssertComRCThrowRC(rc);
11057 if (aOnline)
11058 {
11059 alock.release();
11060 /* The currently attached medium will be read-only, change
11061 * the lock type to read. */
11062 rc = pMediumLockList->Update(pMedium, false);
11063 alock.acquire();
11064 AssertComRCThrowRC(rc);
11065 }
11066
11067 /* release the locks before the potentially lengthy operation */
11068 alock.release();
11069 rc = pMedium->i_createDiffStorage(diff,
11070 pMedium->i_getPreferredDiffVariant(),
11071 pMediumLockList,
11072 NULL /* aProgress */,
11073 true /* aWait */);
11074 alock.acquire();
11075 if (FAILED(rc)) throw rc;
11076
11077 /* actual lock list update is done in Machine::i_commitMedia */
11078
11079 rc = diff->i_addBackReference(mData->mUuid);
11080 AssertComRCThrowRC(rc);
11081
11082 /* add a new attachment */
11083 ComObjPtr<MediumAttachment> attachment;
11084 attachment.createObject();
11085 rc = attachment->init(this,
11086 diff,
11087 pAtt->i_getControllerName(),
11088 pAtt->i_getPort(),
11089 pAtt->i_getDevice(),
11090 DeviceType_HardDisk,
11091 true /* aImplicit */,
11092 false /* aPassthrough */,
11093 false /* aTempEject */,
11094 pAtt->i_getNonRotational(),
11095 pAtt->i_getDiscard(),
11096 pAtt->i_getHotPluggable(),
11097 pAtt->i_getBandwidthGroup());
11098 if (FAILED(rc)) throw rc;
11099
11100 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11101 AssertComRCThrowRC(rc);
11102 mMediumAttachments->push_back(attachment);
11103 }
11104 }
11105 catch (HRESULT aRC) { rc = aRC; }
11106
11107 /* unlock all hard disks we locked when there is no VM */
11108 if (!aOnline)
11109 {
11110 ErrorInfoKeeper eik;
11111
11112 HRESULT rc1 = lockedMediaMap->Clear();
11113 AssertComRC(rc1);
11114 }
11115
11116 return rc;
11117}
11118
11119/**
11120 * Deletes implicit differencing hard disks created either by
11121 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11122 * mMediumAttachments.
11123 *
11124 * Note that to delete hard disks created by #attachDevice() this method is
11125 * called from #i_rollbackMedia() when the changes are rolled back.
11126 *
11127 * @note Locks this object and the media tree for writing.
11128 */
11129HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11130{
11131 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11132
11133 AutoCaller autoCaller(this);
11134 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11135
11136 AutoMultiWriteLock2 alock(this->lockHandle(),
11137 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11138
11139 /* We absolutely must have backed up state. */
11140 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11141
11142 /* Check if there are any implicitly created diff images. */
11143 bool fImplicitDiffs = false;
11144 for (MediumAttachmentList::const_iterator
11145 it = mMediumAttachments->begin();
11146 it != mMediumAttachments->end();
11147 ++it)
11148 {
11149 const ComObjPtr<MediumAttachment> &pAtt = *it;
11150 if (pAtt->i_isImplicit())
11151 {
11152 fImplicitDiffs = true;
11153 break;
11154 }
11155 }
11156 /* If there is nothing to do, leave early. This saves lots of image locking
11157 * effort. It also avoids a MachineStateChanged event without real reason.
11158 * This is important e.g. when loading a VM config, because there should be
11159 * no events. Otherwise API clients can become thoroughly confused for
11160 * inaccessible VMs (the code for loading VM configs uses this method for
11161 * cleanup if the config makes no sense), as they take such events as an
11162 * indication that the VM is alive, and they would force the VM config to
11163 * be reread, leading to an endless loop. */
11164 if (!fImplicitDiffs)
11165 return S_OK;
11166
11167 HRESULT rc = S_OK;
11168 MachineState_T oldState = mData->mMachineState;
11169
11170 /* will release the lock before the potentially lengthy operation,
11171 * so protect with the special state (unless already protected) */
11172 if ( oldState != MachineState_Snapshotting
11173 && oldState != MachineState_OnlineSnapshotting
11174 && oldState != MachineState_LiveSnapshotting
11175 && oldState != MachineState_RestoringSnapshot
11176 && oldState != MachineState_DeletingSnapshot
11177 && oldState != MachineState_DeletingSnapshotOnline
11178 && oldState != MachineState_DeletingSnapshotPaused
11179 )
11180 i_setMachineState(MachineState_SettingUp);
11181
11182 // use appropriate locked media map (online or offline)
11183 MediumLockListMap lockedMediaOffline;
11184 MediumLockListMap *lockedMediaMap;
11185 if (aOnline)
11186 lockedMediaMap = &mData->mSession.mLockedMedia;
11187 else
11188 lockedMediaMap = &lockedMediaOffline;
11189
11190 try
11191 {
11192 if (!aOnline)
11193 {
11194 /* lock all attached hard disks early to detect "in use"
11195 * situations before deleting actual diffs */
11196 for (MediumAttachmentList::const_iterator
11197 it = mMediumAttachments->begin();
11198 it != mMediumAttachments->end();
11199 ++it)
11200 {
11201 MediumAttachment *pAtt = *it;
11202 if (pAtt->i_getType() == DeviceType_HardDisk)
11203 {
11204 Medium *pMedium = pAtt->i_getMedium();
11205 Assert(pMedium);
11206
11207 MediumLockList *pMediumLockList(new MediumLockList());
11208 alock.release();
11209 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11210 NULL /* pToLockWrite */,
11211 false /* fMediumLockWriteAll */,
11212 NULL,
11213 *pMediumLockList);
11214 alock.acquire();
11215
11216 if (FAILED(rc))
11217 {
11218 delete pMediumLockList;
11219 throw rc;
11220 }
11221
11222 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11223 if (FAILED(rc))
11224 throw rc;
11225 }
11226 }
11227
11228 if (FAILED(rc))
11229 throw rc;
11230 } // end of offline
11231
11232 /* Lock lists are now up to date and include implicitly created media */
11233
11234 /* Go through remembered attachments and delete all implicitly created
11235 * diffs and fix up the attachment information */
11236 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11237 MediumAttachmentList implicitAtts;
11238 for (MediumAttachmentList::const_iterator
11239 it = mMediumAttachments->begin();
11240 it != mMediumAttachments->end();
11241 ++it)
11242 {
11243 ComObjPtr<MediumAttachment> pAtt = *it;
11244 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11245 if (pMedium.isNull())
11246 continue;
11247
11248 // Implicit attachments go on the list for deletion and back references are removed.
11249 if (pAtt->i_isImplicit())
11250 {
11251 /* Deassociate and mark for deletion */
11252 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11253 rc = pMedium->i_removeBackReference(mData->mUuid);
11254 if (FAILED(rc))
11255 throw rc;
11256 implicitAtts.push_back(pAtt);
11257 continue;
11258 }
11259
11260 /* Was this medium attached before? */
11261 if (!i_findAttachment(oldAtts, pMedium))
11262 {
11263 /* no: de-associate */
11264 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11265 rc = pMedium->i_removeBackReference(mData->mUuid);
11266 if (FAILED(rc))
11267 throw rc;
11268 continue;
11269 }
11270 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11271 }
11272
11273 /* If there are implicit attachments to delete, throw away the lock
11274 * map contents (which will unlock all media) since the medium
11275 * attachments will be rolled back. Below we need to completely
11276 * recreate the lock map anyway since it is infinitely complex to
11277 * do this incrementally (would need reconstructing each attachment
11278 * change, which would be extremely hairy). */
11279 if (implicitAtts.size() != 0)
11280 {
11281 ErrorInfoKeeper eik;
11282
11283 HRESULT rc1 = lockedMediaMap->Clear();
11284 AssertComRC(rc1);
11285 }
11286
11287 /* rollback hard disk changes */
11288 mMediumAttachments.rollback();
11289
11290 MultiResult mrc(S_OK);
11291
11292 // Delete unused implicit diffs.
11293 if (implicitAtts.size() != 0)
11294 {
11295 alock.release();
11296
11297 for (MediumAttachmentList::const_iterator
11298 it = implicitAtts.begin();
11299 it != implicitAtts.end();
11300 ++it)
11301 {
11302 // Remove medium associated with this attachment.
11303 ComObjPtr<MediumAttachment> pAtt = *it;
11304 Assert(pAtt);
11305 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11306 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11307 Assert(pMedium);
11308
11309 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11310 // continue on delete failure, just collect error messages
11311 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11312 pMedium->i_getLocationFull().c_str() ));
11313 mrc = rc;
11314 }
11315 // Clear the list of deleted implicit attachments now, while not
11316 // holding the lock, as it will ultimately trigger Medium::uninit()
11317 // calls which assume that the media tree lock isn't held.
11318 implicitAtts.clear();
11319
11320 alock.acquire();
11321
11322 /* if there is a VM recreate media lock map as mentioned above,
11323 * otherwise it is a waste of time and we leave things unlocked */
11324 if (aOnline)
11325 {
11326 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11327 /* must never be NULL, but better safe than sorry */
11328 if (!pMachine.isNull())
11329 {
11330 alock.release();
11331 rc = mData->mSession.mMachine->i_lockMedia();
11332 alock.acquire();
11333 if (FAILED(rc))
11334 throw rc;
11335 }
11336 }
11337 }
11338 }
11339 catch (HRESULT aRC) {rc = aRC;}
11340
11341 if (mData->mMachineState == MachineState_SettingUp)
11342 i_setMachineState(oldState);
11343
11344 /* unlock all hard disks we locked when there is no VM */
11345 if (!aOnline)
11346 {
11347 ErrorInfoKeeper eik;
11348
11349 HRESULT rc1 = lockedMediaMap->Clear();
11350 AssertComRC(rc1);
11351 }
11352
11353 return rc;
11354}
11355
11356
11357/**
11358 * Looks through the given list of media attachments for one with the given parameters
11359 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11360 * can be searched as well if needed.
11361 *
11362 * @param ll
11363 * @param aControllerName
11364 * @param aControllerPort
11365 * @param aDevice
11366 * @return
11367 */
11368MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11369 const Utf8Str &aControllerName,
11370 LONG aControllerPort,
11371 LONG aDevice)
11372{
11373 for (MediumAttachmentList::const_iterator
11374 it = ll.begin();
11375 it != ll.end();
11376 ++it)
11377 {
11378 MediumAttachment *pAttach = *it;
11379 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11380 return pAttach;
11381 }
11382
11383 return NULL;
11384}
11385
11386/**
11387 * Looks through the given list of media attachments for one with the given parameters
11388 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11389 * can be searched as well if needed.
11390 *
11391 * @param ll
11392 * @param pMedium
11393 * @return
11394 */
11395MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11396 ComObjPtr<Medium> pMedium)
11397{
11398 for (MediumAttachmentList::const_iterator
11399 it = ll.begin();
11400 it != ll.end();
11401 ++it)
11402 {
11403 MediumAttachment *pAttach = *it;
11404 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11405 if (pMediumThis == pMedium)
11406 return pAttach;
11407 }
11408
11409 return NULL;
11410}
11411
11412/**
11413 * Looks through the given list of media attachments for one with the given parameters
11414 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11415 * can be searched as well if needed.
11416 *
11417 * @param ll
11418 * @param id
11419 * @return
11420 */
11421MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11422 Guid &id)
11423{
11424 for (MediumAttachmentList::const_iterator
11425 it = ll.begin();
11426 it != ll.end();
11427 ++it)
11428 {
11429 MediumAttachment *pAttach = *it;
11430 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11431 if (pMediumThis->i_getId() == id)
11432 return pAttach;
11433 }
11434
11435 return NULL;
11436}
11437
11438/**
11439 * Main implementation for Machine::DetachDevice. This also gets called
11440 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11441 *
11442 * @param pAttach Medium attachment to detach.
11443 * @param writeLock Machine write lock which the caller must have locked once.
11444 * This may be released temporarily in here.
11445 * @param pSnapshot If NULL, then the detachment is for the current machine.
11446 * Otherwise this is for a SnapshotMachine, and this must be
11447 * its snapshot.
11448 * @return
11449 */
11450HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11451 AutoWriteLock &writeLock,
11452 Snapshot *pSnapshot)
11453{
11454 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11455 DeviceType_T mediumType = pAttach->i_getType();
11456
11457 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11458
11459 if (pAttach->i_isImplicit())
11460 {
11461 /* attempt to implicitly delete the implicitly created diff */
11462
11463 /// @todo move the implicit flag from MediumAttachment to Medium
11464 /// and forbid any hard disk operation when it is implicit. Or maybe
11465 /// a special media state for it to make it even more simple.
11466
11467 Assert(mMediumAttachments.isBackedUp());
11468
11469 /* will release the lock before the potentially lengthy operation, so
11470 * protect with the special state */
11471 MachineState_T oldState = mData->mMachineState;
11472 i_setMachineState(MachineState_SettingUp);
11473
11474 writeLock.release();
11475
11476 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11477 true /*aWait*/);
11478
11479 writeLock.acquire();
11480
11481 i_setMachineState(oldState);
11482
11483 if (FAILED(rc)) return rc;
11484 }
11485
11486 i_setModified(IsModified_Storage);
11487 mMediumAttachments.backup();
11488 mMediumAttachments->remove(pAttach);
11489
11490 if (!oldmedium.isNull())
11491 {
11492 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11493 if (pSnapshot)
11494 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11495 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11496 else if (mediumType != DeviceType_HardDisk)
11497 oldmedium->i_removeBackReference(mData->mUuid);
11498 }
11499
11500 return S_OK;
11501}
11502
11503/**
11504 * Goes thru all media of the given list and
11505 *
11506 * 1) calls i_detachDevice() on each of them for this machine and
11507 * 2) adds all Medium objects found in the process to the given list,
11508 * depending on cleanupMode.
11509 *
11510 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11511 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11512 * media to the list.
11513 *
11514 * This gets called from Machine::Unregister, both for the actual Machine and
11515 * the SnapshotMachine objects that might be found in the snapshots.
11516 *
11517 * Requires caller and locking. The machine lock must be passed in because it
11518 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11519 *
11520 * @param writeLock Machine lock from top-level caller; this gets passed to
11521 * i_detachDevice.
11522 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11523 * object if called for a SnapshotMachine.
11524 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11525 * added to llMedia; if Full, then all media get added;
11526 * otherwise no media get added.
11527 * @param llMedia Caller's list to receive Medium objects which got detached so
11528 * caller can close() them, depending on cleanupMode.
11529 * @return
11530 */
11531HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11532 Snapshot *pSnapshot,
11533 CleanupMode_T cleanupMode,
11534 MediaList &llMedia)
11535{
11536 Assert(isWriteLockOnCurrentThread());
11537
11538 HRESULT rc;
11539
11540 // make a temporary list because i_detachDevice invalidates iterators into
11541 // mMediumAttachments
11542 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11543
11544 for (MediumAttachmentList::iterator
11545 it = llAttachments2.begin();
11546 it != llAttachments2.end();
11547 ++it)
11548 {
11549 ComObjPtr<MediumAttachment> &pAttach = *it;
11550 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11551
11552 if (!pMedium.isNull())
11553 {
11554 AutoCaller mac(pMedium);
11555 if (FAILED(mac.rc())) return mac.rc();
11556 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11557 DeviceType_T devType = pMedium->i_getDeviceType();
11558 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11559 && devType == DeviceType_HardDisk)
11560 || (cleanupMode == CleanupMode_Full)
11561 )
11562 {
11563 llMedia.push_back(pMedium);
11564 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11565 /* Not allowed to keep this lock as below we need the parent
11566 * medium lock, and the lock order is parent to child. */
11567 lock.release();
11568 /*
11569 * Search for medias which are not attached to any machine, but
11570 * in the chain to an attached disk. Mediums are only consided
11571 * if they are:
11572 * - have only one child
11573 * - no references to any machines
11574 * - are of normal medium type
11575 */
11576 while (!pParent.isNull())
11577 {
11578 AutoCaller mac1(pParent);
11579 if (FAILED(mac1.rc())) return mac1.rc();
11580 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11581 if (pParent->i_getChildren().size() == 1)
11582 {
11583 if ( pParent->i_getMachineBackRefCount() == 0
11584 && pParent->i_getType() == MediumType_Normal
11585 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11586 llMedia.push_back(pParent);
11587 }
11588 else
11589 break;
11590 pParent = pParent->i_getParent();
11591 }
11592 }
11593 }
11594
11595 // real machine: then we need to use the proper method
11596 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11597
11598 if (FAILED(rc))
11599 return rc;
11600 }
11601
11602 return S_OK;
11603}
11604
11605/**
11606 * Perform deferred hard disk detachments.
11607 *
11608 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11609 * changed (not backed up).
11610 *
11611 * If @a aOnline is @c true then this method will also unlock the old hard
11612 * disks for which the new implicit diffs were created and will lock these new
11613 * diffs for writing.
11614 *
11615 * @param aOnline Whether the VM was online prior to this operation.
11616 *
11617 * @note Locks this object for writing!
11618 */
11619void Machine::i_commitMedia(bool aOnline /*= false*/)
11620{
11621 AutoCaller autoCaller(this);
11622 AssertComRCReturnVoid(autoCaller.rc());
11623
11624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11625
11626 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11627
11628 HRESULT rc = S_OK;
11629
11630 /* no attach/detach operations -- nothing to do */
11631 if (!mMediumAttachments.isBackedUp())
11632 return;
11633
11634 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11635 bool fMediaNeedsLocking = false;
11636
11637 /* enumerate new attachments */
11638 for (MediumAttachmentList::const_iterator
11639 it = mMediumAttachments->begin();
11640 it != mMediumAttachments->end();
11641 ++it)
11642 {
11643 MediumAttachment *pAttach = *it;
11644
11645 pAttach->i_commit();
11646
11647 Medium *pMedium = pAttach->i_getMedium();
11648 bool fImplicit = pAttach->i_isImplicit();
11649
11650 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11651 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11652 fImplicit));
11653
11654 /** @todo convert all this Machine-based voodoo to MediumAttachment
11655 * based commit logic. */
11656 if (fImplicit)
11657 {
11658 /* convert implicit attachment to normal */
11659 pAttach->i_setImplicit(false);
11660
11661 if ( aOnline
11662 && pMedium
11663 && pAttach->i_getType() == DeviceType_HardDisk
11664 )
11665 {
11666 /* update the appropriate lock list */
11667 MediumLockList *pMediumLockList;
11668 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11669 AssertComRC(rc);
11670 if (pMediumLockList)
11671 {
11672 /* unlock if there's a need to change the locking */
11673 if (!fMediaNeedsLocking)
11674 {
11675 rc = mData->mSession.mLockedMedia.Unlock();
11676 AssertComRC(rc);
11677 fMediaNeedsLocking = true;
11678 }
11679 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11680 AssertComRC(rc);
11681 rc = pMediumLockList->Append(pMedium, true);
11682 AssertComRC(rc);
11683 }
11684 }
11685
11686 continue;
11687 }
11688
11689 if (pMedium)
11690 {
11691 /* was this medium attached before? */
11692 for (MediumAttachmentList::iterator
11693 oldIt = oldAtts.begin();
11694 oldIt != oldAtts.end();
11695 ++oldIt)
11696 {
11697 MediumAttachment *pOldAttach = *oldIt;
11698 if (pOldAttach->i_getMedium() == pMedium)
11699 {
11700 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11701
11702 /* yes: remove from old to avoid de-association */
11703 oldAtts.erase(oldIt);
11704 break;
11705 }
11706 }
11707 }
11708 }
11709
11710 /* enumerate remaining old attachments and de-associate from the
11711 * current machine state */
11712 for (MediumAttachmentList::const_iterator
11713 it = oldAtts.begin();
11714 it != oldAtts.end();
11715 ++it)
11716 {
11717 MediumAttachment *pAttach = *it;
11718 Medium *pMedium = pAttach->i_getMedium();
11719
11720 /* Detach only hard disks, since DVD/floppy media is detached
11721 * instantly in MountMedium. */
11722 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11723 {
11724 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11725
11726 /* now de-associate from the current machine state */
11727 rc = pMedium->i_removeBackReference(mData->mUuid);
11728 AssertComRC(rc);
11729
11730 if (aOnline)
11731 {
11732 /* unlock since medium is not used anymore */
11733 MediumLockList *pMediumLockList;
11734 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11735 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11736 {
11737 /* this happens for online snapshots, there the attachment
11738 * is changing, but only to a diff image created under
11739 * the old one, so there is no separate lock list */
11740 Assert(!pMediumLockList);
11741 }
11742 else
11743 {
11744 AssertComRC(rc);
11745 if (pMediumLockList)
11746 {
11747 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11748 AssertComRC(rc);
11749 }
11750 }
11751 }
11752 }
11753 }
11754
11755 /* take media locks again so that the locking state is consistent */
11756 if (fMediaNeedsLocking)
11757 {
11758 Assert(aOnline);
11759 rc = mData->mSession.mLockedMedia.Lock();
11760 AssertComRC(rc);
11761 }
11762
11763 /* commit the hard disk changes */
11764 mMediumAttachments.commit();
11765
11766 if (i_isSessionMachine())
11767 {
11768 /*
11769 * Update the parent machine to point to the new owner.
11770 * This is necessary because the stored parent will point to the
11771 * session machine otherwise and cause crashes or errors later
11772 * when the session machine gets invalid.
11773 */
11774 /** @todo Change the MediumAttachment class to behave like any other
11775 * class in this regard by creating peer MediumAttachment
11776 * objects for session machines and share the data with the peer
11777 * machine.
11778 */
11779 for (MediumAttachmentList::const_iterator
11780 it = mMediumAttachments->begin();
11781 it != mMediumAttachments->end();
11782 ++it)
11783 (*it)->i_updateParentMachine(mPeer);
11784
11785 /* attach new data to the primary machine and reshare it */
11786 mPeer->mMediumAttachments.attach(mMediumAttachments);
11787 }
11788
11789 return;
11790}
11791
11792/**
11793 * Perform deferred deletion of implicitly created diffs.
11794 *
11795 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11796 * changed (not backed up).
11797 *
11798 * @note Locks this object for writing!
11799 */
11800void Machine::i_rollbackMedia()
11801{
11802 AutoCaller autoCaller(this);
11803 AssertComRCReturnVoid(autoCaller.rc());
11804
11805 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11806 LogFlowThisFunc(("Entering rollbackMedia\n"));
11807
11808 HRESULT rc = S_OK;
11809
11810 /* no attach/detach operations -- nothing to do */
11811 if (!mMediumAttachments.isBackedUp())
11812 return;
11813
11814 /* enumerate new attachments */
11815 for (MediumAttachmentList::const_iterator
11816 it = mMediumAttachments->begin();
11817 it != mMediumAttachments->end();
11818 ++it)
11819 {
11820 MediumAttachment *pAttach = *it;
11821 /* Fix up the backrefs for DVD/floppy media. */
11822 if (pAttach->i_getType() != DeviceType_HardDisk)
11823 {
11824 Medium *pMedium = pAttach->i_getMedium();
11825 if (pMedium)
11826 {
11827 rc = pMedium->i_removeBackReference(mData->mUuid);
11828 AssertComRC(rc);
11829 }
11830 }
11831
11832 (*it)->i_rollback();
11833
11834 pAttach = *it;
11835 /* Fix up the backrefs for DVD/floppy media. */
11836 if (pAttach->i_getType() != DeviceType_HardDisk)
11837 {
11838 Medium *pMedium = pAttach->i_getMedium();
11839 if (pMedium)
11840 {
11841 rc = pMedium->i_addBackReference(mData->mUuid);
11842 AssertComRC(rc);
11843 }
11844 }
11845 }
11846
11847 /** @todo convert all this Machine-based voodoo to MediumAttachment
11848 * based rollback logic. */
11849 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11850
11851 return;
11852}
11853
11854/**
11855 * Returns true if the settings file is located in the directory named exactly
11856 * as the machine; this means, among other things, that the machine directory
11857 * should be auto-renamed.
11858 *
11859 * @param aSettingsDir if not NULL, the full machine settings file directory
11860 * name will be assigned there.
11861 *
11862 * @note Doesn't lock anything.
11863 * @note Not thread safe (must be called from this object's lock).
11864 */
11865bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11866{
11867 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11868 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11869 if (aSettingsDir)
11870 *aSettingsDir = strMachineDirName;
11871 strMachineDirName.stripPath(); // vmname
11872 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11873 strConfigFileOnly.stripPath() // vmname.vbox
11874 .stripSuffix(); // vmname
11875 /** @todo hack, make somehow use of ComposeMachineFilename */
11876 if (mUserData->s.fDirectoryIncludesUUID)
11877 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11878
11879 AssertReturn(!strMachineDirName.isEmpty(), false);
11880 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11881
11882 return strMachineDirName == strConfigFileOnly;
11883}
11884
11885/**
11886 * Discards all changes to machine settings.
11887 *
11888 * @param aNotify Whether to notify the direct session about changes or not.
11889 *
11890 * @note Locks objects for writing!
11891 */
11892void Machine::i_rollback(bool aNotify)
11893{
11894 AutoCaller autoCaller(this);
11895 AssertComRCReturn(autoCaller.rc(), (void)0);
11896
11897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11898
11899 if (!mStorageControllers.isNull())
11900 {
11901 if (mStorageControllers.isBackedUp())
11902 {
11903 /* unitialize all new devices (absent in the backed up list). */
11904 StorageControllerList *backedList = mStorageControllers.backedUpData();
11905 for (StorageControllerList::const_iterator
11906 it = mStorageControllers->begin();
11907 it != mStorageControllers->end();
11908 ++it)
11909 {
11910 if ( std::find(backedList->begin(), backedList->end(), *it)
11911 == backedList->end()
11912 )
11913 {
11914 (*it)->uninit();
11915 }
11916 }
11917
11918 /* restore the list */
11919 mStorageControllers.rollback();
11920 }
11921
11922 /* rollback any changes to devices after restoring the list */
11923 if (mData->flModifications & IsModified_Storage)
11924 {
11925 for (StorageControllerList::const_iterator
11926 it = mStorageControllers->begin();
11927 it != mStorageControllers->end();
11928 ++it)
11929 {
11930 (*it)->i_rollback();
11931 }
11932 }
11933 }
11934
11935 if (!mUSBControllers.isNull())
11936 {
11937 if (mUSBControllers.isBackedUp())
11938 {
11939 /* unitialize all new devices (absent in the backed up list). */
11940 USBControllerList *backedList = mUSBControllers.backedUpData();
11941 for (USBControllerList::const_iterator
11942 it = mUSBControllers->begin();
11943 it != mUSBControllers->end();
11944 ++it)
11945 {
11946 if ( std::find(backedList->begin(), backedList->end(), *it)
11947 == backedList->end()
11948 )
11949 {
11950 (*it)->uninit();
11951 }
11952 }
11953
11954 /* restore the list */
11955 mUSBControllers.rollback();
11956 }
11957
11958 /* rollback any changes to devices after restoring the list */
11959 if (mData->flModifications & IsModified_USB)
11960 {
11961 for (USBControllerList::const_iterator
11962 it = mUSBControllers->begin();
11963 it != mUSBControllers->end();
11964 ++it)
11965 {
11966 (*it)->i_rollback();
11967 }
11968 }
11969 }
11970
11971 mUserData.rollback();
11972
11973 mHWData.rollback();
11974
11975 if (mData->flModifications & IsModified_Storage)
11976 i_rollbackMedia();
11977
11978 if (mBIOSSettings)
11979 mBIOSSettings->i_rollback();
11980
11981 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11982 mVRDEServer->i_rollback();
11983
11984 if (mAudioAdapter)
11985 mAudioAdapter->i_rollback();
11986
11987 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11988 mUSBDeviceFilters->i_rollback();
11989
11990 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11991 mBandwidthControl->i_rollback();
11992
11993 if (!mHWData.isNull())
11994 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11995 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11996 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11997 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11998
11999 if (mData->flModifications & IsModified_NetworkAdapters)
12000 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12001 if ( mNetworkAdapters[slot]
12002 && mNetworkAdapters[slot]->i_isModified())
12003 {
12004 mNetworkAdapters[slot]->i_rollback();
12005 networkAdapters[slot] = mNetworkAdapters[slot];
12006 }
12007
12008 if (mData->flModifications & IsModified_SerialPorts)
12009 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12010 if ( mSerialPorts[slot]
12011 && mSerialPorts[slot]->i_isModified())
12012 {
12013 mSerialPorts[slot]->i_rollback();
12014 serialPorts[slot] = mSerialPorts[slot];
12015 }
12016
12017 if (mData->flModifications & IsModified_ParallelPorts)
12018 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12019 if ( mParallelPorts[slot]
12020 && mParallelPorts[slot]->i_isModified())
12021 {
12022 mParallelPorts[slot]->i_rollback();
12023 parallelPorts[slot] = mParallelPorts[slot];
12024 }
12025
12026 if (aNotify)
12027 {
12028 /* inform the direct session about changes */
12029
12030 ComObjPtr<Machine> that = this;
12031 uint32_t flModifications = mData->flModifications;
12032 alock.release();
12033
12034 if (flModifications & IsModified_SharedFolders)
12035 that->i_onSharedFolderChange();
12036
12037 if (flModifications & IsModified_VRDEServer)
12038 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12039 if (flModifications & IsModified_USB)
12040 that->i_onUSBControllerChange();
12041
12042 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12043 if (networkAdapters[slot])
12044 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12045 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12046 if (serialPorts[slot])
12047 that->i_onSerialPortChange(serialPorts[slot]);
12048 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12049 if (parallelPorts[slot])
12050 that->i_onParallelPortChange(parallelPorts[slot]);
12051
12052 if (flModifications & IsModified_Storage)
12053 that->i_onStorageControllerChange();
12054
12055#if 0
12056 if (flModifications & IsModified_BandwidthControl)
12057 that->onBandwidthControlChange();
12058#endif
12059 }
12060}
12061
12062/**
12063 * Commits all the changes to machine settings.
12064 *
12065 * Note that this operation is supposed to never fail.
12066 *
12067 * @note Locks this object and children for writing.
12068 */
12069void Machine::i_commit()
12070{
12071 AutoCaller autoCaller(this);
12072 AssertComRCReturnVoid(autoCaller.rc());
12073
12074 AutoCaller peerCaller(mPeer);
12075 AssertComRCReturnVoid(peerCaller.rc());
12076
12077 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12078
12079 /*
12080 * use safe commit to ensure Snapshot machines (that share mUserData)
12081 * will still refer to a valid memory location
12082 */
12083 mUserData.commitCopy();
12084
12085 mHWData.commit();
12086
12087 if (mMediumAttachments.isBackedUp())
12088 i_commitMedia(Global::IsOnline(mData->mMachineState));
12089
12090 mBIOSSettings->i_commit();
12091 mVRDEServer->i_commit();
12092 mAudioAdapter->i_commit();
12093 mUSBDeviceFilters->i_commit();
12094 mBandwidthControl->i_commit();
12095
12096 /* Since mNetworkAdapters is a list which might have been changed (resized)
12097 * without using the Backupable<> template we need to handle the copying
12098 * of the list entries manually, including the creation of peers for the
12099 * new objects. */
12100 bool commitNetworkAdapters = false;
12101 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12102 if (mPeer)
12103 {
12104 /* commit everything, even the ones which will go away */
12105 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12106 mNetworkAdapters[slot]->i_commit();
12107 /* copy over the new entries, creating a peer and uninit the original */
12108 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12109 for (size_t slot = 0; slot < newSize; slot++)
12110 {
12111 /* look if this adapter has a peer device */
12112 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12113 if (!peer)
12114 {
12115 /* no peer means the adapter is a newly created one;
12116 * create a peer owning data this data share it with */
12117 peer.createObject();
12118 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12119 }
12120 mPeer->mNetworkAdapters[slot] = peer;
12121 }
12122 /* uninit any no longer needed network adapters */
12123 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12124 mNetworkAdapters[slot]->uninit();
12125 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12126 {
12127 if (mPeer->mNetworkAdapters[slot])
12128 mPeer->mNetworkAdapters[slot]->uninit();
12129 }
12130 /* Keep the original network adapter count until this point, so that
12131 * discarding a chipset type change will not lose settings. */
12132 mNetworkAdapters.resize(newSize);
12133 mPeer->mNetworkAdapters.resize(newSize);
12134 }
12135 else
12136 {
12137 /* we have no peer (our parent is the newly created machine);
12138 * just commit changes to the network adapters */
12139 commitNetworkAdapters = true;
12140 }
12141 if (commitNetworkAdapters)
12142 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12143 mNetworkAdapters[slot]->i_commit();
12144
12145 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12146 mSerialPorts[slot]->i_commit();
12147 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12148 mParallelPorts[slot]->i_commit();
12149
12150 bool commitStorageControllers = false;
12151
12152 if (mStorageControllers.isBackedUp())
12153 {
12154 mStorageControllers.commit();
12155
12156 if (mPeer)
12157 {
12158 /* Commit all changes to new controllers (this will reshare data with
12159 * peers for those who have peers) */
12160 StorageControllerList *newList = new StorageControllerList();
12161 for (StorageControllerList::const_iterator
12162 it = mStorageControllers->begin();
12163 it != mStorageControllers->end();
12164 ++it)
12165 {
12166 (*it)->i_commit();
12167
12168 /* look if this controller has a peer device */
12169 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12170 if (!peer)
12171 {
12172 /* no peer means the device is a newly created one;
12173 * create a peer owning data this device share it with */
12174 peer.createObject();
12175 peer->init(mPeer, *it, true /* aReshare */);
12176 }
12177 else
12178 {
12179 /* remove peer from the old list */
12180 mPeer->mStorageControllers->remove(peer);
12181 }
12182 /* and add it to the new list */
12183 newList->push_back(peer);
12184 }
12185
12186 /* uninit old peer's controllers that are left */
12187 for (StorageControllerList::const_iterator
12188 it = mPeer->mStorageControllers->begin();
12189 it != mPeer->mStorageControllers->end();
12190 ++it)
12191 {
12192 (*it)->uninit();
12193 }
12194
12195 /* attach new list of controllers to our peer */
12196 mPeer->mStorageControllers.attach(newList);
12197 }
12198 else
12199 {
12200 /* we have no peer (our parent is the newly created machine);
12201 * just commit changes to devices */
12202 commitStorageControllers = true;
12203 }
12204 }
12205 else
12206 {
12207 /* the list of controllers itself is not changed,
12208 * just commit changes to controllers themselves */
12209 commitStorageControllers = true;
12210 }
12211
12212 if (commitStorageControllers)
12213 {
12214 for (StorageControllerList::const_iterator
12215 it = mStorageControllers->begin();
12216 it != mStorageControllers->end();
12217 ++it)
12218 {
12219 (*it)->i_commit();
12220 }
12221 }
12222
12223 bool commitUSBControllers = false;
12224
12225 if (mUSBControllers.isBackedUp())
12226 {
12227 mUSBControllers.commit();
12228
12229 if (mPeer)
12230 {
12231 /* Commit all changes to new controllers (this will reshare data with
12232 * peers for those who have peers) */
12233 USBControllerList *newList = new USBControllerList();
12234 for (USBControllerList::const_iterator
12235 it = mUSBControllers->begin();
12236 it != mUSBControllers->end();
12237 ++it)
12238 {
12239 (*it)->i_commit();
12240
12241 /* look if this controller has a peer device */
12242 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12243 if (!peer)
12244 {
12245 /* no peer means the device is a newly created one;
12246 * create a peer owning data this device share it with */
12247 peer.createObject();
12248 peer->init(mPeer, *it, true /* aReshare */);
12249 }
12250 else
12251 {
12252 /* remove peer from the old list */
12253 mPeer->mUSBControllers->remove(peer);
12254 }
12255 /* and add it to the new list */
12256 newList->push_back(peer);
12257 }
12258
12259 /* uninit old peer's controllers that are left */
12260 for (USBControllerList::const_iterator
12261 it = mPeer->mUSBControllers->begin();
12262 it != mPeer->mUSBControllers->end();
12263 ++it)
12264 {
12265 (*it)->uninit();
12266 }
12267
12268 /* attach new list of controllers to our peer */
12269 mPeer->mUSBControllers.attach(newList);
12270 }
12271 else
12272 {
12273 /* we have no peer (our parent is the newly created machine);
12274 * just commit changes to devices */
12275 commitUSBControllers = true;
12276 }
12277 }
12278 else
12279 {
12280 /* the list of controllers itself is not changed,
12281 * just commit changes to controllers themselves */
12282 commitUSBControllers = true;
12283 }
12284
12285 if (commitUSBControllers)
12286 {
12287 for (USBControllerList::const_iterator
12288 it = mUSBControllers->begin();
12289 it != mUSBControllers->end();
12290 ++it)
12291 {
12292 (*it)->i_commit();
12293 }
12294 }
12295
12296 if (i_isSessionMachine())
12297 {
12298 /* attach new data to the primary machine and reshare it */
12299 mPeer->mUserData.attach(mUserData);
12300 mPeer->mHWData.attach(mHWData);
12301 /* mmMediumAttachments is reshared by fixupMedia */
12302 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12303 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12304 }
12305}
12306
12307/**
12308 * Copies all the hardware data from the given machine.
12309 *
12310 * Currently, only called when the VM is being restored from a snapshot. In
12311 * particular, this implies that the VM is not running during this method's
12312 * call.
12313 *
12314 * @note This method must be called from under this object's lock.
12315 *
12316 * @note This method doesn't call #i_commit(), so all data remains backed up and
12317 * unsaved.
12318 */
12319void Machine::i_copyFrom(Machine *aThat)
12320{
12321 AssertReturnVoid(!i_isSnapshotMachine());
12322 AssertReturnVoid(aThat->i_isSnapshotMachine());
12323
12324 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12325
12326 mHWData.assignCopy(aThat->mHWData);
12327
12328 // create copies of all shared folders (mHWData after attaching a copy
12329 // contains just references to original objects)
12330 for (HWData::SharedFolderList::iterator
12331 it = mHWData->mSharedFolders.begin();
12332 it != mHWData->mSharedFolders.end();
12333 ++it)
12334 {
12335 ComObjPtr<SharedFolder> folder;
12336 folder.createObject();
12337 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12338 AssertComRC(rc);
12339 *it = folder;
12340 }
12341
12342 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12343 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12344 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12345 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12346 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12347
12348 /* create private copies of all controllers */
12349 mStorageControllers.backup();
12350 mStorageControllers->clear();
12351 for (StorageControllerList::const_iterator
12352 it = aThat->mStorageControllers->begin();
12353 it != aThat->mStorageControllers->end();
12354 ++it)
12355 {
12356 ComObjPtr<StorageController> ctrl;
12357 ctrl.createObject();
12358 ctrl->initCopy(this, *it);
12359 mStorageControllers->push_back(ctrl);
12360 }
12361
12362 /* create private copies of all USB controllers */
12363 mUSBControllers.backup();
12364 mUSBControllers->clear();
12365 for (USBControllerList::const_iterator
12366 it = aThat->mUSBControllers->begin();
12367 it != aThat->mUSBControllers->end();
12368 ++it)
12369 {
12370 ComObjPtr<USBController> ctrl;
12371 ctrl.createObject();
12372 ctrl->initCopy(this, *it);
12373 mUSBControllers->push_back(ctrl);
12374 }
12375
12376 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12377 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12378 {
12379 if (mNetworkAdapters[slot].isNotNull())
12380 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12381 else
12382 {
12383 unconst(mNetworkAdapters[slot]).createObject();
12384 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12385 }
12386 }
12387 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12388 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12389 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12390 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12391}
12392
12393/**
12394 * Returns whether the given storage controller is hotplug capable.
12395 *
12396 * @returns true if the controller supports hotplugging
12397 * false otherwise.
12398 * @param enmCtrlType The controller type to check for.
12399 */
12400bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12401{
12402 ComPtr<ISystemProperties> systemProperties;
12403 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12404 if (FAILED(rc))
12405 return false;
12406
12407 BOOL aHotplugCapable = FALSE;
12408 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12409
12410 return RT_BOOL(aHotplugCapable);
12411}
12412
12413#ifdef VBOX_WITH_RESOURCE_USAGE_API
12414
12415void Machine::i_getDiskList(MediaList &list)
12416{
12417 for (MediumAttachmentList::const_iterator
12418 it = mMediumAttachments->begin();
12419 it != mMediumAttachments->end();
12420 ++it)
12421 {
12422 MediumAttachment *pAttach = *it;
12423 /* just in case */
12424 AssertContinue(pAttach);
12425
12426 AutoCaller localAutoCallerA(pAttach);
12427 if (FAILED(localAutoCallerA.rc())) continue;
12428
12429 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12430
12431 if (pAttach->i_getType() == DeviceType_HardDisk)
12432 list.push_back(pAttach->i_getMedium());
12433 }
12434}
12435
12436void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12437{
12438 AssertReturnVoid(isWriteLockOnCurrentThread());
12439 AssertPtrReturnVoid(aCollector);
12440
12441 pm::CollectorHAL *hal = aCollector->getHAL();
12442 /* Create sub metrics */
12443 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12444 "Percentage of processor time spent in user mode by the VM process.");
12445 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12446 "Percentage of processor time spent in kernel mode by the VM process.");
12447 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12448 "Size of resident portion of VM process in memory.");
12449 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12450 "Actual size of all VM disks combined.");
12451 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12452 "Network receive rate.");
12453 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12454 "Network transmit rate.");
12455 /* Create and register base metrics */
12456 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12457 cpuLoadUser, cpuLoadKernel);
12458 aCollector->registerBaseMetric(cpuLoad);
12459 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12460 ramUsageUsed);
12461 aCollector->registerBaseMetric(ramUsage);
12462 MediaList disks;
12463 i_getDiskList(disks);
12464 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12465 diskUsageUsed);
12466 aCollector->registerBaseMetric(diskUsage);
12467
12468 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12469 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12470 new pm::AggregateAvg()));
12471 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12472 new pm::AggregateMin()));
12473 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12474 new pm::AggregateMax()));
12475 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12476 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12477 new pm::AggregateAvg()));
12478 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12479 new pm::AggregateMin()));
12480 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12481 new pm::AggregateMax()));
12482
12483 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12484 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12485 new pm::AggregateAvg()));
12486 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12487 new pm::AggregateMin()));
12488 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12489 new pm::AggregateMax()));
12490
12491 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12492 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12493 new pm::AggregateAvg()));
12494 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12495 new pm::AggregateMin()));
12496 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12497 new pm::AggregateMax()));
12498
12499
12500 /* Guest metrics collector */
12501 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12502 aCollector->registerGuest(mCollectorGuest);
12503 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12504
12505 /* Create sub metrics */
12506 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12507 "Percentage of processor time spent in user mode as seen by the guest.");
12508 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12509 "Percentage of processor time spent in kernel mode as seen by the guest.");
12510 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12511 "Percentage of processor time spent idling as seen by the guest.");
12512
12513 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12514 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12515 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12516 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12517 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12518 pm::SubMetric *guestMemCache = new pm::SubMetric(
12519 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12520
12521 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12522 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12523
12524 /* Create and register base metrics */
12525 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12526 machineNetRx, machineNetTx);
12527 aCollector->registerBaseMetric(machineNetRate);
12528
12529 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12530 guestLoadUser, guestLoadKernel, guestLoadIdle);
12531 aCollector->registerBaseMetric(guestCpuLoad);
12532
12533 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12534 guestMemTotal, guestMemFree,
12535 guestMemBalloon, guestMemShared,
12536 guestMemCache, guestPagedTotal);
12537 aCollector->registerBaseMetric(guestCpuMem);
12538
12539 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12540 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12541 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12542 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12543
12544 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12545 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12546 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12547 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12548
12549 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12550 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12551 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12552 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12553
12554 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12555 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12556 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12557 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12558
12559 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12560 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12561 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12562 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12563
12564 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12565 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12566 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12567 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12568
12569 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12570 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12571 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12572 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12573
12574 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12575 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12576 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12577 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12578
12579 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12580 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12581 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12582 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12583
12584 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12585 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12586 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12587 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12588
12589 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12590 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12591 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12592 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12593}
12594
12595void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12596{
12597 AssertReturnVoid(isWriteLockOnCurrentThread());
12598
12599 if (aCollector)
12600 {
12601 aCollector->unregisterMetricsFor(aMachine);
12602 aCollector->unregisterBaseMetricsFor(aMachine);
12603 }
12604}
12605
12606#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12607
12608
12609////////////////////////////////////////////////////////////////////////////////
12610
12611DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12612
12613HRESULT SessionMachine::FinalConstruct()
12614{
12615 LogFlowThisFunc(("\n"));
12616
12617 mClientToken = NULL;
12618
12619 return BaseFinalConstruct();
12620}
12621
12622void SessionMachine::FinalRelease()
12623{
12624 LogFlowThisFunc(("\n"));
12625
12626 Assert(!mClientToken);
12627 /* paranoia, should not hang around any more */
12628 if (mClientToken)
12629 {
12630 delete mClientToken;
12631 mClientToken = NULL;
12632 }
12633
12634 uninit(Uninit::Unexpected);
12635
12636 BaseFinalRelease();
12637}
12638
12639/**
12640 * @note Must be called only by Machine::LockMachine() from its own write lock.
12641 */
12642HRESULT SessionMachine::init(Machine *aMachine)
12643{
12644 LogFlowThisFuncEnter();
12645 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12646
12647 AssertReturn(aMachine, E_INVALIDARG);
12648
12649 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12650
12651 /* Enclose the state transition NotReady->InInit->Ready */
12652 AutoInitSpan autoInitSpan(this);
12653 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12654
12655 HRESULT rc = S_OK;
12656
12657 RT_ZERO(mAuthLibCtx);
12658
12659 /* create the machine client token */
12660 try
12661 {
12662 mClientToken = new ClientToken(aMachine, this);
12663 if (!mClientToken->isReady())
12664 {
12665 delete mClientToken;
12666 mClientToken = NULL;
12667 rc = E_FAIL;
12668 }
12669 }
12670 catch (std::bad_alloc &)
12671 {
12672 rc = E_OUTOFMEMORY;
12673 }
12674 if (FAILED(rc))
12675 return rc;
12676
12677 /* memorize the peer Machine */
12678 unconst(mPeer) = aMachine;
12679 /* share the parent pointer */
12680 unconst(mParent) = aMachine->mParent;
12681
12682 /* take the pointers to data to share */
12683 mData.share(aMachine->mData);
12684 mSSData.share(aMachine->mSSData);
12685
12686 mUserData.share(aMachine->mUserData);
12687 mHWData.share(aMachine->mHWData);
12688 mMediumAttachments.share(aMachine->mMediumAttachments);
12689
12690 mStorageControllers.allocate();
12691 for (StorageControllerList::const_iterator
12692 it = aMachine->mStorageControllers->begin();
12693 it != aMachine->mStorageControllers->end();
12694 ++it)
12695 {
12696 ComObjPtr<StorageController> ctl;
12697 ctl.createObject();
12698 ctl->init(this, *it);
12699 mStorageControllers->push_back(ctl);
12700 }
12701
12702 mUSBControllers.allocate();
12703 for (USBControllerList::const_iterator
12704 it = aMachine->mUSBControllers->begin();
12705 it != aMachine->mUSBControllers->end();
12706 ++it)
12707 {
12708 ComObjPtr<USBController> ctl;
12709 ctl.createObject();
12710 ctl->init(this, *it);
12711 mUSBControllers->push_back(ctl);
12712 }
12713
12714 unconst(mBIOSSettings).createObject();
12715 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12716 /* create another VRDEServer object that will be mutable */
12717 unconst(mVRDEServer).createObject();
12718 mVRDEServer->init(this, aMachine->mVRDEServer);
12719 /* create another audio adapter object that will be mutable */
12720 unconst(mAudioAdapter).createObject();
12721 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12722 /* create a list of serial ports that will be mutable */
12723 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12724 {
12725 unconst(mSerialPorts[slot]).createObject();
12726 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12727 }
12728 /* create a list of parallel ports that will be mutable */
12729 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12730 {
12731 unconst(mParallelPorts[slot]).createObject();
12732 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12733 }
12734
12735 /* create another USB device filters object that will be mutable */
12736 unconst(mUSBDeviceFilters).createObject();
12737 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12738
12739 /* create a list of network adapters that will be mutable */
12740 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12741 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12742 {
12743 unconst(mNetworkAdapters[slot]).createObject();
12744 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12745 }
12746
12747 /* create another bandwidth control object that will be mutable */
12748 unconst(mBandwidthControl).createObject();
12749 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12750
12751 /* default is to delete saved state on Saved -> PoweredOff transition */
12752 mRemoveSavedState = true;
12753
12754 /* Confirm a successful initialization when it's the case */
12755 autoInitSpan.setSucceeded();
12756
12757 miNATNetworksStarted = 0;
12758
12759 LogFlowThisFuncLeave();
12760 return rc;
12761}
12762
12763/**
12764 * Uninitializes this session object. If the reason is other than
12765 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12766 * or the client watcher code.
12767 *
12768 * @param aReason uninitialization reason
12769 *
12770 * @note Locks mParent + this object for writing.
12771 */
12772void SessionMachine::uninit(Uninit::Reason aReason)
12773{
12774 LogFlowThisFuncEnter();
12775 LogFlowThisFunc(("reason=%d\n", aReason));
12776
12777 /*
12778 * Strongly reference ourselves to prevent this object deletion after
12779 * mData->mSession.mMachine.setNull() below (which can release the last
12780 * reference and call the destructor). Important: this must be done before
12781 * accessing any members (and before AutoUninitSpan that does it as well).
12782 * This self reference will be released as the very last step on return.
12783 */
12784 ComObjPtr<SessionMachine> selfRef;
12785 if (aReason != Uninit::Unexpected)
12786 selfRef = this;
12787
12788 /* Enclose the state transition Ready->InUninit->NotReady */
12789 AutoUninitSpan autoUninitSpan(this);
12790 if (autoUninitSpan.uninitDone())
12791 {
12792 LogFlowThisFunc(("Already uninitialized\n"));
12793 LogFlowThisFuncLeave();
12794 return;
12795 }
12796
12797 if (autoUninitSpan.initFailed())
12798 {
12799 /* We've been called by init() because it's failed. It's not really
12800 * necessary (nor it's safe) to perform the regular uninit sequence
12801 * below, the following is enough.
12802 */
12803 LogFlowThisFunc(("Initialization failed.\n"));
12804 /* destroy the machine client token */
12805 if (mClientToken)
12806 {
12807 delete mClientToken;
12808 mClientToken = NULL;
12809 }
12810 uninitDataAndChildObjects();
12811 mData.free();
12812 unconst(mParent) = NULL;
12813 unconst(mPeer) = NULL;
12814 LogFlowThisFuncLeave();
12815 return;
12816 }
12817
12818 MachineState_T lastState;
12819 {
12820 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12821 lastState = mData->mMachineState;
12822 }
12823 NOREF(lastState);
12824
12825#ifdef VBOX_WITH_USB
12826 // release all captured USB devices, but do this before requesting the locks below
12827 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12828 {
12829 /* Console::captureUSBDevices() is called in the VM process only after
12830 * setting the machine state to Starting or Restoring.
12831 * Console::detachAllUSBDevices() will be called upon successful
12832 * termination. So, we need to release USB devices only if there was
12833 * an abnormal termination of a running VM.
12834 *
12835 * This is identical to SessionMachine::DetachAllUSBDevices except
12836 * for the aAbnormal argument. */
12837 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12838 AssertComRC(rc);
12839 NOREF(rc);
12840
12841 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12842 if (service)
12843 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12844 }
12845#endif /* VBOX_WITH_USB */
12846
12847 // we need to lock this object in uninit() because the lock is shared
12848 // with mPeer (as well as data we modify below). mParent lock is needed
12849 // by several calls to it.
12850 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12851
12852#ifdef VBOX_WITH_RESOURCE_USAGE_API
12853 /*
12854 * It is safe to call Machine::i_unregisterMetrics() here because
12855 * PerformanceCollector::samplerCallback no longer accesses guest methods
12856 * holding the lock.
12857 */
12858 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12859 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12860 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12861 if (mCollectorGuest)
12862 {
12863 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12864 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12865 mCollectorGuest = NULL;
12866 }
12867#endif
12868
12869 if (aReason == Uninit::Abnormal)
12870 {
12871 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12872
12873 /* reset the state to Aborted */
12874 if (mData->mMachineState != MachineState_Aborted)
12875 i_setMachineState(MachineState_Aborted);
12876 }
12877
12878 // any machine settings modified?
12879 if (mData->flModifications)
12880 {
12881 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12882 i_rollback(false /* aNotify */);
12883 }
12884
12885 mData->mSession.mPID = NIL_RTPROCESS;
12886
12887 if (aReason == Uninit::Unexpected)
12888 {
12889 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12890 * client watcher thread to update the set of machines that have open
12891 * sessions. */
12892 mParent->i_updateClientWatcher();
12893 }
12894
12895 /* uninitialize all remote controls */
12896 if (mData->mSession.mRemoteControls.size())
12897 {
12898 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12899 mData->mSession.mRemoteControls.size()));
12900
12901 /* Always restart a the beginning, since the iterator is invalidated
12902 * by using erase(). */
12903 for (Data::Session::RemoteControlList::iterator
12904 it = mData->mSession.mRemoteControls.begin();
12905 it != mData->mSession.mRemoteControls.end();
12906 it = mData->mSession.mRemoteControls.begin())
12907 {
12908 ComPtr<IInternalSessionControl> pControl = *it;
12909 mData->mSession.mRemoteControls.erase(it);
12910 multilock.release();
12911 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12912 HRESULT rc = pControl->Uninitialize();
12913 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12914 if (FAILED(rc))
12915 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12916 multilock.acquire();
12917 }
12918 mData->mSession.mRemoteControls.clear();
12919 }
12920
12921 /* Remove all references to the NAT network service. The service will stop
12922 * if all references (also from other VMs) are removed. */
12923 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12924 {
12925 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12926 {
12927 BOOL enabled;
12928 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12929 if ( FAILED(hrc)
12930 || !enabled)
12931 continue;
12932
12933 NetworkAttachmentType_T type;
12934 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12935 if ( SUCCEEDED(hrc)
12936 && type == NetworkAttachmentType_NATNetwork)
12937 {
12938 Bstr name;
12939 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12940 if (SUCCEEDED(hrc))
12941 {
12942 multilock.release();
12943 Utf8Str strName(name);
12944 LogRel(("VM '%s' stops using NAT network '%s'\n",
12945 mUserData->s.strName.c_str(), strName.c_str()));
12946 mParent->i_natNetworkRefDec(strName);
12947 multilock.acquire();
12948 }
12949 }
12950 }
12951 }
12952
12953 /*
12954 * An expected uninitialization can come only from #i_checkForDeath().
12955 * Otherwise it means that something's gone really wrong (for example,
12956 * the Session implementation has released the VirtualBox reference
12957 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12958 * etc). However, it's also possible, that the client releases the IPC
12959 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12960 * but the VirtualBox release event comes first to the server process.
12961 * This case is practically possible, so we should not assert on an
12962 * unexpected uninit, just log a warning.
12963 */
12964
12965 if (aReason == Uninit::Unexpected)
12966 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12967
12968 if (aReason != Uninit::Normal)
12969 {
12970 mData->mSession.mDirectControl.setNull();
12971 }
12972 else
12973 {
12974 /* this must be null here (see #OnSessionEnd()) */
12975 Assert(mData->mSession.mDirectControl.isNull());
12976 Assert(mData->mSession.mState == SessionState_Unlocking);
12977 Assert(!mData->mSession.mProgress.isNull());
12978 }
12979 if (mData->mSession.mProgress)
12980 {
12981 if (aReason == Uninit::Normal)
12982 mData->mSession.mProgress->i_notifyComplete(S_OK);
12983 else
12984 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12985 COM_IIDOF(ISession),
12986 getComponentName(),
12987 tr("The VM session was aborted"));
12988 mData->mSession.mProgress.setNull();
12989 }
12990
12991 if (mConsoleTaskData.mProgress)
12992 {
12993 Assert(aReason == Uninit::Abnormal);
12994 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12995 COM_IIDOF(ISession),
12996 getComponentName(),
12997 tr("The VM session was aborted"));
12998 mConsoleTaskData.mProgress.setNull();
12999 }
13000
13001 /* remove the association between the peer machine and this session machine */
13002 Assert( (SessionMachine*)mData->mSession.mMachine == this
13003 || aReason == Uninit::Unexpected);
13004
13005 /* reset the rest of session data */
13006 mData->mSession.mLockType = LockType_Null;
13007 mData->mSession.mMachine.setNull();
13008 mData->mSession.mState = SessionState_Unlocked;
13009 mData->mSession.mName.setNull();
13010
13011 /* destroy the machine client token before leaving the exclusive lock */
13012 if (mClientToken)
13013 {
13014 delete mClientToken;
13015 mClientToken = NULL;
13016 }
13017
13018 /* fire an event */
13019 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13020
13021 uninitDataAndChildObjects();
13022
13023 /* free the essential data structure last */
13024 mData.free();
13025
13026 /* release the exclusive lock before setting the below two to NULL */
13027 multilock.release();
13028
13029 unconst(mParent) = NULL;
13030 unconst(mPeer) = NULL;
13031
13032 AuthLibUnload(&mAuthLibCtx);
13033
13034 LogFlowThisFuncLeave();
13035}
13036
13037// util::Lockable interface
13038////////////////////////////////////////////////////////////////////////////////
13039
13040/**
13041 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13042 * with the primary Machine instance (mPeer).
13043 */
13044RWLockHandle *SessionMachine::lockHandle() const
13045{
13046 AssertReturn(mPeer != NULL, NULL);
13047 return mPeer->lockHandle();
13048}
13049
13050// IInternalMachineControl methods
13051////////////////////////////////////////////////////////////////////////////////
13052
13053/**
13054 * Passes collected guest statistics to performance collector object
13055 */
13056HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13057 ULONG aCpuKernel, ULONG aCpuIdle,
13058 ULONG aMemTotal, ULONG aMemFree,
13059 ULONG aMemBalloon, ULONG aMemShared,
13060 ULONG aMemCache, ULONG aPageTotal,
13061 ULONG aAllocVMM, ULONG aFreeVMM,
13062 ULONG aBalloonedVMM, ULONG aSharedVMM,
13063 ULONG aVmNetRx, ULONG aVmNetTx)
13064{
13065#ifdef VBOX_WITH_RESOURCE_USAGE_API
13066 if (mCollectorGuest)
13067 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13068 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13069 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13070 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13071
13072 return S_OK;
13073#else
13074 NOREF(aValidStats);
13075 NOREF(aCpuUser);
13076 NOREF(aCpuKernel);
13077 NOREF(aCpuIdle);
13078 NOREF(aMemTotal);
13079 NOREF(aMemFree);
13080 NOREF(aMemBalloon);
13081 NOREF(aMemShared);
13082 NOREF(aMemCache);
13083 NOREF(aPageTotal);
13084 NOREF(aAllocVMM);
13085 NOREF(aFreeVMM);
13086 NOREF(aBalloonedVMM);
13087 NOREF(aSharedVMM);
13088 NOREF(aVmNetRx);
13089 NOREF(aVmNetTx);
13090 return E_NOTIMPL;
13091#endif
13092}
13093
13094////////////////////////////////////////////////////////////////////////////////
13095//
13096// SessionMachine task records
13097//
13098////////////////////////////////////////////////////////////////////////////////
13099
13100/**
13101 * Task record for saving the machine state.
13102 */
13103class SessionMachine::SaveStateTask
13104 : public Machine::Task
13105{
13106public:
13107 SaveStateTask(SessionMachine *m,
13108 Progress *p,
13109 const Utf8Str &t,
13110 Reason_T enmReason,
13111 const Utf8Str &strStateFilePath)
13112 : Task(m, p, t),
13113 m_enmReason(enmReason),
13114 m_strStateFilePath(strStateFilePath)
13115 {}
13116
13117private:
13118 void handler()
13119 {
13120 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13121 }
13122
13123 Reason_T m_enmReason;
13124 Utf8Str m_strStateFilePath;
13125
13126 friend class SessionMachine;
13127};
13128
13129/**
13130 * Task thread implementation for SessionMachine::SaveState(), called from
13131 * SessionMachine::taskHandler().
13132 *
13133 * @note Locks this object for writing.
13134 *
13135 * @param task
13136 * @return
13137 */
13138void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13139{
13140 LogFlowThisFuncEnter();
13141
13142 AutoCaller autoCaller(this);
13143 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13144 if (FAILED(autoCaller.rc()))
13145 {
13146 /* we might have been uninitialized because the session was accidentally
13147 * closed by the client, so don't assert */
13148 HRESULT rc = setError(E_FAIL,
13149 tr("The session has been accidentally closed"));
13150 task.m_pProgress->i_notifyComplete(rc);
13151 LogFlowThisFuncLeave();
13152 return;
13153 }
13154
13155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13156
13157 HRESULT rc = S_OK;
13158
13159 try
13160 {
13161 ComPtr<IInternalSessionControl> directControl;
13162 if (mData->mSession.mLockType == LockType_VM)
13163 directControl = mData->mSession.mDirectControl;
13164 if (directControl.isNull())
13165 throw setError(VBOX_E_INVALID_VM_STATE,
13166 tr("Trying to save state without a running VM"));
13167 alock.release();
13168 BOOL fSuspendedBySave;
13169 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13170 Assert(!fSuspendedBySave);
13171 alock.acquire();
13172
13173 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13174 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13175 throw E_FAIL);
13176
13177 if (SUCCEEDED(rc))
13178 {
13179 mSSData->strStateFilePath = task.m_strStateFilePath;
13180
13181 /* save all VM settings */
13182 rc = i_saveSettings(NULL);
13183 // no need to check whether VirtualBox.xml needs saving also since
13184 // we can't have a name change pending at this point
13185 }
13186 else
13187 {
13188 // On failure, set the state to the state we had at the beginning.
13189 i_setMachineState(task.m_machineStateBackup);
13190 i_updateMachineStateOnClient();
13191
13192 // Delete the saved state file (might have been already created).
13193 // No need to check whether this is shared with a snapshot here
13194 // because we certainly created a fresh saved state file here.
13195 RTFileDelete(task.m_strStateFilePath.c_str());
13196 }
13197 }
13198 catch (HRESULT aRC) { rc = aRC; }
13199
13200 task.m_pProgress->i_notifyComplete(rc);
13201
13202 LogFlowThisFuncLeave();
13203}
13204
13205/**
13206 * @note Locks this object for writing.
13207 */
13208HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13209{
13210 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13211}
13212
13213HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13214{
13215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13216
13217 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13218 if (FAILED(rc)) return rc;
13219
13220 if ( mData->mMachineState != MachineState_Running
13221 && mData->mMachineState != MachineState_Paused
13222 )
13223 return setError(VBOX_E_INVALID_VM_STATE,
13224 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13225 Global::stringifyMachineState(mData->mMachineState));
13226
13227 ComObjPtr<Progress> pProgress;
13228 pProgress.createObject();
13229 rc = pProgress->init(i_getVirtualBox(),
13230 static_cast<IMachine *>(this) /* aInitiator */,
13231 tr("Saving the execution state of the virtual machine"),
13232 FALSE /* aCancelable */);
13233 if (FAILED(rc))
13234 return rc;
13235
13236 Utf8Str strStateFilePath;
13237 i_composeSavedStateFilename(strStateFilePath);
13238
13239 /* create and start the task on a separate thread (note that it will not
13240 * start working until we release alock) */
13241 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13242 rc = pTask->createThread();
13243 if (FAILED(rc))
13244 return rc;
13245
13246 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13247 i_setMachineState(MachineState_Saving);
13248 i_updateMachineStateOnClient();
13249
13250 pProgress.queryInterfaceTo(aProgress.asOutParam());
13251
13252 return S_OK;
13253}
13254
13255/**
13256 * @note Locks this object for writing.
13257 */
13258HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13259{
13260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13261
13262 HRESULT rc = i_checkStateDependency(MutableStateDep);
13263 if (FAILED(rc)) return rc;
13264
13265 if ( mData->mMachineState != MachineState_PoweredOff
13266 && mData->mMachineState != MachineState_Teleported
13267 && mData->mMachineState != MachineState_Aborted
13268 )
13269 return setError(VBOX_E_INVALID_VM_STATE,
13270 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13271 Global::stringifyMachineState(mData->mMachineState));
13272
13273 com::Utf8Str stateFilePathFull;
13274 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13275 if (RT_FAILURE(vrc))
13276 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13277 tr("Invalid saved state file path '%s' (%Rrc)"),
13278 aSavedStateFile.c_str(),
13279 vrc);
13280
13281 mSSData->strStateFilePath = stateFilePathFull;
13282
13283 /* The below i_setMachineState() will detect the state transition and will
13284 * update the settings file */
13285
13286 return i_setMachineState(MachineState_Saved);
13287}
13288
13289/**
13290 * @note Locks this object for writing.
13291 */
13292HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13293{
13294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13295
13296 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13297 if (FAILED(rc)) return rc;
13298
13299 if (mData->mMachineState != MachineState_Saved)
13300 return setError(VBOX_E_INVALID_VM_STATE,
13301 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13302 Global::stringifyMachineState(mData->mMachineState));
13303
13304 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13305
13306 /*
13307 * Saved -> PoweredOff transition will be detected in the SessionMachine
13308 * and properly handled.
13309 */
13310 rc = i_setMachineState(MachineState_PoweredOff);
13311 return rc;
13312}
13313
13314
13315/**
13316 * @note Locks the same as #i_setMachineState() does.
13317 */
13318HRESULT SessionMachine::updateState(MachineState_T aState)
13319{
13320 return i_setMachineState(aState);
13321}
13322
13323/**
13324 * @note Locks this object for writing.
13325 */
13326HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13327{
13328 IProgress *pProgress(aProgress);
13329
13330 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13331
13332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13333
13334 if (mData->mSession.mState != SessionState_Locked)
13335 return VBOX_E_INVALID_OBJECT_STATE;
13336
13337 if (!mData->mSession.mProgress.isNull())
13338 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13339
13340 /* If we didn't reference the NAT network service yet, add a reference to
13341 * force a start */
13342 if (miNATNetworksStarted < 1)
13343 {
13344 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13345 {
13346 BOOL enabled;
13347 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13348 if ( FAILED(hrc)
13349 || !enabled)
13350 continue;
13351
13352 NetworkAttachmentType_T type;
13353 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13354 if ( SUCCEEDED(hrc)
13355 && type == NetworkAttachmentType_NATNetwork)
13356 {
13357 Bstr name;
13358 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13359 if (SUCCEEDED(hrc))
13360 {
13361 Utf8Str strName(name);
13362 LogRel(("VM '%s' starts using NAT network '%s'\n",
13363 mUserData->s.strName.c_str(), strName.c_str()));
13364 mPeer->lockHandle()->unlockWrite();
13365 mParent->i_natNetworkRefInc(strName);
13366#ifdef RT_LOCK_STRICT
13367 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13368#else
13369 mPeer->lockHandle()->lockWrite();
13370#endif
13371 }
13372 }
13373 }
13374 miNATNetworksStarted++;
13375 }
13376
13377 LogFlowThisFunc(("returns S_OK.\n"));
13378 return S_OK;
13379}
13380
13381/**
13382 * @note Locks this object for writing.
13383 */
13384HRESULT SessionMachine::endPowerUp(LONG aResult)
13385{
13386 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13387
13388 if (mData->mSession.mState != SessionState_Locked)
13389 return VBOX_E_INVALID_OBJECT_STATE;
13390
13391 /* Finalize the LaunchVMProcess progress object. */
13392 if (mData->mSession.mProgress)
13393 {
13394 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13395 mData->mSession.mProgress.setNull();
13396 }
13397
13398 if (SUCCEEDED((HRESULT)aResult))
13399 {
13400#ifdef VBOX_WITH_RESOURCE_USAGE_API
13401 /* The VM has been powered up successfully, so it makes sense
13402 * now to offer the performance metrics for a running machine
13403 * object. Doing it earlier wouldn't be safe. */
13404 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13405 mData->mSession.mPID);
13406#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13407 }
13408
13409 return S_OK;
13410}
13411
13412/**
13413 * @note Locks this object for writing.
13414 */
13415HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13416{
13417 LogFlowThisFuncEnter();
13418
13419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13420
13421 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13422 E_FAIL);
13423
13424 /* create a progress object to track operation completion */
13425 ComObjPtr<Progress> pProgress;
13426 pProgress.createObject();
13427 pProgress->init(i_getVirtualBox(),
13428 static_cast<IMachine *>(this) /* aInitiator */,
13429 tr("Stopping the virtual machine"),
13430 FALSE /* aCancelable */);
13431
13432 /* fill in the console task data */
13433 mConsoleTaskData.mLastState = mData->mMachineState;
13434 mConsoleTaskData.mProgress = pProgress;
13435
13436 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13437 i_setMachineState(MachineState_Stopping);
13438
13439 pProgress.queryInterfaceTo(aProgress.asOutParam());
13440
13441 return S_OK;
13442}
13443
13444/**
13445 * @note Locks this object for writing.
13446 */
13447HRESULT SessionMachine::endPoweringDown(LONG aResult,
13448 const com::Utf8Str &aErrMsg)
13449{
13450 LogFlowThisFuncEnter();
13451
13452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13453
13454 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13455 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13456 && mConsoleTaskData.mLastState != MachineState_Null,
13457 E_FAIL);
13458
13459 /*
13460 * On failure, set the state to the state we had when BeginPoweringDown()
13461 * was called (this is expected by Console::PowerDown() and the associated
13462 * task). On success the VM process already changed the state to
13463 * MachineState_PoweredOff, so no need to do anything.
13464 */
13465 if (FAILED(aResult))
13466 i_setMachineState(mConsoleTaskData.mLastState);
13467
13468 /* notify the progress object about operation completion */
13469 Assert(mConsoleTaskData.mProgress);
13470 if (SUCCEEDED(aResult))
13471 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13472 else
13473 {
13474 if (aErrMsg.length())
13475 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13476 COM_IIDOF(ISession),
13477 getComponentName(),
13478 aErrMsg.c_str());
13479 else
13480 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13481 }
13482
13483 /* clear out the temporary saved state data */
13484 mConsoleTaskData.mLastState = MachineState_Null;
13485 mConsoleTaskData.mProgress.setNull();
13486
13487 LogFlowThisFuncLeave();
13488 return S_OK;
13489}
13490
13491
13492/**
13493 * Goes through the USB filters of the given machine to see if the given
13494 * device matches any filter or not.
13495 *
13496 * @note Locks the same as USBController::hasMatchingFilter() does.
13497 */
13498HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13499 BOOL *aMatched,
13500 ULONG *aMaskedInterfaces)
13501{
13502 LogFlowThisFunc(("\n"));
13503
13504#ifdef VBOX_WITH_USB
13505 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13506#else
13507 NOREF(aDevice);
13508 NOREF(aMaskedInterfaces);
13509 *aMatched = FALSE;
13510#endif
13511
13512 return S_OK;
13513}
13514
13515/**
13516 * @note Locks the same as Host::captureUSBDevice() does.
13517 */
13518HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13519{
13520 LogFlowThisFunc(("\n"));
13521
13522#ifdef VBOX_WITH_USB
13523 /* if captureDeviceForVM() fails, it must have set extended error info */
13524 clearError();
13525 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13526 if (FAILED(rc)) return rc;
13527
13528 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13529 AssertReturn(service, E_FAIL);
13530 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13531#else
13532 NOREF(aId);
13533 return E_NOTIMPL;
13534#endif
13535}
13536
13537/**
13538 * @note Locks the same as Host::detachUSBDevice() does.
13539 */
13540HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13541 BOOL aDone)
13542{
13543 LogFlowThisFunc(("\n"));
13544
13545#ifdef VBOX_WITH_USB
13546 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13547 AssertReturn(service, E_FAIL);
13548 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13549#else
13550 NOREF(aId);
13551 NOREF(aDone);
13552 return E_NOTIMPL;
13553#endif
13554}
13555
13556/**
13557 * Inserts all machine filters to the USB proxy service and then calls
13558 * Host::autoCaptureUSBDevices().
13559 *
13560 * Called by Console from the VM process upon VM startup.
13561 *
13562 * @note Locks what called methods lock.
13563 */
13564HRESULT SessionMachine::autoCaptureUSBDevices()
13565{
13566 LogFlowThisFunc(("\n"));
13567
13568#ifdef VBOX_WITH_USB
13569 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13570 AssertComRC(rc);
13571 NOREF(rc);
13572
13573 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13574 AssertReturn(service, E_FAIL);
13575 return service->autoCaptureDevicesForVM(this);
13576#else
13577 return S_OK;
13578#endif
13579}
13580
13581/**
13582 * Removes all machine filters from the USB proxy service and then calls
13583 * Host::detachAllUSBDevices().
13584 *
13585 * Called by Console from the VM process upon normal VM termination or by
13586 * SessionMachine::uninit() upon abnormal VM termination (from under the
13587 * Machine/SessionMachine lock).
13588 *
13589 * @note Locks what called methods lock.
13590 */
13591HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13592{
13593 LogFlowThisFunc(("\n"));
13594
13595#ifdef VBOX_WITH_USB
13596 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13597 AssertComRC(rc);
13598 NOREF(rc);
13599
13600 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13601 AssertReturn(service, E_FAIL);
13602 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13603#else
13604 NOREF(aDone);
13605 return S_OK;
13606#endif
13607}
13608
13609/**
13610 * @note Locks this object for writing.
13611 */
13612HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13613 ComPtr<IProgress> &aProgress)
13614{
13615 LogFlowThisFuncEnter();
13616
13617 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13618 /*
13619 * We don't assert below because it might happen that a non-direct session
13620 * informs us it is closed right after we've been uninitialized -- it's ok.
13621 */
13622
13623 /* get IInternalSessionControl interface */
13624 ComPtr<IInternalSessionControl> control(aSession);
13625
13626 ComAssertRet(!control.isNull(), E_INVALIDARG);
13627
13628 /* Creating a Progress object requires the VirtualBox lock, and
13629 * thus locking it here is required by the lock order rules. */
13630 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13631
13632 if (control == mData->mSession.mDirectControl)
13633 {
13634 /* The direct session is being normally closed by the client process
13635 * ----------------------------------------------------------------- */
13636
13637 /* go to the closing state (essential for all open*Session() calls and
13638 * for #i_checkForDeath()) */
13639 Assert(mData->mSession.mState == SessionState_Locked);
13640 mData->mSession.mState = SessionState_Unlocking;
13641
13642 /* set direct control to NULL to release the remote instance */
13643 mData->mSession.mDirectControl.setNull();
13644 LogFlowThisFunc(("Direct control is set to NULL\n"));
13645
13646 if (mData->mSession.mProgress)
13647 {
13648 /* finalize the progress, someone might wait if a frontend
13649 * closes the session before powering on the VM. */
13650 mData->mSession.mProgress->notifyComplete(E_FAIL,
13651 COM_IIDOF(ISession),
13652 getComponentName(),
13653 tr("The VM session was closed before any attempt to power it on"));
13654 mData->mSession.mProgress.setNull();
13655 }
13656
13657 /* Create the progress object the client will use to wait until
13658 * #i_checkForDeath() is called to uninitialize this session object after
13659 * it releases the IPC semaphore.
13660 * Note! Because we're "reusing" mProgress here, this must be a proxy
13661 * object just like for LaunchVMProcess. */
13662 Assert(mData->mSession.mProgress.isNull());
13663 ComObjPtr<ProgressProxy> progress;
13664 progress.createObject();
13665 ComPtr<IUnknown> pPeer(mPeer);
13666 progress->init(mParent, pPeer,
13667 Bstr(tr("Closing session")).raw(),
13668 FALSE /* aCancelable */);
13669 progress.queryInterfaceTo(aProgress.asOutParam());
13670 mData->mSession.mProgress = progress;
13671 }
13672 else
13673 {
13674 /* the remote session is being normally closed */
13675 bool found = false;
13676 for (Data::Session::RemoteControlList::iterator
13677 it = mData->mSession.mRemoteControls.begin();
13678 it != mData->mSession.mRemoteControls.end();
13679 ++it)
13680 {
13681 if (control == *it)
13682 {
13683 found = true;
13684 // This MUST be erase(it), not remove(*it) as the latter
13685 // triggers a very nasty use after free due to the place where
13686 // the value "lives".
13687 mData->mSession.mRemoteControls.erase(it);
13688 break;
13689 }
13690 }
13691 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13692 E_INVALIDARG);
13693 }
13694
13695 /* signal the client watcher thread, because the client is going away */
13696 mParent->i_updateClientWatcher();
13697
13698 LogFlowThisFuncLeave();
13699 return S_OK;
13700}
13701
13702HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13703 std::vector<com::Utf8Str> &aValues,
13704 std::vector<LONG64> &aTimestamps,
13705 std::vector<com::Utf8Str> &aFlags)
13706{
13707 LogFlowThisFunc(("\n"));
13708
13709#ifdef VBOX_WITH_GUEST_PROPS
13710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13711
13712 size_t cEntries = mHWData->mGuestProperties.size();
13713 aNames.resize(cEntries);
13714 aValues.resize(cEntries);
13715 aTimestamps.resize(cEntries);
13716 aFlags.resize(cEntries);
13717
13718 size_t i = 0;
13719 for (HWData::GuestPropertyMap::const_iterator
13720 it = mHWData->mGuestProperties.begin();
13721 it != mHWData->mGuestProperties.end();
13722 ++it, ++i)
13723 {
13724 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13725 aNames[i] = it->first;
13726 aValues[i] = it->second.strValue;
13727 aTimestamps[i] = it->second.mTimestamp;
13728
13729 /* If it is NULL, keep it NULL. */
13730 if (it->second.mFlags)
13731 {
13732 GuestPropWriteFlags(it->second.mFlags, szFlags);
13733 aFlags[i] = szFlags;
13734 }
13735 else
13736 aFlags[i] = "";
13737 }
13738 return S_OK;
13739#else
13740 ReturnComNotImplemented();
13741#endif
13742}
13743
13744HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13745 const com::Utf8Str &aValue,
13746 LONG64 aTimestamp,
13747 const com::Utf8Str &aFlags)
13748{
13749 LogFlowThisFunc(("\n"));
13750
13751#ifdef VBOX_WITH_GUEST_PROPS
13752 try
13753 {
13754 /*
13755 * Convert input up front.
13756 */
13757 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13758 if (aFlags.length())
13759 {
13760 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13761 AssertRCReturn(vrc, E_INVALIDARG);
13762 }
13763
13764 /*
13765 * Now grab the object lock, validate the state and do the update.
13766 */
13767
13768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13769
13770 if (!Global::IsOnline(mData->mMachineState))
13771 {
13772 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13773 VBOX_E_INVALID_VM_STATE);
13774 }
13775
13776 i_setModified(IsModified_MachineData);
13777 mHWData.backup();
13778
13779 bool fDelete = !aValue.length();
13780 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13781 if (it != mHWData->mGuestProperties.end())
13782 {
13783 if (!fDelete)
13784 {
13785 it->second.strValue = aValue;
13786 it->second.mTimestamp = aTimestamp;
13787 it->second.mFlags = fFlags;
13788 }
13789 else
13790 mHWData->mGuestProperties.erase(it);
13791
13792 mData->mGuestPropertiesModified = TRUE;
13793 }
13794 else if (!fDelete)
13795 {
13796 HWData::GuestProperty prop;
13797 prop.strValue = aValue;
13798 prop.mTimestamp = aTimestamp;
13799 prop.mFlags = fFlags;
13800
13801 mHWData->mGuestProperties[aName] = prop;
13802 mData->mGuestPropertiesModified = TRUE;
13803 }
13804
13805 alock.release();
13806
13807 mParent->i_onGuestPropertyChange(mData->mUuid,
13808 Bstr(aName).raw(),
13809 Bstr(aValue).raw(),
13810 Bstr(aFlags).raw());
13811 }
13812 catch (...)
13813 {
13814 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13815 }
13816 return S_OK;
13817#else
13818 ReturnComNotImplemented();
13819#endif
13820}
13821
13822
13823HRESULT SessionMachine::lockMedia()
13824{
13825 AutoMultiWriteLock2 alock(this->lockHandle(),
13826 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13827
13828 AssertReturn( mData->mMachineState == MachineState_Starting
13829 || mData->mMachineState == MachineState_Restoring
13830 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13831
13832 clearError();
13833 alock.release();
13834 return i_lockMedia();
13835}
13836
13837HRESULT SessionMachine::unlockMedia()
13838{
13839 HRESULT hrc = i_unlockMedia();
13840 return hrc;
13841}
13842
13843HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13844 ComPtr<IMediumAttachment> &aNewAttachment)
13845{
13846 // request the host lock first, since might be calling Host methods for getting host drives;
13847 // next, protect the media tree all the while we're in here, as well as our member variables
13848 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13849 this->lockHandle(),
13850 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13851
13852 IMediumAttachment *iAttach = aAttachment;
13853 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13854
13855 Utf8Str ctrlName;
13856 LONG lPort;
13857 LONG lDevice;
13858 bool fTempEject;
13859 {
13860 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13861
13862 /* Need to query the details first, as the IMediumAttachment reference
13863 * might be to the original settings, which we are going to change. */
13864 ctrlName = pAttach->i_getControllerName();
13865 lPort = pAttach->i_getPort();
13866 lDevice = pAttach->i_getDevice();
13867 fTempEject = pAttach->i_getTempEject();
13868 }
13869
13870 if (!fTempEject)
13871 {
13872 /* Remember previously mounted medium. The medium before taking the
13873 * backup is not necessarily the same thing. */
13874 ComObjPtr<Medium> oldmedium;
13875 oldmedium = pAttach->i_getMedium();
13876
13877 i_setModified(IsModified_Storage);
13878 mMediumAttachments.backup();
13879
13880 // The backup operation makes the pAttach reference point to the
13881 // old settings. Re-get the correct reference.
13882 pAttach = i_findAttachment(*mMediumAttachments.data(),
13883 ctrlName,
13884 lPort,
13885 lDevice);
13886
13887 {
13888 AutoCaller autoAttachCaller(this);
13889 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13890
13891 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13892 if (!oldmedium.isNull())
13893 oldmedium->i_removeBackReference(mData->mUuid);
13894
13895 pAttach->i_updateMedium(NULL);
13896 pAttach->i_updateEjected();
13897 }
13898
13899 i_setModified(IsModified_Storage);
13900 }
13901 else
13902 {
13903 {
13904 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13905 pAttach->i_updateEjected();
13906 }
13907 }
13908
13909 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13910
13911 return S_OK;
13912}
13913
13914HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13915 com::Utf8Str &aResult)
13916{
13917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13918
13919 HRESULT hr = S_OK;
13920
13921 if (!mAuthLibCtx.hAuthLibrary)
13922 {
13923 /* Load the external authentication library. */
13924 Bstr authLibrary;
13925 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13926
13927 Utf8Str filename = authLibrary;
13928
13929 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13930 if (RT_FAILURE(vrc))
13931 hr = setErrorBoth(E_FAIL, vrc,
13932 tr("Could not load the external authentication library '%s' (%Rrc)"),
13933 filename.c_str(), vrc);
13934 }
13935
13936 /* The auth library might need the machine lock. */
13937 alock.release();
13938
13939 if (FAILED(hr))
13940 return hr;
13941
13942 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13943 {
13944 enum VRDEAuthParams
13945 {
13946 parmUuid = 1,
13947 parmGuestJudgement,
13948 parmUser,
13949 parmPassword,
13950 parmDomain,
13951 parmClientId
13952 };
13953
13954 AuthResult result = AuthResultAccessDenied;
13955
13956 Guid uuid(aAuthParams[parmUuid]);
13957 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13958 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13959
13960 result = AuthLibAuthenticate(&mAuthLibCtx,
13961 uuid.raw(), guestJudgement,
13962 aAuthParams[parmUser].c_str(),
13963 aAuthParams[parmPassword].c_str(),
13964 aAuthParams[parmDomain].c_str(),
13965 u32ClientId);
13966
13967 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13968 size_t cbPassword = aAuthParams[parmPassword].length();
13969 if (cbPassword)
13970 {
13971 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13972 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13973 }
13974
13975 if (result == AuthResultAccessGranted)
13976 aResult = "granted";
13977 else
13978 aResult = "denied";
13979
13980 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13981 aAuthParams[parmUser].c_str(), aResult.c_str()));
13982 }
13983 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13984 {
13985 enum VRDEAuthDisconnectParams
13986 {
13987 parmUuid = 1,
13988 parmClientId
13989 };
13990
13991 Guid uuid(aAuthParams[parmUuid]);
13992 uint32_t u32ClientId = 0;
13993 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13994 }
13995 else
13996 {
13997 hr = E_INVALIDARG;
13998 }
13999
14000 return hr;
14001}
14002
14003// public methods only for internal purposes
14004/////////////////////////////////////////////////////////////////////////////
14005
14006#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14007/**
14008 * Called from the client watcher thread to check for expected or unexpected
14009 * death of the client process that has a direct session to this machine.
14010 *
14011 * On Win32 and on OS/2, this method is called only when we've got the
14012 * mutex (i.e. the client has either died or terminated normally) so it always
14013 * returns @c true (the client is terminated, the session machine is
14014 * uninitialized).
14015 *
14016 * On other platforms, the method returns @c true if the client process has
14017 * terminated normally or abnormally and the session machine was uninitialized,
14018 * and @c false if the client process is still alive.
14019 *
14020 * @note Locks this object for writing.
14021 */
14022bool SessionMachine::i_checkForDeath()
14023{
14024 Uninit::Reason reason;
14025 bool terminated = false;
14026
14027 /* Enclose autoCaller with a block because calling uninit() from under it
14028 * will deadlock. */
14029 {
14030 AutoCaller autoCaller(this);
14031 if (!autoCaller.isOk())
14032 {
14033 /* return true if not ready, to cause the client watcher to exclude
14034 * the corresponding session from watching */
14035 LogFlowThisFunc(("Already uninitialized!\n"));
14036 return true;
14037 }
14038
14039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14040
14041 /* Determine the reason of death: if the session state is Closing here,
14042 * everything is fine. Otherwise it means that the client did not call
14043 * OnSessionEnd() before it released the IPC semaphore. This may happen
14044 * either because the client process has abnormally terminated, or
14045 * because it simply forgot to call ISession::Close() before exiting. We
14046 * threat the latter also as an abnormal termination (see
14047 * Session::uninit() for details). */
14048 reason = mData->mSession.mState == SessionState_Unlocking ?
14049 Uninit::Normal :
14050 Uninit::Abnormal;
14051
14052 if (mClientToken)
14053 terminated = mClientToken->release();
14054 } /* AutoCaller block */
14055
14056 if (terminated)
14057 uninit(reason);
14058
14059 return terminated;
14060}
14061
14062void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14063{
14064 LogFlowThisFunc(("\n"));
14065
14066 strTokenId.setNull();
14067
14068 AutoCaller autoCaller(this);
14069 AssertComRCReturnVoid(autoCaller.rc());
14070
14071 Assert(mClientToken);
14072 if (mClientToken)
14073 mClientToken->getId(strTokenId);
14074}
14075#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14076IToken *SessionMachine::i_getToken()
14077{
14078 LogFlowThisFunc(("\n"));
14079
14080 AutoCaller autoCaller(this);
14081 AssertComRCReturn(autoCaller.rc(), NULL);
14082
14083 Assert(mClientToken);
14084 if (mClientToken)
14085 return mClientToken->getToken();
14086 else
14087 return NULL;
14088}
14089#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14090
14091Machine::ClientToken *SessionMachine::i_getClientToken()
14092{
14093 LogFlowThisFunc(("\n"));
14094
14095 AutoCaller autoCaller(this);
14096 AssertComRCReturn(autoCaller.rc(), NULL);
14097
14098 return mClientToken;
14099}
14100
14101
14102/**
14103 * @note Locks this object for reading.
14104 */
14105HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14106{
14107 LogFlowThisFunc(("\n"));
14108
14109 AutoCaller autoCaller(this);
14110 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14111
14112 ComPtr<IInternalSessionControl> directControl;
14113 {
14114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14115 if (mData->mSession.mLockType == LockType_VM)
14116 directControl = mData->mSession.mDirectControl;
14117 }
14118
14119 /* ignore notifications sent after #OnSessionEnd() is called */
14120 if (!directControl)
14121 return S_OK;
14122
14123 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14124}
14125
14126/**
14127 * @note Locks this object for reading.
14128 */
14129HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14130 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14131 IN_BSTR aGuestIp, LONG aGuestPort)
14132{
14133 LogFlowThisFunc(("\n"));
14134
14135 AutoCaller autoCaller(this);
14136 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14137
14138 ComPtr<IInternalSessionControl> directControl;
14139 {
14140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14141 if (mData->mSession.mLockType == LockType_VM)
14142 directControl = mData->mSession.mDirectControl;
14143 }
14144
14145 /* ignore notifications sent after #OnSessionEnd() is called */
14146 if (!directControl)
14147 return S_OK;
14148 /*
14149 * instead acting like callback we ask IVirtualBox deliver corresponding event
14150 */
14151
14152 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14153 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14154 return S_OK;
14155}
14156
14157/**
14158 * @note Locks this object for reading.
14159 */
14160HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14161{
14162 LogFlowThisFunc(("\n"));
14163
14164 AutoCaller autoCaller(this);
14165 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14166
14167 ComPtr<IInternalSessionControl> directControl;
14168 {
14169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14170 if (mData->mSession.mLockType == LockType_VM)
14171 directControl = mData->mSession.mDirectControl;
14172 }
14173
14174 /* ignore notifications sent after #OnSessionEnd() is called */
14175 if (!directControl)
14176 return S_OK;
14177
14178 return directControl->OnAudioAdapterChange(audioAdapter);
14179}
14180
14181/**
14182 * @note Locks this object for reading.
14183 */
14184HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14185{
14186 LogFlowThisFunc(("\n"));
14187
14188 AutoCaller autoCaller(this);
14189 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14190
14191 ComPtr<IInternalSessionControl> directControl;
14192 {
14193 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14194 if (mData->mSession.mLockType == LockType_VM)
14195 directControl = mData->mSession.mDirectControl;
14196 }
14197
14198 /* ignore notifications sent after #OnSessionEnd() is called */
14199 if (!directControl)
14200 return S_OK;
14201
14202 return directControl->OnSerialPortChange(serialPort);
14203}
14204
14205/**
14206 * @note Locks this object for reading.
14207 */
14208HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14209{
14210 LogFlowThisFunc(("\n"));
14211
14212 AutoCaller autoCaller(this);
14213 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14214
14215 ComPtr<IInternalSessionControl> directControl;
14216 {
14217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14218 if (mData->mSession.mLockType == LockType_VM)
14219 directControl = mData->mSession.mDirectControl;
14220 }
14221
14222 /* ignore notifications sent after #OnSessionEnd() is called */
14223 if (!directControl)
14224 return S_OK;
14225
14226 return directControl->OnParallelPortChange(parallelPort);
14227}
14228
14229/**
14230 * @note Locks this object for reading.
14231 */
14232HRESULT SessionMachine::i_onStorageControllerChange()
14233{
14234 LogFlowThisFunc(("\n"));
14235
14236 AutoCaller autoCaller(this);
14237 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14238
14239 ComPtr<IInternalSessionControl> directControl;
14240 {
14241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14242 if (mData->mSession.mLockType == LockType_VM)
14243 directControl = mData->mSession.mDirectControl;
14244 }
14245
14246 /* ignore notifications sent after #OnSessionEnd() is called */
14247 if (!directControl)
14248 return S_OK;
14249
14250 return directControl->OnStorageControllerChange();
14251}
14252
14253/**
14254 * @note Locks this object for reading.
14255 */
14256HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14257{
14258 LogFlowThisFunc(("\n"));
14259
14260 AutoCaller autoCaller(this);
14261 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14262
14263 ComPtr<IInternalSessionControl> directControl;
14264 {
14265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14266 if (mData->mSession.mLockType == LockType_VM)
14267 directControl = mData->mSession.mDirectControl;
14268 }
14269
14270 /* ignore notifications sent after #OnSessionEnd() is called */
14271 if (!directControl)
14272 return S_OK;
14273
14274 return directControl->OnMediumChange(aAttachment, aForce);
14275}
14276
14277/**
14278 * @note Locks this object for reading.
14279 */
14280HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14281{
14282 LogFlowThisFunc(("\n"));
14283
14284 AutoCaller autoCaller(this);
14285 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14286
14287 ComPtr<IInternalSessionControl> directControl;
14288 {
14289 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14290 if (mData->mSession.mLockType == LockType_VM)
14291 directControl = mData->mSession.mDirectControl;
14292 }
14293
14294 /* ignore notifications sent after #OnSessionEnd() is called */
14295 if (!directControl)
14296 return S_OK;
14297
14298 return directControl->OnCPUChange(aCPU, aRemove);
14299}
14300
14301HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14302{
14303 LogFlowThisFunc(("\n"));
14304
14305 AutoCaller autoCaller(this);
14306 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14307
14308 ComPtr<IInternalSessionControl> directControl;
14309 {
14310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14311 if (mData->mSession.mLockType == LockType_VM)
14312 directControl = mData->mSession.mDirectControl;
14313 }
14314
14315 /* ignore notifications sent after #OnSessionEnd() is called */
14316 if (!directControl)
14317 return S_OK;
14318
14319 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14320}
14321
14322/**
14323 * @note Locks this object for reading.
14324 */
14325HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14326{
14327 LogFlowThisFunc(("\n"));
14328
14329 AutoCaller autoCaller(this);
14330 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14331
14332 ComPtr<IInternalSessionControl> directControl;
14333 {
14334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14335 if (mData->mSession.mLockType == LockType_VM)
14336 directControl = mData->mSession.mDirectControl;
14337 }
14338
14339 /* ignore notifications sent after #OnSessionEnd() is called */
14340 if (!directControl)
14341 return S_OK;
14342
14343 return directControl->OnVRDEServerChange(aRestart);
14344}
14345
14346/**
14347 * @note Locks this object for reading.
14348 */
14349HRESULT SessionMachine::i_onVideoCaptureChange()
14350{
14351 LogFlowThisFunc(("\n"));
14352
14353 AutoCaller autoCaller(this);
14354 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14355
14356 ComPtr<IInternalSessionControl> directControl;
14357 {
14358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14359 if (mData->mSession.mLockType == LockType_VM)
14360 directControl = mData->mSession.mDirectControl;
14361 }
14362
14363 /* ignore notifications sent after #OnSessionEnd() is called */
14364 if (!directControl)
14365 return S_OK;
14366
14367 return directControl->OnVideoCaptureChange();
14368}
14369
14370/**
14371 * @note Locks this object for reading.
14372 */
14373HRESULT SessionMachine::i_onUSBControllerChange()
14374{
14375 LogFlowThisFunc(("\n"));
14376
14377 AutoCaller autoCaller(this);
14378 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14379
14380 ComPtr<IInternalSessionControl> directControl;
14381 {
14382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14383 if (mData->mSession.mLockType == LockType_VM)
14384 directControl = mData->mSession.mDirectControl;
14385 }
14386
14387 /* ignore notifications sent after #OnSessionEnd() is called */
14388 if (!directControl)
14389 return S_OK;
14390
14391 return directControl->OnUSBControllerChange();
14392}
14393
14394/**
14395 * @note Locks this object for reading.
14396 */
14397HRESULT SessionMachine::i_onSharedFolderChange()
14398{
14399 LogFlowThisFunc(("\n"));
14400
14401 AutoCaller autoCaller(this);
14402 AssertComRCReturnRC(autoCaller.rc());
14403
14404 ComPtr<IInternalSessionControl> directControl;
14405 {
14406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14407 if (mData->mSession.mLockType == LockType_VM)
14408 directControl = mData->mSession.mDirectControl;
14409 }
14410
14411 /* ignore notifications sent after #OnSessionEnd() is called */
14412 if (!directControl)
14413 return S_OK;
14414
14415 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14416}
14417
14418/**
14419 * @note Locks this object for reading.
14420 */
14421HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14422{
14423 LogFlowThisFunc(("\n"));
14424
14425 AutoCaller autoCaller(this);
14426 AssertComRCReturnRC(autoCaller.rc());
14427
14428 ComPtr<IInternalSessionControl> directControl;
14429 {
14430 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14431 if (mData->mSession.mLockType == LockType_VM)
14432 directControl = mData->mSession.mDirectControl;
14433 }
14434
14435 /* ignore notifications sent after #OnSessionEnd() is called */
14436 if (!directControl)
14437 return S_OK;
14438
14439 return directControl->OnClipboardModeChange(aClipboardMode);
14440}
14441
14442/**
14443 * @note Locks this object for reading.
14444 */
14445HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14446{
14447 LogFlowThisFunc(("\n"));
14448
14449 AutoCaller autoCaller(this);
14450 AssertComRCReturnRC(autoCaller.rc());
14451
14452 ComPtr<IInternalSessionControl> directControl;
14453 {
14454 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14455 if (mData->mSession.mLockType == LockType_VM)
14456 directControl = mData->mSession.mDirectControl;
14457 }
14458
14459 /* ignore notifications sent after #OnSessionEnd() is called */
14460 if (!directControl)
14461 return S_OK;
14462
14463 return directControl->OnDnDModeChange(aDnDMode);
14464}
14465
14466/**
14467 * @note Locks this object for reading.
14468 */
14469HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14470{
14471 LogFlowThisFunc(("\n"));
14472
14473 AutoCaller autoCaller(this);
14474 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14475
14476 ComPtr<IInternalSessionControl> directControl;
14477 {
14478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14479 if (mData->mSession.mLockType == LockType_VM)
14480 directControl = mData->mSession.mDirectControl;
14481 }
14482
14483 /* ignore notifications sent after #OnSessionEnd() is called */
14484 if (!directControl)
14485 return S_OK;
14486
14487 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14488}
14489
14490/**
14491 * @note Locks this object for reading.
14492 */
14493HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14494{
14495 LogFlowThisFunc(("\n"));
14496
14497 AutoCaller autoCaller(this);
14498 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14499
14500 ComPtr<IInternalSessionControl> directControl;
14501 {
14502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14503 if (mData->mSession.mLockType == LockType_VM)
14504 directControl = mData->mSession.mDirectControl;
14505 }
14506
14507 /* ignore notifications sent after #OnSessionEnd() is called */
14508 if (!directControl)
14509 return S_OK;
14510
14511 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14512}
14513
14514/**
14515 * Returns @c true if this machine's USB controller reports it has a matching
14516 * filter for the given USB device and @c false otherwise.
14517 *
14518 * @note locks this object for reading.
14519 */
14520bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14521{
14522 AutoCaller autoCaller(this);
14523 /* silently return if not ready -- this method may be called after the
14524 * direct machine session has been called */
14525 if (!autoCaller.isOk())
14526 return false;
14527
14528#ifdef VBOX_WITH_USB
14529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14530
14531 switch (mData->mMachineState)
14532 {
14533 case MachineState_Starting:
14534 case MachineState_Restoring:
14535 case MachineState_TeleportingIn:
14536 case MachineState_Paused:
14537 case MachineState_Running:
14538 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14539 * elsewhere... */
14540 alock.release();
14541 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14542 default: break;
14543 }
14544#else
14545 NOREF(aDevice);
14546 NOREF(aMaskedIfs);
14547#endif
14548 return false;
14549}
14550
14551/**
14552 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14553 */
14554HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14555 IVirtualBoxErrorInfo *aError,
14556 ULONG aMaskedIfs,
14557 const com::Utf8Str &aCaptureFilename)
14558{
14559 LogFlowThisFunc(("\n"));
14560
14561 AutoCaller autoCaller(this);
14562
14563 /* This notification may happen after the machine object has been
14564 * uninitialized (the session was closed), so don't assert. */
14565 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14566
14567 ComPtr<IInternalSessionControl> directControl;
14568 {
14569 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14570 if (mData->mSession.mLockType == LockType_VM)
14571 directControl = mData->mSession.mDirectControl;
14572 }
14573
14574 /* fail on notifications sent after #OnSessionEnd() is called, it is
14575 * expected by the caller */
14576 if (!directControl)
14577 return E_FAIL;
14578
14579 /* No locks should be held at this point. */
14580 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14581 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14582
14583 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14584}
14585
14586/**
14587 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14588 */
14589HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14590 IVirtualBoxErrorInfo *aError)
14591{
14592 LogFlowThisFunc(("\n"));
14593
14594 AutoCaller autoCaller(this);
14595
14596 /* This notification may happen after the machine object has been
14597 * uninitialized (the session was closed), so don't assert. */
14598 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14599
14600 ComPtr<IInternalSessionControl> directControl;
14601 {
14602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14603 if (mData->mSession.mLockType == LockType_VM)
14604 directControl = mData->mSession.mDirectControl;
14605 }
14606
14607 /* fail on notifications sent after #OnSessionEnd() is called, it is
14608 * expected by the caller */
14609 if (!directControl)
14610 return E_FAIL;
14611
14612 /* No locks should be held at this point. */
14613 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14614 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14615
14616 return directControl->OnUSBDeviceDetach(aId, aError);
14617}
14618
14619// protected methods
14620/////////////////////////////////////////////////////////////////////////////
14621
14622/**
14623 * Deletes the given file if it is no longer in use by either the current machine state
14624 * (if the machine is "saved") or any of the machine's snapshots.
14625 *
14626 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14627 * but is different for each SnapshotMachine. When calling this, the order of calling this
14628 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14629 * is therefore critical. I know, it's all rather messy.
14630 *
14631 * @param strStateFile
14632 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14633 * the test for whether the saved state file is in use.
14634 */
14635void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14636 Snapshot *pSnapshotToIgnore)
14637{
14638 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14639 if ( (strStateFile.isNotEmpty())
14640 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14641 )
14642 // ... and it must also not be shared with other snapshots
14643 if ( !mData->mFirstSnapshot
14644 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14645 // this checks the SnapshotMachine's state file paths
14646 )
14647 RTFileDelete(strStateFile.c_str());
14648}
14649
14650/**
14651 * Locks the attached media.
14652 *
14653 * All attached hard disks are locked for writing and DVD/floppy are locked for
14654 * reading. Parents of attached hard disks (if any) are locked for reading.
14655 *
14656 * This method also performs accessibility check of all media it locks: if some
14657 * media is inaccessible, the method will return a failure and a bunch of
14658 * extended error info objects per each inaccessible medium.
14659 *
14660 * Note that this method is atomic: if it returns a success, all media are
14661 * locked as described above; on failure no media is locked at all (all
14662 * succeeded individual locks will be undone).
14663 *
14664 * The caller is responsible for doing the necessary state sanity checks.
14665 *
14666 * The locks made by this method must be undone by calling #unlockMedia() when
14667 * no more needed.
14668 */
14669HRESULT SessionMachine::i_lockMedia()
14670{
14671 AutoCaller autoCaller(this);
14672 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14673
14674 AutoMultiWriteLock2 alock(this->lockHandle(),
14675 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14676
14677 /* bail out if trying to lock things with already set up locking */
14678 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14679
14680 MultiResult mrc(S_OK);
14681
14682 /* Collect locking information for all medium objects attached to the VM. */
14683 for (MediumAttachmentList::const_iterator
14684 it = mMediumAttachments->begin();
14685 it != mMediumAttachments->end();
14686 ++it)
14687 {
14688 MediumAttachment *pAtt = *it;
14689 DeviceType_T devType = pAtt->i_getType();
14690 Medium *pMedium = pAtt->i_getMedium();
14691
14692 MediumLockList *pMediumLockList(new MediumLockList());
14693 // There can be attachments without a medium (floppy/dvd), and thus
14694 // it's impossible to create a medium lock list. It still makes sense
14695 // to have the empty medium lock list in the map in case a medium is
14696 // attached later.
14697 if (pMedium != NULL)
14698 {
14699 MediumType_T mediumType = pMedium->i_getType();
14700 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14701 || mediumType == MediumType_Shareable;
14702 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14703
14704 alock.release();
14705 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14706 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14707 false /* fMediumLockWriteAll */,
14708 NULL,
14709 *pMediumLockList);
14710 alock.acquire();
14711 if (FAILED(mrc))
14712 {
14713 delete pMediumLockList;
14714 mData->mSession.mLockedMedia.Clear();
14715 break;
14716 }
14717 }
14718
14719 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14720 if (FAILED(rc))
14721 {
14722 mData->mSession.mLockedMedia.Clear();
14723 mrc = setError(rc,
14724 tr("Collecting locking information for all attached media failed"));
14725 break;
14726 }
14727 }
14728
14729 if (SUCCEEDED(mrc))
14730 {
14731 /* Now lock all media. If this fails, nothing is locked. */
14732 alock.release();
14733 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14734 alock.acquire();
14735 if (FAILED(rc))
14736 {
14737 mrc = setError(rc,
14738 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14739 }
14740 }
14741
14742 return mrc;
14743}
14744
14745/**
14746 * Undoes the locks made by by #lockMedia().
14747 */
14748HRESULT SessionMachine::i_unlockMedia()
14749{
14750 AutoCaller autoCaller(this);
14751 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14752
14753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14754
14755 /* we may be holding important error info on the current thread;
14756 * preserve it */
14757 ErrorInfoKeeper eik;
14758
14759 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14760 AssertComRC(rc);
14761 return rc;
14762}
14763
14764/**
14765 * Helper to change the machine state (reimplementation).
14766 *
14767 * @note Locks this object for writing.
14768 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14769 * it can cause crashes in random places due to unexpectedly committing
14770 * the current settings. The caller is responsible for that. The call
14771 * to saveStateSettings is fine, because this method does not commit.
14772 */
14773HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14774{
14775 LogFlowThisFuncEnter();
14776 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14777
14778 AutoCaller autoCaller(this);
14779 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14780
14781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14782
14783 MachineState_T oldMachineState = mData->mMachineState;
14784
14785 AssertMsgReturn(oldMachineState != aMachineState,
14786 ("oldMachineState=%s, aMachineState=%s\n",
14787 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14788 E_FAIL);
14789
14790 HRESULT rc = S_OK;
14791
14792 int stsFlags = 0;
14793 bool deleteSavedState = false;
14794
14795 /* detect some state transitions */
14796
14797 if ( ( oldMachineState == MachineState_Saved
14798 && aMachineState == MachineState_Restoring)
14799 || ( ( oldMachineState == MachineState_PoweredOff
14800 || oldMachineState == MachineState_Teleported
14801 || oldMachineState == MachineState_Aborted
14802 )
14803 && ( aMachineState == MachineState_TeleportingIn
14804 || aMachineState == MachineState_Starting
14805 )
14806 )
14807 )
14808 {
14809 /* The EMT thread is about to start */
14810
14811 /* Nothing to do here for now... */
14812
14813 /// @todo NEWMEDIA don't let mDVDDrive and other children
14814 /// change anything when in the Starting/Restoring state
14815 }
14816 else if ( ( oldMachineState == MachineState_Running
14817 || oldMachineState == MachineState_Paused
14818 || oldMachineState == MachineState_Teleporting
14819 || oldMachineState == MachineState_OnlineSnapshotting
14820 || oldMachineState == MachineState_LiveSnapshotting
14821 || oldMachineState == MachineState_Stuck
14822 || oldMachineState == MachineState_Starting
14823 || oldMachineState == MachineState_Stopping
14824 || oldMachineState == MachineState_Saving
14825 || oldMachineState == MachineState_Restoring
14826 || oldMachineState == MachineState_TeleportingPausedVM
14827 || oldMachineState == MachineState_TeleportingIn
14828 )
14829 && ( aMachineState == MachineState_PoweredOff
14830 || aMachineState == MachineState_Saved
14831 || aMachineState == MachineState_Teleported
14832 || aMachineState == MachineState_Aborted
14833 )
14834 )
14835 {
14836 /* The EMT thread has just stopped, unlock attached media. Note that as
14837 * opposed to locking that is done from Console, we do unlocking here
14838 * because the VM process may have aborted before having a chance to
14839 * properly unlock all media it locked. */
14840
14841 unlockMedia();
14842 }
14843
14844 if (oldMachineState == MachineState_Restoring)
14845 {
14846 if (aMachineState != MachineState_Saved)
14847 {
14848 /*
14849 * delete the saved state file once the machine has finished
14850 * restoring from it (note that Console sets the state from
14851 * Restoring to Saved if the VM couldn't restore successfully,
14852 * to give the user an ability to fix an error and retry --
14853 * we keep the saved state file in this case)
14854 */
14855 deleteSavedState = true;
14856 }
14857 }
14858 else if ( oldMachineState == MachineState_Saved
14859 && ( aMachineState == MachineState_PoweredOff
14860 || aMachineState == MachineState_Aborted
14861 || aMachineState == MachineState_Teleported
14862 )
14863 )
14864 {
14865 /*
14866 * delete the saved state after SessionMachine::ForgetSavedState() is called
14867 * or if the VM process (owning a direct VM session) crashed while the
14868 * VM was Saved
14869 */
14870
14871 /// @todo (dmik)
14872 // Not sure that deleting the saved state file just because of the
14873 // client death before it attempted to restore the VM is a good
14874 // thing. But when it crashes we need to go to the Aborted state
14875 // which cannot have the saved state file associated... The only
14876 // way to fix this is to make the Aborted condition not a VM state
14877 // but a bool flag: i.e., when a crash occurs, set it to true and
14878 // change the state to PoweredOff or Saved depending on the
14879 // saved state presence.
14880
14881 deleteSavedState = true;
14882 mData->mCurrentStateModified = TRUE;
14883 stsFlags |= SaveSTS_CurStateModified;
14884 }
14885
14886 if ( aMachineState == MachineState_Starting
14887 || aMachineState == MachineState_Restoring
14888 || aMachineState == MachineState_TeleportingIn
14889 )
14890 {
14891 /* set the current state modified flag to indicate that the current
14892 * state is no more identical to the state in the
14893 * current snapshot */
14894 if (!mData->mCurrentSnapshot.isNull())
14895 {
14896 mData->mCurrentStateModified = TRUE;
14897 stsFlags |= SaveSTS_CurStateModified;
14898 }
14899 }
14900
14901 if (deleteSavedState)
14902 {
14903 if (mRemoveSavedState)
14904 {
14905 Assert(!mSSData->strStateFilePath.isEmpty());
14906
14907 // it is safe to delete the saved state file if ...
14908 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14909 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14910 // ... none of the snapshots share the saved state file
14911 )
14912 RTFileDelete(mSSData->strStateFilePath.c_str());
14913 }
14914
14915 mSSData->strStateFilePath.setNull();
14916 stsFlags |= SaveSTS_StateFilePath;
14917 }
14918
14919 /* redirect to the underlying peer machine */
14920 mPeer->i_setMachineState(aMachineState);
14921
14922 if ( oldMachineState != MachineState_RestoringSnapshot
14923 && ( aMachineState == MachineState_PoweredOff
14924 || aMachineState == MachineState_Teleported
14925 || aMachineState == MachineState_Aborted
14926 || aMachineState == MachineState_Saved))
14927 {
14928 /* the machine has stopped execution
14929 * (or the saved state file was adopted) */
14930 stsFlags |= SaveSTS_StateTimeStamp;
14931 }
14932
14933 if ( ( oldMachineState == MachineState_PoweredOff
14934 || oldMachineState == MachineState_Aborted
14935 || oldMachineState == MachineState_Teleported
14936 )
14937 && aMachineState == MachineState_Saved)
14938 {
14939 /* the saved state file was adopted */
14940 Assert(!mSSData->strStateFilePath.isEmpty());
14941 stsFlags |= SaveSTS_StateFilePath;
14942 }
14943
14944#ifdef VBOX_WITH_GUEST_PROPS
14945 if ( aMachineState == MachineState_PoweredOff
14946 || aMachineState == MachineState_Aborted
14947 || aMachineState == MachineState_Teleported)
14948 {
14949 /* Make sure any transient guest properties get removed from the
14950 * property store on shutdown. */
14951 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14952
14953 /* remove it from the settings representation */
14954 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14955 for (settings::GuestPropertiesList::iterator
14956 it = llGuestProperties.begin();
14957 it != llGuestProperties.end();
14958 /*nothing*/)
14959 {
14960 const settings::GuestProperty &prop = *it;
14961 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14962 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14963 {
14964 it = llGuestProperties.erase(it);
14965 fNeedsSaving = true;
14966 }
14967 else
14968 {
14969 ++it;
14970 }
14971 }
14972
14973 /* Additionally remove it from the HWData representation. Required to
14974 * keep everything in sync, as this is what the API keeps using. */
14975 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14976 for (HWData::GuestPropertyMap::iterator
14977 it = llHWGuestProperties.begin();
14978 it != llHWGuestProperties.end();
14979 /*nothing*/)
14980 {
14981 uint32_t fFlags = it->second.mFlags;
14982 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14983 {
14984 /* iterator where we need to continue after the erase call
14985 * (C++03 is a fact still, and it doesn't return the iterator
14986 * which would allow continuing) */
14987 HWData::GuestPropertyMap::iterator it2 = it;
14988 ++it2;
14989 llHWGuestProperties.erase(it);
14990 it = it2;
14991 fNeedsSaving = true;
14992 }
14993 else
14994 {
14995 ++it;
14996 }
14997 }
14998
14999 if (fNeedsSaving)
15000 {
15001 mData->mCurrentStateModified = TRUE;
15002 stsFlags |= SaveSTS_CurStateModified;
15003 }
15004 }
15005#endif /* VBOX_WITH_GUEST_PROPS */
15006
15007 rc = i_saveStateSettings(stsFlags);
15008
15009 if ( ( oldMachineState != MachineState_PoweredOff
15010 && oldMachineState != MachineState_Aborted
15011 && oldMachineState != MachineState_Teleported
15012 )
15013 && ( aMachineState == MachineState_PoweredOff
15014 || aMachineState == MachineState_Aborted
15015 || aMachineState == MachineState_Teleported
15016 )
15017 )
15018 {
15019 /* we've been shut down for any reason */
15020 /* no special action so far */
15021 }
15022
15023 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15024 LogFlowThisFuncLeave();
15025 return rc;
15026}
15027
15028/**
15029 * Sends the current machine state value to the VM process.
15030 *
15031 * @note Locks this object for reading, then calls a client process.
15032 */
15033HRESULT SessionMachine::i_updateMachineStateOnClient()
15034{
15035 AutoCaller autoCaller(this);
15036 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15037
15038 ComPtr<IInternalSessionControl> directControl;
15039 {
15040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15041 AssertReturn(!!mData, E_FAIL);
15042 if (mData->mSession.mLockType == LockType_VM)
15043 directControl = mData->mSession.mDirectControl;
15044
15045 /* directControl may be already set to NULL here in #OnSessionEnd()
15046 * called too early by the direct session process while there is still
15047 * some operation (like deleting the snapshot) in progress. The client
15048 * process in this case is waiting inside Session::close() for the
15049 * "end session" process object to complete, while #uninit() called by
15050 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15051 * operation to complete. For now, we accept this inconsistent behavior
15052 * and simply do nothing here. */
15053
15054 if (mData->mSession.mState == SessionState_Unlocking)
15055 return S_OK;
15056 }
15057
15058 /* ignore notifications sent after #OnSessionEnd() is called */
15059 if (!directControl)
15060 return S_OK;
15061
15062 return directControl->UpdateMachineState(mData->mMachineState);
15063}
15064
15065
15066/*static*/
15067HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15068{
15069 va_list args;
15070 va_start(args, pcszMsg);
15071 HRESULT rc = setErrorInternal(aResultCode,
15072 getStaticClassIID(),
15073 getStaticComponentName(),
15074 Utf8Str(pcszMsg, args),
15075 false /* aWarning */,
15076 true /* aLogIt */);
15077 va_end(args);
15078 return rc;
15079}
15080
15081
15082HRESULT Machine::updateState(MachineState_T aState)
15083{
15084 NOREF(aState);
15085 ReturnComNotImplemented();
15086}
15087
15088HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15089{
15090 NOREF(aProgress);
15091 ReturnComNotImplemented();
15092}
15093
15094HRESULT Machine::endPowerUp(LONG aResult)
15095{
15096 NOREF(aResult);
15097 ReturnComNotImplemented();
15098}
15099
15100HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15101{
15102 NOREF(aProgress);
15103 ReturnComNotImplemented();
15104}
15105
15106HRESULT Machine::endPoweringDown(LONG aResult,
15107 const com::Utf8Str &aErrMsg)
15108{
15109 NOREF(aResult);
15110 NOREF(aErrMsg);
15111 ReturnComNotImplemented();
15112}
15113
15114HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15115 BOOL *aMatched,
15116 ULONG *aMaskedInterfaces)
15117{
15118 NOREF(aDevice);
15119 NOREF(aMatched);
15120 NOREF(aMaskedInterfaces);
15121 ReturnComNotImplemented();
15122
15123}
15124
15125HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15126{
15127 NOREF(aId); NOREF(aCaptureFilename);
15128 ReturnComNotImplemented();
15129}
15130
15131HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15132 BOOL aDone)
15133{
15134 NOREF(aId);
15135 NOREF(aDone);
15136 ReturnComNotImplemented();
15137}
15138
15139HRESULT Machine::autoCaptureUSBDevices()
15140{
15141 ReturnComNotImplemented();
15142}
15143
15144HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15145{
15146 NOREF(aDone);
15147 ReturnComNotImplemented();
15148}
15149
15150HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15151 ComPtr<IProgress> &aProgress)
15152{
15153 NOREF(aSession);
15154 NOREF(aProgress);
15155 ReturnComNotImplemented();
15156}
15157
15158HRESULT Machine::finishOnlineMergeMedium()
15159{
15160 ReturnComNotImplemented();
15161}
15162
15163HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15164 std::vector<com::Utf8Str> &aValues,
15165 std::vector<LONG64> &aTimestamps,
15166 std::vector<com::Utf8Str> &aFlags)
15167{
15168 NOREF(aNames);
15169 NOREF(aValues);
15170 NOREF(aTimestamps);
15171 NOREF(aFlags);
15172 ReturnComNotImplemented();
15173}
15174
15175HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15176 const com::Utf8Str &aValue,
15177 LONG64 aTimestamp,
15178 const com::Utf8Str &aFlags)
15179{
15180 NOREF(aName);
15181 NOREF(aValue);
15182 NOREF(aTimestamp);
15183 NOREF(aFlags);
15184 ReturnComNotImplemented();
15185}
15186
15187HRESULT Machine::lockMedia()
15188{
15189 ReturnComNotImplemented();
15190}
15191
15192HRESULT Machine::unlockMedia()
15193{
15194 ReturnComNotImplemented();
15195}
15196
15197HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15198 ComPtr<IMediumAttachment> &aNewAttachment)
15199{
15200 NOREF(aAttachment);
15201 NOREF(aNewAttachment);
15202 ReturnComNotImplemented();
15203}
15204
15205HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15206 ULONG aCpuUser,
15207 ULONG aCpuKernel,
15208 ULONG aCpuIdle,
15209 ULONG aMemTotal,
15210 ULONG aMemFree,
15211 ULONG aMemBalloon,
15212 ULONG aMemShared,
15213 ULONG aMemCache,
15214 ULONG aPagedTotal,
15215 ULONG aMemAllocTotal,
15216 ULONG aMemFreeTotal,
15217 ULONG aMemBalloonTotal,
15218 ULONG aMemSharedTotal,
15219 ULONG aVmNetRx,
15220 ULONG aVmNetTx)
15221{
15222 NOREF(aValidStats);
15223 NOREF(aCpuUser);
15224 NOREF(aCpuKernel);
15225 NOREF(aCpuIdle);
15226 NOREF(aMemTotal);
15227 NOREF(aMemFree);
15228 NOREF(aMemBalloon);
15229 NOREF(aMemShared);
15230 NOREF(aMemCache);
15231 NOREF(aPagedTotal);
15232 NOREF(aMemAllocTotal);
15233 NOREF(aMemFreeTotal);
15234 NOREF(aMemBalloonTotal);
15235 NOREF(aMemSharedTotal);
15236 NOREF(aVmNetRx);
15237 NOREF(aVmNetTx);
15238 ReturnComNotImplemented();
15239}
15240
15241HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15242 com::Utf8Str &aResult)
15243{
15244 NOREF(aAuthParams);
15245 NOREF(aResult);
15246 ReturnComNotImplemented();
15247}
15248
15249HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15250{
15251 NOREF(aFlags);
15252 ReturnComNotImplemented();
15253}
15254
15255/* This isn't handled entirely by the wrapper generator yet. */
15256#ifdef VBOX_WITH_XPCOM
15257NS_DECL_CLASSINFO(SessionMachine)
15258NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15259
15260NS_DECL_CLASSINFO(SnapshotMachine)
15261NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15262#endif
Note: See TracBrowser for help on using the repository browser.

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