VirtualBox

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

Last change on this file since 61362 was 61042, checked in by vboxsync, 9 years ago

Main/Machine+BIOSSettings: introduce APIC/X2APIC CPU feature settings and a BIOS setting what should be used.
Frontends/VBoxManage: corresponding changes to allow making the settings changes
Devices/BIOS+EFI: placeholder for the BIOS setting part, which isn't implemented yet

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 515.4 KB
Line 
1/* $Id: MachineImpl.cpp 61042 2016-05-19 11:57:10Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2016 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
48// generated header
49#include "VBoxEvents.h"
50
51#ifdef VBOX_WITH_USB
52# include "USBProxyService.h"
53#endif
54
55#include "AutoCaller.h"
56#include "HashedPw.h"
57#include "Performance.h"
58
59#include <iprt/asm.h>
60#include <iprt/path.h>
61#include <iprt/dir.h>
62#include <iprt/env.h>
63#include <iprt/lockvalidator.h>
64#include <iprt/process.h>
65#include <iprt/cpp/utils.h>
66#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
67#include <iprt/sha.h>
68#include <iprt/string.h>
69
70#include <VBox/com/array.h>
71#include <VBox/com/list.h>
72
73#include <VBox/err.h>
74#include <VBox/param.h>
75#include <VBox/settings.h>
76#include <VBox/vmm/ssm.h>
77
78#ifdef VBOX_WITH_GUEST_PROPS
79# include <VBox/HostServices/GuestPropertySvc.h>
80# include <VBox/com/array.h>
81#endif
82
83#include "VBox/com/MultiResult.h"
84
85#include <algorithm>
86
87#ifdef VBOX_WITH_DTRACE_R3_MAIN
88# include "dtrace/VBoxAPI.h"
89#endif
90
91#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
92# define HOSTSUFF_EXE ".exe"
93#else /* !RT_OS_WINDOWS */
94# define HOSTSUFF_EXE ""
95#endif /* !RT_OS_WINDOWS */
96
97// defines / prototypes
98/////////////////////////////////////////////////////////////////////////////
99
100/////////////////////////////////////////////////////////////////////////////
101// Machine::Data structure
102/////////////////////////////////////////////////////////////////////////////
103
104Machine::Data::Data()
105{
106 mRegistered = FALSE;
107 pMachineConfigFile = NULL;
108 /* Contains hints on what has changed when the user is using the VM (config
109 * changes, running the VM, ...). This is used to decide if a config needs
110 * to be written to disk. */
111 flModifications = 0;
112 /* VM modification usually also trigger setting the current state to
113 * "Modified". Although this is not always the case. An e.g. is the VM
114 * initialization phase or when snapshot related data is changed. The
115 * actually behavior is controlled by the following flag. */
116 m_fAllowStateModification = false;
117 mAccessible = FALSE;
118 /* mUuid is initialized in Machine::init() */
119
120 mMachineState = MachineState_PoweredOff;
121 RTTimeNow(&mLastStateChange);
122
123 mMachineStateDeps = 0;
124 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
125 mMachineStateChangePending = 0;
126
127 mCurrentStateModified = TRUE;
128 mGuestPropertiesModified = FALSE;
129
130 mSession.mPID = NIL_RTPROCESS;
131 mSession.mLockType = LockType_Null;
132 mSession.mState = SessionState_Unlocked;
133}
134
135Machine::Data::~Data()
136{
137 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
138 {
139 RTSemEventMultiDestroy(mMachineStateDepsSem);
140 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
141 }
142 if (pMachineConfigFile)
143 {
144 delete pMachineConfigFile;
145 pMachineConfigFile = NULL;
146 }
147}
148
149/////////////////////////////////////////////////////////////////////////////
150// Machine::HWData structure
151/////////////////////////////////////////////////////////////////////////////
152
153Machine::HWData::HWData()
154{
155 /* default values for a newly created machine */
156 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
157 mMemorySize = 128;
158 mCPUCount = 1;
159 mCPUHotPlugEnabled = false;
160 mMemoryBalloonSize = 0;
161 mPageFusionEnabled = false;
162 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
163 mVRAMSize = 8;
164 mAccelerate3DEnabled = false;
165 mAccelerate2DVideoEnabled = false;
166 mMonitorCount = 1;
167 mVideoCaptureWidth = 1024;
168 mVideoCaptureHeight = 768;
169 mVideoCaptureRate = 512;
170 mVideoCaptureFPS = 25;
171 mVideoCaptureMaxTime = 0;
172 mVideoCaptureMaxFileSize = 0;
173 mVideoCaptureEnabled = false;
174 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
175 maVideoCaptureScreens[i] = true;
176
177 mHWVirtExEnabled = true;
178 mHWVirtExNestedPagingEnabled = true;
179#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
180 mHWVirtExLargePagesEnabled = true;
181#else
182 /* Not supported on 32 bits hosts. */
183 mHWVirtExLargePagesEnabled = false;
184#endif
185 mHWVirtExVPIDEnabled = true;
186 mHWVirtExUXEnabled = true;
187 mHWVirtExForceEnabled = false;
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
194 mTripleFaultReset = false;
195 mAPIC = true;
196 mX2APIC = false;
197 mHPETEnabled = false;
198 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
199 mCpuIdPortabilityLevel = 0;
200 mCpuProfile = "host";
201
202 /* default boot order: floppy - DVD - HDD */
203 mBootOrder[0] = DeviceType_Floppy;
204 mBootOrder[1] = DeviceType_DVD;
205 mBootOrder[2] = DeviceType_HardDisk;
206 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
207 mBootOrder[i] = DeviceType_Null;
208
209 mClipboardMode = ClipboardMode_Disabled;
210 mDnDMode = DnDMode_Disabled;
211
212 mFirmwareType = FirmwareType_BIOS;
213 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
214 mPointingHIDType = PointingHIDType_PS2Mouse;
215 mChipsetType = ChipsetType_PIIX3;
216 mParavirtProvider = ParavirtProvider_Default;
217 mEmulatedUSBCardReaderEnabled = FALSE;
218
219 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
220 mCPUAttached[i] = false;
221
222 mIOCacheEnabled = true;
223 mIOCacheSize = 5; /* 5MB */
224}
225
226Machine::HWData::~HWData()
227{
228}
229
230/////////////////////////////////////////////////////////////////////////////
231// Machine::HDData structure
232/////////////////////////////////////////////////////////////////////////////
233
234Machine::MediaData::MediaData()
235{
236}
237
238Machine::MediaData::~MediaData()
239{
240}
241
242/////////////////////////////////////////////////////////////////////////////
243// Machine class
244/////////////////////////////////////////////////////////////////////////////
245
246// constructor / destructor
247/////////////////////////////////////////////////////////////////////////////
248
249Machine::Machine() :
250#ifdef VBOX_WITH_RESOURCE_USAGE_API
251 mCollectorGuest(NULL),
252#endif
253 mPeer(NULL),
254 mParent(NULL),
255 mSerialPorts(),
256 mParallelPorts(),
257 uRegistryNeedsSaving(0)
258{}
259
260Machine::~Machine()
261{}
262
263HRESULT Machine::FinalConstruct()
264{
265 LogFlowThisFunc(("\n"));
266 return BaseFinalConstruct();
267}
268
269void Machine::FinalRelease()
270{
271 LogFlowThisFunc(("\n"));
272 uninit();
273 BaseFinalRelease();
274}
275
276/**
277 * Initializes a new machine instance; this init() variant creates a new, empty machine.
278 * This gets called from VirtualBox::CreateMachine().
279 *
280 * @param aParent Associated parent object
281 * @param strConfigFile Local file system path to the VM settings file (can
282 * be relative to the VirtualBox config directory).
283 * @param strName name for the machine
284 * @param llGroups list of groups for the machine
285 * @param aOsType OS Type of this machine or NULL.
286 * @param aId UUID for the new machine.
287 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
288 *
289 * @return Success indicator. if not S_OK, the machine object is invalid
290 */
291HRESULT Machine::init(VirtualBox *aParent,
292 const Utf8Str &strConfigFile,
293 const Utf8Str &strName,
294 const StringsList &llGroups,
295 GuestOSType *aOsType,
296 const Guid &aId,
297 bool fForceOverwrite,
298 bool fDirectoryIncludesUUID)
299{
300 LogFlowThisFuncEnter();
301 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
302
303 /* Enclose the state transition NotReady->InInit->Ready */
304 AutoInitSpan autoInitSpan(this);
305 AssertReturn(autoInitSpan.isOk(), E_FAIL);
306
307 HRESULT rc = initImpl(aParent, strConfigFile);
308 if (FAILED(rc)) return rc;
309
310 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
311 if (FAILED(rc)) return rc;
312
313 if (SUCCEEDED(rc))
314 {
315 // create an empty machine config
316 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
317
318 rc = initDataAndChildObjects();
319 }
320
321 if (SUCCEEDED(rc))
322 {
323 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
324 mData->mAccessible = TRUE;
325
326 unconst(mData->mUuid) = aId;
327
328 mUserData->s.strName = strName;
329
330 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 /* Apply BIOS defaults */
348 mBIOSSettings->i_applyDefaults(aOsType);
349
350 /* Apply network adapters defaults */
351 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
352 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
353
354 /* Apply serial port defaults */
355 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
356 mSerialPorts[slot]->i_applyDefaults(aOsType);
357
358 /* Let the OS type select 64-bit ness. */
359 mHWData->mLongMode = aOsType->i_is64Bit()
360 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
361 }
362
363 /* Apply parallel port defaults */
364 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
365 mParallelPorts[slot]->i_applyDefaults();
366
367 /* At this point the changing of the current state modification
368 * flag is allowed. */
369 i_allowStateModification();
370
371 /* commit all changes made during the initialization */
372 i_commit();
373 }
374
375 /* Confirm a successful initialization when it's the case */
376 if (SUCCEEDED(rc))
377 {
378 if (mData->mAccessible)
379 autoInitSpan.setSucceeded();
380 else
381 autoInitSpan.setLimited();
382 }
383
384 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
385 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
386 mData->mRegistered,
387 mData->mAccessible,
388 rc));
389
390 LogFlowThisFuncLeave();
391
392 return rc;
393}
394
395/**
396 * Initializes a new instance with data from machine XML (formerly Init_Registered).
397 * Gets called in two modes:
398 *
399 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
400 * UUID is specified and we mark the machine as "registered";
401 *
402 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
403 * and the machine remains unregistered until RegisterMachine() is called.
404 *
405 * @param aParent Associated parent object
406 * @param aConfigFile Local file system path to the VM settings file (can
407 * be relative to the VirtualBox config directory).
408 * @param aId UUID of the machine or NULL (see above).
409 *
410 * @return Success indicator. if not S_OK, the machine object is invalid
411 */
412HRESULT Machine::initFromSettings(VirtualBox *aParent,
413 const Utf8Str &strConfigFile,
414 const Guid *aId)
415{
416 LogFlowThisFuncEnter();
417 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
418
419 /* Enclose the state transition NotReady->InInit->Ready */
420 AutoInitSpan autoInitSpan(this);
421 AssertReturn(autoInitSpan.isOk(), E_FAIL);
422
423 HRESULT rc = initImpl(aParent, strConfigFile);
424 if (FAILED(rc)) return rc;
425
426 if (aId)
427 {
428 // loading a registered VM:
429 unconst(mData->mUuid) = *aId;
430 mData->mRegistered = TRUE;
431 // now load the settings from XML:
432 rc = i_registeredInit();
433 // this calls initDataAndChildObjects() and loadSettings()
434 }
435 else
436 {
437 // opening an unregistered VM (VirtualBox::OpenMachine()):
438 rc = initDataAndChildObjects();
439
440 if (SUCCEEDED(rc))
441 {
442 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
443 mData->mAccessible = TRUE;
444
445 try
446 {
447 // load and parse machine XML; this will throw on XML or logic errors
448 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
449
450 // reject VM UUID duplicates, they can happen if someone
451 // tries to register an already known VM config again
452 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
453 true /* fPermitInaccessible */,
454 false /* aDoSetError */,
455 NULL) != VBOX_E_OBJECT_NOT_FOUND)
456 {
457 throw setError(E_FAIL,
458 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
459 mData->m_strConfigFile.c_str());
460 }
461
462 // use UUID from machine config
463 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
464
465 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
466 NULL /* puuidRegistry */);
467 if (FAILED(rc)) throw rc;
468
469 /* At this point the changing of the current state modification
470 * flag is allowed. */
471 i_allowStateModification();
472
473 i_commit();
474 }
475 catch (HRESULT err)
476 {
477 /* we assume that error info is set by the thrower */
478 rc = err;
479 }
480 catch (...)
481 {
482 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
483 }
484 }
485 }
486
487 /* Confirm a successful initialization when it's the case */
488 if (SUCCEEDED(rc))
489 {
490 if (mData->mAccessible)
491 autoInitSpan.setSucceeded();
492 else
493 {
494 autoInitSpan.setLimited();
495
496 // uninit media from this machine's media registry, or else
497 // reloading the settings will fail
498 mParent->i_unregisterMachineMedia(i_getId());
499 }
500 }
501
502 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
503 "rc=%08X\n",
504 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
505 mData->mRegistered, mData->mAccessible, rc));
506
507 LogFlowThisFuncLeave();
508
509 return rc;
510}
511
512/**
513 * Initializes a new instance from a machine config that is already in memory
514 * (import OVF case). Since we are importing, the UUID in the machine
515 * config is ignored and we always generate a fresh one.
516 *
517 * @param strName Name for the new machine; this overrides what is specified in config and is used
518 * for the settings file as well.
519 * @param config Machine configuration loaded and parsed from XML.
520 *
521 * @return Success indicator. if not S_OK, the machine object is invalid
522 */
523HRESULT Machine::init(VirtualBox *aParent,
524 const Utf8Str &strName,
525 const settings::MachineConfigFile &config)
526{
527 LogFlowThisFuncEnter();
528
529 /* Enclose the state transition NotReady->InInit->Ready */
530 AutoInitSpan autoInitSpan(this);
531 AssertReturn(autoInitSpan.isOk(), E_FAIL);
532
533 Utf8Str strConfigFile;
534 aParent->i_getDefaultMachineFolder(strConfigFile);
535 strConfigFile.append(RTPATH_DELIMITER);
536 strConfigFile.append(strName);
537 strConfigFile.append(RTPATH_DELIMITER);
538 strConfigFile.append(strName);
539 strConfigFile.append(".vbox");
540
541 HRESULT rc = initImpl(aParent, strConfigFile);
542 if (FAILED(rc)) return rc;
543
544 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
545 if (FAILED(rc)) return rc;
546
547 rc = initDataAndChildObjects();
548
549 if (SUCCEEDED(rc))
550 {
551 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
552 mData->mAccessible = TRUE;
553
554 // create empty machine config for instance data
555 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
556
557 // generate fresh UUID, ignore machine config
558 unconst(mData->mUuid).create();
559
560 rc = i_loadMachineDataFromSettings(config,
561 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
562
563 // override VM name as well, it may be different
564 mUserData->s.strName = strName;
565
566 if (SUCCEEDED(rc))
567 {
568 /* At this point the changing of the current state modification
569 * flag is allowed. */
570 i_allowStateModification();
571
572 /* commit all changes made during the initialization */
573 i_commit();
574 }
575 }
576
577 /* Confirm a successful initialization when it's the case */
578 if (SUCCEEDED(rc))
579 {
580 if (mData->mAccessible)
581 autoInitSpan.setSucceeded();
582 else
583 {
584 /* Ignore all errors from unregistering, they would destroy
585- * the more interesting error information we already have,
586- * pinpointing the issue with the VM config. */
587 ErrorInfoKeeper eik;
588
589 autoInitSpan.setLimited();
590
591 // uninit media from this machine's media registry, or else
592 // reloading the settings will fail
593 mParent->i_unregisterMachineMedia(i_getId());
594 }
595 }
596
597 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
598 "rc=%08X\n",
599 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
600 mData->mRegistered, mData->mAccessible, rc));
601
602 LogFlowThisFuncLeave();
603
604 return rc;
605}
606
607/**
608 * Shared code between the various init() implementations.
609 * @param aParent
610 * @return
611 */
612HRESULT Machine::initImpl(VirtualBox *aParent,
613 const Utf8Str &strConfigFile)
614{
615 LogFlowThisFuncEnter();
616
617 AssertReturn(aParent, E_INVALIDARG);
618 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
619
620 HRESULT rc = S_OK;
621
622 /* share the parent weakly */
623 unconst(mParent) = aParent;
624
625 /* allocate the essential machine data structure (the rest will be
626 * allocated later by initDataAndChildObjects() */
627 mData.allocate();
628
629 /* memorize the config file name (as provided) */
630 mData->m_strConfigFile = strConfigFile;
631
632 /* get the full file name */
633 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
634 if (RT_FAILURE(vrc1))
635 return setError(VBOX_E_FILE_ERROR,
636 tr("Invalid machine settings file name '%s' (%Rrc)"),
637 strConfigFile.c_str(),
638 vrc1);
639
640 LogFlowThisFuncLeave();
641
642 return rc;
643}
644
645/**
646 * Tries to create a machine settings file in the path stored in the machine
647 * instance data. Used when a new machine is created to fail gracefully if
648 * the settings file could not be written (e.g. because machine dir is read-only).
649 * @return
650 */
651HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
652{
653 HRESULT rc = S_OK;
654
655 // when we create a new machine, we must be able to create the settings file
656 RTFILE f = NIL_RTFILE;
657 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
658 if ( RT_SUCCESS(vrc)
659 || vrc == VERR_SHARING_VIOLATION
660 )
661 {
662 if (RT_SUCCESS(vrc))
663 RTFileClose(f);
664 if (!fForceOverwrite)
665 rc = setError(VBOX_E_FILE_ERROR,
666 tr("Machine settings file '%s' already exists"),
667 mData->m_strConfigFileFull.c_str());
668 else
669 {
670 /* try to delete the config file, as otherwise the creation
671 * of a new settings file will fail. */
672 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
673 if (RT_FAILURE(vrc2))
674 rc = setError(VBOX_E_FILE_ERROR,
675 tr("Could not delete the existing settings file '%s' (%Rrc)"),
676 mData->m_strConfigFileFull.c_str(), vrc2);
677 }
678 }
679 else if ( vrc != VERR_FILE_NOT_FOUND
680 && vrc != VERR_PATH_NOT_FOUND
681 )
682 rc = setError(VBOX_E_FILE_ERROR,
683 tr("Invalid machine settings file name '%s' (%Rrc)"),
684 mData->m_strConfigFileFull.c_str(),
685 vrc);
686 return rc;
687}
688
689/**
690 * Initializes the registered machine by loading the settings file.
691 * This method is separated from #init() in order to make it possible to
692 * retry the operation after VirtualBox startup instead of refusing to
693 * startup the whole VirtualBox server in case if the settings file of some
694 * registered VM is invalid or inaccessible.
695 *
696 * @note Must be always called from this object's write lock
697 * (unless called from #init() that doesn't need any locking).
698 * @note Locks the mUSBController method for writing.
699 * @note Subclasses must not call this method.
700 */
701HRESULT Machine::i_registeredInit()
702{
703 AssertReturn(!i_isSessionMachine(), E_FAIL);
704 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
705 AssertReturn(mData->mUuid.isValid(), E_FAIL);
706 AssertReturn(!mData->mAccessible, E_FAIL);
707
708 HRESULT rc = initDataAndChildObjects();
709
710 if (SUCCEEDED(rc))
711 {
712 /* Temporarily reset the registered flag in order to let setters
713 * potentially called from loadSettings() succeed (isMutable() used in
714 * all setters will return FALSE for a Machine instance if mRegistered
715 * is TRUE). */
716 mData->mRegistered = FALSE;
717
718 try
719 {
720 // load and parse machine XML; this will throw on XML or logic errors
721 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
722
723 if (mData->mUuid != mData->pMachineConfigFile->uuid)
724 throw setError(E_FAIL,
725 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
726 mData->pMachineConfigFile->uuid.raw(),
727 mData->m_strConfigFileFull.c_str(),
728 mData->mUuid.toString().c_str(),
729 mParent->i_settingsFilePath().c_str());
730
731 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
732 NULL /* const Guid *puuidRegistry */);
733 if (FAILED(rc)) throw rc;
734 }
735 catch (HRESULT err)
736 {
737 /* we assume that error info is set by the thrower */
738 rc = err;
739 }
740 catch (...)
741 {
742 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
743 }
744
745 /* Restore the registered flag (even on failure) */
746 mData->mRegistered = TRUE;
747 }
748
749 if (SUCCEEDED(rc))
750 {
751 /* Set mAccessible to TRUE only if we successfully locked and loaded
752 * the settings file */
753 mData->mAccessible = TRUE;
754
755 /* commit all changes made during loading the settings file */
756 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
757 /// @todo r=klaus for some reason the settings loading logic backs up
758 // the settings, and therefore a commit is needed. Should probably be changed.
759 }
760 else
761 {
762 /* If the machine is registered, then, instead of returning a
763 * failure, we mark it as inaccessible and set the result to
764 * success to give it a try later */
765
766 /* fetch the current error info */
767 mData->mAccessError = com::ErrorInfo();
768 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
769
770 /* rollback all changes */
771 i_rollback(false /* aNotify */);
772
773 // uninit media from this machine's media registry, or else
774 // reloading the settings will fail
775 mParent->i_unregisterMachineMedia(i_getId());
776
777 /* uninitialize the common part to make sure all data is reset to
778 * default (null) values */
779 uninitDataAndChildObjects();
780
781 rc = S_OK;
782 }
783
784 return rc;
785}
786
787/**
788 * Uninitializes the instance.
789 * Called either from FinalRelease() or by the parent when it gets destroyed.
790 *
791 * @note The caller of this method must make sure that this object
792 * a) doesn't have active callers on the current thread and b) is not locked
793 * by the current thread; otherwise uninit() will hang either a) due to
794 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
795 * a dead-lock caused by this thread waiting for all callers on the other
796 * threads are done but preventing them from doing so by holding a lock.
797 */
798void Machine::uninit()
799{
800 LogFlowThisFuncEnter();
801
802 Assert(!isWriteLockOnCurrentThread());
803
804 Assert(!uRegistryNeedsSaving);
805 if (uRegistryNeedsSaving)
806 {
807 AutoCaller autoCaller(this);
808 if (SUCCEEDED(autoCaller.rc()))
809 {
810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
811 i_saveSettings(NULL, Machine::SaveS_Force);
812 }
813 }
814
815 /* Enclose the state transition Ready->InUninit->NotReady */
816 AutoUninitSpan autoUninitSpan(this);
817 if (autoUninitSpan.uninitDone())
818 return;
819
820 Assert(!i_isSnapshotMachine());
821 Assert(!i_isSessionMachine());
822 Assert(!!mData);
823
824 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
825 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
826
827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
828
829 if (!mData->mSession.mMachine.isNull())
830 {
831 /* Theoretically, this can only happen if the VirtualBox server has been
832 * terminated while there were clients running that owned open direct
833 * sessions. Since in this case we are definitely called by
834 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
835 * won't happen on the client watcher thread (because it has a
836 * VirtualBox caller for the duration of the
837 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
838 * cannot happen until the VirtualBox caller is released). This is
839 * important, because SessionMachine::uninit() cannot correctly operate
840 * after we return from this method (it expects the Machine instance is
841 * still valid). We'll call it ourselves below.
842 */
843 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
844 (SessionMachine*)mData->mSession.mMachine));
845
846 if (Global::IsOnlineOrTransient(mData->mMachineState))
847 {
848 Log1WarningThisFunc(("Setting state to Aborted!\n"));
849 /* set machine state using SessionMachine reimplementation */
850 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
851 }
852
853 /*
854 * Uninitialize SessionMachine using public uninit() to indicate
855 * an unexpected uninitialization.
856 */
857 mData->mSession.mMachine->uninit();
858 /* SessionMachine::uninit() must set mSession.mMachine to null */
859 Assert(mData->mSession.mMachine.isNull());
860 }
861
862 // uninit media from this machine's media registry, if they're still there
863 Guid uuidMachine(i_getId());
864
865 /* the lock is no more necessary (SessionMachine is uninitialized) */
866 alock.release();
867
868 /* XXX This will fail with
869 * "cannot be closed because it is still attached to 1 virtual machines"
870 * because at this point we did not call uninitDataAndChildObjects() yet
871 * and therefore also removeBackReference() for all these mediums was not called! */
872
873 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
874 mParent->i_unregisterMachineMedia(uuidMachine);
875
876 // has machine been modified?
877 if (mData->flModifications)
878 {
879 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
880 i_rollback(false /* aNotify */);
881 }
882
883 if (mData->mAccessible)
884 uninitDataAndChildObjects();
885
886 /* free the essential data structure last */
887 mData.free();
888
889 LogFlowThisFuncLeave();
890}
891
892// Wrapped IMachine properties
893/////////////////////////////////////////////////////////////////////////////
894HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
895{
896 /* mParent is constant during life time, no need to lock */
897 ComObjPtr<VirtualBox> pVirtualBox(mParent);
898 aParent = pVirtualBox;
899
900 return S_OK;
901}
902
903
904HRESULT Machine::getAccessible(BOOL *aAccessible)
905{
906 /* In some cases (medium registry related), it is necessary to be able to
907 * go through the list of all machines. Happens when an inaccessible VM
908 * has a sensible medium registry. */
909 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
911
912 HRESULT rc = S_OK;
913
914 if (!mData->mAccessible)
915 {
916 /* try to initialize the VM once more if not accessible */
917
918 AutoReinitSpan autoReinitSpan(this);
919 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
920
921#ifdef DEBUG
922 LogFlowThisFunc(("Dumping media backreferences\n"));
923 mParent->i_dumpAllBackRefs();
924#endif
925
926 if (mData->pMachineConfigFile)
927 {
928 // reset the XML file to force loadSettings() (called from registeredInit())
929 // to parse it again; the file might have changed
930 delete mData->pMachineConfigFile;
931 mData->pMachineConfigFile = NULL;
932 }
933
934 rc = i_registeredInit();
935
936 if (SUCCEEDED(rc) && mData->mAccessible)
937 {
938 autoReinitSpan.setSucceeded();
939
940 /* make sure interesting parties will notice the accessibility
941 * state change */
942 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
943 mParent->i_onMachineDataChange(mData->mUuid);
944 }
945 }
946
947 if (SUCCEEDED(rc))
948 *aAccessible = mData->mAccessible;
949
950 LogFlowThisFuncLeave();
951
952 return rc;
953}
954
955HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
956{
957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
958
959 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
960 {
961 /* return shortly */
962 aAccessError = NULL;
963 return S_OK;
964 }
965
966 HRESULT rc = S_OK;
967
968 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
969 rc = errorInfo.createObject();
970 if (SUCCEEDED(rc))
971 {
972 errorInfo->init(mData->mAccessError.getResultCode(),
973 mData->mAccessError.getInterfaceID().ref(),
974 Utf8Str(mData->mAccessError.getComponent()).c_str(),
975 Utf8Str(mData->mAccessError.getText()));
976 aAccessError = errorInfo;
977 }
978
979 return rc;
980}
981
982HRESULT Machine::getName(com::Utf8Str &aName)
983{
984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
985
986 aName = mUserData->s.strName;
987
988 return S_OK;
989}
990
991HRESULT Machine::setName(const com::Utf8Str &aName)
992{
993 // prohibit setting a UUID only as the machine name, or else it can
994 // never be found by findMachine()
995 Guid test(aName);
996
997 if (test.isValid())
998 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
999
1000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1001
1002 HRESULT rc = i_checkStateDependency(MutableStateDep);
1003 if (FAILED(rc)) return rc;
1004
1005 i_setModified(IsModified_MachineData);
1006 mUserData.backup();
1007 mUserData->s.strName = aName;
1008
1009 return S_OK;
1010}
1011
1012HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1013{
1014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1015
1016 aDescription = mUserData->s.strDescription;
1017
1018 return S_OK;
1019}
1020
1021HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1022{
1023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1024
1025 // this can be done in principle in any state as it doesn't affect the VM
1026 // significantly, but play safe by not messing around while complex
1027 // activities are going on
1028 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1029 if (FAILED(rc)) return rc;
1030
1031 i_setModified(IsModified_MachineData);
1032 mUserData.backup();
1033 mUserData->s.strDescription = aDescription;
1034
1035 return S_OK;
1036}
1037
1038HRESULT Machine::getId(com::Guid &aId)
1039{
1040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1041
1042 aId = mData->mUuid;
1043
1044 return S_OK;
1045}
1046
1047HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1048{
1049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1050 aGroups.resize(mUserData->s.llGroups.size());
1051 size_t i = 0;
1052 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1053 it != mUserData->s.llGroups.end(); ++it, ++i)
1054 aGroups[i] = (*it);
1055
1056 return S_OK;
1057}
1058
1059HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1060{
1061 StringsList llGroups;
1062 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1063 if (FAILED(rc))
1064 return rc;
1065
1066 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1067
1068 rc = i_checkStateDependency(MutableOrSavedStateDep);
1069 if (FAILED(rc)) return rc;
1070
1071 i_setModified(IsModified_MachineData);
1072 mUserData.backup();
1073 mUserData->s.llGroups = llGroups;
1074
1075 return S_OK;
1076}
1077
1078HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1079{
1080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1081
1082 aOSTypeId = mUserData->s.strOsType;
1083
1084 return S_OK;
1085}
1086
1087HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1088{
1089 /* look up the object by Id to check it is valid */
1090 ComPtr<IGuestOSType> guestOSType;
1091 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1092 if (FAILED(rc)) return rc;
1093
1094 /* when setting, always use the "etalon" value for consistency -- lookup
1095 * by ID is case-insensitive and the input value may have different case */
1096 Bstr osTypeId;
1097 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1098 if (FAILED(rc)) return rc;
1099
1100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1101
1102 rc = i_checkStateDependency(MutableStateDep);
1103 if (FAILED(rc)) return rc;
1104
1105 i_setModified(IsModified_MachineData);
1106 mUserData.backup();
1107 mUserData->s.strOsType = osTypeId;
1108
1109 return S_OK;
1110}
1111
1112HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1113{
1114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1115
1116 *aFirmwareType = mHWData->mFirmwareType;
1117
1118 return S_OK;
1119}
1120
1121HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1122{
1123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1124
1125 HRESULT rc = i_checkStateDependency(MutableStateDep);
1126 if (FAILED(rc)) return rc;
1127
1128 i_setModified(IsModified_MachineData);
1129 mHWData.backup();
1130 mHWData->mFirmwareType = aFirmwareType;
1131
1132 return S_OK;
1133}
1134
1135HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1136{
1137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1138
1139 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1140
1141 return S_OK;
1142}
1143
1144HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1145{
1146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1147
1148 HRESULT rc = i_checkStateDependency(MutableStateDep);
1149 if (FAILED(rc)) return rc;
1150
1151 i_setModified(IsModified_MachineData);
1152 mHWData.backup();
1153 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1154
1155 return S_OK;
1156}
1157
1158HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1159{
1160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1161
1162 *aPointingHIDType = mHWData->mPointingHIDType;
1163
1164 return S_OK;
1165}
1166
1167HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1168{
1169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1170
1171 HRESULT rc = i_checkStateDependency(MutableStateDep);
1172 if (FAILED(rc)) return rc;
1173
1174 i_setModified(IsModified_MachineData);
1175 mHWData.backup();
1176 mHWData->mPointingHIDType = aPointingHIDType;
1177
1178 return S_OK;
1179}
1180
1181HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1182{
1183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1184
1185 *aChipsetType = mHWData->mChipsetType;
1186
1187 return S_OK;
1188}
1189
1190HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1191{
1192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 HRESULT rc = i_checkStateDependency(MutableStateDep);
1195 if (FAILED(rc)) return rc;
1196
1197 if (aChipsetType != mHWData->mChipsetType)
1198 {
1199 i_setModified(IsModified_MachineData);
1200 mHWData.backup();
1201 mHWData->mChipsetType = aChipsetType;
1202
1203 // Resize network adapter array, to be finalized on commit/rollback.
1204 // We must not throw away entries yet, otherwise settings are lost
1205 // without a way to roll back.
1206 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1207 size_t oldCount = mNetworkAdapters.size();
1208 if (newCount > oldCount)
1209 {
1210 mNetworkAdapters.resize(newCount);
1211 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1212 {
1213 unconst(mNetworkAdapters[slot]).createObject();
1214 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1215 }
1216 }
1217 }
1218
1219 return S_OK;
1220}
1221
1222HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1223{
1224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1225
1226 aParavirtDebug = mHWData->mParavirtDebug;
1227 return S_OK;
1228}
1229
1230HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1231{
1232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1233
1234 HRESULT rc = i_checkStateDependency(MutableStateDep);
1235 if (FAILED(rc)) return rc;
1236
1237 /** @todo Parse/validate options? */
1238 if (aParavirtDebug != mHWData->mParavirtDebug)
1239 {
1240 i_setModified(IsModified_MachineData);
1241 mHWData.backup();
1242 mHWData->mParavirtDebug = aParavirtDebug;
1243 }
1244
1245 return S_OK;
1246}
1247
1248HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1249{
1250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1251
1252 *aParavirtProvider = mHWData->mParavirtProvider;
1253
1254 return S_OK;
1255}
1256
1257HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1258{
1259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1260
1261 HRESULT rc = i_checkStateDependency(MutableStateDep);
1262 if (FAILED(rc)) return rc;
1263
1264 if (aParavirtProvider != mHWData->mParavirtProvider)
1265 {
1266 i_setModified(IsModified_MachineData);
1267 mHWData.backup();
1268 mHWData->mParavirtProvider = aParavirtProvider;
1269 }
1270
1271 return S_OK;
1272}
1273
1274HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1275{
1276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1277
1278 *aParavirtProvider = mHWData->mParavirtProvider;
1279 switch (mHWData->mParavirtProvider)
1280 {
1281 case ParavirtProvider_None:
1282 case ParavirtProvider_HyperV:
1283 case ParavirtProvider_KVM:
1284 case ParavirtProvider_Minimal:
1285 break;
1286
1287 /* Resolve dynamic provider types to the effective types. */
1288 default:
1289 {
1290 ComPtr<IGuestOSType> ptrGuestOSType;
1291 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1292 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1293
1294 Bstr guestTypeFamilyId;
1295 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1296 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1297 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1298
1299 switch (mHWData->mParavirtProvider)
1300 {
1301 case ParavirtProvider_Legacy:
1302 {
1303 if (fOsXGuest)
1304 *aParavirtProvider = ParavirtProvider_Minimal;
1305 else
1306 *aParavirtProvider = ParavirtProvider_None;
1307 break;
1308 }
1309
1310 case ParavirtProvider_Default:
1311 {
1312 if (fOsXGuest)
1313 *aParavirtProvider = ParavirtProvider_Minimal;
1314 else if ( mUserData->s.strOsType == "Windows10"
1315 || mUserData->s.strOsType == "Windows10_64"
1316 || mUserData->s.strOsType == "Windows81"
1317 || mUserData->s.strOsType == "Windows81_64"
1318 || mUserData->s.strOsType == "Windows8"
1319 || mUserData->s.strOsType == "Windows8_64"
1320 || mUserData->s.strOsType == "Windows7"
1321 || mUserData->s.strOsType == "Windows7_64"
1322 || mUserData->s.strOsType == "WindowsVista"
1323 || mUserData->s.strOsType == "WindowsVista_64"
1324 || mUserData->s.strOsType == "Windows2012"
1325 || mUserData->s.strOsType == "Windows2012_64"
1326 || mUserData->s.strOsType == "Windows2008"
1327 || mUserData->s.strOsType == "Windows2008_64")
1328 {
1329 *aParavirtProvider = ParavirtProvider_HyperV;
1330 }
1331 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1332 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1333 || mUserData->s.strOsType == "Linux"
1334 || mUserData->s.strOsType == "Linux_64"
1335 || mUserData->s.strOsType == "ArchLinux"
1336 || mUserData->s.strOsType == "ArchLinux_64"
1337 || mUserData->s.strOsType == "Debian"
1338 || mUserData->s.strOsType == "Debian_64"
1339 || mUserData->s.strOsType == "Fedora"
1340 || mUserData->s.strOsType == "Fedora_64"
1341 || mUserData->s.strOsType == "Gentoo"
1342 || mUserData->s.strOsType == "Gentoo_64"
1343 || mUserData->s.strOsType == "Mandriva"
1344 || mUserData->s.strOsType == "Mandriva_64"
1345 || mUserData->s.strOsType == "OpenSUSE"
1346 || mUserData->s.strOsType == "OpenSUSE_64"
1347 || mUserData->s.strOsType == "Oracle"
1348 || mUserData->s.strOsType == "Oracle_64"
1349 || mUserData->s.strOsType == "RedHat"
1350 || mUserData->s.strOsType == "RedHat_64"
1351 || mUserData->s.strOsType == "Turbolinux"
1352 || mUserData->s.strOsType == "Turbolinux_64"
1353 || mUserData->s.strOsType == "Ubuntu"
1354 || mUserData->s.strOsType == "Ubuntu_64"
1355 || mUserData->s.strOsType == "Xandros"
1356 || mUserData->s.strOsType == "Xandros_64")
1357 {
1358 *aParavirtProvider = ParavirtProvider_KVM;
1359 }
1360 else
1361 *aParavirtProvider = ParavirtProvider_None;
1362 break;
1363 }
1364 }
1365 break;
1366 }
1367 }
1368
1369 Assert( *aParavirtProvider == ParavirtProvider_None
1370 || *aParavirtProvider == ParavirtProvider_Minimal
1371 || *aParavirtProvider == ParavirtProvider_HyperV
1372 || *aParavirtProvider == ParavirtProvider_KVM);
1373 return S_OK;
1374}
1375
1376HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1377{
1378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1379
1380 aHardwareVersion = mHWData->mHWVersion;
1381
1382 return S_OK;
1383}
1384
1385HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1386{
1387 /* check known version */
1388 Utf8Str hwVersion = aHardwareVersion;
1389 if ( hwVersion.compare("1") != 0
1390 && hwVersion.compare("2") != 0)
1391 return setError(E_INVALIDARG,
1392 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1393
1394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 HRESULT rc = i_checkStateDependency(MutableStateDep);
1397 if (FAILED(rc)) return rc;
1398
1399 i_setModified(IsModified_MachineData);
1400 mHWData.backup();
1401 mHWData->mHWVersion = aHardwareVersion;
1402
1403 return S_OK;
1404}
1405
1406HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1407{
1408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 if (!mHWData->mHardwareUUID.isZero())
1411 aHardwareUUID = mHWData->mHardwareUUID;
1412 else
1413 aHardwareUUID = mData->mUuid;
1414
1415 return S_OK;
1416}
1417
1418HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1419{
1420 if (!aHardwareUUID.isValid())
1421 return E_INVALIDARG;
1422
1423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1424
1425 HRESULT rc = i_checkStateDependency(MutableStateDep);
1426 if (FAILED(rc)) return rc;
1427
1428 i_setModified(IsModified_MachineData);
1429 mHWData.backup();
1430 if (aHardwareUUID == mData->mUuid)
1431 mHWData->mHardwareUUID.clear();
1432 else
1433 mHWData->mHardwareUUID = aHardwareUUID;
1434
1435 return S_OK;
1436}
1437
1438HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1439{
1440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1441
1442 *aMemorySize = mHWData->mMemorySize;
1443
1444 return S_OK;
1445}
1446
1447HRESULT Machine::setMemorySize(ULONG aMemorySize)
1448{
1449 /* check RAM limits */
1450 if ( aMemorySize < MM_RAM_MIN_IN_MB
1451 || aMemorySize > MM_RAM_MAX_IN_MB
1452 )
1453 return setError(E_INVALIDARG,
1454 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1455 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1456
1457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1458
1459 HRESULT rc = i_checkStateDependency(MutableStateDep);
1460 if (FAILED(rc)) return rc;
1461
1462 i_setModified(IsModified_MachineData);
1463 mHWData.backup();
1464 mHWData->mMemorySize = aMemorySize;
1465
1466 return S_OK;
1467}
1468
1469HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1470{
1471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1472
1473 *aCPUCount = mHWData->mCPUCount;
1474
1475 return S_OK;
1476}
1477
1478HRESULT Machine::setCPUCount(ULONG aCPUCount)
1479{
1480 /* check CPU limits */
1481 if ( aCPUCount < SchemaDefs::MinCPUCount
1482 || aCPUCount > SchemaDefs::MaxCPUCount
1483 )
1484 return setError(E_INVALIDARG,
1485 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1486 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1487
1488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1489
1490 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1491 if (mHWData->mCPUHotPlugEnabled)
1492 {
1493 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1494 {
1495 if (mHWData->mCPUAttached[idx])
1496 return setError(E_INVALIDARG,
1497 tr("There is still a CPU attached to socket %lu."
1498 "Detach the CPU before removing the socket"),
1499 aCPUCount, idx+1);
1500 }
1501 }
1502
1503 HRESULT rc = i_checkStateDependency(MutableStateDep);
1504 if (FAILED(rc)) return rc;
1505
1506 i_setModified(IsModified_MachineData);
1507 mHWData.backup();
1508 mHWData->mCPUCount = aCPUCount;
1509
1510 return S_OK;
1511}
1512
1513HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1514{
1515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1516
1517 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1518
1519 return S_OK;
1520}
1521
1522HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1523{
1524 HRESULT rc = S_OK;
1525
1526 /* check throttle limits */
1527 if ( aCPUExecutionCap < 1
1528 || aCPUExecutionCap > 100
1529 )
1530 return setError(E_INVALIDARG,
1531 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1532 aCPUExecutionCap, 1, 100);
1533
1534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1535
1536 alock.release();
1537 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1538 alock.acquire();
1539 if (FAILED(rc)) return rc;
1540
1541 i_setModified(IsModified_MachineData);
1542 mHWData.backup();
1543 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1544
1545 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1546 if (Global::IsOnline(mData->mMachineState))
1547 i_saveSettings(NULL);
1548
1549 return S_OK;
1550}
1551
1552HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1553{
1554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1557
1558 return S_OK;
1559}
1560
1561HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1562{
1563 HRESULT rc = S_OK;
1564
1565 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1566
1567 rc = i_checkStateDependency(MutableStateDep);
1568 if (FAILED(rc)) return rc;
1569
1570 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1571 {
1572 if (aCPUHotPlugEnabled)
1573 {
1574 i_setModified(IsModified_MachineData);
1575 mHWData.backup();
1576
1577 /* Add the amount of CPUs currently attached */
1578 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1579 mHWData->mCPUAttached[i] = true;
1580 }
1581 else
1582 {
1583 /*
1584 * We can disable hotplug only if the amount of maximum CPUs is equal
1585 * to the amount of attached CPUs
1586 */
1587 unsigned cCpusAttached = 0;
1588 unsigned iHighestId = 0;
1589
1590 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1591 {
1592 if (mHWData->mCPUAttached[i])
1593 {
1594 cCpusAttached++;
1595 iHighestId = i;
1596 }
1597 }
1598
1599 if ( (cCpusAttached != mHWData->mCPUCount)
1600 || (iHighestId >= mHWData->mCPUCount))
1601 return setError(E_INVALIDARG,
1602 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1603
1604 i_setModified(IsModified_MachineData);
1605 mHWData.backup();
1606 }
1607 }
1608
1609 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1610
1611 return rc;
1612}
1613
1614HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1615{
1616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1617
1618 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1619
1620 return S_OK;
1621}
1622
1623HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1624{
1625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1626
1627 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1628 if (SUCCEEDED(hrc))
1629 {
1630 i_setModified(IsModified_MachineData);
1631 mHWData.backup();
1632 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1633 }
1634 return hrc;
1635}
1636
1637HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1638{
1639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1640 aCPUProfile = mHWData->mCpuProfile;
1641 return S_OK;
1642}
1643
1644HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1645{
1646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1647 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1648 if (SUCCEEDED(hrc))
1649 {
1650 i_setModified(IsModified_MachineData);
1651 mHWData.backup();
1652 /* Empty equals 'host'. */
1653 if (aCPUProfile.isNotEmpty())
1654 mHWData->mCpuProfile = aCPUProfile;
1655 else
1656 mHWData->mCpuProfile = "host";
1657 }
1658 return hrc;
1659}
1660
1661HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1662{
1663#ifdef VBOX_WITH_USB_CARDREADER
1664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1665
1666 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1667
1668 return S_OK;
1669#else
1670 NOREF(aEmulatedUSBCardReaderEnabled);
1671 return E_NOTIMPL;
1672#endif
1673}
1674
1675HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1676{
1677#ifdef VBOX_WITH_USB_CARDREADER
1678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1679
1680 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1681 if (FAILED(rc)) return rc;
1682
1683 i_setModified(IsModified_MachineData);
1684 mHWData.backup();
1685 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1686
1687 return S_OK;
1688#else
1689 NOREF(aEmulatedUSBCardReaderEnabled);
1690 return E_NOTIMPL;
1691#endif
1692}
1693
1694HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1695{
1696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1697
1698 *aHPETEnabled = mHWData->mHPETEnabled;
1699
1700 return S_OK;
1701}
1702
1703HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1704{
1705 HRESULT rc = S_OK;
1706
1707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1708
1709 rc = i_checkStateDependency(MutableStateDep);
1710 if (FAILED(rc)) return rc;
1711
1712 i_setModified(IsModified_MachineData);
1713 mHWData.backup();
1714
1715 mHWData->mHPETEnabled = aHPETEnabled;
1716
1717 return rc;
1718}
1719
1720HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1721{
1722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1723
1724 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1725 return S_OK;
1726}
1727
1728HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1729{
1730 HRESULT rc = S_OK;
1731
1732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1733
1734 i_setModified(IsModified_MachineData);
1735 mHWData.backup();
1736 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1737
1738 alock.release();
1739 rc = i_onVideoCaptureChange();
1740 alock.acquire();
1741 if (FAILED(rc))
1742 {
1743 /*
1744 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1745 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1746 * determine if it should start or stop capturing. Therefore we need to manually
1747 * undo change.
1748 */
1749 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1750 return rc;
1751 }
1752
1753 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1754 if (Global::IsOnline(mData->mMachineState))
1755 i_saveSettings(NULL);
1756
1757 return rc;
1758}
1759
1760HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1761{
1762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1763 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1764 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1765 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1766 return S_OK;
1767}
1768
1769HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1770{
1771 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1772 bool fChanged = false;
1773
1774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1775
1776 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1777 {
1778 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1779 {
1780 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1781 fChanged = true;
1782 }
1783 }
1784 if (fChanged)
1785 {
1786 alock.release();
1787 HRESULT rc = i_onVideoCaptureChange();
1788 alock.acquire();
1789 if (FAILED(rc)) return rc;
1790 i_setModified(IsModified_MachineData);
1791
1792 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1793 if (Global::IsOnline(mData->mMachineState))
1794 i_saveSettings(NULL);
1795 }
1796
1797 return S_OK;
1798}
1799
1800HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1801{
1802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1803 if (mHWData->mVideoCaptureFile.isEmpty())
1804 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1805 else
1806 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1807 return S_OK;
1808}
1809
1810HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1811{
1812 Utf8Str strFile(aVideoCaptureFile);
1813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 if ( Global::IsOnline(mData->mMachineState)
1816 && mHWData->mVideoCaptureEnabled)
1817 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1818
1819 if (!RTPathStartsWithRoot(strFile.c_str()))
1820 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1821
1822 if (!strFile.isEmpty())
1823 {
1824 Utf8Str defaultFile;
1825 i_getDefaultVideoCaptureFile(defaultFile);
1826 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1827 strFile.setNull();
1828 }
1829
1830 i_setModified(IsModified_MachineData);
1831 mHWData.backup();
1832 mHWData->mVideoCaptureFile = strFile;
1833
1834 return S_OK;
1835}
1836
1837HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1838{
1839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1840 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1841 return S_OK;
1842}
1843
1844HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1845{
1846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1847
1848 if ( Global::IsOnline(mData->mMachineState)
1849 && mHWData->mVideoCaptureEnabled)
1850 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1851
1852 i_setModified(IsModified_MachineData);
1853 mHWData.backup();
1854 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1855
1856 return S_OK;
1857}
1858
1859HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1860{
1861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1862 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1863 return S_OK;
1864}
1865
1866HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1867{
1868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1869
1870 if ( Global::IsOnline(mData->mMachineState)
1871 && mHWData->mVideoCaptureEnabled)
1872 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1873
1874 i_setModified(IsModified_MachineData);
1875 mHWData.backup();
1876 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1877
1878 return S_OK;
1879}
1880
1881HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1882{
1883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1884 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1885 return S_OK;
1886}
1887
1888HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1889{
1890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1891
1892 if ( Global::IsOnline(mData->mMachineState)
1893 && mHWData->mVideoCaptureEnabled)
1894 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1895
1896 i_setModified(IsModified_MachineData);
1897 mHWData.backup();
1898 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1899
1900 return S_OK;
1901}
1902
1903HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1904{
1905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1906 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1907 return S_OK;
1908}
1909
1910HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1911{
1912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1913
1914 if ( Global::IsOnline(mData->mMachineState)
1915 && mHWData->mVideoCaptureEnabled)
1916 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1917
1918 i_setModified(IsModified_MachineData);
1919 mHWData.backup();
1920 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1921
1922 return S_OK;
1923}
1924
1925HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1926{
1927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1928 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1929 return S_OK;
1930}
1931
1932HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1933{
1934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1935
1936 if ( Global::IsOnline(mData->mMachineState)
1937 && mHWData->mVideoCaptureEnabled)
1938 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1939
1940 i_setModified(IsModified_MachineData);
1941 mHWData.backup();
1942 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1943
1944 return S_OK;
1945}
1946
1947HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1948{
1949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1950 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1951 return S_OK;
1952}
1953
1954HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1955{
1956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1957
1958 if ( Global::IsOnline(mData->mMachineState)
1959 && mHWData->mVideoCaptureEnabled)
1960 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1961
1962 i_setModified(IsModified_MachineData);
1963 mHWData.backup();
1964 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1965
1966 return S_OK;
1967}
1968
1969HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1970{
1971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1972
1973 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1974 return S_OK;
1975}
1976
1977HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1978{
1979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1980
1981 if ( Global::IsOnline(mData->mMachineState)
1982 && mHWData->mVideoCaptureEnabled)
1983 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1984
1985 i_setModified(IsModified_MachineData);
1986 mHWData.backup();
1987 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1988
1989 return S_OK;
1990}
1991
1992HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1993{
1994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1995
1996 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1997
1998 return S_OK;
1999}
2000
2001HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2002{
2003 switch (aGraphicsControllerType)
2004 {
2005 case GraphicsControllerType_Null:
2006 case GraphicsControllerType_VBoxVGA:
2007#ifdef VBOX_WITH_VMSVGA
2008 case GraphicsControllerType_VMSVGA:
2009#endif
2010 break;
2011 default:
2012 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2013 }
2014
2015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2016
2017 HRESULT rc = i_checkStateDependency(MutableStateDep);
2018 if (FAILED(rc)) return rc;
2019
2020 i_setModified(IsModified_MachineData);
2021 mHWData.backup();
2022 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2023
2024 return S_OK;
2025}
2026
2027HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2028{
2029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2030
2031 *aVRAMSize = mHWData->mVRAMSize;
2032
2033 return S_OK;
2034}
2035
2036HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2037{
2038 /* check VRAM limits */
2039 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2040 return setError(E_INVALIDARG,
2041 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2042 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2043
2044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2045
2046 HRESULT rc = i_checkStateDependency(MutableStateDep);
2047 if (FAILED(rc)) return rc;
2048
2049 i_setModified(IsModified_MachineData);
2050 mHWData.backup();
2051 mHWData->mVRAMSize = aVRAMSize;
2052
2053 return S_OK;
2054}
2055
2056/** @todo this method should not be public */
2057HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2058{
2059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2060
2061 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2062
2063 return S_OK;
2064}
2065
2066/**
2067 * Set the memory balloon size.
2068 *
2069 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2070 * we have to make sure that we never call IGuest from here.
2071 */
2072HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2073{
2074 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2075#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2076 /* check limits */
2077 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2078 return setError(E_INVALIDARG,
2079 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2080 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2081
2082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2083
2084 i_setModified(IsModified_MachineData);
2085 mHWData.backup();
2086 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2087
2088 return S_OK;
2089#else
2090 NOREF(aMemoryBalloonSize);
2091 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2092#endif
2093}
2094
2095HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2096{
2097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2098
2099 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2100 return S_OK;
2101}
2102
2103HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2104{
2105#ifdef VBOX_WITH_PAGE_SHARING
2106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2107
2108 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2109 i_setModified(IsModified_MachineData);
2110 mHWData.backup();
2111 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2112 return S_OK;
2113#else
2114 NOREF(aPageFusionEnabled);
2115 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2116#endif
2117}
2118
2119HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2120{
2121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2122
2123 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2124
2125 return S_OK;
2126}
2127
2128HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2129{
2130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2131
2132 HRESULT rc = i_checkStateDependency(MutableStateDep);
2133 if (FAILED(rc)) return rc;
2134
2135 /** @todo check validity! */
2136
2137 i_setModified(IsModified_MachineData);
2138 mHWData.backup();
2139 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2140
2141 return S_OK;
2142}
2143
2144
2145HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2146{
2147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2148
2149 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2150
2151 return S_OK;
2152}
2153
2154HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2155{
2156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2157
2158 HRESULT rc = i_checkStateDependency(MutableStateDep);
2159 if (FAILED(rc)) return rc;
2160
2161 /** @todo check validity! */
2162 i_setModified(IsModified_MachineData);
2163 mHWData.backup();
2164 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2165
2166 return S_OK;
2167}
2168
2169HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2170{
2171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2172
2173 *aMonitorCount = mHWData->mMonitorCount;
2174
2175 return S_OK;
2176}
2177
2178HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2179{
2180 /* make sure monitor count is a sensible number */
2181 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2182 return setError(E_INVALIDARG,
2183 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2184 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2185
2186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2187
2188 HRESULT rc = i_checkStateDependency(MutableStateDep);
2189 if (FAILED(rc)) return rc;
2190
2191 i_setModified(IsModified_MachineData);
2192 mHWData.backup();
2193 mHWData->mMonitorCount = aMonitorCount;
2194
2195 return S_OK;
2196}
2197
2198HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2199{
2200 /* mBIOSSettings is constant during life time, no need to lock */
2201 aBIOSSettings = mBIOSSettings;
2202
2203 return S_OK;
2204}
2205
2206HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2207{
2208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2209
2210 switch (aProperty)
2211 {
2212 case CPUPropertyType_PAE:
2213 *aValue = mHWData->mPAEEnabled;
2214 break;
2215
2216 case CPUPropertyType_LongMode:
2217 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2218 *aValue = TRUE;
2219 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2220 *aValue = FALSE;
2221#if HC_ARCH_BITS == 64
2222 else
2223 *aValue = TRUE;
2224#else
2225 else
2226 {
2227 *aValue = FALSE;
2228
2229 ComPtr<IGuestOSType> ptrGuestOSType;
2230 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2231 if (SUCCEEDED(hrc2))
2232 {
2233 BOOL fIs64Bit = FALSE;
2234 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2235 if (SUCCEEDED(hrc2) && fIs64Bit)
2236 {
2237 ComObjPtr<Host> ptrHost = mParent->i_host();
2238 alock.release();
2239
2240 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2241 if (FAILED(hrc2))
2242 *aValue = FALSE;
2243 }
2244 }
2245 }
2246#endif
2247 break;
2248
2249 case CPUPropertyType_TripleFaultReset:
2250 *aValue = mHWData->mTripleFaultReset;
2251 break;
2252
2253 case CPUPropertyType_APIC:
2254 *aValue = mHWData->mAPIC;
2255 break;
2256
2257 case CPUPropertyType_X2APIC:
2258 *aValue = mHWData->mX2APIC;
2259 break;
2260
2261 default:
2262 return E_INVALIDARG;
2263 }
2264 return S_OK;
2265}
2266
2267HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2268{
2269 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2270
2271 HRESULT rc = i_checkStateDependency(MutableStateDep);
2272 if (FAILED(rc)) return rc;
2273
2274 switch (aProperty)
2275 {
2276 case CPUPropertyType_PAE:
2277 i_setModified(IsModified_MachineData);
2278 mHWData.backup();
2279 mHWData->mPAEEnabled = !!aValue;
2280 break;
2281
2282 case CPUPropertyType_LongMode:
2283 i_setModified(IsModified_MachineData);
2284 mHWData.backup();
2285 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2286 break;
2287
2288 case CPUPropertyType_TripleFaultReset:
2289 i_setModified(IsModified_MachineData);
2290 mHWData.backup();
2291 mHWData->mTripleFaultReset = !!aValue;
2292 break;
2293
2294 case CPUPropertyType_APIC:
2295 if (mHWData->mX2APIC)
2296 aValue = TRUE;
2297 i_setModified(IsModified_MachineData);
2298 mHWData.backup();
2299 mHWData->mAPIC = !!aValue;
2300 break;
2301
2302 case CPUPropertyType_X2APIC:
2303 i_setModified(IsModified_MachineData);
2304 mHWData.backup();
2305 mHWData->mX2APIC = !!aValue;
2306 if (aValue)
2307 mHWData->mAPIC = !!aValue;
2308 break;
2309
2310 default:
2311 return E_INVALIDARG;
2312 }
2313 return S_OK;
2314}
2315
2316HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2317{
2318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2319
2320 switch(aId)
2321 {
2322 case 0x0:
2323 case 0x1:
2324 case 0x2:
2325 case 0x3:
2326 case 0x4:
2327 case 0x5:
2328 case 0x6:
2329 case 0x7:
2330 case 0x8:
2331 case 0x9:
2332 case 0xA:
2333 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2334 return E_INVALIDARG;
2335
2336 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2337 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2338 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2339 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2340 break;
2341
2342 case 0x80000000:
2343 case 0x80000001:
2344 case 0x80000002:
2345 case 0x80000003:
2346 case 0x80000004:
2347 case 0x80000005:
2348 case 0x80000006:
2349 case 0x80000007:
2350 case 0x80000008:
2351 case 0x80000009:
2352 case 0x8000000A:
2353 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2354 return E_INVALIDARG;
2355
2356 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2357 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2358 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2359 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2360 break;
2361
2362 default:
2363 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2364 }
2365 return S_OK;
2366}
2367
2368
2369HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2370{
2371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2372
2373 HRESULT rc = i_checkStateDependency(MutableStateDep);
2374 if (FAILED(rc)) return rc;
2375
2376 switch(aId)
2377 {
2378 case 0x0:
2379 case 0x1:
2380 case 0x2:
2381 case 0x3:
2382 case 0x4:
2383 case 0x5:
2384 case 0x6:
2385 case 0x7:
2386 case 0x8:
2387 case 0x9:
2388 case 0xA:
2389 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2390 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2391 i_setModified(IsModified_MachineData);
2392 mHWData.backup();
2393 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2394 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2395 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2396 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2397 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2398 break;
2399
2400 case 0x80000000:
2401 case 0x80000001:
2402 case 0x80000002:
2403 case 0x80000003:
2404 case 0x80000004:
2405 case 0x80000005:
2406 case 0x80000006:
2407 case 0x80000007:
2408 case 0x80000008:
2409 case 0x80000009:
2410 case 0x8000000A:
2411 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2412 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2413 i_setModified(IsModified_MachineData);
2414 mHWData.backup();
2415 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2416 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2417 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2418 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2419 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2420 break;
2421
2422 default:
2423 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2424 }
2425 return S_OK;
2426}
2427
2428HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2429{
2430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2431
2432 HRESULT rc = i_checkStateDependency(MutableStateDep);
2433 if (FAILED(rc)) return rc;
2434
2435 switch(aId)
2436 {
2437 case 0x0:
2438 case 0x1:
2439 case 0x2:
2440 case 0x3:
2441 case 0x4:
2442 case 0x5:
2443 case 0x6:
2444 case 0x7:
2445 case 0x8:
2446 case 0x9:
2447 case 0xA:
2448 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2449 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2450 i_setModified(IsModified_MachineData);
2451 mHWData.backup();
2452 /* Invalidate leaf. */
2453 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2454 break;
2455
2456 case 0x80000000:
2457 case 0x80000001:
2458 case 0x80000002:
2459 case 0x80000003:
2460 case 0x80000004:
2461 case 0x80000005:
2462 case 0x80000006:
2463 case 0x80000007:
2464 case 0x80000008:
2465 case 0x80000009:
2466 case 0x8000000A:
2467 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2468 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2469 i_setModified(IsModified_MachineData);
2470 mHWData.backup();
2471 /* Invalidate leaf. */
2472 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2473 break;
2474
2475 default:
2476 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2477 }
2478 return S_OK;
2479}
2480
2481HRESULT Machine::removeAllCPUIDLeaves()
2482{
2483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2484
2485 HRESULT rc = i_checkStateDependency(MutableStateDep);
2486 if (FAILED(rc)) return rc;
2487
2488 i_setModified(IsModified_MachineData);
2489 mHWData.backup();
2490
2491 /* Invalidate all standard leafs. */
2492 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2493 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2494
2495 /* Invalidate all extended leafs. */
2496 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2497 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2498
2499 return S_OK;
2500}
2501HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2502{
2503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2504
2505 switch(aProperty)
2506 {
2507 case HWVirtExPropertyType_Enabled:
2508 *aValue = mHWData->mHWVirtExEnabled;
2509 break;
2510
2511 case HWVirtExPropertyType_VPID:
2512 *aValue = mHWData->mHWVirtExVPIDEnabled;
2513 break;
2514
2515 case HWVirtExPropertyType_NestedPaging:
2516 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2517 break;
2518
2519 case HWVirtExPropertyType_UnrestrictedExecution:
2520 *aValue = mHWData->mHWVirtExUXEnabled;
2521 break;
2522
2523 case HWVirtExPropertyType_LargePages:
2524 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2525#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2526 *aValue = FALSE;
2527#endif
2528 break;
2529
2530 case HWVirtExPropertyType_Force:
2531 *aValue = mHWData->mHWVirtExForceEnabled;
2532 break;
2533
2534 default:
2535 return E_INVALIDARG;
2536 }
2537 return S_OK;
2538}
2539
2540HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2541{
2542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2543
2544 HRESULT rc = i_checkStateDependency(MutableStateDep);
2545 if (FAILED(rc)) return rc;
2546
2547 switch(aProperty)
2548 {
2549 case HWVirtExPropertyType_Enabled:
2550 i_setModified(IsModified_MachineData);
2551 mHWData.backup();
2552 mHWData->mHWVirtExEnabled = !!aValue;
2553 break;
2554
2555 case HWVirtExPropertyType_VPID:
2556 i_setModified(IsModified_MachineData);
2557 mHWData.backup();
2558 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2559 break;
2560
2561 case HWVirtExPropertyType_NestedPaging:
2562 i_setModified(IsModified_MachineData);
2563 mHWData.backup();
2564 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2565 break;
2566
2567 case HWVirtExPropertyType_UnrestrictedExecution:
2568 i_setModified(IsModified_MachineData);
2569 mHWData.backup();
2570 mHWData->mHWVirtExUXEnabled = !!aValue;
2571 break;
2572
2573 case HWVirtExPropertyType_LargePages:
2574 i_setModified(IsModified_MachineData);
2575 mHWData.backup();
2576 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2577 break;
2578
2579 case HWVirtExPropertyType_Force:
2580 i_setModified(IsModified_MachineData);
2581 mHWData.backup();
2582 mHWData->mHWVirtExForceEnabled = !!aValue;
2583 break;
2584
2585 default:
2586 return E_INVALIDARG;
2587 }
2588
2589 return S_OK;
2590}
2591
2592HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2593{
2594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2595
2596 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2597
2598 return S_OK;
2599}
2600
2601HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2602{
2603 /* @todo (r=dmik):
2604 * 1. Allow to change the name of the snapshot folder containing snapshots
2605 * 2. Rename the folder on disk instead of just changing the property
2606 * value (to be smart and not to leave garbage). Note that it cannot be
2607 * done here because the change may be rolled back. Thus, the right
2608 * place is #saveSettings().
2609 */
2610
2611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2612
2613 HRESULT rc = i_checkStateDependency(MutableStateDep);
2614 if (FAILED(rc)) return rc;
2615
2616 if (!mData->mCurrentSnapshot.isNull())
2617 return setError(E_FAIL,
2618 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2619
2620 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2621
2622 if (strSnapshotFolder.isEmpty())
2623 strSnapshotFolder = "Snapshots";
2624 int vrc = i_calculateFullPath(strSnapshotFolder,
2625 strSnapshotFolder);
2626 if (RT_FAILURE(vrc))
2627 return setError(E_FAIL,
2628 tr("Invalid snapshot folder '%s' (%Rrc)"),
2629 strSnapshotFolder.c_str(), vrc);
2630
2631 i_setModified(IsModified_MachineData);
2632 mUserData.backup();
2633
2634 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2635
2636 return S_OK;
2637}
2638
2639HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2640{
2641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2642
2643 aMediumAttachments.resize(mMediaData->mAttachments.size());
2644 size_t i = 0;
2645 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2646 it != mMediaData->mAttachments.end(); ++it, ++i)
2647 aMediumAttachments[i] = *it;
2648
2649 return S_OK;
2650}
2651
2652HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2653{
2654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2655
2656 Assert(!!mVRDEServer);
2657
2658 aVRDEServer = mVRDEServer;
2659
2660 return S_OK;
2661}
2662
2663HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2664{
2665 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2666
2667 aAudioAdapter = mAudioAdapter;
2668
2669 return S_OK;
2670}
2671
2672HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2673{
2674#ifdef VBOX_WITH_VUSB
2675 clearError();
2676 MultiResult rc(S_OK);
2677
2678# ifdef VBOX_WITH_USB
2679 rc = mParent->i_host()->i_checkUSBProxyService();
2680 if (FAILED(rc)) return rc;
2681# endif
2682
2683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2684
2685 USBControllerList data = *mUSBControllers.data();
2686 aUSBControllers.resize(data.size());
2687 size_t i = 0;
2688 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2689 aUSBControllers[i] = *it;
2690
2691 return S_OK;
2692#else
2693 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2694 * extended error info to indicate that USB is simply not available
2695 * (w/o treating it as a failure), for example, as in OSE */
2696 NOREF(aUSBControllers);
2697 ReturnComNotImplemented();
2698#endif /* VBOX_WITH_VUSB */
2699}
2700
2701HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2702{
2703#ifdef VBOX_WITH_VUSB
2704 clearError();
2705 MultiResult rc(S_OK);
2706
2707# ifdef VBOX_WITH_USB
2708 rc = mParent->i_host()->i_checkUSBProxyService();
2709 if (FAILED(rc)) return rc;
2710# endif
2711
2712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2713
2714 aUSBDeviceFilters = mUSBDeviceFilters;
2715 return rc;
2716#else
2717 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2718 * extended error info to indicate that USB is simply not available
2719 * (w/o treating it as a failure), for example, as in OSE */
2720 NOREF(aUSBDeviceFilters);
2721 ReturnComNotImplemented();
2722#endif /* VBOX_WITH_VUSB */
2723}
2724
2725HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2726{
2727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2728
2729 aSettingsFilePath = mData->m_strConfigFileFull;
2730
2731 return S_OK;
2732}
2733
2734HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2735{
2736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2737
2738 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2739 if (FAILED(rc)) return rc;
2740
2741 if (!mData->pMachineConfigFile->fileExists())
2742 // this is a new machine, and no config file exists yet:
2743 *aSettingsModified = TRUE;
2744 else
2745 *aSettingsModified = (mData->flModifications != 0);
2746
2747 return S_OK;
2748}
2749
2750HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2751{
2752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2753
2754 *aSessionState = mData->mSession.mState;
2755
2756 return S_OK;
2757}
2758
2759HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2760{
2761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2762
2763 aSessionName = mData->mSession.mName;
2764
2765 return S_OK;
2766}
2767
2768HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2769{
2770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2771
2772 *aSessionPID = mData->mSession.mPID;
2773
2774 return S_OK;
2775}
2776
2777HRESULT Machine::getState(MachineState_T *aState)
2778{
2779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2780
2781 *aState = mData->mMachineState;
2782 Assert(mData->mMachineState != MachineState_Null);
2783
2784 return S_OK;
2785}
2786
2787HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2788{
2789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2790
2791 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2792
2793 return S_OK;
2794}
2795
2796HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2797{
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 aStateFilePath = mSSData->strStateFilePath;
2801
2802 return S_OK;
2803}
2804
2805HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2806{
2807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2808
2809 i_getLogFolder(aLogFolder);
2810
2811 return S_OK;
2812}
2813
2814HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2815{
2816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2817
2818 aCurrentSnapshot = mData->mCurrentSnapshot;
2819
2820 return S_OK;
2821}
2822
2823HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2824{
2825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2826
2827 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2828 ? 0
2829 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2830
2831 return S_OK;
2832}
2833
2834HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2835{
2836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2837
2838 /* Note: for machines with no snapshots, we always return FALSE
2839 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2840 * reasons :) */
2841
2842 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2843 ? FALSE
2844 : mData->mCurrentStateModified;
2845
2846 return S_OK;
2847}
2848
2849HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2850{
2851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2852
2853 aSharedFolders.resize(mHWData->mSharedFolders.size());
2854 size_t i = 0;
2855 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2856 it != mHWData->mSharedFolders.end(); ++i, ++it)
2857 aSharedFolders[i] = *it;
2858
2859 return S_OK;
2860}
2861
2862HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2863{
2864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2865
2866 *aClipboardMode = mHWData->mClipboardMode;
2867
2868 return S_OK;
2869}
2870
2871HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2872{
2873 HRESULT rc = S_OK;
2874
2875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2876
2877 alock.release();
2878 rc = i_onClipboardModeChange(aClipboardMode);
2879 alock.acquire();
2880 if (FAILED(rc)) return rc;
2881
2882 i_setModified(IsModified_MachineData);
2883 mHWData.backup();
2884 mHWData->mClipboardMode = aClipboardMode;
2885
2886 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2887 if (Global::IsOnline(mData->mMachineState))
2888 i_saveSettings(NULL);
2889
2890 return S_OK;
2891}
2892
2893HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2894{
2895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2896
2897 *aDnDMode = mHWData->mDnDMode;
2898
2899 return S_OK;
2900}
2901
2902HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2903{
2904 HRESULT rc = S_OK;
2905
2906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 alock.release();
2909 rc = i_onDnDModeChange(aDnDMode);
2910
2911 alock.acquire();
2912 if (FAILED(rc)) return rc;
2913
2914 i_setModified(IsModified_MachineData);
2915 mHWData.backup();
2916 mHWData->mDnDMode = aDnDMode;
2917
2918 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2919 if (Global::IsOnline(mData->mMachineState))
2920 i_saveSettings(NULL);
2921
2922 return S_OK;
2923}
2924
2925HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2926{
2927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2928 StorageControllerList data = *mStorageControllers.data();
2929 size_t i = 0;
2930 aStorageControllers.resize(data.size());
2931 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2932 aStorageControllers[i] = *it;
2933 return S_OK;
2934}
2935
2936HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2937{
2938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2939
2940 *aEnabled = mUserData->s.fTeleporterEnabled;
2941
2942 return S_OK;
2943}
2944
2945HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2946{
2947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2948
2949 /* Only allow it to be set to true when PoweredOff or Aborted.
2950 (Clearing it is always permitted.) */
2951 if ( aTeleporterEnabled
2952 && mData->mRegistered
2953 && ( !i_isSessionMachine()
2954 || ( mData->mMachineState != MachineState_PoweredOff
2955 && mData->mMachineState != MachineState_Teleported
2956 && mData->mMachineState != MachineState_Aborted
2957 )
2958 )
2959 )
2960 return setError(VBOX_E_INVALID_VM_STATE,
2961 tr("The machine is not powered off (state is %s)"),
2962 Global::stringifyMachineState(mData->mMachineState));
2963
2964 i_setModified(IsModified_MachineData);
2965 mUserData.backup();
2966 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2967
2968 return S_OK;
2969}
2970
2971HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2972{
2973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2976
2977 return S_OK;
2978}
2979
2980HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2981{
2982 if (aTeleporterPort >= _64K)
2983 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2984
2985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2986
2987 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2988 if (FAILED(rc)) return rc;
2989
2990 i_setModified(IsModified_MachineData);
2991 mUserData.backup();
2992 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2993
2994 return S_OK;
2995}
2996
2997HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2998{
2999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3000
3001 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3002
3003 return S_OK;
3004}
3005
3006HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3007{
3008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3009
3010 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3011 if (FAILED(rc)) return rc;
3012
3013 i_setModified(IsModified_MachineData);
3014 mUserData.backup();
3015 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3016
3017 return S_OK;
3018}
3019
3020HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3021{
3022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3023 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3024
3025 return S_OK;
3026}
3027
3028HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3029{
3030 /*
3031 * Hash the password first.
3032 */
3033 com::Utf8Str aT = aTeleporterPassword;
3034
3035 if (!aT.isEmpty())
3036 {
3037 if (VBoxIsPasswordHashed(&aT))
3038 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3039 VBoxHashPassword(&aT);
3040 }
3041
3042 /*
3043 * Do the update.
3044 */
3045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3046 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3047 if (SUCCEEDED(hrc))
3048 {
3049 i_setModified(IsModified_MachineData);
3050 mUserData.backup();
3051 mUserData->s.strTeleporterPassword = aT;
3052 }
3053
3054 return hrc;
3055}
3056
3057HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3058{
3059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3060
3061 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3062 return S_OK;
3063}
3064
3065HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3066{
3067 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3068
3069 /* @todo deal with running state change. */
3070 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3071 if (FAILED(rc)) return rc;
3072
3073 i_setModified(IsModified_MachineData);
3074 mUserData.backup();
3075 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3076 return S_OK;
3077}
3078
3079HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3080{
3081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3082
3083 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3084 return S_OK;
3085}
3086
3087HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3088{
3089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3090
3091 /* @todo deal with running state change. */
3092 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3093 if (FAILED(rc)) return rc;
3094
3095 i_setModified(IsModified_MachineData);
3096 mUserData.backup();
3097 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3098 return S_OK;
3099}
3100
3101HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3102{
3103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3104
3105 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3106 return S_OK;
3107}
3108
3109HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3110{
3111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3112
3113 /* @todo deal with running state change. */
3114 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3115 if (FAILED(rc)) return rc;
3116
3117 i_setModified(IsModified_MachineData);
3118 mUserData.backup();
3119 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3120 return S_OK;
3121}
3122
3123HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3124{
3125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3126
3127 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3128
3129 return S_OK;
3130}
3131
3132HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3133{
3134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3135
3136 /* @todo deal with running state change. */
3137 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3138 if (FAILED(rc)) return rc;
3139
3140 i_setModified(IsModified_MachineData);
3141 mUserData.backup();
3142 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3143
3144 return S_OK;
3145}
3146
3147HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3148{
3149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3150
3151 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3152 return S_OK;
3153}
3154
3155HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3156{
3157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3158
3159 /* @todo deal with running state change. */
3160 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3161 if (FAILED(rc)) return rc;
3162
3163 i_setModified(IsModified_MachineData);
3164 mUserData.backup();
3165 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3166 return S_OK;
3167}
3168
3169HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3170{
3171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3172
3173 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3174
3175 return S_OK;
3176}
3177
3178HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3179{
3180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 /* Only allow it to be set to true when PoweredOff or Aborted.
3183 (Clearing it is always permitted.) */
3184 if ( aRTCUseUTC
3185 && mData->mRegistered
3186 && ( !i_isSessionMachine()
3187 || ( mData->mMachineState != MachineState_PoweredOff
3188 && mData->mMachineState != MachineState_Teleported
3189 && mData->mMachineState != MachineState_Aborted
3190 )
3191 )
3192 )
3193 return setError(VBOX_E_INVALID_VM_STATE,
3194 tr("The machine is not powered off (state is %s)"),
3195 Global::stringifyMachineState(mData->mMachineState));
3196
3197 i_setModified(IsModified_MachineData);
3198 mUserData.backup();
3199 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3200
3201 return S_OK;
3202}
3203
3204HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3205{
3206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3207
3208 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3209
3210 return S_OK;
3211}
3212
3213HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3214{
3215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3216
3217 HRESULT rc = i_checkStateDependency(MutableStateDep);
3218 if (FAILED(rc)) return rc;
3219
3220 i_setModified(IsModified_MachineData);
3221 mHWData.backup();
3222 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3223
3224 return S_OK;
3225}
3226
3227HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3228{
3229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3230
3231 *aIOCacheSize = mHWData->mIOCacheSize;
3232
3233 return S_OK;
3234}
3235
3236HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3237{
3238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3239
3240 HRESULT rc = i_checkStateDependency(MutableStateDep);
3241 if (FAILED(rc)) return rc;
3242
3243 i_setModified(IsModified_MachineData);
3244 mHWData.backup();
3245 mHWData->mIOCacheSize = aIOCacheSize;
3246
3247 return S_OK;
3248}
3249
3250
3251/**
3252 * @note Locks objects!
3253 */
3254HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3255 LockType_T aLockType)
3256{
3257 /* check the session state */
3258 SessionState_T state;
3259 HRESULT rc = aSession->COMGETTER(State)(&state);
3260 if (FAILED(rc)) return rc;
3261
3262 if (state != SessionState_Unlocked)
3263 return setError(VBOX_E_INVALID_OBJECT_STATE,
3264 tr("The given session is busy"));
3265
3266 // get the client's IInternalSessionControl interface
3267 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3268 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3269 E_INVALIDARG);
3270
3271 // session name (only used in some code paths)
3272 Utf8Str strSessionName;
3273
3274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3275
3276 if (!mData->mRegistered)
3277 return setError(E_UNEXPECTED,
3278 tr("The machine '%s' is not registered"),
3279 mUserData->s.strName.c_str());
3280
3281 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3282
3283 SessionState_T oldState = mData->mSession.mState;
3284 /* Hack: in case the session is closing and there is a progress object
3285 * which allows waiting for the session to be closed, take the opportunity
3286 * and do a limited wait (max. 1 second). This helps a lot when the system
3287 * is busy and thus session closing can take a little while. */
3288 if ( mData->mSession.mState == SessionState_Unlocking
3289 && mData->mSession.mProgress)
3290 {
3291 alock.release();
3292 mData->mSession.mProgress->WaitForCompletion(1000);
3293 alock.acquire();
3294 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3295 }
3296
3297 // try again now
3298 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3299 // (i.e. session machine exists)
3300 && (aLockType == LockType_Shared) // caller wants a shared link to the
3301 // existing session that holds the write lock:
3302 )
3303 {
3304 // OK, share the session... we are now dealing with three processes:
3305 // 1) VBoxSVC (where this code runs);
3306 // 2) process C: the caller's client process (who wants a shared session);
3307 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3308
3309 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3310 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3311 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3312 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3313 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3314
3315 /*
3316 * Release the lock before calling the client process. It's safe here
3317 * since the only thing to do after we get the lock again is to add
3318 * the remote control to the list (which doesn't directly influence
3319 * anything).
3320 */
3321 alock.release();
3322
3323 // get the console of the session holding the write lock (this is a remote call)
3324 ComPtr<IConsole> pConsoleW;
3325 if (mData->mSession.mLockType == LockType_VM)
3326 {
3327 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3328 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3329 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3330 if (FAILED(rc))
3331 // the failure may occur w/o any error info (from RPC), so provide one
3332 return setError(VBOX_E_VM_ERROR,
3333 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3334 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3335 }
3336
3337 // share the session machine and W's console with the caller's session
3338 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3339 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3340 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3341
3342 if (FAILED(rc))
3343 // the failure may occur w/o any error info (from RPC), so provide one
3344 return setError(VBOX_E_VM_ERROR,
3345 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3346 alock.acquire();
3347
3348 // need to revalidate the state after acquiring the lock again
3349 if (mData->mSession.mState != SessionState_Locked)
3350 {
3351 pSessionControl->Uninitialize();
3352 return setError(VBOX_E_INVALID_SESSION_STATE,
3353 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3354 mUserData->s.strName.c_str());
3355 }
3356
3357 // add the caller's session to the list
3358 mData->mSession.mRemoteControls.push_back(pSessionControl);
3359 }
3360 else if ( mData->mSession.mState == SessionState_Locked
3361 || mData->mSession.mState == SessionState_Unlocking
3362 )
3363 {
3364 // sharing not permitted, or machine still unlocking:
3365 return setError(VBOX_E_INVALID_OBJECT_STATE,
3366 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3367 mUserData->s.strName.c_str());
3368 }
3369 else
3370 {
3371 // machine is not locked: then write-lock the machine (create the session machine)
3372
3373 // must not be busy
3374 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3375
3376 // get the caller's session PID
3377 RTPROCESS pid = NIL_RTPROCESS;
3378 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3379 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3380 Assert(pid != NIL_RTPROCESS);
3381
3382 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3383
3384 if (fLaunchingVMProcess)
3385 {
3386 if (mData->mSession.mPID == NIL_RTPROCESS)
3387 {
3388 // two or more clients racing for a lock, the one which set the
3389 // session state to Spawning will win, the others will get an
3390 // error as we can't decide here if waiting a little would help
3391 // (only for shared locks this would avoid an error)
3392 return setError(VBOX_E_INVALID_OBJECT_STATE,
3393 tr("The machine '%s' already has a lock request pending"),
3394 mUserData->s.strName.c_str());
3395 }
3396
3397 // this machine is awaiting for a spawning session to be opened:
3398 // then the calling process must be the one that got started by
3399 // LaunchVMProcess()
3400
3401 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3402 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3403
3404#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3405 /* Hardened windows builds spawns three processes when a VM is
3406 launched, the 3rd one is the one that will end up here. */
3407 RTPROCESS ppid;
3408 int rc = RTProcQueryParent(pid, &ppid);
3409 if (RT_SUCCESS(rc))
3410 rc = RTProcQueryParent(ppid, &ppid);
3411 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3412 || rc == VERR_ACCESS_DENIED)
3413 {
3414 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3415 mData->mSession.mPID = pid;
3416 }
3417#endif
3418
3419 if (mData->mSession.mPID != pid)
3420 return setError(E_ACCESSDENIED,
3421 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3422 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3423 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3424 }
3425
3426 // create the mutable SessionMachine from the current machine
3427 ComObjPtr<SessionMachine> sessionMachine;
3428 sessionMachine.createObject();
3429 rc = sessionMachine->init(this);
3430 AssertComRC(rc);
3431
3432 /* NOTE: doing return from this function after this point but
3433 * before the end is forbidden since it may call SessionMachine::uninit()
3434 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3435 * lock while still holding the Machine lock in alock so that a deadlock
3436 * is possible due to the wrong lock order. */
3437
3438 if (SUCCEEDED(rc))
3439 {
3440 /*
3441 * Set the session state to Spawning to protect against subsequent
3442 * attempts to open a session and to unregister the machine after
3443 * we release the lock.
3444 */
3445 SessionState_T origState = mData->mSession.mState;
3446 mData->mSession.mState = SessionState_Spawning;
3447
3448#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3449 /* Get the client token ID to be passed to the client process */
3450 Utf8Str strTokenId;
3451 sessionMachine->i_getTokenId(strTokenId);
3452 Assert(!strTokenId.isEmpty());
3453#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3454 /* Get the client token to be passed to the client process */
3455 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3456 /* The token is now "owned" by pToken, fix refcount */
3457 if (!pToken.isNull())
3458 pToken->Release();
3459#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3460
3461 /*
3462 * Release the lock before calling the client process -- it will call
3463 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3464 * because the state is Spawning, so that LaunchVMProcess() and
3465 * LockMachine() calls will fail. This method, called before we
3466 * acquire the lock again, will fail because of the wrong PID.
3467 *
3468 * Note that mData->mSession.mRemoteControls accessed outside
3469 * the lock may not be modified when state is Spawning, so it's safe.
3470 */
3471 alock.release();
3472
3473 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3474#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3475 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3476#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3477 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3478 /* Now the token is owned by the client process. */
3479 pToken.setNull();
3480#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3481 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3482
3483 /* The failure may occur w/o any error info (from RPC), so provide one */
3484 if (FAILED(rc))
3485 setError(VBOX_E_VM_ERROR,
3486 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3487
3488 // get session name, either to remember or to compare against
3489 // the already known session name.
3490 {
3491 Bstr bstrSessionName;
3492 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3493 if (SUCCEEDED(rc2))
3494 strSessionName = bstrSessionName;
3495 }
3496
3497 if ( SUCCEEDED(rc)
3498 && fLaunchingVMProcess
3499 )
3500 {
3501 /* complete the remote session initialization */
3502
3503 /* get the console from the direct session */
3504 ComPtr<IConsole> console;
3505 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3506 ComAssertComRC(rc);
3507
3508 if (SUCCEEDED(rc) && !console)
3509 {
3510 ComAssert(!!console);
3511 rc = E_FAIL;
3512 }
3513
3514 /* assign machine & console to the remote session */
3515 if (SUCCEEDED(rc))
3516 {
3517 /*
3518 * after LaunchVMProcess(), the first and the only
3519 * entry in remoteControls is that remote session
3520 */
3521 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3522 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3523 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3524
3525 /* The failure may occur w/o any error info (from RPC), so provide one */
3526 if (FAILED(rc))
3527 setError(VBOX_E_VM_ERROR,
3528 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3529 }
3530
3531 if (FAILED(rc))
3532 pSessionControl->Uninitialize();
3533 }
3534
3535 /* acquire the lock again */
3536 alock.acquire();
3537
3538 /* Restore the session state */
3539 mData->mSession.mState = origState;
3540 }
3541
3542 // finalize spawning anyway (this is why we don't return on errors above)
3543 if (fLaunchingVMProcess)
3544 {
3545 Assert(mData->mSession.mName == strSessionName);
3546 /* Note that the progress object is finalized later */
3547 /** @todo Consider checking mData->mSession.mProgress for cancellation
3548 * around here. */
3549
3550 /* We don't reset mSession.mPID here because it is necessary for
3551 * SessionMachine::uninit() to reap the child process later. */
3552
3553 if (FAILED(rc))
3554 {
3555 /* Close the remote session, remove the remote control from the list
3556 * and reset session state to Closed (@note keep the code in sync
3557 * with the relevant part in checkForSpawnFailure()). */
3558
3559 Assert(mData->mSession.mRemoteControls.size() == 1);
3560 if (mData->mSession.mRemoteControls.size() == 1)
3561 {
3562 ErrorInfoKeeper eik;
3563 mData->mSession.mRemoteControls.front()->Uninitialize();
3564 }
3565
3566 mData->mSession.mRemoteControls.clear();
3567 mData->mSession.mState = SessionState_Unlocked;
3568 }
3569 }
3570 else
3571 {
3572 /* memorize PID of the directly opened session */
3573 if (SUCCEEDED(rc))
3574 mData->mSession.mPID = pid;
3575 }
3576
3577 if (SUCCEEDED(rc))
3578 {
3579 mData->mSession.mLockType = aLockType;
3580 /* memorize the direct session control and cache IUnknown for it */
3581 mData->mSession.mDirectControl = pSessionControl;
3582 mData->mSession.mState = SessionState_Locked;
3583 if (!fLaunchingVMProcess)
3584 mData->mSession.mName = strSessionName;
3585 /* associate the SessionMachine with this Machine */
3586 mData->mSession.mMachine = sessionMachine;
3587
3588 /* request an IUnknown pointer early from the remote party for later
3589 * identity checks (it will be internally cached within mDirectControl
3590 * at least on XPCOM) */
3591 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3592 NOREF(unk);
3593 }
3594
3595 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3596 * would break the lock order */
3597 alock.release();
3598
3599 /* uninitialize the created session machine on failure */
3600 if (FAILED(rc))
3601 sessionMachine->uninit();
3602 }
3603
3604 if (SUCCEEDED(rc))
3605 {
3606 /*
3607 * tell the client watcher thread to update the set of
3608 * machines that have open sessions
3609 */
3610 mParent->i_updateClientWatcher();
3611
3612 if (oldState != SessionState_Locked)
3613 /* fire an event */
3614 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3615 }
3616
3617 return rc;
3618}
3619
3620/**
3621 * @note Locks objects!
3622 */
3623HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3624 const com::Utf8Str &aName,
3625 const com::Utf8Str &aEnvironment,
3626 ComPtr<IProgress> &aProgress)
3627{
3628 Utf8Str strFrontend(aName);
3629 /* "emergencystop" doesn't need the session, so skip the checks/interface
3630 * retrieval. This code doesn't quite fit in here, but introducing a
3631 * special API method would be even more effort, and would require explicit
3632 * support by every API client. It's better to hide the feature a bit. */
3633 if (strFrontend != "emergencystop")
3634 CheckComArgNotNull(aSession);
3635
3636 HRESULT rc = S_OK;
3637 if (strFrontend.isEmpty())
3638 {
3639 Bstr bstrFrontend;
3640 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3641 if (FAILED(rc))
3642 return rc;
3643 strFrontend = bstrFrontend;
3644 if (strFrontend.isEmpty())
3645 {
3646 ComPtr<ISystemProperties> systemProperties;
3647 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3648 if (FAILED(rc))
3649 return rc;
3650 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3651 if (FAILED(rc))
3652 return rc;
3653 strFrontend = bstrFrontend;
3654 }
3655 /* paranoia - emergencystop is not a valid default */
3656 if (strFrontend == "emergencystop")
3657 strFrontend = Utf8Str::Empty;
3658 }
3659 /* default frontend: Qt GUI */
3660 if (strFrontend.isEmpty())
3661 strFrontend = "GUI/Qt";
3662
3663 if (strFrontend != "emergencystop")
3664 {
3665 /* check the session state */
3666 SessionState_T state;
3667 rc = aSession->COMGETTER(State)(&state);
3668 if (FAILED(rc))
3669 return rc;
3670
3671 if (state != SessionState_Unlocked)
3672 return setError(VBOX_E_INVALID_OBJECT_STATE,
3673 tr("The given session is busy"));
3674
3675 /* get the IInternalSessionControl interface */
3676 ComPtr<IInternalSessionControl> control(aSession);
3677 ComAssertMsgRet(!control.isNull(),
3678 ("No IInternalSessionControl interface"),
3679 E_INVALIDARG);
3680
3681 /* get the teleporter enable state for the progress object init. */
3682 BOOL fTeleporterEnabled;
3683 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3684 if (FAILED(rc))
3685 return rc;
3686
3687 /* create a progress object */
3688 ComObjPtr<ProgressProxy> progress;
3689 progress.createObject();
3690 rc = progress->init(mParent,
3691 static_cast<IMachine*>(this),
3692 Bstr(tr("Starting VM")).raw(),
3693 TRUE /* aCancelable */,
3694 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3695 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3696 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3697 2 /* uFirstOperationWeight */,
3698 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3699
3700 if (SUCCEEDED(rc))
3701 {
3702 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3703 if (SUCCEEDED(rc))
3704 {
3705 aProgress = progress;
3706
3707 /* signal the client watcher thread */
3708 mParent->i_updateClientWatcher();
3709
3710 /* fire an event */
3711 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3712 }
3713 }
3714 }
3715 else
3716 {
3717 /* no progress object - either instant success or failure */
3718 aProgress = NULL;
3719
3720 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3721
3722 if (mData->mSession.mState != SessionState_Locked)
3723 return setError(VBOX_E_INVALID_OBJECT_STATE,
3724 tr("The machine '%s' is not locked by a session"),
3725 mUserData->s.strName.c_str());
3726
3727 /* must have a VM process associated - do not kill normal API clients
3728 * with an open session */
3729 if (!Global::IsOnline(mData->mMachineState))
3730 return setError(VBOX_E_INVALID_OBJECT_STATE,
3731 tr("The machine '%s' does not have a VM process"),
3732 mUserData->s.strName.c_str());
3733
3734 /* forcibly terminate the VM process */
3735 if (mData->mSession.mPID != NIL_RTPROCESS)
3736 RTProcTerminate(mData->mSession.mPID);
3737
3738 /* signal the client watcher thread, as most likely the client has
3739 * been terminated */
3740 mParent->i_updateClientWatcher();
3741 }
3742
3743 return rc;
3744}
3745
3746HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3747{
3748 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3749 return setError(E_INVALIDARG,
3750 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3751 aPosition, SchemaDefs::MaxBootPosition);
3752
3753 if (aDevice == DeviceType_USB)
3754 return setError(E_NOTIMPL,
3755 tr("Booting from USB device is currently not supported"));
3756
3757 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3758
3759 HRESULT rc = i_checkStateDependency(MutableStateDep);
3760 if (FAILED(rc)) return rc;
3761
3762 i_setModified(IsModified_MachineData);
3763 mHWData.backup();
3764 mHWData->mBootOrder[aPosition - 1] = aDevice;
3765
3766 return S_OK;
3767}
3768
3769HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3770{
3771 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3772 return setError(E_INVALIDARG,
3773 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3774 aPosition, SchemaDefs::MaxBootPosition);
3775
3776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3777
3778 *aDevice = mHWData->mBootOrder[aPosition - 1];
3779
3780 return S_OK;
3781}
3782
3783HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3784 LONG aControllerPort,
3785 LONG aDevice,
3786 DeviceType_T aType,
3787 const ComPtr<IMedium> &aMedium)
3788{
3789 IMedium *aM = aMedium;
3790 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3791 aName.c_str(), aControllerPort, aDevice, aType, aM));
3792
3793 // request the host lock first, since might be calling Host methods for getting host drives;
3794 // next, protect the media tree all the while we're in here, as well as our member variables
3795 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3796 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3797
3798 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3799 if (FAILED(rc)) return rc;
3800
3801 /// @todo NEWMEDIA implicit machine registration
3802 if (!mData->mRegistered)
3803 return setError(VBOX_E_INVALID_OBJECT_STATE,
3804 tr("Cannot attach storage devices to an unregistered machine"));
3805
3806 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3807
3808 /* Check for an existing controller. */
3809 ComObjPtr<StorageController> ctl;
3810 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3811 if (FAILED(rc)) return rc;
3812
3813 StorageControllerType_T ctrlType;
3814 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3815 if (FAILED(rc))
3816 return setError(E_FAIL,
3817 tr("Could not get type of controller '%s'"),
3818 aName.c_str());
3819
3820 bool fSilent = false;
3821 Utf8Str strReconfig;
3822
3823 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3824 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3825 if ( mData->mMachineState == MachineState_Paused
3826 && strReconfig == "1")
3827 fSilent = true;
3828
3829 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3830 bool fHotplug = false;
3831 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3832 fHotplug = true;
3833
3834 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3835 return setError(VBOX_E_INVALID_VM_STATE,
3836 tr("Controller '%s' does not support hotplugging"),
3837 aName.c_str());
3838
3839 // check that the port and device are not out of range
3840 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3841 if (FAILED(rc)) return rc;
3842
3843 /* check if the device slot is already busy */
3844 MediumAttachment *pAttachTemp;
3845 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3846 Bstr(aName).raw(),
3847 aControllerPort,
3848 aDevice)))
3849 {
3850 Medium *pMedium = pAttachTemp->i_getMedium();
3851 if (pMedium)
3852 {
3853 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3854 return setError(VBOX_E_OBJECT_IN_USE,
3855 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3856 pMedium->i_getLocationFull().c_str(),
3857 aControllerPort,
3858 aDevice,
3859 aName.c_str());
3860 }
3861 else
3862 return setError(VBOX_E_OBJECT_IN_USE,
3863 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3864 aControllerPort, aDevice, aName.c_str());
3865 }
3866
3867 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3868 if (aMedium && medium.isNull())
3869 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3870
3871 AutoCaller mediumCaller(medium);
3872 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3873
3874 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3875
3876 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3877 && !medium.isNull()
3878 )
3879 return setError(VBOX_E_OBJECT_IN_USE,
3880 tr("Medium '%s' is already attached to this virtual machine"),
3881 medium->i_getLocationFull().c_str());
3882
3883 if (!medium.isNull())
3884 {
3885 MediumType_T mtype = medium->i_getType();
3886 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3887 // For DVDs it's not written to the config file, so needs no global config
3888 // version bump. For floppies it's a new attribute "type", which is ignored
3889 // by older VirtualBox version, so needs no global config version bump either.
3890 // For hard disks this type is not accepted.
3891 if (mtype == MediumType_MultiAttach)
3892 {
3893 // This type is new with VirtualBox 4.0 and therefore requires settings
3894 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3895 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3896 // two reasons: The medium type is a property of the media registry tree, which
3897 // can reside in the global config file (for pre-4.0 media); we would therefore
3898 // possibly need to bump the global config version. We don't want to do that though
3899 // because that might make downgrading to pre-4.0 impossible.
3900 // As a result, we can only use these two new types if the medium is NOT in the
3901 // global registry:
3902 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3903 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3904 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3905 )
3906 return setError(VBOX_E_INVALID_OBJECT_STATE,
3907 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3908 "to machines that were created with VirtualBox 4.0 or later"),
3909 medium->i_getLocationFull().c_str());
3910 }
3911 }
3912
3913 bool fIndirect = false;
3914 if (!medium.isNull())
3915 fIndirect = medium->i_isReadOnly();
3916 bool associate = true;
3917
3918 do
3919 {
3920 if ( aType == DeviceType_HardDisk
3921 && mMediaData.isBackedUp())
3922 {
3923 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3924
3925 /* check if the medium was attached to the VM before we started
3926 * changing attachments in which case the attachment just needs to
3927 * be restored */
3928 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3929 {
3930 AssertReturn(!fIndirect, E_FAIL);
3931
3932 /* see if it's the same bus/channel/device */
3933 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3934 {
3935 /* the simplest case: restore the whole attachment
3936 * and return, nothing else to do */
3937 mMediaData->mAttachments.push_back(pAttachTemp);
3938
3939 /* Reattach the medium to the VM. */
3940 if (fHotplug || fSilent)
3941 {
3942 mediumLock.release();
3943 treeLock.release();
3944 alock.release();
3945
3946 MediumLockList *pMediumLockList(new MediumLockList());
3947
3948 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3949 medium /* pToLockWrite */,
3950 false /* fMediumLockWriteAll */,
3951 NULL,
3952 *pMediumLockList);
3953 alock.acquire();
3954 if (FAILED(rc))
3955 delete pMediumLockList;
3956 else
3957 {
3958 mData->mSession.mLockedMedia.Unlock();
3959 alock.release();
3960 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3961 mData->mSession.mLockedMedia.Lock();
3962 alock.acquire();
3963 }
3964 alock.release();
3965
3966 if (SUCCEEDED(rc))
3967 {
3968 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3969 /* Remove lock list in case of error. */
3970 if (FAILED(rc))
3971 {
3972 mData->mSession.mLockedMedia.Unlock();
3973 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3974 mData->mSession.mLockedMedia.Lock();
3975 }
3976 }
3977 }
3978
3979 return S_OK;
3980 }
3981
3982 /* bus/channel/device differ; we need a new attachment object,
3983 * but don't try to associate it again */
3984 associate = false;
3985 break;
3986 }
3987 }
3988
3989 /* go further only if the attachment is to be indirect */
3990 if (!fIndirect)
3991 break;
3992
3993 /* perform the so called smart attachment logic for indirect
3994 * attachments. Note that smart attachment is only applicable to base
3995 * hard disks. */
3996
3997 if (medium->i_getParent().isNull())
3998 {
3999 /* first, investigate the backup copy of the current hard disk
4000 * attachments to make it possible to re-attach existing diffs to
4001 * another device slot w/o losing their contents */
4002 if (mMediaData.isBackedUp())
4003 {
4004 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4005
4006 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4007 uint32_t foundLevel = 0;
4008
4009 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
4010 {
4011 uint32_t level = 0;
4012 MediumAttachment *pAttach = *it;
4013 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4014 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4015 if (pMedium.isNull())
4016 continue;
4017
4018 if (pMedium->i_getBase(&level) == medium)
4019 {
4020 /* skip the hard disk if its currently attached (we
4021 * cannot attach the same hard disk twice) */
4022 if (i_findAttachment(mMediaData->mAttachments,
4023 pMedium))
4024 continue;
4025
4026 /* matched device, channel and bus (i.e. attached to the
4027 * same place) will win and immediately stop the search;
4028 * otherwise the attachment that has the youngest
4029 * descendant of medium will be used
4030 */
4031 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
4032 {
4033 /* the simplest case: restore the whole attachment
4034 * and return, nothing else to do */
4035 mMediaData->mAttachments.push_back(*it);
4036
4037 /* Reattach the medium to the VM. */
4038 if (fHotplug || fSilent)
4039 {
4040 mediumLock.release();
4041 treeLock.release();
4042 alock.release();
4043
4044 MediumLockList *pMediumLockList(new MediumLockList());
4045
4046 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4047 medium /* pToLockWrite */,
4048 false /* fMediumLockWriteAll */,
4049 NULL,
4050 *pMediumLockList);
4051 alock.acquire();
4052 if (FAILED(rc))
4053 delete pMediumLockList;
4054 else
4055 {
4056 mData->mSession.mLockedMedia.Unlock();
4057 alock.release();
4058 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4059 mData->mSession.mLockedMedia.Lock();
4060 alock.acquire();
4061 }
4062 alock.release();
4063
4064 if (SUCCEEDED(rc))
4065 {
4066 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4067 /* Remove lock list in case of error. */
4068 if (FAILED(rc))
4069 {
4070 mData->mSession.mLockedMedia.Unlock();
4071 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4072 mData->mSession.mLockedMedia.Lock();
4073 }
4074 }
4075 }
4076
4077 return S_OK;
4078 }
4079 else if ( foundIt == oldAtts.end()
4080 || level > foundLevel /* prefer younger */
4081 )
4082 {
4083 foundIt = it;
4084 foundLevel = level;
4085 }
4086 }
4087 }
4088
4089 if (foundIt != oldAtts.end())
4090 {
4091 /* use the previously attached hard disk */
4092 medium = (*foundIt)->i_getMedium();
4093 mediumCaller.attach(medium);
4094 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4095 mediumLock.attach(medium);
4096 /* not implicit, doesn't require association with this VM */
4097 fIndirect = false;
4098 associate = false;
4099 /* go right to the MediumAttachment creation */
4100 break;
4101 }
4102 }
4103
4104 /* must give up the medium lock and medium tree lock as below we
4105 * go over snapshots, which needs a lock with higher lock order. */
4106 mediumLock.release();
4107 treeLock.release();
4108
4109 /* then, search through snapshots for the best diff in the given
4110 * hard disk's chain to base the new diff on */
4111
4112 ComObjPtr<Medium> base;
4113 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4114 while (snap)
4115 {
4116 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4117
4118 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4119
4120 MediumAttachment *pAttachFound = NULL;
4121 uint32_t foundLevel = 0;
4122
4123 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4124 {
4125 MediumAttachment *pAttach = *it;
4126 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4127 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4128 if (pMedium.isNull())
4129 continue;
4130
4131 uint32_t level = 0;
4132 if (pMedium->i_getBase(&level) == medium)
4133 {
4134 /* matched device, channel and bus (i.e. attached to the
4135 * same place) will win and immediately stop the search;
4136 * otherwise the attachment that has the youngest
4137 * descendant of medium will be used
4138 */
4139 if ( pAttach->i_getDevice() == aDevice
4140 && pAttach->i_getPort() == aControllerPort
4141 && pAttach->i_getControllerName() == aName
4142 )
4143 {
4144 pAttachFound = pAttach;
4145 break;
4146 }
4147 else if ( !pAttachFound
4148 || level > foundLevel /* prefer younger */
4149 )
4150 {
4151 pAttachFound = pAttach;
4152 foundLevel = level;
4153 }
4154 }
4155 }
4156
4157 if (pAttachFound)
4158 {
4159 base = pAttachFound->i_getMedium();
4160 break;
4161 }
4162
4163 snap = snap->i_getParent();
4164 }
4165
4166 /* re-lock medium tree and the medium, as we need it below */
4167 treeLock.acquire();
4168 mediumLock.acquire();
4169
4170 /* found a suitable diff, use it as a base */
4171 if (!base.isNull())
4172 {
4173 medium = base;
4174 mediumCaller.attach(medium);
4175 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4176 mediumLock.attach(medium);
4177 }
4178 }
4179
4180 Utf8Str strFullSnapshotFolder;
4181 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4182
4183 ComObjPtr<Medium> diff;
4184 diff.createObject();
4185 // store this diff in the same registry as the parent
4186 Guid uuidRegistryParent;
4187 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4188 {
4189 // parent image has no registry: this can happen if we're attaching a new immutable
4190 // image that has not yet been attached (medium then points to the base and we're
4191 // creating the diff image for the immutable, and the parent is not yet registered);
4192 // put the parent in the machine registry then
4193 mediumLock.release();
4194 treeLock.release();
4195 alock.release();
4196 i_addMediumToRegistry(medium);
4197 alock.acquire();
4198 treeLock.acquire();
4199 mediumLock.acquire();
4200 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4201 }
4202 rc = diff->init(mParent,
4203 medium->i_getPreferredDiffFormat(),
4204 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4205 uuidRegistryParent,
4206 DeviceType_HardDisk);
4207 if (FAILED(rc)) return rc;
4208
4209 /* Apply the normal locking logic to the entire chain. */
4210 MediumLockList *pMediumLockList(new MediumLockList());
4211 mediumLock.release();
4212 treeLock.release();
4213 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4214 diff /* pToLockWrite */,
4215 false /* fMediumLockWriteAll */,
4216 medium,
4217 *pMediumLockList);
4218 treeLock.acquire();
4219 mediumLock.acquire();
4220 if (SUCCEEDED(rc))
4221 {
4222 mediumLock.release();
4223 treeLock.release();
4224 rc = pMediumLockList->Lock();
4225 treeLock.acquire();
4226 mediumLock.acquire();
4227 if (FAILED(rc))
4228 setError(rc,
4229 tr("Could not lock medium when creating diff '%s'"),
4230 diff->i_getLocationFull().c_str());
4231 else
4232 {
4233 /* will release the lock before the potentially lengthy
4234 * operation, so protect with the special state */
4235 MachineState_T oldState = mData->mMachineState;
4236 i_setMachineState(MachineState_SettingUp);
4237
4238 mediumLock.release();
4239 treeLock.release();
4240 alock.release();
4241
4242 rc = medium->i_createDiffStorage(diff,
4243 medium->i_getPreferredDiffVariant(),
4244 pMediumLockList,
4245 NULL /* aProgress */,
4246 true /* aWait */);
4247
4248 alock.acquire();
4249 treeLock.acquire();
4250 mediumLock.acquire();
4251
4252 i_setMachineState(oldState);
4253 }
4254 }
4255
4256 /* Unlock the media and free the associated memory. */
4257 delete pMediumLockList;
4258
4259 if (FAILED(rc)) return rc;
4260
4261 /* use the created diff for the actual attachment */
4262 medium = diff;
4263 mediumCaller.attach(medium);
4264 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4265 mediumLock.attach(medium);
4266 }
4267 while (0);
4268
4269 ComObjPtr<MediumAttachment> attachment;
4270 attachment.createObject();
4271 rc = attachment->init(this,
4272 medium,
4273 aName,
4274 aControllerPort,
4275 aDevice,
4276 aType,
4277 fIndirect,
4278 false /* fPassthrough */,
4279 false /* fTempEject */,
4280 false /* fNonRotational */,
4281 false /* fDiscard */,
4282 fHotplug /* fHotPluggable */,
4283 Utf8Str::Empty);
4284 if (FAILED(rc)) return rc;
4285
4286 if (associate && !medium.isNull())
4287 {
4288 // as the last step, associate the medium to the VM
4289 rc = medium->i_addBackReference(mData->mUuid);
4290 // here we can fail because of Deleting, or being in process of creating a Diff
4291 if (FAILED(rc)) return rc;
4292
4293 mediumLock.release();
4294 treeLock.release();
4295 alock.release();
4296 i_addMediumToRegistry(medium);
4297 alock.acquire();
4298 treeLock.acquire();
4299 mediumLock.acquire();
4300 }
4301
4302 /* success: finally remember the attachment */
4303 i_setModified(IsModified_Storage);
4304 mMediaData.backup();
4305 mMediaData->mAttachments.push_back(attachment);
4306
4307 mediumLock.release();
4308 treeLock.release();
4309 alock.release();
4310
4311 if (fHotplug || fSilent)
4312 {
4313 if (!medium.isNull())
4314 {
4315 MediumLockList *pMediumLockList(new MediumLockList());
4316
4317 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4318 medium /* pToLockWrite */,
4319 false /* fMediumLockWriteAll */,
4320 NULL,
4321 *pMediumLockList);
4322 alock.acquire();
4323 if (FAILED(rc))
4324 delete pMediumLockList;
4325 else
4326 {
4327 mData->mSession.mLockedMedia.Unlock();
4328 alock.release();
4329 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4330 mData->mSession.mLockedMedia.Lock();
4331 alock.acquire();
4332 }
4333 alock.release();
4334 }
4335
4336 if (SUCCEEDED(rc))
4337 {
4338 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4339 /* Remove lock list in case of error. */
4340 if (FAILED(rc))
4341 {
4342 mData->mSession.mLockedMedia.Unlock();
4343 mData->mSession.mLockedMedia.Remove(attachment);
4344 mData->mSession.mLockedMedia.Lock();
4345 }
4346 }
4347 }
4348
4349 /* Save modified registries, but skip this machine as it's the caller's
4350 * job to save its settings like all other settings changes. */
4351 mParent->i_unmarkRegistryModified(i_getId());
4352 mParent->i_saveModifiedRegistries();
4353
4354 return rc;
4355}
4356
4357HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4358 LONG aDevice)
4359{
4360 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4361 aName.c_str(), aControllerPort, aDevice));
4362
4363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4364
4365 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4366 if (FAILED(rc)) return rc;
4367
4368 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4369
4370 /* Check for an existing controller. */
4371 ComObjPtr<StorageController> ctl;
4372 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4373 if (FAILED(rc)) return rc;
4374
4375 StorageControllerType_T ctrlType;
4376 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4377 if (FAILED(rc))
4378 return setError(E_FAIL,
4379 tr("Could not get type of controller '%s'"),
4380 aName.c_str());
4381
4382 bool fSilent = false;
4383 Utf8Str strReconfig;
4384
4385 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4386 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4387 if ( mData->mMachineState == MachineState_Paused
4388 && strReconfig == "1")
4389 fSilent = true;
4390
4391 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4392 bool fHotplug = false;
4393 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4394 fHotplug = true;
4395
4396 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4397 return setError(VBOX_E_INVALID_VM_STATE,
4398 tr("Controller '%s' does not support hotplugging"),
4399 aName.c_str());
4400
4401 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4402 Bstr(aName).raw(),
4403 aControllerPort,
4404 aDevice);
4405 if (!pAttach)
4406 return setError(VBOX_E_OBJECT_NOT_FOUND,
4407 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4408 aDevice, aControllerPort, aName.c_str());
4409
4410 if (fHotplug && !pAttach->i_getHotPluggable())
4411 return setError(VBOX_E_NOT_SUPPORTED,
4412 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4413 aDevice, aControllerPort, aName.c_str());
4414
4415 /*
4416 * The VM has to detach the device before we delete any implicit diffs.
4417 * If this fails we can roll back without loosing data.
4418 */
4419 if (fHotplug || fSilent)
4420 {
4421 alock.release();
4422 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4423 alock.acquire();
4424 }
4425 if (FAILED(rc)) return rc;
4426
4427 /* If we are here everything went well and we can delete the implicit now. */
4428 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4429
4430 alock.release();
4431
4432 /* Save modified registries, but skip this machine as it's the caller's
4433 * job to save its settings like all other settings changes. */
4434 mParent->i_unmarkRegistryModified(i_getId());
4435 mParent->i_saveModifiedRegistries();
4436
4437 return rc;
4438}
4439
4440HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4441 LONG aDevice, BOOL aPassthrough)
4442{
4443 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4444 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4445
4446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4447
4448 HRESULT rc = i_checkStateDependency(MutableStateDep);
4449 if (FAILED(rc)) return rc;
4450
4451 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4452
4453 if (Global::IsOnlineOrTransient(mData->mMachineState))
4454 return setError(VBOX_E_INVALID_VM_STATE,
4455 tr("Invalid machine state: %s"),
4456 Global::stringifyMachineState(mData->mMachineState));
4457
4458 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4459 Bstr(aName).raw(),
4460 aControllerPort,
4461 aDevice);
4462 if (!pAttach)
4463 return setError(VBOX_E_OBJECT_NOT_FOUND,
4464 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4465 aDevice, aControllerPort, aName.c_str());
4466
4467
4468 i_setModified(IsModified_Storage);
4469 mMediaData.backup();
4470
4471 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4472
4473 if (pAttach->i_getType() != DeviceType_DVD)
4474 return setError(E_INVALIDARG,
4475 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4476 aDevice, aControllerPort, aName.c_str());
4477 pAttach->i_updatePassthrough(!!aPassthrough);
4478
4479 return S_OK;
4480}
4481
4482HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4483 LONG aDevice, BOOL aTemporaryEject)
4484{
4485
4486 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4487 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4488
4489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4490
4491 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4492 if (FAILED(rc)) return rc;
4493
4494 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4495 Bstr(aName).raw(),
4496 aControllerPort,
4497 aDevice);
4498 if (!pAttach)
4499 return setError(VBOX_E_OBJECT_NOT_FOUND,
4500 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4501 aDevice, aControllerPort, aName.c_str());
4502
4503
4504 i_setModified(IsModified_Storage);
4505 mMediaData.backup();
4506
4507 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4508
4509 if (pAttach->i_getType() != DeviceType_DVD)
4510 return setError(E_INVALIDARG,
4511 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4512 aDevice, aControllerPort, aName.c_str());
4513 pAttach->i_updateTempEject(!!aTemporaryEject);
4514
4515 return S_OK;
4516}
4517
4518HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4519 LONG aDevice, BOOL aNonRotational)
4520{
4521
4522 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4523 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4524
4525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4526
4527 HRESULT rc = i_checkStateDependency(MutableStateDep);
4528 if (FAILED(rc)) return rc;
4529
4530 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4531
4532 if (Global::IsOnlineOrTransient(mData->mMachineState))
4533 return setError(VBOX_E_INVALID_VM_STATE,
4534 tr("Invalid machine state: %s"),
4535 Global::stringifyMachineState(mData->mMachineState));
4536
4537 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4538 Bstr(aName).raw(),
4539 aControllerPort,
4540 aDevice);
4541 if (!pAttach)
4542 return setError(VBOX_E_OBJECT_NOT_FOUND,
4543 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4544 aDevice, aControllerPort, aName.c_str());
4545
4546
4547 i_setModified(IsModified_Storage);
4548 mMediaData.backup();
4549
4550 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4551
4552 if (pAttach->i_getType() != DeviceType_HardDisk)
4553 return setError(E_INVALIDARG,
4554 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"),
4555 aDevice, aControllerPort, aName.c_str());
4556 pAttach->i_updateNonRotational(!!aNonRotational);
4557
4558 return S_OK;
4559}
4560
4561HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4562 LONG aDevice, BOOL aDiscard)
4563{
4564
4565 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4566 aName.c_str(), aControllerPort, aDevice, aDiscard));
4567
4568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4569
4570 HRESULT rc = i_checkStateDependency(MutableStateDep);
4571 if (FAILED(rc)) return rc;
4572
4573 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4574
4575 if (Global::IsOnlineOrTransient(mData->mMachineState))
4576 return setError(VBOX_E_INVALID_VM_STATE,
4577 tr("Invalid machine state: %s"),
4578 Global::stringifyMachineState(mData->mMachineState));
4579
4580 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4581 Bstr(aName).raw(),
4582 aControllerPort,
4583 aDevice);
4584 if (!pAttach)
4585 return setError(VBOX_E_OBJECT_NOT_FOUND,
4586 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4587 aDevice, aControllerPort, aName.c_str());
4588
4589
4590 i_setModified(IsModified_Storage);
4591 mMediaData.backup();
4592
4593 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4594
4595 if (pAttach->i_getType() != DeviceType_HardDisk)
4596 return setError(E_INVALIDARG,
4597 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"),
4598 aDevice, aControllerPort, aName.c_str());
4599 pAttach->i_updateDiscard(!!aDiscard);
4600
4601 return S_OK;
4602}
4603
4604HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4605 LONG aDevice, BOOL aHotPluggable)
4606{
4607 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4608 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4609
4610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4611
4612 HRESULT rc = i_checkStateDependency(MutableStateDep);
4613 if (FAILED(rc)) return rc;
4614
4615 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4616
4617 if (Global::IsOnlineOrTransient(mData->mMachineState))
4618 return setError(VBOX_E_INVALID_VM_STATE,
4619 tr("Invalid machine state: %s"),
4620 Global::stringifyMachineState(mData->mMachineState));
4621
4622 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4623 Bstr(aName).raw(),
4624 aControllerPort,
4625 aDevice);
4626 if (!pAttach)
4627 return setError(VBOX_E_OBJECT_NOT_FOUND,
4628 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4629 aDevice, aControllerPort, aName.c_str());
4630
4631 /* Check for an existing controller. */
4632 ComObjPtr<StorageController> ctl;
4633 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4634 if (FAILED(rc)) return rc;
4635
4636 StorageControllerType_T ctrlType;
4637 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4638 if (FAILED(rc))
4639 return setError(E_FAIL,
4640 tr("Could not get type of controller '%s'"),
4641 aName.c_str());
4642
4643 if (!i_isControllerHotplugCapable(ctrlType))
4644 return setError(VBOX_E_NOT_SUPPORTED,
4645 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4646 aName.c_str());
4647
4648 i_setModified(IsModified_Storage);
4649 mMediaData.backup();
4650
4651 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4652
4653 if (pAttach->i_getType() == DeviceType_Floppy)
4654 return setError(E_INVALIDARG,
4655 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"),
4656 aDevice, aControllerPort, aName.c_str());
4657 pAttach->i_updateHotPluggable(!!aHotPluggable);
4658
4659 return S_OK;
4660}
4661
4662HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4663 LONG aDevice)
4664{
4665 int rc = S_OK;
4666 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4667 aName.c_str(), aControllerPort, aDevice));
4668
4669 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4670
4671 return rc;
4672}
4673
4674HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4675 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4676{
4677 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4678 aName.c_str(), aControllerPort, aDevice));
4679
4680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4681
4682 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4683 if (FAILED(rc)) return rc;
4684
4685 if (Global::IsOnlineOrTransient(mData->mMachineState))
4686 return setError(VBOX_E_INVALID_VM_STATE,
4687 tr("Invalid machine state: %s"),
4688 Global::stringifyMachineState(mData->mMachineState));
4689
4690 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4691 Bstr(aName).raw(),
4692 aControllerPort,
4693 aDevice);
4694 if (!pAttach)
4695 return setError(VBOX_E_OBJECT_NOT_FOUND,
4696 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4697 aDevice, aControllerPort, aName.c_str());
4698
4699
4700 i_setModified(IsModified_Storage);
4701 mMediaData.backup();
4702
4703 IBandwidthGroup *iB = aBandwidthGroup;
4704 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4705 if (aBandwidthGroup && group.isNull())
4706 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4707
4708 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4709
4710 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4711 if (strBandwidthGroupOld.isNotEmpty())
4712 {
4713 /* Get the bandwidth group object and release it - this must not fail. */
4714 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4715 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4716 Assert(SUCCEEDED(rc));
4717
4718 pBandwidthGroupOld->i_release();
4719 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4720 }
4721
4722 if (!group.isNull())
4723 {
4724 group->i_reference();
4725 pAttach->i_updateBandwidthGroup(group->i_getName());
4726 }
4727
4728 return S_OK;
4729}
4730
4731HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4732 LONG aControllerPort,
4733 LONG aDevice,
4734 DeviceType_T aType)
4735{
4736 HRESULT rc = S_OK;
4737
4738 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4739 aName.c_str(), aControllerPort, aDevice, aType));
4740
4741 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4742
4743 return rc;
4744}
4745
4746
4747HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4748 LONG aControllerPort,
4749 LONG aDevice,
4750 BOOL aForce)
4751{
4752 int rc = S_OK;
4753 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4754 aName.c_str(), aControllerPort, aForce));
4755
4756 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4757
4758 return rc;
4759}
4760
4761HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4762 LONG aControllerPort,
4763 LONG aDevice,
4764 const ComPtr<IMedium> &aMedium,
4765 BOOL aForce)
4766{
4767 int rc = S_OK;
4768 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4769 aName.c_str(), aControllerPort, aDevice, aForce));
4770
4771 // request the host lock first, since might be calling Host methods for getting host drives;
4772 // next, protect the media tree all the while we're in here, as well as our member variables
4773 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4774 this->lockHandle(),
4775 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4776
4777 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4778 Bstr(aName).raw(),
4779 aControllerPort,
4780 aDevice);
4781 if (pAttach.isNull())
4782 return setError(VBOX_E_OBJECT_NOT_FOUND,
4783 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4784 aDevice, aControllerPort, aName.c_str());
4785
4786 /* Remember previously mounted medium. The medium before taking the
4787 * backup is not necessarily the same thing. */
4788 ComObjPtr<Medium> oldmedium;
4789 oldmedium = pAttach->i_getMedium();
4790
4791 IMedium *iM = aMedium;
4792 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4793 if (aMedium && pMedium.isNull())
4794 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4795
4796 AutoCaller mediumCaller(pMedium);
4797 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4798
4799 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4800 if (pMedium)
4801 {
4802 DeviceType_T mediumType = pAttach->i_getType();
4803 switch (mediumType)
4804 {
4805 case DeviceType_DVD:
4806 case DeviceType_Floppy:
4807 break;
4808
4809 default:
4810 return setError(VBOX_E_INVALID_OBJECT_STATE,
4811 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4812 aControllerPort,
4813 aDevice,
4814 aName.c_str());
4815 }
4816 }
4817
4818 i_setModified(IsModified_Storage);
4819 mMediaData.backup();
4820
4821 {
4822 // The backup operation makes the pAttach reference point to the
4823 // old settings. Re-get the correct reference.
4824 pAttach = i_findAttachment(mMediaData->mAttachments,
4825 Bstr(aName).raw(),
4826 aControllerPort,
4827 aDevice);
4828 if (!oldmedium.isNull())
4829 oldmedium->i_removeBackReference(mData->mUuid);
4830 if (!pMedium.isNull())
4831 {
4832 pMedium->i_addBackReference(mData->mUuid);
4833
4834 mediumLock.release();
4835 multiLock.release();
4836 i_addMediumToRegistry(pMedium);
4837 multiLock.acquire();
4838 mediumLock.acquire();
4839 }
4840
4841 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4842 pAttach->i_updateMedium(pMedium);
4843 }
4844
4845 i_setModified(IsModified_Storage);
4846
4847 mediumLock.release();
4848 multiLock.release();
4849 rc = i_onMediumChange(pAttach, aForce);
4850 multiLock.acquire();
4851 mediumLock.acquire();
4852
4853 /* On error roll back this change only. */
4854 if (FAILED(rc))
4855 {
4856 if (!pMedium.isNull())
4857 pMedium->i_removeBackReference(mData->mUuid);
4858 pAttach = i_findAttachment(mMediaData->mAttachments,
4859 Bstr(aName).raw(),
4860 aControllerPort,
4861 aDevice);
4862 /* If the attachment is gone in the meantime, bail out. */
4863 if (pAttach.isNull())
4864 return rc;
4865 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4866 if (!oldmedium.isNull())
4867 oldmedium->i_addBackReference(mData->mUuid);
4868 pAttach->i_updateMedium(oldmedium);
4869 }
4870
4871 mediumLock.release();
4872 multiLock.release();
4873
4874 /* Save modified registries, but skip this machine as it's the caller's
4875 * job to save its settings like all other settings changes. */
4876 mParent->i_unmarkRegistryModified(i_getId());
4877 mParent->i_saveModifiedRegistries();
4878
4879 return rc;
4880}
4881HRESULT Machine::getMedium(const com::Utf8Str &aName,
4882 LONG aControllerPort,
4883 LONG aDevice,
4884 ComPtr<IMedium> &aMedium)
4885{
4886 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4887 aName.c_str(), aControllerPort, aDevice));
4888
4889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4890
4891 aMedium = NULL;
4892
4893 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4894 Bstr(aName).raw(),
4895 aControllerPort,
4896 aDevice);
4897 if (pAttach.isNull())
4898 return setError(VBOX_E_OBJECT_NOT_FOUND,
4899 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4900 aDevice, aControllerPort, aName.c_str());
4901
4902 aMedium = pAttach->i_getMedium();
4903
4904 return S_OK;
4905}
4906
4907HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4908{
4909
4910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4911
4912 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4913
4914 return S_OK;
4915}
4916
4917HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4918{
4919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4920
4921 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4922
4923 return S_OK;
4924}
4925
4926HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4927{
4928 /* Do not assert if slot is out of range, just return the advertised
4929 status. testdriver/vbox.py triggers this in logVmInfo. */
4930 if (aSlot >= mNetworkAdapters.size())
4931 return setError(E_INVALIDARG,
4932 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4933 aSlot, mNetworkAdapters.size());
4934
4935 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4936
4937 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4938
4939 return S_OK;
4940}
4941
4942HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4943{
4944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4945
4946 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4947 size_t i = 0;
4948 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4949 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4950 ++it, ++i)
4951 aKeys[i] = it->first;
4952
4953 return S_OK;
4954}
4955
4956 /**
4957 * @note Locks this object for reading.
4958 */
4959HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4960 com::Utf8Str &aValue)
4961{
4962 /* start with nothing found */
4963 aValue = "";
4964
4965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4966
4967 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4968 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4969 // found:
4970 aValue = it->second; // source is a Utf8Str
4971
4972 /* return the result to caller (may be empty) */
4973 return S_OK;
4974}
4975
4976 /**
4977 * @note Locks mParent for writing + this object for writing.
4978 */
4979HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4980{
4981 Utf8Str strOldValue; // empty
4982
4983 // locking note: we only hold the read lock briefly to look up the old value,
4984 // then release it and call the onExtraCanChange callbacks. There is a small
4985 // chance of a race insofar as the callback might be called twice if two callers
4986 // change the same key at the same time, but that's a much better solution
4987 // than the deadlock we had here before. The actual changing of the extradata
4988 // is then performed under the write lock and race-free.
4989
4990 // look up the old value first; if nothing has changed then we need not do anything
4991 {
4992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4993
4994 // For snapshots don't even think about allowing changes, extradata
4995 // is global for a machine, so there is nothing snapshot specific.
4996 if (i_isSnapshotMachine())
4997 return setError(VBOX_E_INVALID_VM_STATE,
4998 tr("Cannot set extradata for a snapshot"));
4999
5000 // check if the right IMachine instance is used
5001 if (mData->mRegistered && !i_isSessionMachine())
5002 return setError(VBOX_E_INVALID_VM_STATE,
5003 tr("Cannot set extradata for an immutable machine"));
5004
5005 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5006 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5007 strOldValue = it->second;
5008 }
5009
5010 bool fChanged;
5011 if ((fChanged = (strOldValue != aValue)))
5012 {
5013 // ask for permission from all listeners outside the locks;
5014 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5015 // lock to copy the list of callbacks to invoke
5016 Bstr error;
5017 Bstr bstrValue(aValue);
5018
5019 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5020 {
5021 const char *sep = error.isEmpty() ? "" : ": ";
5022 CBSTR err = error.raw();
5023 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5024 return setError(E_ACCESSDENIED,
5025 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5026 aKey.c_str(),
5027 aValue.c_str(),
5028 sep,
5029 err);
5030 }
5031
5032 // data is changing and change not vetoed: then write it out under the lock
5033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5034
5035 if (aValue.isEmpty())
5036 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5037 else
5038 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5039 // creates a new key if needed
5040
5041 bool fNeedsGlobalSaveSettings = false;
5042 // This saving of settings is tricky: there is no "old state" for the
5043 // extradata items at all (unlike all other settings), so the old/new
5044 // settings comparison would give a wrong result!
5045 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5046
5047 if (fNeedsGlobalSaveSettings)
5048 {
5049 // save the global settings; for that we should hold only the VirtualBox lock
5050 alock.release();
5051 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5052 mParent->i_saveSettings();
5053 }
5054 }
5055
5056 // fire notification outside the lock
5057 if (fChanged)
5058 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5059
5060 return S_OK;
5061}
5062
5063HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5064{
5065 aProgress = NULL;
5066 NOREF(aSettingsFilePath);
5067 ReturnComNotImplemented();
5068}
5069
5070HRESULT Machine::saveSettings()
5071{
5072 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5073
5074 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5075 if (FAILED(rc)) return rc;
5076
5077 /* the settings file path may never be null */
5078 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5079
5080 /* save all VM data excluding snapshots */
5081 bool fNeedsGlobalSaveSettings = false;
5082 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5083 mlock.release();
5084
5085 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5086 {
5087 // save the global settings; for that we should hold only the VirtualBox lock
5088 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5089 rc = mParent->i_saveSettings();
5090 }
5091
5092 return rc;
5093}
5094
5095
5096HRESULT Machine::discardSettings()
5097{
5098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5099
5100 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5101 if (FAILED(rc)) return rc;
5102
5103 /*
5104 * during this rollback, the session will be notified if data has
5105 * been actually changed
5106 */
5107 i_rollback(true /* aNotify */);
5108
5109 return S_OK;
5110}
5111
5112/** @note Locks objects! */
5113HRESULT Machine::unregister(AutoCaller &autoCaller,
5114 CleanupMode_T aCleanupMode,
5115 std::vector<ComPtr<IMedium> > &aMedia)
5116{
5117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5118
5119 Guid id(i_getId());
5120
5121 if (mData->mSession.mState != SessionState_Unlocked)
5122 return setError(VBOX_E_INVALID_OBJECT_STATE,
5123 tr("Cannot unregister the machine '%s' while it is locked"),
5124 mUserData->s.strName.c_str());
5125
5126 // wait for state dependents to drop to zero
5127 i_ensureNoStateDependencies();
5128
5129 if (!mData->mAccessible)
5130 {
5131 // inaccessible maschines can only be unregistered; uninitialize ourselves
5132 // here because currently there may be no unregistered that are inaccessible
5133 // (this state combination is not supported). Note releasing the caller and
5134 // leaving the lock before calling uninit()
5135 alock.release();
5136 autoCaller.release();
5137
5138 uninit();
5139
5140 mParent->i_unregisterMachine(this, id);
5141 // calls VirtualBox::i_saveSettings()
5142
5143 return S_OK;
5144 }
5145
5146 HRESULT rc = S_OK;
5147
5148 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5149 // discard saved state
5150 if (mData->mMachineState == MachineState_Saved)
5151 {
5152 // add the saved state file to the list of files the caller should delete
5153 Assert(!mSSData->strStateFilePath.isEmpty());
5154 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5155
5156 mSSData->strStateFilePath.setNull();
5157
5158 // unconditionally set the machine state to powered off, we now
5159 // know no session has locked the machine
5160 mData->mMachineState = MachineState_PoweredOff;
5161 }
5162
5163 size_t cSnapshots = 0;
5164 if (mData->mFirstSnapshot)
5165 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5166 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5167 // fail now before we start detaching media
5168 return setError(VBOX_E_INVALID_OBJECT_STATE,
5169 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5170 mUserData->s.strName.c_str(), cSnapshots);
5171
5172 // This list collects the medium objects from all medium attachments
5173 // which we will detach from the machine and its snapshots, in a specific
5174 // order which allows for closing all media without getting "media in use"
5175 // errors, simply by going through the list from the front to the back:
5176 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5177 // and must be closed before the parent media from the snapshots, or closing the parents
5178 // will fail because they still have children);
5179 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5180 // the root ("first") snapshot of the machine.
5181 MediaList llMedia;
5182
5183 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5184 && mMediaData->mAttachments.size()
5185 )
5186 {
5187 // we have media attachments: detach them all and add the Medium objects to our list
5188 if (aCleanupMode != CleanupMode_UnregisterOnly)
5189 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5190 else
5191 return setError(VBOX_E_INVALID_OBJECT_STATE,
5192 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5193 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5194 }
5195
5196 if (cSnapshots)
5197 {
5198 // add the media from the medium attachments of the snapshots to llMedia
5199 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5200 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5201 // into the children first
5202
5203 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5204 MachineState_T oldState = mData->mMachineState;
5205 mData->mMachineState = MachineState_DeletingSnapshot;
5206
5207 // make a copy of the first snapshot so the refcount does not drop to 0
5208 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5209 // because of the AutoCaller voodoo)
5210 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5211
5212 // GO!
5213 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5214
5215 mData->mMachineState = oldState;
5216 }
5217
5218 if (FAILED(rc))
5219 {
5220 i_rollbackMedia();
5221 return rc;
5222 }
5223
5224 // commit all the media changes made above
5225 i_commitMedia();
5226
5227 mData->mRegistered = false;
5228
5229 // machine lock no longer needed
5230 alock.release();
5231
5232 // return media to caller
5233 size_t i = 0;
5234 aMedia.resize(llMedia.size());
5235 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5236 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5237
5238 mParent->i_unregisterMachine(this, id);
5239 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5240
5241 return S_OK;
5242}
5243
5244/**
5245 * Task record for deleting a machine config.
5246 */
5247struct Machine::DeleteConfigTask
5248 : public Machine::Task
5249{
5250 DeleteConfigTask(Machine *m,
5251 Progress *p,
5252 const Utf8Str &t,
5253 const RTCList<ComPtr<IMedium> > &llMediums,
5254 const StringsList &llFilesToDelete)
5255 : Task(m, p, t),
5256 m_llMediums(llMediums),
5257 m_llFilesToDelete(llFilesToDelete)
5258 {}
5259
5260 void handler()
5261 {
5262 m_pMachine->i_deleteConfigHandler(*this);
5263 }
5264
5265 RTCList<ComPtr<IMedium> > m_llMediums;
5266 StringsList m_llFilesToDelete;
5267};
5268
5269/**
5270 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5271 * SessionMachine::taskHandler().
5272 *
5273 * @note Locks this object for writing.
5274 *
5275 * @param task
5276 * @return
5277 */
5278void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5279{
5280 LogFlowThisFuncEnter();
5281
5282 AutoCaller autoCaller(this);
5283 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5284 if (FAILED(autoCaller.rc()))
5285 {
5286 /* we might have been uninitialized because the session was accidentally
5287 * closed by the client, so don't assert */
5288 HRESULT rc = setError(E_FAIL,
5289 tr("The session has been accidentally closed"));
5290 task.m_pProgress->i_notifyComplete(rc);
5291 LogFlowThisFuncLeave();
5292 return;
5293 }
5294
5295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5296
5297 HRESULT rc = S_OK;
5298
5299 try
5300 {
5301 ULONG uLogHistoryCount = 3;
5302 ComPtr<ISystemProperties> systemProperties;
5303 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5304 if (FAILED(rc)) throw rc;
5305
5306 if (!systemProperties.isNull())
5307 {
5308 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5309 if (FAILED(rc)) throw rc;
5310 }
5311
5312 MachineState_T oldState = mData->mMachineState;
5313 i_setMachineState(MachineState_SettingUp);
5314 alock.release();
5315 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5316 {
5317 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5318 {
5319 AutoCaller mac(pMedium);
5320 if (FAILED(mac.rc())) throw mac.rc();
5321 Utf8Str strLocation = pMedium->i_getLocationFull();
5322 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5323 if (FAILED(rc)) throw rc;
5324 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5325 }
5326 if (pMedium->i_isMediumFormatFile())
5327 {
5328 ComPtr<IProgress> pProgress2;
5329 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5330 if (FAILED(rc)) throw rc;
5331 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5332 if (FAILED(rc)) throw rc;
5333 /* Check the result of the asynchronous process. */
5334 LONG iRc;
5335 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5336 if (FAILED(rc)) throw rc;
5337 /* If the thread of the progress object has an error, then
5338 * retrieve the error info from there, or it'll be lost. */
5339 if (FAILED(iRc))
5340 throw setError(ProgressErrorInfo(pProgress2));
5341 }
5342
5343 /* Close the medium, deliberately without checking the return
5344 * code, and without leaving any trace in the error info, as
5345 * a failure here is a very minor issue, which shouldn't happen
5346 * as above we even managed to delete the medium. */
5347 {
5348 ErrorInfoKeeper eik;
5349 pMedium->Close();
5350 }
5351 }
5352 i_setMachineState(oldState);
5353 alock.acquire();
5354
5355 // delete the files pushed on the task list by Machine::Delete()
5356 // (this includes saved states of the machine and snapshots and
5357 // medium storage files from the IMedium list passed in, and the
5358 // machine XML file)
5359 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5360 while (it != task.m_llFilesToDelete.end())
5361 {
5362 const Utf8Str &strFile = *it;
5363 LogFunc(("Deleting file %s\n", strFile.c_str()));
5364 int vrc = RTFileDelete(strFile.c_str());
5365 if (RT_FAILURE(vrc))
5366 throw setError(VBOX_E_IPRT_ERROR,
5367 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5368
5369 ++it;
5370 if (it == task.m_llFilesToDelete.end())
5371 {
5372 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5373 if (FAILED(rc)) throw rc;
5374 break;
5375 }
5376
5377 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5378 if (FAILED(rc)) throw rc;
5379 }
5380
5381 /* delete the settings only when the file actually exists */
5382 if (mData->pMachineConfigFile->fileExists())
5383 {
5384 /* Delete any backup or uncommitted XML files. Ignore failures.
5385 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5386 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5387 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5388 RTFileDelete(otherXml.c_str());
5389 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5390 RTFileDelete(otherXml.c_str());
5391
5392 /* delete the Logs folder, nothing important should be left
5393 * there (we don't check for errors because the user might have
5394 * some private files there that we don't want to delete) */
5395 Utf8Str logFolder;
5396 getLogFolder(logFolder);
5397 Assert(logFolder.length());
5398 if (RTDirExists(logFolder.c_str()))
5399 {
5400 /* Delete all VBox.log[.N] files from the Logs folder
5401 * (this must be in sync with the rotation logic in
5402 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5403 * files that may have been created by the GUI. */
5404 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5405 logFolder.c_str(), RTPATH_DELIMITER);
5406 RTFileDelete(log.c_str());
5407 log = Utf8StrFmt("%s%cVBox.png",
5408 logFolder.c_str(), RTPATH_DELIMITER);
5409 RTFileDelete(log.c_str());
5410 for (int i = uLogHistoryCount; i > 0; i--)
5411 {
5412 log = Utf8StrFmt("%s%cVBox.log.%d",
5413 logFolder.c_str(), RTPATH_DELIMITER, i);
5414 RTFileDelete(log.c_str());
5415 log = Utf8StrFmt("%s%cVBox.png.%d",
5416 logFolder.c_str(), RTPATH_DELIMITER, i);
5417 RTFileDelete(log.c_str());
5418 }
5419#if defined(RT_OS_WINDOWS)
5420 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5421 RTFileDelete(log.c_str());
5422 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5423 RTFileDelete(log.c_str());
5424#endif
5425
5426 RTDirRemove(logFolder.c_str());
5427 }
5428
5429 /* delete the Snapshots folder, nothing important should be left
5430 * there (we don't check for errors because the user might have
5431 * some private files there that we don't want to delete) */
5432 Utf8Str strFullSnapshotFolder;
5433 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5434 Assert(!strFullSnapshotFolder.isEmpty());
5435 if (RTDirExists(strFullSnapshotFolder.c_str()))
5436 RTDirRemove(strFullSnapshotFolder.c_str());
5437
5438 // delete the directory that contains the settings file, but only
5439 // if it matches the VM name
5440 Utf8Str settingsDir;
5441 if (i_isInOwnDir(&settingsDir))
5442 RTDirRemove(settingsDir.c_str());
5443 }
5444
5445 alock.release();
5446
5447 mParent->i_saveModifiedRegistries();
5448 }
5449 catch (HRESULT aRC) { rc = aRC; }
5450
5451 task.m_pProgress->i_notifyComplete(rc);
5452
5453 LogFlowThisFuncLeave();
5454}
5455
5456HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5457{
5458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5459
5460 HRESULT rc = i_checkStateDependency(MutableStateDep);
5461 if (FAILED(rc)) return rc;
5462
5463 if (mData->mRegistered)
5464 return setError(VBOX_E_INVALID_VM_STATE,
5465 tr("Cannot delete settings of a registered machine"));
5466
5467 // collect files to delete
5468 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5469 if (mData->pMachineConfigFile->fileExists())
5470 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5471
5472 RTCList<ComPtr<IMedium> > llMediums;
5473 for (size_t i = 0; i < aMedia.size(); ++i)
5474 {
5475 IMedium *pIMedium(aMedia[i]);
5476 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5477 if (pMedium.isNull())
5478 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5479 SafeArray<BSTR> ids;
5480 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5481 if (FAILED(rc)) return rc;
5482 /* At this point the medium should not have any back references
5483 * anymore. If it has it is attached to another VM and *must* not
5484 * deleted. */
5485 if (ids.size() < 1)
5486 llMediums.append(pMedium);
5487 }
5488
5489 ComObjPtr<Progress> pProgress;
5490 pProgress.createObject();
5491 rc = pProgress->init(i_getVirtualBox(),
5492 static_cast<IMachine*>(this) /* aInitiator */,
5493 Bstr(tr("Deleting files")).raw(),
5494 true /* fCancellable */,
5495 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5496 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5497 if (FAILED(rc))
5498 return rc;
5499
5500 /* create and start the task on a separate thread (note that it will not
5501 * start working until we release alock) */
5502 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5503 rc = pTask->createThread();
5504 if (FAILED(rc))
5505 return rc;
5506
5507 pProgress.queryInterfaceTo(aProgress.asOutParam());
5508
5509 LogFlowFuncLeave();
5510
5511 return S_OK;
5512}
5513
5514HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5515{
5516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5517
5518 ComObjPtr<Snapshot> pSnapshot;
5519 HRESULT rc;
5520
5521 if (aNameOrId.isEmpty())
5522 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5523 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5524 else
5525 {
5526 Guid uuid(aNameOrId);
5527 if (uuid.isValid())
5528 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5529 else
5530 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5531 }
5532 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5533
5534 return rc;
5535}
5536
5537HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5538{
5539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5540
5541 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5542 if (FAILED(rc)) return rc;
5543
5544 ComObjPtr<SharedFolder> sharedFolder;
5545 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5546 if (SUCCEEDED(rc))
5547 return setError(VBOX_E_OBJECT_IN_USE,
5548 tr("Shared folder named '%s' already exists"),
5549 aName.c_str());
5550
5551 sharedFolder.createObject();
5552 rc = sharedFolder->init(i_getMachine(),
5553 aName,
5554 aHostPath,
5555 !!aWritable,
5556 !!aAutomount,
5557 true /* fFailOnError */);
5558 if (FAILED(rc)) return rc;
5559
5560 i_setModified(IsModified_SharedFolders);
5561 mHWData.backup();
5562 mHWData->mSharedFolders.push_back(sharedFolder);
5563
5564 /* inform the direct session if any */
5565 alock.release();
5566 i_onSharedFolderChange();
5567
5568 return S_OK;
5569}
5570
5571HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5572{
5573 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5574
5575 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5576 if (FAILED(rc)) return rc;
5577
5578 ComObjPtr<SharedFolder> sharedFolder;
5579 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5580 if (FAILED(rc)) return rc;
5581
5582 i_setModified(IsModified_SharedFolders);
5583 mHWData.backup();
5584 mHWData->mSharedFolders.remove(sharedFolder);
5585
5586 /* inform the direct session if any */
5587 alock.release();
5588 i_onSharedFolderChange();
5589
5590 return S_OK;
5591}
5592
5593HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5594{
5595 /* start with No */
5596 *aCanShow = FALSE;
5597
5598 ComPtr<IInternalSessionControl> directControl;
5599 {
5600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5601
5602 if (mData->mSession.mState != SessionState_Locked)
5603 return setError(VBOX_E_INVALID_VM_STATE,
5604 tr("Machine is not locked for session (session state: %s)"),
5605 Global::stringifySessionState(mData->mSession.mState));
5606
5607 if (mData->mSession.mLockType == LockType_VM)
5608 directControl = mData->mSession.mDirectControl;
5609 }
5610
5611 /* ignore calls made after #OnSessionEnd() is called */
5612 if (!directControl)
5613 return S_OK;
5614
5615 LONG64 dummy;
5616 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5617}
5618
5619HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5620{
5621 ComPtr<IInternalSessionControl> directControl;
5622 {
5623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5624
5625 if (mData->mSession.mState != SessionState_Locked)
5626 return setError(E_FAIL,
5627 tr("Machine is not locked for session (session state: %s)"),
5628 Global::stringifySessionState(mData->mSession.mState));
5629
5630 if (mData->mSession.mLockType == LockType_VM)
5631 directControl = mData->mSession.mDirectControl;
5632 }
5633
5634 /* ignore calls made after #OnSessionEnd() is called */
5635 if (!directControl)
5636 return S_OK;
5637
5638 BOOL dummy;
5639 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5640}
5641
5642#ifdef VBOX_WITH_GUEST_PROPS
5643/**
5644 * Look up a guest property in VBoxSVC's internal structures.
5645 */
5646HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5647 com::Utf8Str &aValue,
5648 LONG64 *aTimestamp,
5649 com::Utf8Str &aFlags) const
5650{
5651 using namespace guestProp;
5652
5653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5654 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5655
5656 if (it != mHWData->mGuestProperties.end())
5657 {
5658 char szFlags[MAX_FLAGS_LEN + 1];
5659 aValue = it->second.strValue;
5660 *aTimestamp = it->second.mTimestamp;
5661 writeFlags(it->second.mFlags, szFlags);
5662 aFlags = Utf8Str(szFlags);
5663 }
5664
5665 return S_OK;
5666}
5667
5668/**
5669 * Query the VM that a guest property belongs to for the property.
5670 * @returns E_ACCESSDENIED if the VM process is not available or not
5671 * currently handling queries and the lookup should then be done in
5672 * VBoxSVC.
5673 */
5674HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5675 com::Utf8Str &aValue,
5676 LONG64 *aTimestamp,
5677 com::Utf8Str &aFlags) const
5678{
5679 HRESULT rc = S_OK;
5680 BSTR bValue = NULL;
5681 BSTR bFlags = NULL;
5682
5683 ComPtr<IInternalSessionControl> directControl;
5684 {
5685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5686 if (mData->mSession.mLockType == LockType_VM)
5687 directControl = mData->mSession.mDirectControl;
5688 }
5689
5690 /* ignore calls made after #OnSessionEnd() is called */
5691 if (!directControl)
5692 rc = E_ACCESSDENIED;
5693 else
5694 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5695 0 /* accessMode */,
5696 &bValue, aTimestamp, &bFlags);
5697
5698 aValue = bValue;
5699 aFlags = bFlags;
5700
5701 return rc;
5702}
5703#endif // VBOX_WITH_GUEST_PROPS
5704
5705HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5706 com::Utf8Str &aValue,
5707 LONG64 *aTimestamp,
5708 com::Utf8Str &aFlags)
5709{
5710#ifndef VBOX_WITH_GUEST_PROPS
5711 ReturnComNotImplemented();
5712#else // VBOX_WITH_GUEST_PROPS
5713
5714 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5715
5716 if (rc == E_ACCESSDENIED)
5717 /* The VM is not running or the service is not (yet) accessible */
5718 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5719 return rc;
5720#endif // VBOX_WITH_GUEST_PROPS
5721}
5722
5723HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5724{
5725 LONG64 dummyTimestamp;
5726 com::Utf8Str dummyFlags;
5727 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5728 return rc;
5729
5730}
5731HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5732{
5733 com::Utf8Str dummyFlags;
5734 com::Utf8Str dummyValue;
5735 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5736 return rc;
5737}
5738
5739#ifdef VBOX_WITH_GUEST_PROPS
5740/**
5741 * Set a guest property in VBoxSVC's internal structures.
5742 */
5743HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5744 const com::Utf8Str &aFlags, bool fDelete)
5745{
5746 using namespace guestProp;
5747
5748 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5749 HRESULT rc = S_OK;
5750
5751 rc = i_checkStateDependency(MutableOrSavedStateDep);
5752 if (FAILED(rc)) return rc;
5753
5754 try
5755 {
5756 uint32_t fFlags = NILFLAG;
5757 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5758 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5759
5760 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5761 if (it == mHWData->mGuestProperties.end())
5762 {
5763 if (!fDelete)
5764 {
5765 i_setModified(IsModified_MachineData);
5766 mHWData.backupEx();
5767
5768 RTTIMESPEC time;
5769 HWData::GuestProperty prop;
5770 prop.strValue = Bstr(aValue).raw();
5771 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5772 prop.mFlags = fFlags;
5773 mHWData->mGuestProperties[aName] = prop;
5774 }
5775 }
5776 else
5777 {
5778 if (it->second.mFlags & (RDONLYHOST))
5779 {
5780 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5781 }
5782 else
5783 {
5784 i_setModified(IsModified_MachineData);
5785 mHWData.backupEx();
5786
5787 /* The backupEx() operation invalidates our iterator,
5788 * so get a new one. */
5789 it = mHWData->mGuestProperties.find(aName);
5790 Assert(it != mHWData->mGuestProperties.end());
5791
5792 if (!fDelete)
5793 {
5794 RTTIMESPEC time;
5795 it->second.strValue = aValue;
5796 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5797 it->second.mFlags = fFlags;
5798 }
5799 else
5800 mHWData->mGuestProperties.erase(it);
5801 }
5802 }
5803
5804 if (SUCCEEDED(rc))
5805 {
5806 alock.release();
5807
5808 mParent->i_onGuestPropertyChange(mData->mUuid,
5809 Bstr(aName).raw(),
5810 Bstr(aValue).raw(),
5811 Bstr(aFlags).raw());
5812 }
5813 }
5814 catch (std::bad_alloc &)
5815 {
5816 rc = E_OUTOFMEMORY;
5817 }
5818
5819 return rc;
5820}
5821
5822/**
5823 * Set a property on the VM that that property belongs to.
5824 * @returns E_ACCESSDENIED if the VM process is not available or not
5825 * currently handling queries and the setting should then be done in
5826 * VBoxSVC.
5827 */
5828HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5829 const com::Utf8Str &aFlags, bool fDelete)
5830{
5831 HRESULT rc;
5832
5833 try
5834 {
5835 ComPtr<IInternalSessionControl> directControl;
5836 {
5837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5838 if (mData->mSession.mLockType == LockType_VM)
5839 directControl = mData->mSession.mDirectControl;
5840 }
5841
5842 BSTR dummy = NULL; /* will not be changed (setter) */
5843 LONG64 dummy64;
5844 if (!directControl)
5845 rc = E_ACCESSDENIED;
5846 else
5847 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5848 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5849 fDelete? 2: 1 /* accessMode */,
5850 &dummy, &dummy64, &dummy);
5851 }
5852 catch (std::bad_alloc &)
5853 {
5854 rc = E_OUTOFMEMORY;
5855 }
5856
5857 return rc;
5858}
5859#endif // VBOX_WITH_GUEST_PROPS
5860
5861HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5862 const com::Utf8Str &aFlags)
5863{
5864#ifndef VBOX_WITH_GUEST_PROPS
5865 ReturnComNotImplemented();
5866#else // VBOX_WITH_GUEST_PROPS
5867 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5868 if (rc == E_ACCESSDENIED)
5869 /* The VM is not running or the service is not (yet) accessible */
5870 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5871 return rc;
5872#endif // VBOX_WITH_GUEST_PROPS
5873}
5874
5875HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5876{
5877 return setGuestProperty(aProperty, aValue, "");
5878}
5879
5880HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5881{
5882#ifndef VBOX_WITH_GUEST_PROPS
5883 ReturnComNotImplemented();
5884#else // VBOX_WITH_GUEST_PROPS
5885 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5886 if (rc == E_ACCESSDENIED)
5887 /* The VM is not running or the service is not (yet) accessible */
5888 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5889 return rc;
5890#endif // VBOX_WITH_GUEST_PROPS
5891}
5892
5893#ifdef VBOX_WITH_GUEST_PROPS
5894/**
5895 * Enumerate the guest properties in VBoxSVC's internal structures.
5896 */
5897HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5898 std::vector<com::Utf8Str> &aNames,
5899 std::vector<com::Utf8Str> &aValues,
5900 std::vector<LONG64> &aTimestamps,
5901 std::vector<com::Utf8Str> &aFlags)
5902{
5903 using namespace guestProp;
5904
5905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5906 Utf8Str strPatterns(aPatterns);
5907
5908 HWData::GuestPropertyMap propMap;
5909
5910 /*
5911 * Look for matching patterns and build up a list.
5912 */
5913 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5914 while (it != mHWData->mGuestProperties.end())
5915 {
5916 if ( strPatterns.isEmpty()
5917 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5918 RTSTR_MAX,
5919 it->first.c_str(),
5920 RTSTR_MAX,
5921 NULL)
5922 )
5923 propMap.insert(*it);
5924 ++it;
5925 }
5926
5927 alock.release();
5928
5929 /*
5930 * And build up the arrays for returning the property information.
5931 */
5932 size_t cEntries = propMap.size();
5933
5934 aNames.resize(cEntries);
5935 aValues.resize(cEntries);
5936 aTimestamps.resize(cEntries);
5937 aFlags.resize(cEntries);
5938
5939 char szFlags[MAX_FLAGS_LEN + 1];
5940 size_t i= 0;
5941 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5942 {
5943 aNames[i] = it->first;
5944 aValues[i] = it->second.strValue;
5945 aTimestamps[i] = it->second.mTimestamp;
5946 writeFlags(it->second.mFlags, szFlags);
5947 aFlags[i] = Utf8Str(szFlags);
5948 }
5949
5950 return S_OK;
5951}
5952
5953/**
5954 * Enumerate the properties managed by a VM.
5955 * @returns E_ACCESSDENIED if the VM process is not available or not
5956 * currently handling queries and the setting should then be done in
5957 * VBoxSVC.
5958 */
5959HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5960 std::vector<com::Utf8Str> &aNames,
5961 std::vector<com::Utf8Str> &aValues,
5962 std::vector<LONG64> &aTimestamps,
5963 std::vector<com::Utf8Str> &aFlags)
5964{
5965 HRESULT rc;
5966 ComPtr<IInternalSessionControl> directControl;
5967 {
5968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5969 if (mData->mSession.mLockType == LockType_VM)
5970 directControl = mData->mSession.mDirectControl;
5971 }
5972
5973 com::SafeArray<BSTR> bNames;
5974 com::SafeArray<BSTR> bValues;
5975 com::SafeArray<LONG64> bTimestamps;
5976 com::SafeArray<BSTR> bFlags;
5977
5978 if (!directControl)
5979 rc = E_ACCESSDENIED;
5980 else
5981 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5982 ComSafeArrayAsOutParam(bNames),
5983 ComSafeArrayAsOutParam(bValues),
5984 ComSafeArrayAsOutParam(bTimestamps),
5985 ComSafeArrayAsOutParam(bFlags));
5986 size_t i;
5987 aNames.resize(bNames.size());
5988 for (i = 0; i < bNames.size(); ++i)
5989 aNames[i] = Utf8Str(bNames[i]);
5990 aValues.resize(bValues.size());
5991 for (i = 0; i < bValues.size(); ++i)
5992 aValues[i] = Utf8Str(bValues[i]);
5993 aTimestamps.resize(bTimestamps.size());
5994 for (i = 0; i < bTimestamps.size(); ++i)
5995 aTimestamps[i] = bTimestamps[i];
5996 aFlags.resize(bFlags.size());
5997 for (i = 0; i < bFlags.size(); ++i)
5998 aFlags[i] = Utf8Str(bFlags[i]);
5999
6000 return rc;
6001}
6002#endif // VBOX_WITH_GUEST_PROPS
6003HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6004 std::vector<com::Utf8Str> &aNames,
6005 std::vector<com::Utf8Str> &aValues,
6006 std::vector<LONG64> &aTimestamps,
6007 std::vector<com::Utf8Str> &aFlags)
6008{
6009#ifndef VBOX_WITH_GUEST_PROPS
6010 ReturnComNotImplemented();
6011#else // VBOX_WITH_GUEST_PROPS
6012
6013 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6014
6015 if (rc == E_ACCESSDENIED)
6016 /* The VM is not running or the service is not (yet) accessible */
6017 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6018 return rc;
6019#endif // VBOX_WITH_GUEST_PROPS
6020}
6021
6022HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6023 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6024{
6025 MediaData::AttachmentList atts;
6026
6027 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6028 if (FAILED(rc)) return rc;
6029
6030 size_t i = 0;
6031 aMediumAttachments.resize(atts.size());
6032 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
6033 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6034
6035 return S_OK;
6036}
6037
6038HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6039 LONG aControllerPort,
6040 LONG aDevice,
6041 ComPtr<IMediumAttachment> &aAttachment)
6042{
6043 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6044 aName.c_str(), aControllerPort, aDevice));
6045
6046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6047
6048 aAttachment = NULL;
6049
6050 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
6051 Bstr(aName).raw(),
6052 aControllerPort,
6053 aDevice);
6054 if (pAttach.isNull())
6055 return setError(VBOX_E_OBJECT_NOT_FOUND,
6056 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6057 aDevice, aControllerPort, aName.c_str());
6058
6059 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6060
6061 return S_OK;
6062}
6063
6064
6065HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6066 StorageBus_T aConnectionType,
6067 ComPtr<IStorageController> &aController)
6068{
6069 if ( (aConnectionType <= StorageBus_Null)
6070 || (aConnectionType > StorageBus_USB))
6071 return setError(E_INVALIDARG,
6072 tr("Invalid connection type: %d"),
6073 aConnectionType);
6074
6075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6076
6077 HRESULT rc = i_checkStateDependency(MutableStateDep);
6078 if (FAILED(rc)) return rc;
6079
6080 /* try to find one with the name first. */
6081 ComObjPtr<StorageController> ctrl;
6082
6083 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6084 if (SUCCEEDED(rc))
6085 return setError(VBOX_E_OBJECT_IN_USE,
6086 tr("Storage controller named '%s' already exists"),
6087 aName.c_str());
6088
6089 ctrl.createObject();
6090
6091 /* get a new instance number for the storage controller */
6092 ULONG ulInstance = 0;
6093 bool fBootable = true;
6094 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6095 it != mStorageControllers->end();
6096 ++it)
6097 {
6098 if ((*it)->i_getStorageBus() == aConnectionType)
6099 {
6100 ULONG ulCurInst = (*it)->i_getInstance();
6101
6102 if (ulCurInst >= ulInstance)
6103 ulInstance = ulCurInst + 1;
6104
6105 /* Only one controller of each type can be marked as bootable. */
6106 if ((*it)->i_getBootable())
6107 fBootable = false;
6108 }
6109 }
6110
6111 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6112 if (FAILED(rc)) return rc;
6113
6114 i_setModified(IsModified_Storage);
6115 mStorageControllers.backup();
6116 mStorageControllers->push_back(ctrl);
6117
6118 ctrl.queryInterfaceTo(aController.asOutParam());
6119
6120 /* inform the direct session if any */
6121 alock.release();
6122 i_onStorageControllerChange();
6123
6124 return S_OK;
6125}
6126
6127HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6128 ComPtr<IStorageController> &aStorageController)
6129{
6130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6131
6132 ComObjPtr<StorageController> ctrl;
6133
6134 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6135 if (SUCCEEDED(rc))
6136 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6137
6138 return rc;
6139}
6140
6141HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6142 ULONG aInstance,
6143 ComPtr<IStorageController> &aStorageController)
6144{
6145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6146
6147 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6148 it != mStorageControllers->end();
6149 ++it)
6150 {
6151 if ( (*it)->i_getStorageBus() == aConnectionType
6152 && (*it)->i_getInstance() == aInstance)
6153 {
6154 (*it).queryInterfaceTo(aStorageController.asOutParam());
6155 return S_OK;
6156 }
6157 }
6158
6159 return setError(VBOX_E_OBJECT_NOT_FOUND,
6160 tr("Could not find a storage controller with instance number '%lu'"),
6161 aInstance);
6162}
6163
6164HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6165{
6166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6167
6168 HRESULT rc = i_checkStateDependency(MutableStateDep);
6169 if (FAILED(rc)) return rc;
6170
6171 ComObjPtr<StorageController> ctrl;
6172
6173 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6174 if (SUCCEEDED(rc))
6175 {
6176 /* Ensure that only one controller of each type is marked as bootable. */
6177 if (aBootable == TRUE)
6178 {
6179 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6180 it != mStorageControllers->end();
6181 ++it)
6182 {
6183 ComObjPtr<StorageController> aCtrl = (*it);
6184
6185 if ( (aCtrl->i_getName() != aName)
6186 && aCtrl->i_getBootable() == TRUE
6187 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6188 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6189 {
6190 aCtrl->i_setBootable(FALSE);
6191 break;
6192 }
6193 }
6194 }
6195
6196 if (SUCCEEDED(rc))
6197 {
6198 ctrl->i_setBootable(aBootable);
6199 i_setModified(IsModified_Storage);
6200 }
6201 }
6202
6203 if (SUCCEEDED(rc))
6204 {
6205 /* inform the direct session if any */
6206 alock.release();
6207 i_onStorageControllerChange();
6208 }
6209
6210 return rc;
6211}
6212
6213HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6214{
6215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6216
6217 HRESULT rc = i_checkStateDependency(MutableStateDep);
6218 if (FAILED(rc)) return rc;
6219
6220 ComObjPtr<StorageController> ctrl;
6221 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6222 if (FAILED(rc)) return rc;
6223
6224 {
6225 /* find all attached devices to the appropriate storage controller and detach them all */
6226 // make a temporary list because detachDevice invalidates iterators into
6227 // mMediaData->mAttachments
6228 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6229
6230 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6231 it != llAttachments2.end();
6232 ++it)
6233 {
6234 MediumAttachment *pAttachTemp = *it;
6235
6236 AutoCaller localAutoCaller(pAttachTemp);
6237 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6238
6239 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6240
6241 if (pAttachTemp->i_getControllerName() == aName)
6242 {
6243 rc = i_detachDevice(pAttachTemp, alock, NULL);
6244 if (FAILED(rc)) return rc;
6245 }
6246 }
6247 }
6248
6249 /* We can remove it now. */
6250 i_setModified(IsModified_Storage);
6251 mStorageControllers.backup();
6252
6253 ctrl->i_unshare();
6254
6255 mStorageControllers->remove(ctrl);
6256
6257 /* inform the direct session if any */
6258 alock.release();
6259 i_onStorageControllerChange();
6260
6261 return S_OK;
6262}
6263
6264HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6265 ComPtr<IUSBController> &aController)
6266{
6267 if ( (aType <= USBControllerType_Null)
6268 || (aType >= USBControllerType_Last))
6269 return setError(E_INVALIDARG,
6270 tr("Invalid USB controller type: %d"),
6271 aType);
6272
6273 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6274
6275 HRESULT rc = i_checkStateDependency(MutableStateDep);
6276 if (FAILED(rc)) return rc;
6277
6278 /* try to find one with the same type first. */
6279 ComObjPtr<USBController> ctrl;
6280
6281 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6282 if (SUCCEEDED(rc))
6283 return setError(VBOX_E_OBJECT_IN_USE,
6284 tr("USB controller named '%s' already exists"),
6285 aName.c_str());
6286
6287 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6288 ULONG maxInstances;
6289 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6290 if (FAILED(rc))
6291 return rc;
6292
6293 ULONG cInstances = i_getUSBControllerCountByType(aType);
6294 if (cInstances >= maxInstances)
6295 return setError(E_INVALIDARG,
6296 tr("Too many USB controllers of this type"));
6297
6298 ctrl.createObject();
6299
6300 rc = ctrl->init(this, aName, aType);
6301 if (FAILED(rc)) return rc;
6302
6303 i_setModified(IsModified_USB);
6304 mUSBControllers.backup();
6305 mUSBControllers->push_back(ctrl);
6306
6307 ctrl.queryInterfaceTo(aController.asOutParam());
6308
6309 /* inform the direct session if any */
6310 alock.release();
6311 i_onUSBControllerChange();
6312
6313 return S_OK;
6314}
6315
6316HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6317{
6318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6319
6320 ComObjPtr<USBController> ctrl;
6321
6322 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6323 if (SUCCEEDED(rc))
6324 ctrl.queryInterfaceTo(aController.asOutParam());
6325
6326 return rc;
6327}
6328
6329HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6330 ULONG *aControllers)
6331{
6332 if ( (aType <= USBControllerType_Null)
6333 || (aType >= USBControllerType_Last))
6334 return setError(E_INVALIDARG,
6335 tr("Invalid USB controller type: %d"),
6336 aType);
6337
6338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6339
6340 ComObjPtr<USBController> ctrl;
6341
6342 *aControllers = i_getUSBControllerCountByType(aType);
6343
6344 return S_OK;
6345}
6346
6347HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6348{
6349
6350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6351
6352 HRESULT rc = i_checkStateDependency(MutableStateDep);
6353 if (FAILED(rc)) return rc;
6354
6355 ComObjPtr<USBController> ctrl;
6356 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6357 if (FAILED(rc)) return rc;
6358
6359 i_setModified(IsModified_USB);
6360 mUSBControllers.backup();
6361
6362 ctrl->i_unshare();
6363
6364 mUSBControllers->remove(ctrl);
6365
6366 /* inform the direct session if any */
6367 alock.release();
6368 i_onUSBControllerChange();
6369
6370 return S_OK;
6371}
6372
6373HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6374 ULONG *aOriginX,
6375 ULONG *aOriginY,
6376 ULONG *aWidth,
6377 ULONG *aHeight,
6378 BOOL *aEnabled)
6379{
6380 uint32_t u32OriginX= 0;
6381 uint32_t u32OriginY= 0;
6382 uint32_t u32Width = 0;
6383 uint32_t u32Height = 0;
6384 uint16_t u16Flags = 0;
6385
6386 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6387 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6388 if (RT_FAILURE(vrc))
6389 {
6390#ifdef RT_OS_WINDOWS
6391 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6392 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6393 * So just assign fEnable to TRUE again.
6394 * The right fix would be to change GUI API wrappers to make sure that parameters
6395 * are changed only if API succeeds.
6396 */
6397 *aEnabled = TRUE;
6398#endif
6399 return setError(VBOX_E_IPRT_ERROR,
6400 tr("Saved guest size is not available (%Rrc)"),
6401 vrc);
6402 }
6403
6404 *aOriginX = u32OriginX;
6405 *aOriginY = u32OriginY;
6406 *aWidth = u32Width;
6407 *aHeight = u32Height;
6408 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6409
6410 return S_OK;
6411}
6412
6413HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6414 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6415{
6416 if (aScreenId != 0)
6417 return E_NOTIMPL;
6418
6419 if ( aBitmapFormat != BitmapFormat_BGR0
6420 && aBitmapFormat != BitmapFormat_BGRA
6421 && aBitmapFormat != BitmapFormat_RGBA
6422 && aBitmapFormat != BitmapFormat_PNG)
6423 return setError(E_NOTIMPL,
6424 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6425
6426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6427
6428 uint8_t *pu8Data = NULL;
6429 uint32_t cbData = 0;
6430 uint32_t u32Width = 0;
6431 uint32_t u32Height = 0;
6432
6433 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6434
6435 if (RT_FAILURE(vrc))
6436 return setError(VBOX_E_IPRT_ERROR,
6437 tr("Saved thumbnail data is not available (%Rrc)"),
6438 vrc);
6439
6440 HRESULT hr = S_OK;
6441
6442 *aWidth = u32Width;
6443 *aHeight = u32Height;
6444
6445 if (cbData > 0)
6446 {
6447 /* Convert pixels to the format expected by the API caller. */
6448 if (aBitmapFormat == BitmapFormat_BGR0)
6449 {
6450 /* [0] B, [1] G, [2] R, [3] 0. */
6451 aData.resize(cbData);
6452 memcpy(&aData.front(), pu8Data, cbData);
6453 }
6454 else if (aBitmapFormat == BitmapFormat_BGRA)
6455 {
6456 /* [0] B, [1] G, [2] R, [3] A. */
6457 aData.resize(cbData);
6458 for (uint32_t i = 0; i < cbData; i += 4)
6459 {
6460 aData[i] = pu8Data[i];
6461 aData[i + 1] = pu8Data[i + 1];
6462 aData[i + 2] = pu8Data[i + 2];
6463 aData[i + 3] = 0xff;
6464 }
6465 }
6466 else if (aBitmapFormat == BitmapFormat_RGBA)
6467 {
6468 /* [0] R, [1] G, [2] B, [3] A. */
6469 aData.resize(cbData);
6470 for (uint32_t i = 0; i < cbData; i += 4)
6471 {
6472 aData[i] = pu8Data[i + 2];
6473 aData[i + 1] = pu8Data[i + 1];
6474 aData[i + 2] = pu8Data[i];
6475 aData[i + 3] = 0xff;
6476 }
6477 }
6478 else if (aBitmapFormat == BitmapFormat_PNG)
6479 {
6480 uint8_t *pu8PNG = NULL;
6481 uint32_t cbPNG = 0;
6482 uint32_t cxPNG = 0;
6483 uint32_t cyPNG = 0;
6484
6485 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6486
6487 if (RT_SUCCESS(vrc))
6488 {
6489 aData.resize(cbPNG);
6490 if (cbPNG)
6491 memcpy(&aData.front(), pu8PNG, cbPNG);
6492 }
6493 else
6494 hr = setError(VBOX_E_IPRT_ERROR,
6495 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6496 vrc);
6497
6498 RTMemFree(pu8PNG);
6499 }
6500 }
6501
6502 freeSavedDisplayScreenshot(pu8Data);
6503
6504 return hr;
6505}
6506
6507HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6508 ULONG *aWidth,
6509 ULONG *aHeight,
6510 std::vector<BitmapFormat_T> &aBitmapFormats)
6511{
6512 if (aScreenId != 0)
6513 return E_NOTIMPL;
6514
6515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6516
6517 uint8_t *pu8Data = NULL;
6518 uint32_t cbData = 0;
6519 uint32_t u32Width = 0;
6520 uint32_t u32Height = 0;
6521
6522 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6523
6524 if (RT_FAILURE(vrc))
6525 return setError(VBOX_E_IPRT_ERROR,
6526 tr("Saved screenshot data is not available (%Rrc)"),
6527 vrc);
6528
6529 *aWidth = u32Width;
6530 *aHeight = u32Height;
6531 aBitmapFormats.resize(1);
6532 aBitmapFormats[0] = BitmapFormat_PNG;
6533
6534 freeSavedDisplayScreenshot(pu8Data);
6535
6536 return S_OK;
6537}
6538
6539HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6540 BitmapFormat_T aBitmapFormat,
6541 ULONG *aWidth,
6542 ULONG *aHeight,
6543 std::vector<BYTE> &aData)
6544{
6545 if (aScreenId != 0)
6546 return E_NOTIMPL;
6547
6548 if (aBitmapFormat != BitmapFormat_PNG)
6549 return E_NOTIMPL;
6550
6551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6552
6553 uint8_t *pu8Data = NULL;
6554 uint32_t cbData = 0;
6555 uint32_t u32Width = 0;
6556 uint32_t u32Height = 0;
6557
6558 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6559
6560 if (RT_FAILURE(vrc))
6561 return setError(VBOX_E_IPRT_ERROR,
6562 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6563 vrc);
6564
6565 *aWidth = u32Width;
6566 *aHeight = u32Height;
6567
6568 aData.resize(cbData);
6569 if (cbData)
6570 memcpy(&aData.front(), pu8Data, cbData);
6571
6572 freeSavedDisplayScreenshot(pu8Data);
6573
6574 return S_OK;
6575}
6576
6577HRESULT Machine::hotPlugCPU(ULONG aCpu)
6578{
6579 HRESULT rc = S_OK;
6580 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6581
6582 if (!mHWData->mCPUHotPlugEnabled)
6583 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6584
6585 if (aCpu >= mHWData->mCPUCount)
6586 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6587
6588 if (mHWData->mCPUAttached[aCpu])
6589 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6590
6591 alock.release();
6592 rc = i_onCPUChange(aCpu, false);
6593 alock.acquire();
6594 if (FAILED(rc)) return rc;
6595
6596 i_setModified(IsModified_MachineData);
6597 mHWData.backup();
6598 mHWData->mCPUAttached[aCpu] = true;
6599
6600 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6601 if (Global::IsOnline(mData->mMachineState))
6602 i_saveSettings(NULL);
6603
6604 return S_OK;
6605}
6606
6607HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6608{
6609 HRESULT rc = S_OK;
6610
6611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6612
6613 if (!mHWData->mCPUHotPlugEnabled)
6614 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6615
6616 if (aCpu >= SchemaDefs::MaxCPUCount)
6617 return setError(E_INVALIDARG,
6618 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6619 SchemaDefs::MaxCPUCount);
6620
6621 if (!mHWData->mCPUAttached[aCpu])
6622 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6623
6624 /* CPU 0 can't be detached */
6625 if (aCpu == 0)
6626 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6627
6628 alock.release();
6629 rc = i_onCPUChange(aCpu, true);
6630 alock.acquire();
6631 if (FAILED(rc)) return rc;
6632
6633 i_setModified(IsModified_MachineData);
6634 mHWData.backup();
6635 mHWData->mCPUAttached[aCpu] = false;
6636
6637 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6638 if (Global::IsOnline(mData->mMachineState))
6639 i_saveSettings(NULL);
6640
6641 return S_OK;
6642}
6643
6644HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6645{
6646 *aAttached = false;
6647
6648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6649
6650 /* If hotplug is enabled the CPU is always enabled. */
6651 if (!mHWData->mCPUHotPlugEnabled)
6652 {
6653 if (aCpu < mHWData->mCPUCount)
6654 *aAttached = true;
6655 }
6656 else
6657 {
6658 if (aCpu < SchemaDefs::MaxCPUCount)
6659 *aAttached = mHWData->mCPUAttached[aCpu];
6660 }
6661
6662 return S_OK;
6663}
6664
6665HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6666{
6667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6668
6669 Utf8Str log = i_getLogFilename(aIdx);
6670 if (!RTFileExists(log.c_str()))
6671 log.setNull();
6672 aFilename = log;
6673
6674 return S_OK;
6675}
6676
6677HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6678{
6679 if (aSize < 0)
6680 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6681
6682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6683
6684 HRESULT rc = S_OK;
6685 Utf8Str log = i_getLogFilename(aIdx);
6686
6687 /* do not unnecessarily hold the lock while doing something which does
6688 * not need the lock and potentially takes a long time. */
6689 alock.release();
6690
6691 /* Limit the chunk size to 32K for now, as that gives better performance
6692 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6693 * One byte expands to approx. 25 bytes of breathtaking XML. */
6694 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6695 aData.resize(cbData);
6696
6697 RTFILE LogFile;
6698 int vrc = RTFileOpen(&LogFile, log.c_str(),
6699 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6700 if (RT_SUCCESS(vrc))
6701 {
6702 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6703 if (RT_SUCCESS(vrc))
6704 aData.resize(cbData);
6705 else
6706 rc = setError(VBOX_E_IPRT_ERROR,
6707 tr("Could not read log file '%s' (%Rrc)"),
6708 log.c_str(), vrc);
6709 RTFileClose(LogFile);
6710 }
6711 else
6712 rc = setError(VBOX_E_IPRT_ERROR,
6713 tr("Could not open log file '%s' (%Rrc)"),
6714 log.c_str(), vrc);
6715
6716 if (FAILED(rc))
6717 aData.resize(0);
6718
6719 return rc;
6720}
6721
6722
6723/**
6724 * Currently this method doesn't attach device to the running VM,
6725 * just makes sure it's plugged on next VM start.
6726 */
6727HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6728{
6729 // lock scope
6730 {
6731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6732
6733 HRESULT rc = i_checkStateDependency(MutableStateDep);
6734 if (FAILED(rc)) return rc;
6735
6736 ChipsetType_T aChipset = ChipsetType_PIIX3;
6737 COMGETTER(ChipsetType)(&aChipset);
6738
6739 if (aChipset != ChipsetType_ICH9)
6740 {
6741 return setError(E_INVALIDARG,
6742 tr("Host PCI attachment only supported with ICH9 chipset"));
6743 }
6744
6745 // check if device with this host PCI address already attached
6746 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6747 it != mHWData->mPCIDeviceAssignments.end();
6748 ++it)
6749 {
6750 LONG iHostAddress = -1;
6751 ComPtr<PCIDeviceAttachment> pAttach;
6752 pAttach = *it;
6753 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6754 if (iHostAddress == aHostAddress)
6755 return setError(E_INVALIDARG,
6756 tr("Device with host PCI address already attached to this VM"));
6757 }
6758
6759 ComObjPtr<PCIDeviceAttachment> pda;
6760 char name[32];
6761
6762 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6763 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6764 Bstr bname(name);
6765 pda.createObject();
6766 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6767 i_setModified(IsModified_MachineData);
6768 mHWData.backup();
6769 mHWData->mPCIDeviceAssignments.push_back(pda);
6770 }
6771
6772 return S_OK;
6773}
6774
6775/**
6776 * Currently this method doesn't detach device from the running VM,
6777 * just makes sure it's not plugged on next VM start.
6778 */
6779HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6780{
6781 ComObjPtr<PCIDeviceAttachment> pAttach;
6782 bool fRemoved = false;
6783 HRESULT rc;
6784
6785 // lock scope
6786 {
6787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6788
6789 rc = i_checkStateDependency(MutableStateDep);
6790 if (FAILED(rc)) return rc;
6791
6792 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6793 it != mHWData->mPCIDeviceAssignments.end();
6794 ++it)
6795 {
6796 LONG iHostAddress = -1;
6797 pAttach = *it;
6798 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6799 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6800 {
6801 i_setModified(IsModified_MachineData);
6802 mHWData.backup();
6803 mHWData->mPCIDeviceAssignments.remove(pAttach);
6804 fRemoved = true;
6805 break;
6806 }
6807 }
6808 }
6809
6810
6811 /* Fire event outside of the lock */
6812 if (fRemoved)
6813 {
6814 Assert(!pAttach.isNull());
6815 ComPtr<IEventSource> es;
6816 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6817 Assert(SUCCEEDED(rc));
6818 Bstr mid;
6819 rc = this->COMGETTER(Id)(mid.asOutParam());
6820 Assert(SUCCEEDED(rc));
6821 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6822 }
6823
6824 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6825 tr("No host PCI device %08x attached"),
6826 aHostAddress
6827 );
6828}
6829
6830HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6831{
6832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6833
6834 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6835
6836 size_t i = 0;
6837 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6838 it != mHWData->mPCIDeviceAssignments.end();
6839 ++i, ++it)
6840 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6841
6842 return S_OK;
6843}
6844
6845HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6846{
6847 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6848
6849 return S_OK;
6850}
6851
6852HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6853{
6854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6855
6856 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6857
6858 return S_OK;
6859}
6860
6861HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6862{
6863 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6864 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6865 if (SUCCEEDED(hrc))
6866 {
6867 hrc = mHWData.backupEx();
6868 if (SUCCEEDED(hrc))
6869 {
6870 i_setModified(IsModified_MachineData);
6871 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6872 }
6873 }
6874 return hrc;
6875}
6876
6877HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6878{
6879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6880 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6881 return S_OK;
6882}
6883
6884HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6885{
6886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6887 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6888 if (SUCCEEDED(hrc))
6889 {
6890 hrc = mHWData.backupEx();
6891 if (SUCCEEDED(hrc))
6892 {
6893 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6894 if (SUCCEEDED(hrc))
6895 i_setModified(IsModified_MachineData);
6896 }
6897 }
6898 return hrc;
6899}
6900
6901HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6902{
6903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6904
6905 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6906
6907 return S_OK;
6908}
6909
6910HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6911{
6912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6913 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6914 if (SUCCEEDED(hrc))
6915 {
6916 hrc = mHWData.backupEx();
6917 if (SUCCEEDED(hrc))
6918 {
6919 i_setModified(IsModified_MachineData);
6920 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6921 }
6922 }
6923 return hrc;
6924}
6925
6926HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6927{
6928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6929
6930 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6931
6932 return S_OK;
6933}
6934
6935HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6936{
6937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6938
6939 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6940 if ( SUCCEEDED(hrc)
6941 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6942 {
6943 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6944 int vrc;
6945
6946 if (aAutostartEnabled)
6947 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6948 else
6949 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6950
6951 if (RT_SUCCESS(vrc))
6952 {
6953 hrc = mHWData.backupEx();
6954 if (SUCCEEDED(hrc))
6955 {
6956 i_setModified(IsModified_MachineData);
6957 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6958 }
6959 }
6960 else if (vrc == VERR_NOT_SUPPORTED)
6961 hrc = setError(VBOX_E_NOT_SUPPORTED,
6962 tr("The VM autostart feature is not supported on this platform"));
6963 else if (vrc == VERR_PATH_NOT_FOUND)
6964 hrc = setError(E_FAIL,
6965 tr("The path to the autostart database is not set"));
6966 else
6967 hrc = setError(E_UNEXPECTED,
6968 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6969 aAutostartEnabled ? "Adding" : "Removing",
6970 mUserData->s.strName.c_str(), vrc);
6971 }
6972 return hrc;
6973}
6974
6975HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6976{
6977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6978
6979 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6980
6981 return S_OK;
6982}
6983
6984HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6985{
6986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6987 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6988 if (SUCCEEDED(hrc))
6989 {
6990 hrc = mHWData.backupEx();
6991 if (SUCCEEDED(hrc))
6992 {
6993 i_setModified(IsModified_MachineData);
6994 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6995 }
6996 }
6997 return hrc;
6998}
6999
7000HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7001{
7002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7003
7004 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7005
7006 return S_OK;
7007}
7008
7009HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7010{
7011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7012 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7013 if ( SUCCEEDED(hrc)
7014 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7015 {
7016 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7017 int vrc;
7018
7019 if (aAutostopType != AutostopType_Disabled)
7020 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7021 else
7022 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7023
7024 if (RT_SUCCESS(vrc))
7025 {
7026 hrc = mHWData.backupEx();
7027 if (SUCCEEDED(hrc))
7028 {
7029 i_setModified(IsModified_MachineData);
7030 mHWData->mAutostart.enmAutostopType = aAutostopType;
7031 }
7032 }
7033 else if (vrc == VERR_NOT_SUPPORTED)
7034 hrc = setError(VBOX_E_NOT_SUPPORTED,
7035 tr("The VM autostop feature is not supported on this platform"));
7036 else if (vrc == VERR_PATH_NOT_FOUND)
7037 hrc = setError(E_FAIL,
7038 tr("The path to the autostart database is not set"));
7039 else
7040 hrc = setError(E_UNEXPECTED,
7041 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7042 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7043 mUserData->s.strName.c_str(), vrc);
7044 }
7045 return hrc;
7046}
7047
7048HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7049{
7050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7051
7052 aDefaultFrontend = mHWData->mDefaultFrontend;
7053
7054 return S_OK;
7055}
7056
7057HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7058{
7059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7060 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7061 if (SUCCEEDED(hrc))
7062 {
7063 hrc = mHWData.backupEx();
7064 if (SUCCEEDED(hrc))
7065 {
7066 i_setModified(IsModified_MachineData);
7067 mHWData->mDefaultFrontend = aDefaultFrontend;
7068 }
7069 }
7070 return hrc;
7071}
7072
7073HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7074{
7075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7076 size_t cbIcon = mUserData->s.ovIcon.size();
7077 aIcon.resize(cbIcon);
7078 if (cbIcon)
7079 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7080 return S_OK;
7081}
7082
7083HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7084{
7085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7086 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7087 if (SUCCEEDED(hrc))
7088 {
7089 i_setModified(IsModified_MachineData);
7090 mUserData.backup();
7091 size_t cbIcon = aIcon.size();
7092 mUserData->s.ovIcon.resize(cbIcon);
7093 if (cbIcon)
7094 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7095 }
7096 return hrc;
7097}
7098
7099HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7100{
7101#ifdef VBOX_WITH_USB
7102 *aUSBProxyAvailable = true;
7103#else
7104 *aUSBProxyAvailable = false;
7105#endif
7106 return S_OK;
7107}
7108
7109HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7110{
7111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7112
7113 aVMProcessPriority = mUserData->s.strVMPriority;
7114
7115 return S_OK;
7116}
7117
7118HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7119{
7120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7121 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7122 if (SUCCEEDED(hrc))
7123 {
7124 /** @todo r=klaus: currently this is marked as not implemented, as
7125 * the code for setting the priority of the process is not there
7126 * (neither when starting the VM nor at runtime). */
7127 ReturnComNotImplemented();
7128 hrc = mUserData.backupEx();
7129 if (SUCCEEDED(hrc))
7130 {
7131 i_setModified(IsModified_MachineData);
7132 mUserData->s.strVMPriority = aVMProcessPriority;
7133 }
7134 }
7135 return hrc;
7136}
7137
7138HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7139 ComPtr<IProgress> &aProgress)
7140{
7141 ComObjPtr<Progress> pP;
7142 Progress *ppP = pP;
7143 IProgress *iP = static_cast<IProgress *>(ppP);
7144 IProgress **pProgress = &iP;
7145
7146 IMachine *pTarget = aTarget;
7147
7148 /* Convert the options. */
7149 RTCList<CloneOptions_T> optList;
7150 if (aOptions.size())
7151 for (size_t i = 0; i < aOptions.size(); ++i)
7152 optList.append(aOptions[i]);
7153
7154 if (optList.contains(CloneOptions_Link))
7155 {
7156 if (!i_isSnapshotMachine())
7157 return setError(E_INVALIDARG,
7158 tr("Linked clone can only be created from a snapshot"));
7159 if (aMode != CloneMode_MachineState)
7160 return setError(E_INVALIDARG,
7161 tr("Linked clone can only be created for a single machine state"));
7162 }
7163 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7164
7165 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7166
7167 HRESULT rc = pWorker->start(pProgress);
7168
7169 pP = static_cast<Progress *>(*pProgress);
7170 pP.queryInterfaceTo(aProgress.asOutParam());
7171
7172 return rc;
7173
7174}
7175
7176HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7177{
7178 NOREF(aProgress);
7179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7180
7181 // This check should always fail.
7182 HRESULT rc = i_checkStateDependency(MutableStateDep);
7183 if (FAILED(rc)) return rc;
7184
7185 AssertFailedReturn(E_NOTIMPL);
7186}
7187
7188HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7189{
7190 NOREF(aSavedStateFile);
7191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7192
7193 // This check should always fail.
7194 HRESULT rc = i_checkStateDependency(MutableStateDep);
7195 if (FAILED(rc)) return rc;
7196
7197 AssertFailedReturn(E_NOTIMPL);
7198}
7199
7200HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7201{
7202 NOREF(aFRemoveFile);
7203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7204
7205 // This check should always fail.
7206 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7207 if (FAILED(rc)) return rc;
7208
7209 AssertFailedReturn(E_NOTIMPL);
7210}
7211
7212// public methods for internal purposes
7213/////////////////////////////////////////////////////////////////////////////
7214
7215/**
7216 * Adds the given IsModified_* flag to the dirty flags of the machine.
7217 * This must be called either during i_loadSettings or under the machine write lock.
7218 * @param fl
7219 */
7220void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7221{
7222 mData->flModifications |= fl;
7223 if (fAllowStateModification && i_isStateModificationAllowed())
7224 mData->mCurrentStateModified = true;
7225}
7226
7227/**
7228 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7229 * care of the write locking.
7230 *
7231 * @param fModifications The flag to add.
7232 */
7233void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7234{
7235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7236 i_setModified(fModification, fAllowStateModification);
7237}
7238
7239/**
7240 * Saves the registry entry of this machine to the given configuration node.
7241 *
7242 * @param aEntryNode Node to save the registry entry to.
7243 *
7244 * @note locks this object for reading.
7245 */
7246HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7247{
7248 AutoLimitedCaller autoCaller(this);
7249 AssertComRCReturnRC(autoCaller.rc());
7250
7251 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7252
7253 data.uuid = mData->mUuid;
7254 data.strSettingsFile = mData->m_strConfigFile;
7255
7256 return S_OK;
7257}
7258
7259/**
7260 * Calculates the absolute path of the given path taking the directory of the
7261 * machine settings file as the current directory.
7262 *
7263 * @param aPath Path to calculate the absolute path for.
7264 * @param aResult Where to put the result (used only on success, can be the
7265 * same Utf8Str instance as passed in @a aPath).
7266 * @return IPRT result.
7267 *
7268 * @note Locks this object for reading.
7269 */
7270int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7271{
7272 AutoCaller autoCaller(this);
7273 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7274
7275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7276
7277 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7278
7279 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7280
7281 strSettingsDir.stripFilename();
7282 char folder[RTPATH_MAX];
7283 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7284 if (RT_SUCCESS(vrc))
7285 aResult = folder;
7286
7287 return vrc;
7288}
7289
7290/**
7291 * Copies strSource to strTarget, making it relative to the machine folder
7292 * if it is a subdirectory thereof, or simply copying it otherwise.
7293 *
7294 * @param strSource Path to evaluate and copy.
7295 * @param strTarget Buffer to receive target path.
7296 *
7297 * @note Locks this object for reading.
7298 */
7299void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7300 Utf8Str &strTarget)
7301{
7302 AutoCaller autoCaller(this);
7303 AssertComRCReturn(autoCaller.rc(), (void)0);
7304
7305 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7306
7307 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7308 // use strTarget as a temporary buffer to hold the machine settings dir
7309 strTarget = mData->m_strConfigFileFull;
7310 strTarget.stripFilename();
7311 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7312 {
7313 // is relative: then append what's left
7314 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7315 // for empty paths (only possible for subdirs) use "." to avoid
7316 // triggering default settings for not present config attributes.
7317 if (strTarget.isEmpty())
7318 strTarget = ".";
7319 }
7320 else
7321 // is not relative: then overwrite
7322 strTarget = strSource;
7323}
7324
7325/**
7326 * Returns the full path to the machine's log folder in the
7327 * \a aLogFolder argument.
7328 */
7329void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7330{
7331 AutoCaller autoCaller(this);
7332 AssertComRCReturnVoid(autoCaller.rc());
7333
7334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7335
7336 char szTmp[RTPATH_MAX];
7337 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7338 if (RT_SUCCESS(vrc))
7339 {
7340 if (szTmp[0] && !mUserData.isNull())
7341 {
7342 char szTmp2[RTPATH_MAX];
7343 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7344 if (RT_SUCCESS(vrc))
7345 aLogFolder = BstrFmt("%s%c%s",
7346 szTmp2,
7347 RTPATH_DELIMITER,
7348 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7349 }
7350 else
7351 vrc = VERR_PATH_IS_RELATIVE;
7352 }
7353
7354 if (RT_FAILURE(vrc))
7355 {
7356 // fallback if VBOX_USER_LOGHOME is not set or invalid
7357 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7358 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7359 aLogFolder.append(RTPATH_DELIMITER);
7360 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7361 }
7362}
7363
7364/**
7365 * Returns the full path to the machine's log file for an given index.
7366 */
7367Utf8Str Machine::i_getLogFilename(ULONG idx)
7368{
7369 Utf8Str logFolder;
7370 getLogFolder(logFolder);
7371 Assert(logFolder.length());
7372
7373 Utf8Str log;
7374 if (idx == 0)
7375 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7376#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7377 else if (idx == 1)
7378 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7379 else
7380 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7381#else
7382 else
7383 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7384#endif
7385 return log;
7386}
7387
7388/**
7389 * Returns the full path to the machine's hardened log file.
7390 */
7391Utf8Str Machine::i_getHardeningLogFilename(void)
7392{
7393 Utf8Str strFilename;
7394 getLogFolder(strFilename);
7395 Assert(strFilename.length());
7396 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7397 return strFilename;
7398}
7399
7400
7401/**
7402 * Composes a unique saved state filename based on the current system time. The filename is
7403 * granular to the second so this will work so long as no more than one snapshot is taken on
7404 * a machine per second.
7405 *
7406 * Before version 4.1, we used this formula for saved state files:
7407 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7408 * which no longer works because saved state files can now be shared between the saved state of the
7409 * "saved" machine and an online snapshot, and the following would cause problems:
7410 * 1) save machine
7411 * 2) create online snapshot from that machine state --> reusing saved state file
7412 * 3) save machine again --> filename would be reused, breaking the online snapshot
7413 *
7414 * So instead we now use a timestamp.
7415 *
7416 * @param str
7417 */
7418
7419void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7420{
7421 AutoCaller autoCaller(this);
7422 AssertComRCReturnVoid(autoCaller.rc());
7423
7424 {
7425 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7426 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7427 }
7428
7429 RTTIMESPEC ts;
7430 RTTimeNow(&ts);
7431 RTTIME time;
7432 RTTimeExplode(&time, &ts);
7433
7434 strStateFilePath += RTPATH_DELIMITER;
7435 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7436 time.i32Year, time.u8Month, time.u8MonthDay,
7437 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7438}
7439
7440/**
7441 * Returns the full path to the default video capture file.
7442 */
7443void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7444{
7445 AutoCaller autoCaller(this);
7446 AssertComRCReturnVoid(autoCaller.rc());
7447
7448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7449
7450 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7451 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7452 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7453}
7454
7455/**
7456 * Returns whether at least one USB controller is present for the VM.
7457 */
7458bool Machine::i_isUSBControllerPresent()
7459{
7460 AutoCaller autoCaller(this);
7461 AssertComRCReturn(autoCaller.rc(), false);
7462
7463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7464
7465 return (mUSBControllers->size() > 0);
7466}
7467
7468/**
7469 * @note Locks this object for writing, calls the client process
7470 * (inside the lock).
7471 */
7472HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7473 const Utf8Str &strFrontend,
7474 const Utf8Str &strEnvironment,
7475 ProgressProxy *aProgress)
7476{
7477 LogFlowThisFuncEnter();
7478
7479 AssertReturn(aControl, E_FAIL);
7480 AssertReturn(aProgress, E_FAIL);
7481 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7482
7483 AutoCaller autoCaller(this);
7484 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7485
7486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7487
7488 if (!mData->mRegistered)
7489 return setError(E_UNEXPECTED,
7490 tr("The machine '%s' is not registered"),
7491 mUserData->s.strName.c_str());
7492
7493 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7494
7495 /* The process started when launching a VM with separate UI/VM processes is always
7496 * the UI process, i.e. needs special handling as it won't claim the session. */
7497 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7498
7499 if (fSeparate)
7500 {
7501 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7502 return setError(VBOX_E_INVALID_OBJECT_STATE,
7503 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7504 mUserData->s.strName.c_str());
7505 }
7506 else
7507 {
7508 if ( mData->mSession.mState == SessionState_Locked
7509 || mData->mSession.mState == SessionState_Spawning
7510 || mData->mSession.mState == SessionState_Unlocking)
7511 return setError(VBOX_E_INVALID_OBJECT_STATE,
7512 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7513 mUserData->s.strName.c_str());
7514
7515 /* may not be busy */
7516 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7517 }
7518
7519 /* get the path to the executable */
7520 char szPath[RTPATH_MAX];
7521 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7522 size_t cchBufLeft = strlen(szPath);
7523 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7524 szPath[cchBufLeft] = 0;
7525 char *pszNamePart = szPath + cchBufLeft;
7526 cchBufLeft = sizeof(szPath) - cchBufLeft;
7527
7528 int vrc = VINF_SUCCESS;
7529 RTPROCESS pid = NIL_RTPROCESS;
7530
7531 RTENV env = RTENV_DEFAULT;
7532
7533 if (!strEnvironment.isEmpty())
7534 {
7535 char *newEnvStr = NULL;
7536
7537 do
7538 {
7539 /* clone the current environment */
7540 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7541 AssertRCBreakStmt(vrc2, vrc = vrc2);
7542
7543 newEnvStr = RTStrDup(strEnvironment.c_str());
7544 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7545
7546 /* put new variables to the environment
7547 * (ignore empty variable names here since RTEnv API
7548 * intentionally doesn't do that) */
7549 char *var = newEnvStr;
7550 for (char *p = newEnvStr; *p; ++p)
7551 {
7552 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7553 {
7554 *p = '\0';
7555 if (*var)
7556 {
7557 char *val = strchr(var, '=');
7558 if (val)
7559 {
7560 *val++ = '\0';
7561 vrc2 = RTEnvSetEx(env, var, val);
7562 }
7563 else
7564 vrc2 = RTEnvUnsetEx(env, var);
7565 if (RT_FAILURE(vrc2))
7566 break;
7567 }
7568 var = p + 1;
7569 }
7570 }
7571 if (RT_SUCCESS(vrc2) && *var)
7572 vrc2 = RTEnvPutEx(env, var);
7573
7574 AssertRCBreakStmt(vrc2, vrc = vrc2);
7575 }
7576 while (0);
7577
7578 if (newEnvStr != NULL)
7579 RTStrFree(newEnvStr);
7580 }
7581
7582 /* Hardening logging */
7583#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7584 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7585 {
7586 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7587 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7588 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7589 {
7590 Utf8Str strStartupLogDir = strHardeningLogFile;
7591 strStartupLogDir.stripFilename();
7592 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7593 file without stripping the file. */
7594 }
7595 strSupHardeningLogArg.append(strHardeningLogFile);
7596
7597 /* Remove legacy log filename to avoid confusion. */
7598 Utf8Str strOldStartupLogFile;
7599 getLogFolder(strOldStartupLogFile);
7600 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7601 RTFileDelete(strOldStartupLogFile.c_str());
7602 }
7603 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7604#else
7605 const char *pszSupHardeningLogArg = NULL;
7606#endif
7607
7608 Utf8Str strCanonicalName;
7609
7610#ifdef VBOX_WITH_QTGUI
7611 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7612 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7613 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7614 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7615 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7616 {
7617 strCanonicalName = "GUI/Qt";
7618# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7619 /* Modify the base path so that we don't need to use ".." below. */
7620 RTPathStripTrailingSlash(szPath);
7621 RTPathStripFilename(szPath);
7622 cchBufLeft = strlen(szPath);
7623 pszNamePart = szPath + cchBufLeft;
7624 cchBufLeft = sizeof(szPath) - cchBufLeft;
7625
7626# define OSX_APP_NAME "VirtualBoxVM"
7627# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7628
7629 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7630 if ( strAppOverride.contains(".")
7631 || strAppOverride.contains("/")
7632 || strAppOverride.contains("\\")
7633 || strAppOverride.contains(":"))
7634 strAppOverride.setNull();
7635 Utf8Str strAppPath;
7636 if (!strAppOverride.isEmpty())
7637 {
7638 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7639 Utf8Str strFullPath(szPath);
7640 strFullPath.append(strAppPath);
7641 /* there is a race, but people using this deserve the failure */
7642 if (!RTFileExists(strFullPath.c_str()))
7643 strAppOverride.setNull();
7644 }
7645 if (strAppOverride.isEmpty())
7646 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7647 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7648 strcpy(pszNamePart, strAppPath.c_str());
7649# else
7650 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7651 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7652 strcpy(pszNamePart, s_szVirtualBox_exe);
7653# endif
7654
7655 Utf8Str idStr = mData->mUuid.toString();
7656 const char *apszArgs[] =
7657 {
7658 szPath,
7659 "--comment", mUserData->s.strName.c_str(),
7660 "--startvm", idStr.c_str(),
7661 "--no-startvm-errormsgbox",
7662 NULL, /* For "--separate". */
7663 NULL, /* For "--sup-startup-log". */
7664 NULL
7665 };
7666 unsigned iArg = 6;
7667 if (fSeparate)
7668 apszArgs[iArg++] = "--separate";
7669 apszArgs[iArg++] = pszSupHardeningLogArg;
7670
7671 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7672 }
7673#else /* !VBOX_WITH_QTGUI */
7674 if (0)
7675 ;
7676#endif /* VBOX_WITH_QTGUI */
7677
7678 else
7679
7680#ifdef VBOX_WITH_VBOXSDL
7681 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7682 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7683 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7684 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7685 {
7686 strCanonicalName = "GUI/SDL";
7687 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7688 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7689 strcpy(pszNamePart, s_szVBoxSDL_exe);
7690
7691 Utf8Str idStr = mData->mUuid.toString();
7692 const char *apszArgs[] =
7693 {
7694 szPath,
7695 "--comment", mUserData->s.strName.c_str(),
7696 "--startvm", idStr.c_str(),
7697 NULL, /* For "--separate". */
7698 NULL, /* For "--sup-startup-log". */
7699 NULL
7700 };
7701 unsigned iArg = 5;
7702 if (fSeparate)
7703 apszArgs[iArg++] = "--separate";
7704 apszArgs[iArg++] = pszSupHardeningLogArg;
7705
7706 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7707 }
7708#else /* !VBOX_WITH_VBOXSDL */
7709 if (0)
7710 ;
7711#endif /* !VBOX_WITH_VBOXSDL */
7712
7713 else
7714
7715#ifdef VBOX_WITH_HEADLESS
7716 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7717 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7718 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7719 )
7720 {
7721 strCanonicalName = "headless";
7722 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7723 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7724 * and a VM works even if the server has not been installed.
7725 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7726 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7727 * differently in 4.0 and 3.x.
7728 */
7729 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7730 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7731 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7732
7733 Utf8Str idStr = mData->mUuid.toString();
7734 const char *apszArgs[] =
7735 {
7736 szPath,
7737 "--comment", mUserData->s.strName.c_str(),
7738 "--startvm", idStr.c_str(),
7739 "--vrde", "config",
7740 NULL, /* For "--capture". */
7741 NULL, /* For "--sup-startup-log". */
7742 NULL
7743 };
7744 unsigned iArg = 7;
7745 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7746 apszArgs[iArg++] = "--capture";
7747 apszArgs[iArg++] = pszSupHardeningLogArg;
7748
7749# ifdef RT_OS_WINDOWS
7750 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7751# else
7752 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7753# endif
7754 }
7755#else /* !VBOX_WITH_HEADLESS */
7756 if (0)
7757 ;
7758#endif /* !VBOX_WITH_HEADLESS */
7759 else
7760 {
7761 RTEnvDestroy(env);
7762 return setError(E_INVALIDARG,
7763 tr("Invalid frontend name: '%s'"),
7764 strFrontend.c_str());
7765 }
7766
7767 RTEnvDestroy(env);
7768
7769 if (RT_FAILURE(vrc))
7770 return setError(VBOX_E_IPRT_ERROR,
7771 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7772 mUserData->s.strName.c_str(), vrc);
7773
7774 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7775
7776 if (!fSeparate)
7777 {
7778 /*
7779 * Note that we don't release the lock here before calling the client,
7780 * because it doesn't need to call us back if called with a NULL argument.
7781 * Releasing the lock here is dangerous because we didn't prepare the
7782 * launch data yet, but the client we've just started may happen to be
7783 * too fast and call LockMachine() that will fail (because of PID, etc.),
7784 * so that the Machine will never get out of the Spawning session state.
7785 */
7786
7787 /* inform the session that it will be a remote one */
7788 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7789#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7790 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7791#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7792 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7793#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7794 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7795
7796 if (FAILED(rc))
7797 {
7798 /* restore the session state */
7799 mData->mSession.mState = SessionState_Unlocked;
7800 alock.release();
7801 mParent->i_addProcessToReap(pid);
7802 /* The failure may occur w/o any error info (from RPC), so provide one */
7803 return setError(VBOX_E_VM_ERROR,
7804 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7805 }
7806
7807 /* attach launch data to the machine */
7808 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7809 mData->mSession.mRemoteControls.push_back(aControl);
7810 mData->mSession.mProgress = aProgress;
7811 mData->mSession.mPID = pid;
7812 mData->mSession.mState = SessionState_Spawning;
7813 Assert(strCanonicalName.isNotEmpty());
7814 mData->mSession.mName = strCanonicalName;
7815 }
7816 else
7817 {
7818 /* For separate UI process we declare the launch as completed instantly, as the
7819 * actual headless VM start may or may not come. No point in remembering anything
7820 * yet, as what matters for us is when the headless VM gets started. */
7821 aProgress->i_notifyComplete(S_OK);
7822 }
7823
7824 alock.release();
7825 mParent->i_addProcessToReap(pid);
7826
7827 LogFlowThisFuncLeave();
7828 return S_OK;
7829}
7830
7831/**
7832 * Returns @c true if the given session machine instance has an open direct
7833 * session (and optionally also for direct sessions which are closing) and
7834 * returns the session control machine instance if so.
7835 *
7836 * Note that when the method returns @c false, the arguments remain unchanged.
7837 *
7838 * @param aMachine Session machine object.
7839 * @param aControl Direct session control object (optional).
7840 * @param aRequireVM If true then only allow VM sessions.
7841 * @param aAllowClosing If true then additionally a session which is currently
7842 * being closed will also be allowed.
7843 *
7844 * @note locks this object for reading.
7845 */
7846bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7847 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7848 bool aRequireVM /*= false*/,
7849 bool aAllowClosing /*= false*/)
7850{
7851 AutoLimitedCaller autoCaller(this);
7852 AssertComRCReturn(autoCaller.rc(), false);
7853
7854 /* just return false for inaccessible machines */
7855 if (getObjectState().getState() != ObjectState::Ready)
7856 return false;
7857
7858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7859
7860 if ( ( mData->mSession.mState == SessionState_Locked
7861 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7862 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7863 )
7864 {
7865 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7866
7867 aMachine = mData->mSession.mMachine;
7868
7869 if (aControl != NULL)
7870 *aControl = mData->mSession.mDirectControl;
7871
7872 return true;
7873 }
7874
7875 return false;
7876}
7877
7878/**
7879 * Returns @c true if the given machine has an spawning direct session.
7880 *
7881 * @note locks this object for reading.
7882 */
7883bool Machine::i_isSessionSpawning()
7884{
7885 AutoLimitedCaller autoCaller(this);
7886 AssertComRCReturn(autoCaller.rc(), false);
7887
7888 /* just return false for inaccessible machines */
7889 if (getObjectState().getState() != ObjectState::Ready)
7890 return false;
7891
7892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7893
7894 if (mData->mSession.mState == SessionState_Spawning)
7895 return true;
7896
7897 return false;
7898}
7899
7900/**
7901 * Called from the client watcher thread to check for unexpected client process
7902 * death during Session_Spawning state (e.g. before it successfully opened a
7903 * direct session).
7904 *
7905 * On Win32 and on OS/2, this method is called only when we've got the
7906 * direct client's process termination notification, so it always returns @c
7907 * true.
7908 *
7909 * On other platforms, this method returns @c true if the client process is
7910 * terminated and @c false if it's still alive.
7911 *
7912 * @note Locks this object for writing.
7913 */
7914bool Machine::i_checkForSpawnFailure()
7915{
7916 AutoCaller autoCaller(this);
7917 if (!autoCaller.isOk())
7918 {
7919 /* nothing to do */
7920 LogFlowThisFunc(("Already uninitialized!\n"));
7921 return true;
7922 }
7923
7924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7925
7926 if (mData->mSession.mState != SessionState_Spawning)
7927 {
7928 /* nothing to do */
7929 LogFlowThisFunc(("Not spawning any more!\n"));
7930 return true;
7931 }
7932
7933 HRESULT rc = S_OK;
7934
7935 /* PID not yet initialized, skip check. */
7936 if (mData->mSession.mPID == NIL_RTPROCESS)
7937 return false;
7938
7939 RTPROCSTATUS status;
7940 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7941
7942 if (vrc != VERR_PROCESS_RUNNING)
7943 {
7944 Utf8Str strExtraInfo;
7945
7946#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7947 /* If the startup logfile exists and is of non-zero length, tell the
7948 user to look there for more details to encourage them to attach it
7949 when reporting startup issues. */
7950 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7951 uint64_t cbStartupLogFile = 0;
7952 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7953 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7954 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7955#endif
7956
7957 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7958 rc = setError(E_FAIL,
7959 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7960 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7961 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7962 rc = setError(E_FAIL,
7963 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7964 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7965 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7966 rc = setError(E_FAIL,
7967 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7968 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7969 else
7970 rc = setError(E_FAIL,
7971 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7972 i_getName().c_str(), vrc, strExtraInfo.c_str());
7973 }
7974
7975 if (FAILED(rc))
7976 {
7977 /* Close the remote session, remove the remote control from the list
7978 * and reset session state to Closed (@note keep the code in sync with
7979 * the relevant part in LockMachine()). */
7980
7981 Assert(mData->mSession.mRemoteControls.size() == 1);
7982 if (mData->mSession.mRemoteControls.size() == 1)
7983 {
7984 ErrorInfoKeeper eik;
7985 mData->mSession.mRemoteControls.front()->Uninitialize();
7986 }
7987
7988 mData->mSession.mRemoteControls.clear();
7989 mData->mSession.mState = SessionState_Unlocked;
7990
7991 /* finalize the progress after setting the state */
7992 if (!mData->mSession.mProgress.isNull())
7993 {
7994 mData->mSession.mProgress->notifyComplete(rc);
7995 mData->mSession.mProgress.setNull();
7996 }
7997
7998 mData->mSession.mPID = NIL_RTPROCESS;
7999
8000 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8001 return true;
8002 }
8003
8004 return false;
8005}
8006
8007/**
8008 * Checks whether the machine can be registered. If so, commits and saves
8009 * all settings.
8010 *
8011 * @note Must be called from mParent's write lock. Locks this object and
8012 * children for writing.
8013 */
8014HRESULT Machine::i_prepareRegister()
8015{
8016 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8017
8018 AutoLimitedCaller autoCaller(this);
8019 AssertComRCReturnRC(autoCaller.rc());
8020
8021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8022
8023 /* wait for state dependents to drop to zero */
8024 i_ensureNoStateDependencies();
8025
8026 if (!mData->mAccessible)
8027 return setError(VBOX_E_INVALID_OBJECT_STATE,
8028 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8029 mUserData->s.strName.c_str(),
8030 mData->mUuid.toString().c_str());
8031
8032 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8033
8034 if (mData->mRegistered)
8035 return setError(VBOX_E_INVALID_OBJECT_STATE,
8036 tr("The machine '%s' with UUID {%s} is already registered"),
8037 mUserData->s.strName.c_str(),
8038 mData->mUuid.toString().c_str());
8039
8040 HRESULT rc = S_OK;
8041
8042 // Ensure the settings are saved. If we are going to be registered and
8043 // no config file exists yet, create it by calling i_saveSettings() too.
8044 if ( (mData->flModifications)
8045 || (!mData->pMachineConfigFile->fileExists())
8046 )
8047 {
8048 rc = i_saveSettings(NULL);
8049 // no need to check whether VirtualBox.xml needs saving too since
8050 // we can't have a machine XML file rename pending
8051 if (FAILED(rc)) return rc;
8052 }
8053
8054 /* more config checking goes here */
8055
8056 if (SUCCEEDED(rc))
8057 {
8058 /* we may have had implicit modifications we want to fix on success */
8059 i_commit();
8060
8061 mData->mRegistered = true;
8062 }
8063 else
8064 {
8065 /* we may have had implicit modifications we want to cancel on failure*/
8066 i_rollback(false /* aNotify */);
8067 }
8068
8069 return rc;
8070}
8071
8072/**
8073 * Increases the number of objects dependent on the machine state or on the
8074 * registered state. Guarantees that these two states will not change at least
8075 * until #releaseStateDependency() is called.
8076 *
8077 * Depending on the @a aDepType value, additional state checks may be made.
8078 * These checks will set extended error info on failure. See
8079 * #checkStateDependency() for more info.
8080 *
8081 * If this method returns a failure, the dependency is not added and the caller
8082 * is not allowed to rely on any particular machine state or registration state
8083 * value and may return the failed result code to the upper level.
8084 *
8085 * @param aDepType Dependency type to add.
8086 * @param aState Current machine state (NULL if not interested).
8087 * @param aRegistered Current registered state (NULL if not interested).
8088 *
8089 * @note Locks this object for writing.
8090 */
8091HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8092 MachineState_T *aState /* = NULL */,
8093 BOOL *aRegistered /* = NULL */)
8094{
8095 AutoCaller autoCaller(this);
8096 AssertComRCReturnRC(autoCaller.rc());
8097
8098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8099
8100 HRESULT rc = i_checkStateDependency(aDepType);
8101 if (FAILED(rc)) return rc;
8102
8103 {
8104 if (mData->mMachineStateChangePending != 0)
8105 {
8106 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8107 * drop to zero so don't add more. It may make sense to wait a bit
8108 * and retry before reporting an error (since the pending state
8109 * transition should be really quick) but let's just assert for
8110 * now to see if it ever happens on practice. */
8111
8112 AssertFailed();
8113
8114 return setError(E_ACCESSDENIED,
8115 tr("Machine state change is in progress. Please retry the operation later."));
8116 }
8117
8118 ++mData->mMachineStateDeps;
8119 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8120 }
8121
8122 if (aState)
8123 *aState = mData->mMachineState;
8124 if (aRegistered)
8125 *aRegistered = mData->mRegistered;
8126
8127 return S_OK;
8128}
8129
8130/**
8131 * Decreases the number of objects dependent on the machine state.
8132 * Must always complete the #addStateDependency() call after the state
8133 * dependency is no more necessary.
8134 */
8135void Machine::i_releaseStateDependency()
8136{
8137 AutoCaller autoCaller(this);
8138 AssertComRCReturnVoid(autoCaller.rc());
8139
8140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8141
8142 /* releaseStateDependency() w/o addStateDependency()? */
8143 AssertReturnVoid(mData->mMachineStateDeps != 0);
8144 -- mData->mMachineStateDeps;
8145
8146 if (mData->mMachineStateDeps == 0)
8147 {
8148 /* inform i_ensureNoStateDependencies() that there are no more deps */
8149 if (mData->mMachineStateChangePending != 0)
8150 {
8151 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8152 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8153 }
8154 }
8155}
8156
8157Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8158{
8159 /* start with nothing found */
8160 Utf8Str strResult("");
8161
8162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8163
8164 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8165 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8166 // found:
8167 strResult = it->second; // source is a Utf8Str
8168
8169 return strResult;
8170}
8171
8172// protected methods
8173/////////////////////////////////////////////////////////////////////////////
8174
8175/**
8176 * Performs machine state checks based on the @a aDepType value. If a check
8177 * fails, this method will set extended error info, otherwise it will return
8178 * S_OK. It is supposed, that on failure, the caller will immediately return
8179 * the return value of this method to the upper level.
8180 *
8181 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8182 *
8183 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8184 * current state of this machine object allows to change settings of the
8185 * machine (i.e. the machine is not registered, or registered but not running
8186 * and not saved). It is useful to call this method from Machine setters
8187 * before performing any change.
8188 *
8189 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8190 * as for MutableStateDep except that if the machine is saved, S_OK is also
8191 * returned. This is useful in setters which allow changing machine
8192 * properties when it is in the saved state.
8193 *
8194 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8195 * if the current state of this machine object allows to change runtime
8196 * changeable settings of the machine (i.e. the machine is not registered, or
8197 * registered but either running or not running and not saved). It is useful
8198 * to call this method from Machine setters before performing any changes to
8199 * runtime changeable settings.
8200 *
8201 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8202 * the same as for MutableOrRunningStateDep except that if the machine is
8203 * saved, S_OK is also returned. This is useful in setters which allow
8204 * changing runtime and saved state changeable machine properties.
8205 *
8206 * @param aDepType Dependency type to check.
8207 *
8208 * @note Non Machine based classes should use #addStateDependency() and
8209 * #releaseStateDependency() methods or the smart AutoStateDependency
8210 * template.
8211 *
8212 * @note This method must be called from under this object's read or write
8213 * lock.
8214 */
8215HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8216{
8217 switch (aDepType)
8218 {
8219 case AnyStateDep:
8220 {
8221 break;
8222 }
8223 case MutableStateDep:
8224 {
8225 if ( mData->mRegistered
8226 && ( !i_isSessionMachine()
8227 || ( mData->mMachineState != MachineState_Aborted
8228 && mData->mMachineState != MachineState_Teleported
8229 && mData->mMachineState != MachineState_PoweredOff
8230 )
8231 )
8232 )
8233 return setError(VBOX_E_INVALID_VM_STATE,
8234 tr("The machine is not mutable (state is %s)"),
8235 Global::stringifyMachineState(mData->mMachineState));
8236 break;
8237 }
8238 case MutableOrSavedStateDep:
8239 {
8240 if ( mData->mRegistered
8241 && ( !i_isSessionMachine()
8242 || ( mData->mMachineState != MachineState_Aborted
8243 && mData->mMachineState != MachineState_Teleported
8244 && mData->mMachineState != MachineState_Saved
8245 && mData->mMachineState != MachineState_PoweredOff
8246 )
8247 )
8248 )
8249 return setError(VBOX_E_INVALID_VM_STATE,
8250 tr("The machine is not mutable or saved (state is %s)"),
8251 Global::stringifyMachineState(mData->mMachineState));
8252 break;
8253 }
8254 case MutableOrRunningStateDep:
8255 {
8256 if ( mData->mRegistered
8257 && ( !i_isSessionMachine()
8258 || ( mData->mMachineState != MachineState_Aborted
8259 && mData->mMachineState != MachineState_Teleported
8260 && mData->mMachineState != MachineState_PoweredOff
8261 && !Global::IsOnline(mData->mMachineState)
8262 )
8263 )
8264 )
8265 return setError(VBOX_E_INVALID_VM_STATE,
8266 tr("The machine is not mutable or running (state is %s)"),
8267 Global::stringifyMachineState(mData->mMachineState));
8268 break;
8269 }
8270 case MutableOrSavedOrRunningStateDep:
8271 {
8272 if ( mData->mRegistered
8273 && ( !i_isSessionMachine()
8274 || ( mData->mMachineState != MachineState_Aborted
8275 && mData->mMachineState != MachineState_Teleported
8276 && mData->mMachineState != MachineState_Saved
8277 && mData->mMachineState != MachineState_PoweredOff
8278 && !Global::IsOnline(mData->mMachineState)
8279 )
8280 )
8281 )
8282 return setError(VBOX_E_INVALID_VM_STATE,
8283 tr("The machine is not mutable, saved or running (state is %s)"),
8284 Global::stringifyMachineState(mData->mMachineState));
8285 break;
8286 }
8287 }
8288
8289 return S_OK;
8290}
8291
8292/**
8293 * Helper to initialize all associated child objects and allocate data
8294 * structures.
8295 *
8296 * This method must be called as a part of the object's initialization procedure
8297 * (usually done in the #init() method).
8298 *
8299 * @note Must be called only from #init() or from #registeredInit().
8300 */
8301HRESULT Machine::initDataAndChildObjects()
8302{
8303 AutoCaller autoCaller(this);
8304 AssertComRCReturnRC(autoCaller.rc());
8305 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8306 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8307
8308 AssertReturn(!mData->mAccessible, E_FAIL);
8309
8310 /* allocate data structures */
8311 mSSData.allocate();
8312 mUserData.allocate();
8313 mHWData.allocate();
8314 mMediaData.allocate();
8315 mStorageControllers.allocate();
8316 mUSBControllers.allocate();
8317
8318 /* initialize mOSTypeId */
8319 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8320
8321 /* create associated BIOS settings object */
8322 unconst(mBIOSSettings).createObject();
8323 mBIOSSettings->init(this);
8324
8325 /* create an associated VRDE object (default is disabled) */
8326 unconst(mVRDEServer).createObject();
8327 mVRDEServer->init(this);
8328
8329 /* create associated serial port objects */
8330 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8331 {
8332 unconst(mSerialPorts[slot]).createObject();
8333 mSerialPorts[slot]->init(this, slot);
8334 }
8335
8336 /* create associated parallel port objects */
8337 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8338 {
8339 unconst(mParallelPorts[slot]).createObject();
8340 mParallelPorts[slot]->init(this, slot);
8341 }
8342
8343 /* create the audio adapter object (always present, default is disabled) */
8344 unconst(mAudioAdapter).createObject();
8345 mAudioAdapter->init(this);
8346
8347 /* create the USB device filters object (always present) */
8348 unconst(mUSBDeviceFilters).createObject();
8349 mUSBDeviceFilters->init(this);
8350
8351 /* create associated network adapter objects */
8352 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8353 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8354 {
8355 unconst(mNetworkAdapters[slot]).createObject();
8356 mNetworkAdapters[slot]->init(this, slot);
8357 }
8358
8359 /* create the bandwidth control */
8360 unconst(mBandwidthControl).createObject();
8361 mBandwidthControl->init(this);
8362
8363 return S_OK;
8364}
8365
8366/**
8367 * Helper to uninitialize all associated child objects and to free all data
8368 * structures.
8369 *
8370 * This method must be called as a part of the object's uninitialization
8371 * procedure (usually done in the #uninit() method).
8372 *
8373 * @note Must be called only from #uninit() or from #registeredInit().
8374 */
8375void Machine::uninitDataAndChildObjects()
8376{
8377 AutoCaller autoCaller(this);
8378 AssertComRCReturnVoid(autoCaller.rc());
8379 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8380 || getObjectState().getState() == ObjectState::Limited);
8381
8382 /* tell all our other child objects we've been uninitialized */
8383 if (mBandwidthControl)
8384 {
8385 mBandwidthControl->uninit();
8386 unconst(mBandwidthControl).setNull();
8387 }
8388
8389 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8390 {
8391 if (mNetworkAdapters[slot])
8392 {
8393 mNetworkAdapters[slot]->uninit();
8394 unconst(mNetworkAdapters[slot]).setNull();
8395 }
8396 }
8397
8398 if (mUSBDeviceFilters)
8399 {
8400 mUSBDeviceFilters->uninit();
8401 unconst(mUSBDeviceFilters).setNull();
8402 }
8403
8404 if (mAudioAdapter)
8405 {
8406 mAudioAdapter->uninit();
8407 unconst(mAudioAdapter).setNull();
8408 }
8409
8410 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8411 {
8412 if (mParallelPorts[slot])
8413 {
8414 mParallelPorts[slot]->uninit();
8415 unconst(mParallelPorts[slot]).setNull();
8416 }
8417 }
8418
8419 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8420 {
8421 if (mSerialPorts[slot])
8422 {
8423 mSerialPorts[slot]->uninit();
8424 unconst(mSerialPorts[slot]).setNull();
8425 }
8426 }
8427
8428 if (mVRDEServer)
8429 {
8430 mVRDEServer->uninit();
8431 unconst(mVRDEServer).setNull();
8432 }
8433
8434 if (mBIOSSettings)
8435 {
8436 mBIOSSettings->uninit();
8437 unconst(mBIOSSettings).setNull();
8438 }
8439
8440 /* Deassociate media (only when a real Machine or a SnapshotMachine
8441 * instance is uninitialized; SessionMachine instances refer to real
8442 * Machine media). This is necessary for a clean re-initialization of
8443 * the VM after successfully re-checking the accessibility state. Note
8444 * that in case of normal Machine or SnapshotMachine uninitialization (as
8445 * a result of unregistering or deleting the snapshot), outdated media
8446 * attachments will already be uninitialized and deleted, so this
8447 * code will not affect them. */
8448 if ( !!mMediaData
8449 && (!i_isSessionMachine())
8450 )
8451 {
8452 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8453 it != mMediaData->mAttachments.end();
8454 ++it)
8455 {
8456 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8457 if (pMedium.isNull())
8458 continue;
8459 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8460 AssertComRC(rc);
8461 }
8462 }
8463
8464 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8465 {
8466 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8467 if (mData->mFirstSnapshot)
8468 {
8469 // snapshots tree is protected by machine write lock; strictly
8470 // this isn't necessary here since we're deleting the entire
8471 // machine, but otherwise we assert in Snapshot::uninit()
8472 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8473 mData->mFirstSnapshot->uninit();
8474 mData->mFirstSnapshot.setNull();
8475 }
8476
8477 mData->mCurrentSnapshot.setNull();
8478 }
8479
8480 /* free data structures (the essential mData structure is not freed here
8481 * since it may be still in use) */
8482 mMediaData.free();
8483 mStorageControllers.free();
8484 mUSBControllers.free();
8485 mHWData.free();
8486 mUserData.free();
8487 mSSData.free();
8488}
8489
8490/**
8491 * Returns a pointer to the Machine object for this machine that acts like a
8492 * parent for complex machine data objects such as shared folders, etc.
8493 *
8494 * For primary Machine objects and for SnapshotMachine objects, returns this
8495 * object's pointer itself. For SessionMachine objects, returns the peer
8496 * (primary) machine pointer.
8497 */
8498Machine* Machine::i_getMachine()
8499{
8500 if (i_isSessionMachine())
8501 return (Machine*)mPeer;
8502 return this;
8503}
8504
8505/**
8506 * Makes sure that there are no machine state dependents. If necessary, waits
8507 * for the number of dependents to drop to zero.
8508 *
8509 * Make sure this method is called from under this object's write lock to
8510 * guarantee that no new dependents may be added when this method returns
8511 * control to the caller.
8512 *
8513 * @note Locks this object for writing. The lock will be released while waiting
8514 * (if necessary).
8515 *
8516 * @warning To be used only in methods that change the machine state!
8517 */
8518void Machine::i_ensureNoStateDependencies()
8519{
8520 AssertReturnVoid(isWriteLockOnCurrentThread());
8521
8522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8523
8524 /* Wait for all state dependents if necessary */
8525 if (mData->mMachineStateDeps != 0)
8526 {
8527 /* lazy semaphore creation */
8528 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8529 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8530
8531 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8532 mData->mMachineStateDeps));
8533
8534 ++mData->mMachineStateChangePending;
8535
8536 /* reset the semaphore before waiting, the last dependent will signal
8537 * it */
8538 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8539
8540 alock.release();
8541
8542 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8543
8544 alock.acquire();
8545
8546 -- mData->mMachineStateChangePending;
8547 }
8548}
8549
8550/**
8551 * Changes the machine state and informs callbacks.
8552 *
8553 * This method is not intended to fail so it either returns S_OK or asserts (and
8554 * returns a failure).
8555 *
8556 * @note Locks this object for writing.
8557 */
8558HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8559{
8560 LogFlowThisFuncEnter();
8561 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8562 Assert(aMachineState != MachineState_Null);
8563
8564 AutoCaller autoCaller(this);
8565 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8566
8567 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8568
8569 /* wait for state dependents to drop to zero */
8570 i_ensureNoStateDependencies();
8571
8572 MachineState_T const enmOldState = mData->mMachineState;
8573 if (enmOldState != aMachineState)
8574 {
8575 mData->mMachineState = aMachineState;
8576 RTTimeNow(&mData->mLastStateChange);
8577
8578#ifdef VBOX_WITH_DTRACE_R3_MAIN
8579 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8580#endif
8581 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8582 }
8583
8584 LogFlowThisFuncLeave();
8585 return S_OK;
8586}
8587
8588/**
8589 * Searches for a shared folder with the given logical name
8590 * in the collection of shared folders.
8591 *
8592 * @param aName logical name of the shared folder
8593 * @param aSharedFolder where to return the found object
8594 * @param aSetError whether to set the error info if the folder is
8595 * not found
8596 * @return
8597 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8598 *
8599 * @note
8600 * must be called from under the object's lock!
8601 */
8602HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8603 ComObjPtr<SharedFolder> &aSharedFolder,
8604 bool aSetError /* = false */)
8605{
8606 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8607 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8608 it != mHWData->mSharedFolders.end();
8609 ++it)
8610 {
8611 SharedFolder *pSF = *it;
8612 AutoCaller autoCaller(pSF);
8613 if (pSF->i_getName() == aName)
8614 {
8615 aSharedFolder = pSF;
8616 rc = S_OK;
8617 break;
8618 }
8619 }
8620
8621 if (aSetError && FAILED(rc))
8622 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8623
8624 return rc;
8625}
8626
8627/**
8628 * Initializes all machine instance data from the given settings structures
8629 * from XML. The exception is the machine UUID which needs special handling
8630 * depending on the caller's use case, so the caller needs to set that herself.
8631 *
8632 * This gets called in several contexts during machine initialization:
8633 *
8634 * -- When machine XML exists on disk already and needs to be loaded into memory,
8635 * for example, from registeredInit() to load all registered machines on
8636 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8637 * attached to the machine should be part of some media registry already.
8638 *
8639 * -- During OVF import, when a machine config has been constructed from an
8640 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8641 * ensure that the media listed as attachments in the config (which have
8642 * been imported from the OVF) receive the correct registry ID.
8643 *
8644 * -- During VM cloning.
8645 *
8646 * @param config Machine settings from XML.
8647 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8648 * for each attached medium in the config.
8649 * @return
8650 */
8651HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8652 const Guid *puuidRegistry)
8653{
8654 // copy name, description, OS type, teleporter, UTC etc.
8655 mUserData->s = config.machineUserData;
8656
8657 // look up the object by Id to check it is valid
8658 ComPtr<IGuestOSType> guestOSType;
8659 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8660 guestOSType.asOutParam());
8661 if (FAILED(rc)) return rc;
8662
8663 // stateFile (optional)
8664 if (config.strStateFile.isEmpty())
8665 mSSData->strStateFilePath.setNull();
8666 else
8667 {
8668 Utf8Str stateFilePathFull(config.strStateFile);
8669 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8670 if (RT_FAILURE(vrc))
8671 return setError(E_FAIL,
8672 tr("Invalid saved state file path '%s' (%Rrc)"),
8673 config.strStateFile.c_str(),
8674 vrc);
8675 mSSData->strStateFilePath = stateFilePathFull;
8676 }
8677
8678 // snapshot folder needs special processing so set it again
8679 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8680 if (FAILED(rc)) return rc;
8681
8682 /* Copy the extra data items (Not in any case config is already the same as
8683 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8684 * make sure the extra data map is copied). */
8685 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8686
8687 /* currentStateModified (optional, default is true) */
8688 mData->mCurrentStateModified = config.fCurrentStateModified;
8689
8690 mData->mLastStateChange = config.timeLastStateChange;
8691
8692 /*
8693 * note: all mUserData members must be assigned prior this point because
8694 * we need to commit changes in order to let mUserData be shared by all
8695 * snapshot machine instances.
8696 */
8697 mUserData.commitCopy();
8698
8699 // machine registry, if present (must be loaded before snapshots)
8700 if (config.canHaveOwnMediaRegistry())
8701 {
8702 // determine machine folder
8703 Utf8Str strMachineFolder = i_getSettingsFileFull();
8704 strMachineFolder.stripFilename();
8705 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8706 config.mediaRegistry,
8707 strMachineFolder);
8708 if (FAILED(rc)) return rc;
8709 }
8710
8711 /* Snapshot node (optional) */
8712 size_t cRootSnapshots;
8713 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8714 {
8715 // there must be only one root snapshot
8716 Assert(cRootSnapshots == 1);
8717
8718 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8719
8720 rc = i_loadSnapshot(snap,
8721 config.uuidCurrentSnapshot,
8722 NULL); // no parent == first snapshot
8723 if (FAILED(rc)) return rc;
8724 }
8725
8726 // hardware data
8727 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8728 if (FAILED(rc)) return rc;
8729
8730 /*
8731 * NOTE: the assignment below must be the last thing to do,
8732 * otherwise it will be not possible to change the settings
8733 * somewhere in the code above because all setters will be
8734 * blocked by i_checkStateDependency(MutableStateDep).
8735 */
8736
8737 /* set the machine state to Aborted or Saved when appropriate */
8738 if (config.fAborted)
8739 {
8740 mSSData->strStateFilePath.setNull();
8741
8742 /* no need to use i_setMachineState() during init() */
8743 mData->mMachineState = MachineState_Aborted;
8744 }
8745 else if (!mSSData->strStateFilePath.isEmpty())
8746 {
8747 /* no need to use i_setMachineState() during init() */
8748 mData->mMachineState = MachineState_Saved;
8749 }
8750
8751 // after loading settings, we are no longer different from the XML on disk
8752 mData->flModifications = 0;
8753
8754 return S_OK;
8755}
8756
8757/**
8758 * Recursively loads all snapshots starting from the given.
8759 *
8760 * @param aNode <Snapshot> node.
8761 * @param aCurSnapshotId Current snapshot ID from the settings file.
8762 * @param aParentSnapshot Parent snapshot.
8763 */
8764HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8765 const Guid &aCurSnapshotId,
8766 Snapshot *aParentSnapshot)
8767{
8768 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8769 AssertReturn(!i_isSessionMachine(), E_FAIL);
8770
8771 HRESULT rc = S_OK;
8772
8773 Utf8Str strStateFile;
8774 if (!data.strStateFile.isEmpty())
8775 {
8776 /* optional */
8777 strStateFile = data.strStateFile;
8778 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8779 if (RT_FAILURE(vrc))
8780 return setError(E_FAIL,
8781 tr("Invalid saved state file path '%s' (%Rrc)"),
8782 strStateFile.c_str(),
8783 vrc);
8784 }
8785
8786 /* create a snapshot machine object */
8787 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8788 pSnapshotMachine.createObject();
8789 rc = pSnapshotMachine->initFromSettings(this,
8790 data.hardware,
8791 &data.debugging,
8792 &data.autostart,
8793 data.uuid.ref(),
8794 strStateFile);
8795 if (FAILED(rc)) return rc;
8796
8797 /* create a snapshot object */
8798 ComObjPtr<Snapshot> pSnapshot;
8799 pSnapshot.createObject();
8800 /* initialize the snapshot */
8801 rc = pSnapshot->init(mParent, // VirtualBox object
8802 data.uuid,
8803 data.strName,
8804 data.strDescription,
8805 data.timestamp,
8806 pSnapshotMachine,
8807 aParentSnapshot);
8808 if (FAILED(rc)) return rc;
8809
8810 /* memorize the first snapshot if necessary */
8811 if (!mData->mFirstSnapshot)
8812 mData->mFirstSnapshot = pSnapshot;
8813
8814 /* memorize the current snapshot when appropriate */
8815 if ( !mData->mCurrentSnapshot
8816 && pSnapshot->i_getId() == aCurSnapshotId
8817 )
8818 mData->mCurrentSnapshot = pSnapshot;
8819
8820 // now create the children
8821 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8822 it != data.llChildSnapshots.end();
8823 ++it)
8824 {
8825 const settings::Snapshot &childData = *it;
8826 // recurse
8827 rc = i_loadSnapshot(childData,
8828 aCurSnapshotId,
8829 pSnapshot); // parent = the one we created above
8830 if (FAILED(rc)) return rc;
8831 }
8832
8833 return rc;
8834}
8835
8836/**
8837 * Loads settings into mHWData.
8838 *
8839 * @param data Reference to the hardware settings.
8840 * @param pDbg Pointer to the debugging settings.
8841 * @param pAutostart Pointer to the autostart settings.
8842 */
8843HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8844 const Guid *puuidSnapshot,
8845 const settings::Hardware &data,
8846 const settings::Debugging *pDbg,
8847 const settings::Autostart *pAutostart)
8848{
8849 AssertReturn(!i_isSessionMachine(), E_FAIL);
8850
8851 HRESULT rc = S_OK;
8852
8853 try
8854 {
8855 /* The hardware version attribute (optional). */
8856 mHWData->mHWVersion = data.strVersion;
8857 mHWData->mHardwareUUID = data.uuid;
8858
8859 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8860 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8861 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8862 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8863 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8864 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8865 mHWData->mPAEEnabled = data.fPAE;
8866 mHWData->mLongMode = data.enmLongMode;
8867 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8868 mHWData->mAPIC = data.fAPIC;
8869 mHWData->mX2APIC = data.fX2APIC;
8870 mHWData->mCPUCount = data.cCPUs;
8871 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8872 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8873 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8874 mHWData->mCpuProfile = data.strCpuProfile;
8875
8876 // cpu
8877 if (mHWData->mCPUHotPlugEnabled)
8878 {
8879 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8880 it != data.llCpus.end();
8881 ++it)
8882 {
8883 const settings::Cpu &cpu = *it;
8884
8885 mHWData->mCPUAttached[cpu.ulId] = true;
8886 }
8887 }
8888
8889 // cpuid leafs
8890 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8891 it != data.llCpuIdLeafs.end();
8892 ++it)
8893 {
8894 const settings::CpuIdLeaf &leaf = *it;
8895
8896 switch (leaf.ulId)
8897 {
8898 case 0x0:
8899 case 0x1:
8900 case 0x2:
8901 case 0x3:
8902 case 0x4:
8903 case 0x5:
8904 case 0x6:
8905 case 0x7:
8906 case 0x8:
8907 case 0x9:
8908 case 0xA:
8909 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8910 break;
8911
8912 case 0x80000000:
8913 case 0x80000001:
8914 case 0x80000002:
8915 case 0x80000003:
8916 case 0x80000004:
8917 case 0x80000005:
8918 case 0x80000006:
8919 case 0x80000007:
8920 case 0x80000008:
8921 case 0x80000009:
8922 case 0x8000000A:
8923 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8924 break;
8925
8926 default:
8927 /* just ignore */
8928 break;
8929 }
8930 }
8931
8932 mHWData->mMemorySize = data.ulMemorySizeMB;
8933 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8934
8935 // boot order
8936 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8937 {
8938 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8939 if (it == data.mapBootOrder.end())
8940 mHWData->mBootOrder[i] = DeviceType_Null;
8941 else
8942 mHWData->mBootOrder[i] = it->second;
8943 }
8944
8945 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8946 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8947 mHWData->mMonitorCount = data.cMonitors;
8948 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8949 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8950 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8951 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8952 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8953 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8954 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8955 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8956 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8957 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8958 if (!data.strVideoCaptureFile.isEmpty())
8959 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8960 else
8961 mHWData->mVideoCaptureFile.setNull();
8962 mHWData->mFirmwareType = data.firmwareType;
8963 mHWData->mPointingHIDType = data.pointingHIDType;
8964 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8965 mHWData->mChipsetType = data.chipsetType;
8966 mHWData->mParavirtProvider = data.paravirtProvider;
8967 mHWData->mParavirtDebug = data.strParavirtDebug;
8968 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8969 mHWData->mHPETEnabled = data.fHPETEnabled;
8970
8971 /* VRDEServer */
8972 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8973 if (FAILED(rc)) return rc;
8974
8975 /* BIOS */
8976 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8977 if (FAILED(rc)) return rc;
8978
8979 // Bandwidth control (must come before network adapters)
8980 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8981 if (FAILED(rc)) return rc;
8982
8983 /* Shared folders */
8984 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8985 it != data.usbSettings.llUSBControllers.end();
8986 ++it)
8987 {
8988 const settings::USBController &settingsCtrl = *it;
8989 ComObjPtr<USBController> newCtrl;
8990
8991 newCtrl.createObject();
8992 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8993 mUSBControllers->push_back(newCtrl);
8994 }
8995
8996 /* USB device filters */
8997 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8998 if (FAILED(rc)) return rc;
8999
9000 // network adapters
9001 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9002 size_t oldCount = mNetworkAdapters.size();
9003 if (newCount > oldCount)
9004 {
9005 mNetworkAdapters.resize(newCount);
9006 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9007 {
9008 unconst(mNetworkAdapters[slot]).createObject();
9009 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9010 }
9011 }
9012 else if (newCount < oldCount)
9013 mNetworkAdapters.resize(newCount);
9014 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9015 it != data.llNetworkAdapters.end();
9016 ++it)
9017 {
9018 const settings::NetworkAdapter &nic = *it;
9019
9020 /* slot unicity is guaranteed by XML Schema */
9021 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9022 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9023 if (FAILED(rc)) return rc;
9024 }
9025
9026 // serial ports
9027 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9028 it != data.llSerialPorts.end();
9029 ++it)
9030 {
9031 const settings::SerialPort &s = *it;
9032
9033 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9034 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9035 if (FAILED(rc)) return rc;
9036 }
9037
9038 // parallel ports (optional)
9039 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9040 it != data.llParallelPorts.end();
9041 ++it)
9042 {
9043 const settings::ParallelPort &p = *it;
9044
9045 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9046 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9047 if (FAILED(rc)) return rc;
9048 }
9049
9050 /* AudioAdapter */
9051 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9052 if (FAILED(rc)) return rc;
9053
9054 /* storage controllers */
9055 rc = i_loadStorageControllers(data.storage,
9056 puuidRegistry,
9057 puuidSnapshot);
9058 if (FAILED(rc)) return rc;
9059
9060 /* Shared folders */
9061 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9062 it != data.llSharedFolders.end();
9063 ++it)
9064 {
9065 const settings::SharedFolder &sf = *it;
9066
9067 ComObjPtr<SharedFolder> sharedFolder;
9068 /* Check for double entries. Not allowed! */
9069 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9070 if (SUCCEEDED(rc))
9071 return setError(VBOX_E_OBJECT_IN_USE,
9072 tr("Shared folder named '%s' already exists"),
9073 sf.strName.c_str());
9074
9075 /* Create the new shared folder. Don't break on error. This will be
9076 * reported when the machine starts. */
9077 sharedFolder.createObject();
9078 rc = sharedFolder->init(i_getMachine(),
9079 sf.strName,
9080 sf.strHostPath,
9081 RT_BOOL(sf.fWritable),
9082 RT_BOOL(sf.fAutoMount),
9083 false /* fFailOnError */);
9084 if (FAILED(rc)) return rc;
9085 mHWData->mSharedFolders.push_back(sharedFolder);
9086 }
9087
9088 // Clipboard
9089 mHWData->mClipboardMode = data.clipboardMode;
9090
9091 // drag'n'drop
9092 mHWData->mDnDMode = data.dndMode;
9093
9094 // guest settings
9095 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9096
9097 // IO settings
9098 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9099 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9100
9101 // Host PCI devices
9102 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9103 it != data.pciAttachments.end();
9104 ++it)
9105 {
9106 const settings::HostPCIDeviceAttachment &hpda = *it;
9107 ComObjPtr<PCIDeviceAttachment> pda;
9108
9109 pda.createObject();
9110 pda->i_loadSettings(this, hpda);
9111 mHWData->mPCIDeviceAssignments.push_back(pda);
9112 }
9113
9114 /*
9115 * (The following isn't really real hardware, but it lives in HWData
9116 * for reasons of convenience.)
9117 */
9118
9119#ifdef VBOX_WITH_GUEST_PROPS
9120 /* Guest properties (optional) */
9121
9122 /* Only load transient guest properties for configs which have saved
9123 * state, because there shouldn't be any for powered off VMs. The same
9124 * logic applies for snapshots, as offline snapshots shouldn't have
9125 * any such properties. They confuse the code in various places.
9126 * Note: can't rely on the machine state, as it isn't set yet. */
9127 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9128 /* apologies for the hacky unconst() usage, but this needs hacking
9129 * actually inconsistent settings into consistency, otherwise there
9130 * will be some corner cases where the inconsistency survives
9131 * surprisingly long without getting fixed, especially for snapshots
9132 * as there are no config changes. */
9133 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9134 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
9135 it != llGuestProperties.end();
9136 /*nothing*/)
9137 {
9138 const settings::GuestProperty &prop = *it;
9139 uint32_t fFlags = guestProp::NILFLAG;
9140 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9141 if ( fSkipTransientGuestProperties
9142 && ( fFlags & guestProp::TRANSIENT
9143 || fFlags & guestProp::TRANSRESET))
9144 {
9145 it = llGuestProperties.erase(it);
9146 continue;
9147 }
9148 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9149 mHWData->mGuestProperties[prop.strName] = property;
9150 ++it;
9151 }
9152#endif /* VBOX_WITH_GUEST_PROPS defined */
9153
9154 rc = i_loadDebugging(pDbg);
9155 if (FAILED(rc))
9156 return rc;
9157
9158 mHWData->mAutostart = *pAutostart;
9159
9160 /* default frontend */
9161 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9162 }
9163 catch(std::bad_alloc &)
9164 {
9165 return E_OUTOFMEMORY;
9166 }
9167
9168 AssertComRC(rc);
9169 return rc;
9170}
9171
9172/**
9173 * Called from Machine::loadHardware() to load the debugging settings of the
9174 * machine.
9175 *
9176 * @param pDbg Pointer to the settings.
9177 */
9178HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9179{
9180 mHWData->mDebugging = *pDbg;
9181 /* no more processing currently required, this will probably change. */
9182 return S_OK;
9183}
9184
9185/**
9186 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9187 *
9188 * @param data
9189 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9190 * @param puuidSnapshot
9191 * @return
9192 */
9193HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9194 const Guid *puuidRegistry,
9195 const Guid *puuidSnapshot)
9196{
9197 AssertReturn(!i_isSessionMachine(), E_FAIL);
9198
9199 HRESULT rc = S_OK;
9200
9201 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9202 it != data.llStorageControllers.end();
9203 ++it)
9204 {
9205 const settings::StorageController &ctlData = *it;
9206
9207 ComObjPtr<StorageController> pCtl;
9208 /* Try to find one with the name first. */
9209 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9210 if (SUCCEEDED(rc))
9211 return setError(VBOX_E_OBJECT_IN_USE,
9212 tr("Storage controller named '%s' already exists"),
9213 ctlData.strName.c_str());
9214
9215 pCtl.createObject();
9216 rc = pCtl->init(this,
9217 ctlData.strName,
9218 ctlData.storageBus,
9219 ctlData.ulInstance,
9220 ctlData.fBootable);
9221 if (FAILED(rc)) return rc;
9222
9223 mStorageControllers->push_back(pCtl);
9224
9225 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9226 if (FAILED(rc)) return rc;
9227
9228 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9229 if (FAILED(rc)) return rc;
9230
9231 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9232 if (FAILED(rc)) return rc;
9233
9234 /* Load the attached devices now. */
9235 rc = i_loadStorageDevices(pCtl,
9236 ctlData,
9237 puuidRegistry,
9238 puuidSnapshot);
9239 if (FAILED(rc)) return rc;
9240 }
9241
9242 return S_OK;
9243}
9244
9245/**
9246 * Called from i_loadStorageControllers for a controller's devices.
9247 *
9248 * @param aStorageController
9249 * @param data
9250 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9251 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9252 * @return
9253 */
9254HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9255 const settings::StorageController &data,
9256 const Guid *puuidRegistry,
9257 const Guid *puuidSnapshot)
9258{
9259 HRESULT rc = S_OK;
9260
9261 /* paranoia: detect duplicate attachments */
9262 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9263 it != data.llAttachedDevices.end();
9264 ++it)
9265 {
9266 const settings::AttachedDevice &ad = *it;
9267
9268 for (settings::AttachedDevicesList::const_iterator it2 = it;
9269 it2 != data.llAttachedDevices.end();
9270 ++it2)
9271 {
9272 if (it == it2)
9273 continue;
9274
9275 const settings::AttachedDevice &ad2 = *it2;
9276
9277 if ( ad.lPort == ad2.lPort
9278 && ad.lDevice == ad2.lDevice)
9279 {
9280 return setError(E_FAIL,
9281 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9282 aStorageController->i_getName().c_str(),
9283 ad.lPort,
9284 ad.lDevice,
9285 mUserData->s.strName.c_str());
9286 }
9287 }
9288 }
9289
9290 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9291 it != data.llAttachedDevices.end();
9292 ++it)
9293 {
9294 const settings::AttachedDevice &dev = *it;
9295 ComObjPtr<Medium> medium;
9296
9297 switch (dev.deviceType)
9298 {
9299 case DeviceType_Floppy:
9300 case DeviceType_DVD:
9301 if (dev.strHostDriveSrc.isNotEmpty())
9302 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9303 false /* fRefresh */, medium);
9304 else
9305 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9306 dev.uuid,
9307 false /* fRefresh */,
9308 false /* aSetError */,
9309 medium);
9310 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9311 // This is not an error. The host drive or UUID might have vanished, so just go
9312 // ahead without this removeable medium attachment
9313 rc = S_OK;
9314 break;
9315
9316 case DeviceType_HardDisk:
9317 {
9318 /* find a hard disk by UUID */
9319 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9320 if (FAILED(rc))
9321 {
9322 if (i_isSnapshotMachine())
9323 {
9324 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9325 // so the user knows that the bad disk is in a snapshot somewhere
9326 com::ErrorInfo info;
9327 return setError(E_FAIL,
9328 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9329 puuidSnapshot->raw(),
9330 info.getText().raw());
9331 }
9332 else
9333 return rc;
9334 }
9335
9336 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9337
9338 if (medium->i_getType() == MediumType_Immutable)
9339 {
9340 if (i_isSnapshotMachine())
9341 return setError(E_FAIL,
9342 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9343 "of the virtual machine '%s' ('%s')"),
9344 medium->i_getLocationFull().c_str(),
9345 dev.uuid.raw(),
9346 puuidSnapshot->raw(),
9347 mUserData->s.strName.c_str(),
9348 mData->m_strConfigFileFull.c_str());
9349
9350 return setError(E_FAIL,
9351 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9352 medium->i_getLocationFull().c_str(),
9353 dev.uuid.raw(),
9354 mUserData->s.strName.c_str(),
9355 mData->m_strConfigFileFull.c_str());
9356 }
9357
9358 if (medium->i_getType() == MediumType_MultiAttach)
9359 {
9360 if (i_isSnapshotMachine())
9361 return setError(E_FAIL,
9362 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9363 "of the virtual machine '%s' ('%s')"),
9364 medium->i_getLocationFull().c_str(),
9365 dev.uuid.raw(),
9366 puuidSnapshot->raw(),
9367 mUserData->s.strName.c_str(),
9368 mData->m_strConfigFileFull.c_str());
9369
9370 return setError(E_FAIL,
9371 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9372 medium->i_getLocationFull().c_str(),
9373 dev.uuid.raw(),
9374 mUserData->s.strName.c_str(),
9375 mData->m_strConfigFileFull.c_str());
9376 }
9377
9378 if ( !i_isSnapshotMachine()
9379 && medium->i_getChildren().size() != 0
9380 )
9381 return setError(E_FAIL,
9382 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9383 "because it has %d differencing child hard disks"),
9384 medium->i_getLocationFull().c_str(),
9385 dev.uuid.raw(),
9386 mUserData->s.strName.c_str(),
9387 mData->m_strConfigFileFull.c_str(),
9388 medium->i_getChildren().size());
9389
9390 if (i_findAttachment(mMediaData->mAttachments,
9391 medium))
9392 return setError(E_FAIL,
9393 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9394 medium->i_getLocationFull().c_str(),
9395 dev.uuid.raw(),
9396 mUserData->s.strName.c_str(),
9397 mData->m_strConfigFileFull.c_str());
9398
9399 break;
9400 }
9401
9402 default:
9403 return setError(E_FAIL,
9404 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9405 medium->i_getLocationFull().c_str(),
9406 mUserData->s.strName.c_str(),
9407 mData->m_strConfigFileFull.c_str());
9408 }
9409
9410 if (FAILED(rc))
9411 break;
9412
9413 /* Bandwidth groups are loaded at this point. */
9414 ComObjPtr<BandwidthGroup> pBwGroup;
9415
9416 if (!dev.strBwGroup.isEmpty())
9417 {
9418 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9419 if (FAILED(rc))
9420 return setError(E_FAIL,
9421 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9422 medium->i_getLocationFull().c_str(),
9423 dev.strBwGroup.c_str(),
9424 mUserData->s.strName.c_str(),
9425 mData->m_strConfigFileFull.c_str());
9426 pBwGroup->i_reference();
9427 }
9428
9429 const Bstr controllerName = aStorageController->i_getName();
9430 ComObjPtr<MediumAttachment> pAttachment;
9431 pAttachment.createObject();
9432 rc = pAttachment->init(this,
9433 medium,
9434 controllerName,
9435 dev.lPort,
9436 dev.lDevice,
9437 dev.deviceType,
9438 false,
9439 dev.fPassThrough,
9440 dev.fTempEject,
9441 dev.fNonRotational,
9442 dev.fDiscard,
9443 dev.fHotPluggable,
9444 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9445 if (FAILED(rc)) break;
9446
9447 /* associate the medium with this machine and snapshot */
9448 if (!medium.isNull())
9449 {
9450 AutoCaller medCaller(medium);
9451 if (FAILED(medCaller.rc())) return medCaller.rc();
9452 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9453
9454 if (i_isSnapshotMachine())
9455 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9456 else
9457 rc = medium->i_addBackReference(mData->mUuid);
9458 /* If the medium->addBackReference fails it sets an appropriate
9459 * error message, so no need to do any guesswork here. */
9460
9461 if (puuidRegistry)
9462 // caller wants registry ID to be set on all attached media (OVF import case)
9463 medium->i_addRegistry(*puuidRegistry);
9464 }
9465
9466 if (FAILED(rc))
9467 break;
9468
9469 /* back up mMediaData to let registeredInit() properly rollback on failure
9470 * (= limited accessibility) */
9471 i_setModified(IsModified_Storage);
9472 mMediaData.backup();
9473 mMediaData->mAttachments.push_back(pAttachment);
9474 }
9475
9476 return rc;
9477}
9478
9479/**
9480 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9481 *
9482 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9483 * @param aSnapshot where to return the found snapshot
9484 * @param aSetError true to set extended error info on failure
9485 */
9486HRESULT Machine::i_findSnapshotById(const Guid &aId,
9487 ComObjPtr<Snapshot> &aSnapshot,
9488 bool aSetError /* = false */)
9489{
9490 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9491
9492 if (!mData->mFirstSnapshot)
9493 {
9494 if (aSetError)
9495 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9496 return E_FAIL;
9497 }
9498
9499 if (aId.isZero())
9500 aSnapshot = mData->mFirstSnapshot;
9501 else
9502 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9503
9504 if (!aSnapshot)
9505 {
9506 if (aSetError)
9507 return setError(E_FAIL,
9508 tr("Could not find a snapshot with UUID {%s}"),
9509 aId.toString().c_str());
9510 return E_FAIL;
9511 }
9512
9513 return S_OK;
9514}
9515
9516/**
9517 * Returns the snapshot with the given name or fails of no such snapshot.
9518 *
9519 * @param aName snapshot name to find
9520 * @param aSnapshot where to return the found snapshot
9521 * @param aSetError true to set extended error info on failure
9522 */
9523HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9524 ComObjPtr<Snapshot> &aSnapshot,
9525 bool aSetError /* = false */)
9526{
9527 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9528
9529 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9530
9531 if (!mData->mFirstSnapshot)
9532 {
9533 if (aSetError)
9534 return setError(VBOX_E_OBJECT_NOT_FOUND,
9535 tr("This machine does not have any snapshots"));
9536 return VBOX_E_OBJECT_NOT_FOUND;
9537 }
9538
9539 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9540
9541 if (!aSnapshot)
9542 {
9543 if (aSetError)
9544 return setError(VBOX_E_OBJECT_NOT_FOUND,
9545 tr("Could not find a snapshot named '%s'"), strName.c_str());
9546 return VBOX_E_OBJECT_NOT_FOUND;
9547 }
9548
9549 return S_OK;
9550}
9551
9552/**
9553 * Returns a storage controller object with the given name.
9554 *
9555 * @param aName storage controller name to find
9556 * @param aStorageController where to return the found storage controller
9557 * @param aSetError true to set extended error info on failure
9558 */
9559HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9560 ComObjPtr<StorageController> &aStorageController,
9561 bool aSetError /* = false */)
9562{
9563 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9564
9565 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9566 it != mStorageControllers->end();
9567 ++it)
9568 {
9569 if ((*it)->i_getName() == aName)
9570 {
9571 aStorageController = (*it);
9572 return S_OK;
9573 }
9574 }
9575
9576 if (aSetError)
9577 return setError(VBOX_E_OBJECT_NOT_FOUND,
9578 tr("Could not find a storage controller named '%s'"),
9579 aName.c_str());
9580 return VBOX_E_OBJECT_NOT_FOUND;
9581}
9582
9583/**
9584 * Returns a USB controller object with the given name.
9585 *
9586 * @param aName USB controller name to find
9587 * @param aUSBController where to return the found USB controller
9588 * @param aSetError true to set extended error info on failure
9589 */
9590HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9591 ComObjPtr<USBController> &aUSBController,
9592 bool aSetError /* = false */)
9593{
9594 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9595
9596 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9597 it != mUSBControllers->end();
9598 ++it)
9599 {
9600 if ((*it)->i_getName() == aName)
9601 {
9602 aUSBController = (*it);
9603 return S_OK;
9604 }
9605 }
9606
9607 if (aSetError)
9608 return setError(VBOX_E_OBJECT_NOT_FOUND,
9609 tr("Could not find a storage controller named '%s'"),
9610 aName.c_str());
9611 return VBOX_E_OBJECT_NOT_FOUND;
9612}
9613
9614/**
9615 * Returns the number of USB controller instance of the given type.
9616 *
9617 * @param enmType USB controller type.
9618 */
9619ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9620{
9621 ULONG cCtrls = 0;
9622
9623 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9624 it != mUSBControllers->end();
9625 ++it)
9626 {
9627 if ((*it)->i_getControllerType() == enmType)
9628 cCtrls++;
9629 }
9630
9631 return cCtrls;
9632}
9633
9634HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9635 MediaData::AttachmentList &atts)
9636{
9637 AutoCaller autoCaller(this);
9638 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9639
9640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9641
9642 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9643 it != mMediaData->mAttachments.end();
9644 ++it)
9645 {
9646 const ComObjPtr<MediumAttachment> &pAtt = *it;
9647 // should never happen, but deal with NULL pointers in the list.
9648 AssertContinue(!pAtt.isNull());
9649
9650 // getControllerName() needs caller+read lock
9651 AutoCaller autoAttCaller(pAtt);
9652 if (FAILED(autoAttCaller.rc()))
9653 {
9654 atts.clear();
9655 return autoAttCaller.rc();
9656 }
9657 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9658
9659 if (pAtt->i_getControllerName() == aName)
9660 atts.push_back(pAtt);
9661 }
9662
9663 return S_OK;
9664}
9665
9666
9667/**
9668 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9669 * file if the machine name was changed and about creating a new settings file
9670 * if this is a new machine.
9671 *
9672 * @note Must be never called directly but only from #saveSettings().
9673 */
9674HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9675{
9676 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9677
9678 HRESULT rc = S_OK;
9679
9680 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9681
9682 /// @todo need to handle primary group change, too
9683
9684 /* attempt to rename the settings file if machine name is changed */
9685 if ( mUserData->s.fNameSync
9686 && mUserData.isBackedUp()
9687 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9688 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9689 )
9690 {
9691 bool dirRenamed = false;
9692 bool fileRenamed = false;
9693
9694 Utf8Str configFile, newConfigFile;
9695 Utf8Str configFilePrev, newConfigFilePrev;
9696 Utf8Str configDir, newConfigDir;
9697
9698 do
9699 {
9700 int vrc = VINF_SUCCESS;
9701
9702 Utf8Str name = mUserData.backedUpData()->s.strName;
9703 Utf8Str newName = mUserData->s.strName;
9704 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9705 if (group == "/")
9706 group.setNull();
9707 Utf8Str newGroup = mUserData->s.llGroups.front();
9708 if (newGroup == "/")
9709 newGroup.setNull();
9710
9711 configFile = mData->m_strConfigFileFull;
9712
9713 /* first, rename the directory if it matches the group and machine name */
9714 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9715 group.c_str(), RTPATH_DELIMITER, name.c_str());
9716 /** @todo hack, make somehow use of ComposeMachineFilename */
9717 if (mUserData->s.fDirectoryIncludesUUID)
9718 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9719 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9720 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9721 /** @todo hack, make somehow use of ComposeMachineFilename */
9722 if (mUserData->s.fDirectoryIncludesUUID)
9723 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9724 configDir = configFile;
9725 configDir.stripFilename();
9726 newConfigDir = configDir;
9727 if ( configDir.length() >= groupPlusName.length()
9728 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9729 groupPlusName.c_str()))
9730 {
9731 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9732 Utf8Str newConfigBaseDir(newConfigDir);
9733 newConfigDir.append(newGroupPlusName);
9734 /* consistency: use \ if appropriate on the platform */
9735 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9736 /* new dir and old dir cannot be equal here because of 'if'
9737 * above and because name != newName */
9738 Assert(configDir != newConfigDir);
9739 if (!fSettingsFileIsNew)
9740 {
9741 /* perform real rename only if the machine is not new */
9742 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9743 if ( vrc == VERR_FILE_NOT_FOUND
9744 || vrc == VERR_PATH_NOT_FOUND)
9745 {
9746 /* create the parent directory, then retry renaming */
9747 Utf8Str parent(newConfigDir);
9748 parent.stripFilename();
9749 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9750 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9751 }
9752 if (RT_FAILURE(vrc))
9753 {
9754 rc = setError(E_FAIL,
9755 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9756 configDir.c_str(),
9757 newConfigDir.c_str(),
9758 vrc);
9759 break;
9760 }
9761 /* delete subdirectories which are no longer needed */
9762 Utf8Str dir(configDir);
9763 dir.stripFilename();
9764 while (dir != newConfigBaseDir && dir != ".")
9765 {
9766 vrc = RTDirRemove(dir.c_str());
9767 if (RT_FAILURE(vrc))
9768 break;
9769 dir.stripFilename();
9770 }
9771 dirRenamed = true;
9772 }
9773 }
9774
9775 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9776 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9777
9778 /* then try to rename the settings file itself */
9779 if (newConfigFile != configFile)
9780 {
9781 /* get the path to old settings file in renamed directory */
9782 configFile = Utf8StrFmt("%s%c%s",
9783 newConfigDir.c_str(),
9784 RTPATH_DELIMITER,
9785 RTPathFilename(configFile.c_str()));
9786 if (!fSettingsFileIsNew)
9787 {
9788 /* perform real rename only if the machine is not new */
9789 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9790 if (RT_FAILURE(vrc))
9791 {
9792 rc = setError(E_FAIL,
9793 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9794 configFile.c_str(),
9795 newConfigFile.c_str(),
9796 vrc);
9797 break;
9798 }
9799 fileRenamed = true;
9800 configFilePrev = configFile;
9801 configFilePrev += "-prev";
9802 newConfigFilePrev = newConfigFile;
9803 newConfigFilePrev += "-prev";
9804 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9805 }
9806 }
9807
9808 // update m_strConfigFileFull amd mConfigFile
9809 mData->m_strConfigFileFull = newConfigFile;
9810 // compute the relative path too
9811 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9812
9813 // store the old and new so that VirtualBox::i_saveSettings() can update
9814 // the media registry
9815 if ( mData->mRegistered
9816 && (configDir != newConfigDir || configFile != newConfigFile))
9817 {
9818 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9819
9820 if (pfNeedsGlobalSaveSettings)
9821 *pfNeedsGlobalSaveSettings = true;
9822 }
9823
9824 // in the saved state file path, replace the old directory with the new directory
9825 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9826 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9827
9828 // and do the same thing for the saved state file paths of all the online snapshots
9829 if (mData->mFirstSnapshot)
9830 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9831 newConfigDir.c_str());
9832 }
9833 while (0);
9834
9835 if (FAILED(rc))
9836 {
9837 /* silently try to rename everything back */
9838 if (fileRenamed)
9839 {
9840 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9841 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9842 }
9843 if (dirRenamed)
9844 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9845 }
9846
9847 if (FAILED(rc)) return rc;
9848 }
9849
9850 if (fSettingsFileIsNew)
9851 {
9852 /* create a virgin config file */
9853 int vrc = VINF_SUCCESS;
9854
9855 /* ensure the settings directory exists */
9856 Utf8Str path(mData->m_strConfigFileFull);
9857 path.stripFilename();
9858 if (!RTDirExists(path.c_str()))
9859 {
9860 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9861 if (RT_FAILURE(vrc))
9862 {
9863 return setError(E_FAIL,
9864 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9865 path.c_str(),
9866 vrc);
9867 }
9868 }
9869
9870 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9871 path = Utf8Str(mData->m_strConfigFileFull);
9872 RTFILE f = NIL_RTFILE;
9873 vrc = RTFileOpen(&f, path.c_str(),
9874 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9875 if (RT_FAILURE(vrc))
9876 return setError(E_FAIL,
9877 tr("Could not create the settings file '%s' (%Rrc)"),
9878 path.c_str(),
9879 vrc);
9880 RTFileClose(f);
9881 }
9882
9883 return rc;
9884}
9885
9886/**
9887 * Saves and commits machine data, user data and hardware data.
9888 *
9889 * Note that on failure, the data remains uncommitted.
9890 *
9891 * @a aFlags may combine the following flags:
9892 *
9893 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9894 * Used when saving settings after an operation that makes them 100%
9895 * correspond to the settings from the current snapshot.
9896 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9897 * #isReallyModified() returns false. This is necessary for cases when we
9898 * change machine data directly, not through the backup()/commit() mechanism.
9899 * - SaveS_Force: settings will be saved without doing a deep compare of the
9900 * settings structures. This is used when this is called because snapshots
9901 * have changed to avoid the overhead of the deep compare.
9902 *
9903 * @note Must be called from under this object's write lock. Locks children for
9904 * writing.
9905 *
9906 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9907 * initialized to false and that will be set to true by this function if
9908 * the caller must invoke VirtualBox::i_saveSettings() because the global
9909 * settings have changed. This will happen if a machine rename has been
9910 * saved and the global machine and media registries will therefore need
9911 * updating.
9912 */
9913HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9914 int aFlags /*= 0*/)
9915{
9916 LogFlowThisFuncEnter();
9917
9918 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9919
9920 /* make sure child objects are unable to modify the settings while we are
9921 * saving them */
9922 i_ensureNoStateDependencies();
9923
9924 AssertReturn(!i_isSnapshotMachine(),
9925 E_FAIL);
9926
9927 HRESULT rc = S_OK;
9928 bool fNeedsWrite = false;
9929
9930 /* First, prepare to save settings. It will care about renaming the
9931 * settings directory and file if the machine name was changed and about
9932 * creating a new settings file if this is a new machine. */
9933 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9934 if (FAILED(rc)) return rc;
9935
9936 // keep a pointer to the current settings structures
9937 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9938 settings::MachineConfigFile *pNewConfig = NULL;
9939
9940 try
9941 {
9942 // make a fresh one to have everyone write stuff into
9943 pNewConfig = new settings::MachineConfigFile(NULL);
9944 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9945
9946 // now go and copy all the settings data from COM to the settings structures
9947 // (this calles i_saveSettings() on all the COM objects in the machine)
9948 i_copyMachineDataToSettings(*pNewConfig);
9949
9950 if (aFlags & SaveS_ResetCurStateModified)
9951 {
9952 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9953 mData->mCurrentStateModified = FALSE;
9954 fNeedsWrite = true; // always, no need to compare
9955 }
9956 else if (aFlags & SaveS_Force)
9957 {
9958 fNeedsWrite = true; // always, no need to compare
9959 }
9960 else
9961 {
9962 if (!mData->mCurrentStateModified)
9963 {
9964 // do a deep compare of the settings that we just saved with the settings
9965 // previously stored in the config file; this invokes MachineConfigFile::operator==
9966 // which does a deep compare of all the settings, which is expensive but less expensive
9967 // than writing out XML in vain
9968 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9969
9970 // could still be modified if any settings changed
9971 mData->mCurrentStateModified = fAnySettingsChanged;
9972
9973 fNeedsWrite = fAnySettingsChanged;
9974 }
9975 else
9976 fNeedsWrite = true;
9977 }
9978
9979 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9980
9981 if (fNeedsWrite)
9982 // now spit it all out!
9983 pNewConfig->write(mData->m_strConfigFileFull);
9984
9985 mData->pMachineConfigFile = pNewConfig;
9986 delete pOldConfig;
9987 i_commit();
9988
9989 // after saving settings, we are no longer different from the XML on disk
9990 mData->flModifications = 0;
9991 }
9992 catch (HRESULT err)
9993 {
9994 // we assume that error info is set by the thrower
9995 rc = err;
9996
9997 // restore old config
9998 delete pNewConfig;
9999 mData->pMachineConfigFile = pOldConfig;
10000 }
10001 catch (...)
10002 {
10003 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10004 }
10005
10006 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10007 {
10008 /* Fire the data change event, even on failure (since we've already
10009 * committed all data). This is done only for SessionMachines because
10010 * mutable Machine instances are always not registered (i.e. private
10011 * to the client process that creates them) and thus don't need to
10012 * inform callbacks. */
10013 if (i_isSessionMachine())
10014 mParent->i_onMachineDataChange(mData->mUuid);
10015 }
10016
10017 LogFlowThisFunc(("rc=%08X\n", rc));
10018 LogFlowThisFuncLeave();
10019 return rc;
10020}
10021
10022/**
10023 * Implementation for saving the machine settings into the given
10024 * settings::MachineConfigFile instance. This copies machine extradata
10025 * from the previous machine config file in the instance data, if any.
10026 *
10027 * This gets called from two locations:
10028 *
10029 * -- Machine::i_saveSettings(), during the regular XML writing;
10030 *
10031 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10032 * exported to OVF and we write the VirtualBox proprietary XML
10033 * into a <vbox:Machine> tag.
10034 *
10035 * This routine fills all the fields in there, including snapshots, *except*
10036 * for the following:
10037 *
10038 * -- fCurrentStateModified. There is some special logic associated with that.
10039 *
10040 * The caller can then call MachineConfigFile::write() or do something else
10041 * with it.
10042 *
10043 * Caller must hold the machine lock!
10044 *
10045 * This throws XML errors and HRESULT, so the caller must have a catch block!
10046 */
10047void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10048{
10049 // deep copy extradata
10050 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10051
10052 config.uuid = mData->mUuid;
10053
10054 // copy name, description, OS type, teleport, UTC etc.
10055 config.machineUserData = mUserData->s;
10056
10057 if ( mData->mMachineState == MachineState_Saved
10058 || mData->mMachineState == MachineState_Restoring
10059 // when doing certain snapshot operations we may or may not have
10060 // a saved state in the current state, so keep everything as is
10061 || ( ( mData->mMachineState == MachineState_Snapshotting
10062 || mData->mMachineState == MachineState_DeletingSnapshot
10063 || mData->mMachineState == MachineState_RestoringSnapshot)
10064 && (!mSSData->strStateFilePath.isEmpty())
10065 )
10066 )
10067 {
10068 Assert(!mSSData->strStateFilePath.isEmpty());
10069 /* try to make the file name relative to the settings file dir */
10070 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10071 }
10072 else
10073 {
10074 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10075 config.strStateFile.setNull();
10076 }
10077
10078 if (mData->mCurrentSnapshot)
10079 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10080 else
10081 config.uuidCurrentSnapshot.clear();
10082
10083 config.timeLastStateChange = mData->mLastStateChange;
10084 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10085 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10086
10087 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10088 if (FAILED(rc)) throw rc;
10089
10090 // save machine's media registry if this is VirtualBox 4.0 or later
10091 if (config.canHaveOwnMediaRegistry())
10092 {
10093 // determine machine folder
10094 Utf8Str strMachineFolder = i_getSettingsFileFull();
10095 strMachineFolder.stripFilename();
10096 mParent->i_saveMediaRegistry(config.mediaRegistry,
10097 i_getId(), // only media with registry ID == machine UUID
10098 strMachineFolder);
10099 // this throws HRESULT
10100 }
10101
10102 // save snapshots
10103 rc = i_saveAllSnapshots(config);
10104 if (FAILED(rc)) throw rc;
10105}
10106
10107/**
10108 * Saves all snapshots of the machine into the given machine config file. Called
10109 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10110 * @param config
10111 * @return
10112 */
10113HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10114{
10115 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10116
10117 HRESULT rc = S_OK;
10118
10119 try
10120 {
10121 config.llFirstSnapshot.clear();
10122
10123 if (mData->mFirstSnapshot)
10124 {
10125 // the settings use a list for "the first snapshot"
10126 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10127
10128 // get reference to the snapshot on the list and work on that
10129 // element straight in the list to avoid excessive copying later
10130 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10131 if (FAILED(rc)) throw rc;
10132 }
10133
10134// if (mType == IsSessionMachine)
10135// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10136
10137 }
10138 catch (HRESULT err)
10139 {
10140 /* we assume that error info is set by the thrower */
10141 rc = err;
10142 }
10143 catch (...)
10144 {
10145 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10146 }
10147
10148 return rc;
10149}
10150
10151/**
10152 * Saves the VM hardware configuration. It is assumed that the
10153 * given node is empty.
10154 *
10155 * @param data Reference to the settings object for the hardware config.
10156 * @param pDbg Pointer to the settings object for the debugging config
10157 * which happens to live in mHWData.
10158 * @param pAutostart Pointer to the settings object for the autostart config
10159 * which happens to live in mHWData.
10160 */
10161HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10162 settings::Autostart *pAutostart)
10163{
10164 HRESULT rc = S_OK;
10165
10166 try
10167 {
10168 /* The hardware version attribute (optional).
10169 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10170 if ( mHWData->mHWVersion == "1"
10171 && mSSData->strStateFilePath.isEmpty()
10172 )
10173 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10174 other point needs to be found where this can be done. */
10175
10176 data.strVersion = mHWData->mHWVersion;
10177 data.uuid = mHWData->mHardwareUUID;
10178
10179 // CPU
10180 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10181 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10182 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10183 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10184 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10185 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10186 data.fPAE = !!mHWData->mPAEEnabled;
10187 data.enmLongMode = mHWData->mLongMode;
10188 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10189 data.fAPIC = !!mHWData->mAPIC;
10190 data.fX2APIC = !!mHWData->mX2APIC;
10191 data.cCPUs = mHWData->mCPUCount;
10192 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10193 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10194 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10195 data.strCpuProfile = mHWData->mCpuProfile;
10196
10197 data.llCpus.clear();
10198 if (data.fCpuHotPlug)
10199 {
10200 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10201 {
10202 if (mHWData->mCPUAttached[idx])
10203 {
10204 settings::Cpu cpu;
10205 cpu.ulId = idx;
10206 data.llCpus.push_back(cpu);
10207 }
10208 }
10209 }
10210
10211 /* Standard and Extended CPUID leafs. */
10212 data.llCpuIdLeafs.clear();
10213 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10214 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10215 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10216 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10217 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10218 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10219
10220 // memory
10221 data.ulMemorySizeMB = mHWData->mMemorySize;
10222 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10223
10224 // firmware
10225 data.firmwareType = mHWData->mFirmwareType;
10226
10227 // HID
10228 data.pointingHIDType = mHWData->mPointingHIDType;
10229 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10230
10231 // chipset
10232 data.chipsetType = mHWData->mChipsetType;
10233
10234 // paravirt
10235 data.paravirtProvider = mHWData->mParavirtProvider;
10236 data.strParavirtDebug = mHWData->mParavirtDebug;
10237
10238 // emulated USB card reader
10239 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10240
10241 // HPET
10242 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10243
10244 // boot order
10245 data.mapBootOrder.clear();
10246 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10247 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10248
10249 // display
10250 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10251 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10252 data.cMonitors = mHWData->mMonitorCount;
10253 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10254 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10255 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10256 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10257 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10258 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10259 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10260 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10261 {
10262 if (mHWData->maVideoCaptureScreens[i])
10263 ASMBitSet(&data.u64VideoCaptureScreens, i);
10264 else
10265 ASMBitClear(&data.u64VideoCaptureScreens, i);
10266 }
10267 /* store relative video capture file if possible */
10268 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10269
10270 /* VRDEServer settings (optional) */
10271 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10272 if (FAILED(rc)) throw rc;
10273
10274 /* BIOS (required) */
10275 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10276 if (FAILED(rc)) throw rc;
10277
10278 /* USB Controller (required) */
10279 data.usbSettings.llUSBControllers.clear();
10280 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10281 {
10282 ComObjPtr<USBController> ctrl = *it;
10283 settings::USBController settingsCtrl;
10284
10285 settingsCtrl.strName = ctrl->i_getName();
10286 settingsCtrl.enmType = ctrl->i_getControllerType();
10287
10288 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10289 }
10290
10291 /* USB device filters (required) */
10292 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10293 if (FAILED(rc)) throw rc;
10294
10295 /* Network adapters (required) */
10296 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10297 data.llNetworkAdapters.clear();
10298 /* Write out only the nominal number of network adapters for this
10299 * chipset type. Since Machine::commit() hasn't been called there
10300 * may be extra NIC settings in the vector. */
10301 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10302 {
10303 settings::NetworkAdapter nic;
10304 nic.ulSlot = (uint32_t)slot;
10305 /* paranoia check... must not be NULL, but must not crash either. */
10306 if (mNetworkAdapters[slot])
10307 {
10308 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10309 if (FAILED(rc)) throw rc;
10310
10311 data.llNetworkAdapters.push_back(nic);
10312 }
10313 }
10314
10315 /* Serial ports */
10316 data.llSerialPorts.clear();
10317 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10318 {
10319 if (mSerialPorts[slot]->i_hasDefaults())
10320 continue;
10321
10322 settings::SerialPort s;
10323 s.ulSlot = slot;
10324 rc = mSerialPorts[slot]->i_saveSettings(s);
10325 if (FAILED(rc)) return rc;
10326
10327 data.llSerialPorts.push_back(s);
10328 }
10329
10330 /* Parallel ports */
10331 data.llParallelPorts.clear();
10332 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10333 {
10334 if (mParallelPorts[slot]->i_hasDefaults())
10335 continue;
10336
10337 settings::ParallelPort p;
10338 p.ulSlot = slot;
10339 rc = mParallelPorts[slot]->i_saveSettings(p);
10340 if (FAILED(rc)) return rc;
10341
10342 data.llParallelPorts.push_back(p);
10343 }
10344
10345 /* Audio adapter */
10346 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10347 if (FAILED(rc)) return rc;
10348
10349 rc = i_saveStorageControllers(data.storage);
10350 if (FAILED(rc)) return rc;
10351
10352 /* Shared folders */
10353 data.llSharedFolders.clear();
10354 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10355 it != mHWData->mSharedFolders.end();
10356 ++it)
10357 {
10358 SharedFolder *pSF = *it;
10359 AutoCaller sfCaller(pSF);
10360 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10361 settings::SharedFolder sf;
10362 sf.strName = pSF->i_getName();
10363 sf.strHostPath = pSF->i_getHostPath();
10364 sf.fWritable = !!pSF->i_isWritable();
10365 sf.fAutoMount = !!pSF->i_isAutoMounted();
10366
10367 data.llSharedFolders.push_back(sf);
10368 }
10369
10370 // clipboard
10371 data.clipboardMode = mHWData->mClipboardMode;
10372
10373 // drag'n'drop
10374 data.dndMode = mHWData->mDnDMode;
10375
10376 /* Guest */
10377 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10378
10379 // IO settings
10380 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10381 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10382
10383 /* BandwidthControl (required) */
10384 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10385 if (FAILED(rc)) throw rc;
10386
10387 /* Host PCI devices */
10388 data.pciAttachments.clear();
10389 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10390 it != mHWData->mPCIDeviceAssignments.end();
10391 ++it)
10392 {
10393 ComObjPtr<PCIDeviceAttachment> pda = *it;
10394 settings::HostPCIDeviceAttachment hpda;
10395
10396 rc = pda->i_saveSettings(hpda);
10397 if (FAILED(rc)) throw rc;
10398
10399 data.pciAttachments.push_back(hpda);
10400 }
10401
10402
10403 // guest properties
10404 data.llGuestProperties.clear();
10405#ifdef VBOX_WITH_GUEST_PROPS
10406 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10407 it != mHWData->mGuestProperties.end();
10408 ++it)
10409 {
10410 HWData::GuestProperty property = it->second;
10411
10412 /* Remove transient guest properties at shutdown unless we
10413 * are saving state. Note that restoring snapshot intentionally
10414 * keeps them, they will be removed if appropriate once the final
10415 * machine state is set (as crashes etc. need to work). */
10416 if ( ( mData->mMachineState == MachineState_PoweredOff
10417 || mData->mMachineState == MachineState_Aborted
10418 || mData->mMachineState == MachineState_Teleported)
10419 && ( property.mFlags & guestProp::TRANSIENT
10420 || property.mFlags & guestProp::TRANSRESET))
10421 continue;
10422 settings::GuestProperty prop;
10423 prop.strName = it->first;
10424 prop.strValue = property.strValue;
10425 prop.timestamp = property.mTimestamp;
10426 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10427 guestProp::writeFlags(property.mFlags, szFlags);
10428 prop.strFlags = szFlags;
10429
10430 data.llGuestProperties.push_back(prop);
10431 }
10432
10433 /* I presume this doesn't require a backup(). */
10434 mData->mGuestPropertiesModified = FALSE;
10435#endif /* VBOX_WITH_GUEST_PROPS defined */
10436
10437 *pDbg = mHWData->mDebugging;
10438 *pAutostart = mHWData->mAutostart;
10439
10440 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10441 }
10442 catch(std::bad_alloc &)
10443 {
10444 return E_OUTOFMEMORY;
10445 }
10446
10447 AssertComRC(rc);
10448 return rc;
10449}
10450
10451/**
10452 * Saves the storage controller configuration.
10453 *
10454 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10455 */
10456HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10457{
10458 data.llStorageControllers.clear();
10459
10460 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10461 it != mStorageControllers->end();
10462 ++it)
10463 {
10464 HRESULT rc;
10465 ComObjPtr<StorageController> pCtl = *it;
10466
10467 settings::StorageController ctl;
10468 ctl.strName = pCtl->i_getName();
10469 ctl.controllerType = pCtl->i_getControllerType();
10470 ctl.storageBus = pCtl->i_getStorageBus();
10471 ctl.ulInstance = pCtl->i_getInstance();
10472 ctl.fBootable = pCtl->i_getBootable();
10473
10474 /* Save the port count. */
10475 ULONG portCount;
10476 rc = pCtl->COMGETTER(PortCount)(&portCount);
10477 ComAssertComRCRet(rc, rc);
10478 ctl.ulPortCount = portCount;
10479
10480 /* Save fUseHostIOCache */
10481 BOOL fUseHostIOCache;
10482 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10483 ComAssertComRCRet(rc, rc);
10484 ctl.fUseHostIOCache = !!fUseHostIOCache;
10485
10486 /* save the devices now. */
10487 rc = i_saveStorageDevices(pCtl, ctl);
10488 ComAssertComRCRet(rc, rc);
10489
10490 data.llStorageControllers.push_back(ctl);
10491 }
10492
10493 return S_OK;
10494}
10495
10496/**
10497 * Saves the hard disk configuration.
10498 */
10499HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10500 settings::StorageController &data)
10501{
10502 MediaData::AttachmentList atts;
10503
10504 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10505 if (FAILED(rc)) return rc;
10506
10507 data.llAttachedDevices.clear();
10508 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10509 it != atts.end();
10510 ++it)
10511 {
10512 settings::AttachedDevice dev;
10513 IMediumAttachment *iA = *it;
10514 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10515 Medium *pMedium = pAttach->i_getMedium();
10516
10517 dev.deviceType = pAttach->i_getType();
10518 dev.lPort = pAttach->i_getPort();
10519 dev.lDevice = pAttach->i_getDevice();
10520 dev.fPassThrough = pAttach->i_getPassthrough();
10521 dev.fHotPluggable = pAttach->i_getHotPluggable();
10522 if (pMedium)
10523 {
10524 if (pMedium->i_isHostDrive())
10525 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10526 else
10527 dev.uuid = pMedium->i_getId();
10528 dev.fTempEject = pAttach->i_getTempEject();
10529 dev.fNonRotational = pAttach->i_getNonRotational();
10530 dev.fDiscard = pAttach->i_getDiscard();
10531 }
10532
10533 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10534
10535 data.llAttachedDevices.push_back(dev);
10536 }
10537
10538 return S_OK;
10539}
10540
10541/**
10542 * Saves machine state settings as defined by aFlags
10543 * (SaveSTS_* values).
10544 *
10545 * @param aFlags Combination of SaveSTS_* flags.
10546 *
10547 * @note Locks objects for writing.
10548 */
10549HRESULT Machine::i_saveStateSettings(int aFlags)
10550{
10551 if (aFlags == 0)
10552 return S_OK;
10553
10554 AutoCaller autoCaller(this);
10555 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10556
10557 /* This object's write lock is also necessary to serialize file access
10558 * (prevent concurrent reads and writes) */
10559 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10560
10561 HRESULT rc = S_OK;
10562
10563 Assert(mData->pMachineConfigFile);
10564
10565 try
10566 {
10567 if (aFlags & SaveSTS_CurStateModified)
10568 mData->pMachineConfigFile->fCurrentStateModified = true;
10569
10570 if (aFlags & SaveSTS_StateFilePath)
10571 {
10572 if (!mSSData->strStateFilePath.isEmpty())
10573 /* try to make the file name relative to the settings file dir */
10574 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10575 else
10576 mData->pMachineConfigFile->strStateFile.setNull();
10577 }
10578
10579 if (aFlags & SaveSTS_StateTimeStamp)
10580 {
10581 Assert( mData->mMachineState != MachineState_Aborted
10582 || mSSData->strStateFilePath.isEmpty());
10583
10584 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10585
10586 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10587//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10588 }
10589
10590 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10591 }
10592 catch (...)
10593 {
10594 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10595 }
10596
10597 return rc;
10598}
10599
10600/**
10601 * Ensures that the given medium is added to a media registry. If this machine
10602 * was created with 4.0 or later, then the machine registry is used. Otherwise
10603 * the global VirtualBox media registry is used.
10604 *
10605 * Caller must NOT hold machine lock, media tree or any medium locks!
10606 *
10607 * @param pMedium
10608 */
10609void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10610{
10611 /* Paranoia checks: do not hold machine or media tree locks. */
10612 AssertReturnVoid(!isWriteLockOnCurrentThread());
10613 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10614
10615 ComObjPtr<Medium> pBase;
10616 {
10617 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10618 pBase = pMedium->i_getBase();
10619 }
10620
10621 /* Paranoia checks: do not hold medium locks. */
10622 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10623 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10624
10625 // decide which medium registry to use now that the medium is attached:
10626 Guid uuid;
10627 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10628 // machine XML is VirtualBox 4.0 or higher:
10629 uuid = i_getId(); // machine UUID
10630 else
10631 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10632
10633 if (pMedium->i_addRegistry(uuid))
10634 mParent->i_markRegistryModified(uuid);
10635
10636 /* For more complex hard disk structures it can happen that the base
10637 * medium isn't yet associated with any medium registry. Do that now. */
10638 if (pMedium != pBase)
10639 {
10640 /* Tree lock needed by Medium::addRegistry when recursing. */
10641 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10642 if (pBase->i_addRegistryRecursive(uuid))
10643 {
10644 treeLock.release();
10645 mParent->i_markRegistryModified(uuid);
10646 }
10647 }
10648}
10649
10650/**
10651 * Creates differencing hard disks for all normal hard disks attached to this
10652 * machine and a new set of attachments to refer to created disks.
10653 *
10654 * Used when taking a snapshot or when deleting the current state. Gets called
10655 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10656 *
10657 * This method assumes that mMediaData contains the original hard disk attachments
10658 * it needs to create diffs for. On success, these attachments will be replaced
10659 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10660 * called to delete created diffs which will also rollback mMediaData and restore
10661 * whatever was backed up before calling this method.
10662 *
10663 * Attachments with non-normal hard disks are left as is.
10664 *
10665 * If @a aOnline is @c false then the original hard disks that require implicit
10666 * diffs will be locked for reading. Otherwise it is assumed that they are
10667 * already locked for writing (when the VM was started). Note that in the latter
10668 * case it is responsibility of the caller to lock the newly created diffs for
10669 * writing if this method succeeds.
10670 *
10671 * @param aProgress Progress object to run (must contain at least as
10672 * many operations left as the number of hard disks
10673 * attached).
10674 * @param aOnline Whether the VM was online prior to this operation.
10675 *
10676 * @note The progress object is not marked as completed, neither on success nor
10677 * on failure. This is a responsibility of the caller.
10678 *
10679 * @note Locks this object and the media tree for writing.
10680 */
10681HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10682 ULONG aWeight,
10683 bool aOnline)
10684{
10685 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10686
10687 AutoCaller autoCaller(this);
10688 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10689
10690 AutoMultiWriteLock2 alock(this->lockHandle(),
10691 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10692
10693 /* must be in a protective state because we release the lock below */
10694 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10695 || mData->mMachineState == MachineState_OnlineSnapshotting
10696 || mData->mMachineState == MachineState_LiveSnapshotting
10697 || mData->mMachineState == MachineState_RestoringSnapshot
10698 || mData->mMachineState == MachineState_DeletingSnapshot
10699 , E_FAIL);
10700
10701 HRESULT rc = S_OK;
10702
10703 // use appropriate locked media map (online or offline)
10704 MediumLockListMap lockedMediaOffline;
10705 MediumLockListMap *lockedMediaMap;
10706 if (aOnline)
10707 lockedMediaMap = &mData->mSession.mLockedMedia;
10708 else
10709 lockedMediaMap = &lockedMediaOffline;
10710
10711 try
10712 {
10713 if (!aOnline)
10714 {
10715 /* lock all attached hard disks early to detect "in use"
10716 * situations before creating actual diffs */
10717 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10718 it != mMediaData->mAttachments.end();
10719 ++it)
10720 {
10721 MediumAttachment* pAtt = *it;
10722 if (pAtt->i_getType() == DeviceType_HardDisk)
10723 {
10724 Medium* pMedium = pAtt->i_getMedium();
10725 Assert(pMedium);
10726
10727 MediumLockList *pMediumLockList(new MediumLockList());
10728 alock.release();
10729 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10730 NULL /* pToLockWrite */,
10731 false /* fMediumLockWriteAll */,
10732 NULL,
10733 *pMediumLockList);
10734 alock.acquire();
10735 if (FAILED(rc))
10736 {
10737 delete pMediumLockList;
10738 throw rc;
10739 }
10740 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10741 if (FAILED(rc))
10742 {
10743 throw setError(rc,
10744 tr("Collecting locking information for all attached media failed"));
10745 }
10746 }
10747 }
10748
10749 /* Now lock all media. If this fails, nothing is locked. */
10750 alock.release();
10751 rc = lockedMediaMap->Lock();
10752 alock.acquire();
10753 if (FAILED(rc))
10754 {
10755 throw setError(rc,
10756 tr("Locking of attached media failed"));
10757 }
10758 }
10759
10760 /* remember the current list (note that we don't use backup() since
10761 * mMediaData may be already backed up) */
10762 MediaData::AttachmentList atts = mMediaData->mAttachments;
10763
10764 /* start from scratch */
10765 mMediaData->mAttachments.clear();
10766
10767 /* go through remembered attachments and create diffs for normal hard
10768 * disks and attach them */
10769 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10770 it != atts.end();
10771 ++it)
10772 {
10773 MediumAttachment* pAtt = *it;
10774
10775 DeviceType_T devType = pAtt->i_getType();
10776 Medium* pMedium = pAtt->i_getMedium();
10777
10778 if ( devType != DeviceType_HardDisk
10779 || pMedium == NULL
10780 || pMedium->i_getType() != MediumType_Normal)
10781 {
10782 /* copy the attachment as is */
10783
10784 /** @todo the progress object created in SessionMachine::TakeSnaphot
10785 * only expects operations for hard disks. Later other
10786 * device types need to show up in the progress as well. */
10787 if (devType == DeviceType_HardDisk)
10788 {
10789 if (pMedium == NULL)
10790 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10791 aWeight); // weight
10792 else
10793 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10794 pMedium->i_getBase()->i_getName().c_str()).raw(),
10795 aWeight); // weight
10796 }
10797
10798 mMediaData->mAttachments.push_back(pAtt);
10799 continue;
10800 }
10801
10802 /* need a diff */
10803 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10804 pMedium->i_getBase()->i_getName().c_str()).raw(),
10805 aWeight); // weight
10806
10807 Utf8Str strFullSnapshotFolder;
10808 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10809
10810 ComObjPtr<Medium> diff;
10811 diff.createObject();
10812 // store the diff in the same registry as the parent
10813 // (this cannot fail here because we can't create implicit diffs for
10814 // unregistered images)
10815 Guid uuidRegistryParent;
10816 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10817 Assert(fInRegistry); NOREF(fInRegistry);
10818 rc = diff->init(mParent,
10819 pMedium->i_getPreferredDiffFormat(),
10820 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10821 uuidRegistryParent,
10822 DeviceType_HardDisk);
10823 if (FAILED(rc)) throw rc;
10824
10825 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10826 * the push_back? Looks like we're going to release medium with the
10827 * wrong kind of lock (general issue with if we fail anywhere at all)
10828 * and an orphaned VDI in the snapshots folder. */
10829
10830 /* update the appropriate lock list */
10831 MediumLockList *pMediumLockList;
10832 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10833 AssertComRCThrowRC(rc);
10834 if (aOnline)
10835 {
10836 alock.release();
10837 /* The currently attached medium will be read-only, change
10838 * the lock type to read. */
10839 rc = pMediumLockList->Update(pMedium, false);
10840 alock.acquire();
10841 AssertComRCThrowRC(rc);
10842 }
10843
10844 /* release the locks before the potentially lengthy operation */
10845 alock.release();
10846 rc = pMedium->i_createDiffStorage(diff,
10847 pMedium->i_getPreferredDiffVariant(),
10848 pMediumLockList,
10849 NULL /* aProgress */,
10850 true /* aWait */);
10851 alock.acquire();
10852 if (FAILED(rc)) throw rc;
10853
10854 /* actual lock list update is done in Medium::commitMedia */
10855
10856 rc = diff->i_addBackReference(mData->mUuid);
10857 AssertComRCThrowRC(rc);
10858
10859 /* add a new attachment */
10860 ComObjPtr<MediumAttachment> attachment;
10861 attachment.createObject();
10862 rc = attachment->init(this,
10863 diff,
10864 pAtt->i_getControllerName(),
10865 pAtt->i_getPort(),
10866 pAtt->i_getDevice(),
10867 DeviceType_HardDisk,
10868 true /* aImplicit */,
10869 false /* aPassthrough */,
10870 false /* aTempEject */,
10871 pAtt->i_getNonRotational(),
10872 pAtt->i_getDiscard(),
10873 pAtt->i_getHotPluggable(),
10874 pAtt->i_getBandwidthGroup());
10875 if (FAILED(rc)) throw rc;
10876
10877 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10878 AssertComRCThrowRC(rc);
10879 mMediaData->mAttachments.push_back(attachment);
10880 }
10881 }
10882 catch (HRESULT aRC) { rc = aRC; }
10883
10884 /* unlock all hard disks we locked when there is no VM */
10885 if (!aOnline)
10886 {
10887 ErrorInfoKeeper eik;
10888
10889 HRESULT rc1 = lockedMediaMap->Clear();
10890 AssertComRC(rc1);
10891 }
10892
10893 return rc;
10894}
10895
10896/**
10897 * Deletes implicit differencing hard disks created either by
10898 * #i_createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10899 *
10900 * Note that to delete hard disks created by #AttachDevice() this method is
10901 * called from #fixupMedia() when the changes are rolled back.
10902 *
10903 * @note Locks this object and the media tree for writing.
10904 */
10905HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10906{
10907 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10908
10909 AutoCaller autoCaller(this);
10910 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10911
10912 AutoMultiWriteLock2 alock(this->lockHandle(),
10913 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10914
10915 /* We absolutely must have backed up state. */
10916 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10917
10918 /* Check if there are any implicitly created diff images. */
10919 bool fImplicitDiffs = false;
10920 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10921 it != mMediaData->mAttachments.end();
10922 ++it)
10923 {
10924 const ComObjPtr<MediumAttachment> &pAtt = *it;
10925 if (pAtt->i_isImplicit())
10926 {
10927 fImplicitDiffs = true;
10928 break;
10929 }
10930 }
10931 /* If there is nothing to do, leave early. This saves lots of image locking
10932 * effort. It also avoids a MachineStateChanged event without real reason.
10933 * This is important e.g. when loading a VM config, because there should be
10934 * no events. Otherwise API clients can become thoroughly confused for
10935 * inaccessible VMs (the code for loading VM configs uses this method for
10936 * cleanup if the config makes no sense), as they take such events as an
10937 * indication that the VM is alive, and they would force the VM config to
10938 * be reread, leading to an endless loop. */
10939 if (!fImplicitDiffs)
10940 return S_OK;
10941
10942 HRESULT rc = S_OK;
10943 MachineState_T oldState = mData->mMachineState;
10944
10945 /* will release the lock before the potentially lengthy operation,
10946 * so protect with the special state (unless already protected) */
10947 if ( oldState != MachineState_Snapshotting
10948 && oldState != MachineState_OnlineSnapshotting
10949 && oldState != MachineState_LiveSnapshotting
10950 && oldState != MachineState_RestoringSnapshot
10951 && oldState != MachineState_DeletingSnapshot
10952 && oldState != MachineState_DeletingSnapshotOnline
10953 && oldState != MachineState_DeletingSnapshotPaused
10954 )
10955 i_setMachineState(MachineState_SettingUp);
10956
10957 // use appropriate locked media map (online or offline)
10958 MediumLockListMap lockedMediaOffline;
10959 MediumLockListMap *lockedMediaMap;
10960 if (aOnline)
10961 lockedMediaMap = &mData->mSession.mLockedMedia;
10962 else
10963 lockedMediaMap = &lockedMediaOffline;
10964
10965 try
10966 {
10967 if (!aOnline)
10968 {
10969 /* lock all attached hard disks early to detect "in use"
10970 * situations before deleting actual diffs */
10971 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10972 it != mMediaData->mAttachments.end();
10973 ++it)
10974 {
10975 MediumAttachment* pAtt = *it;
10976 if (pAtt->i_getType() == DeviceType_HardDisk)
10977 {
10978 Medium* pMedium = pAtt->i_getMedium();
10979 Assert(pMedium);
10980
10981 MediumLockList *pMediumLockList(new MediumLockList());
10982 alock.release();
10983 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10984 NULL /* pToLockWrite */,
10985 false /* fMediumLockWriteAll */,
10986 NULL,
10987 *pMediumLockList);
10988 alock.acquire();
10989
10990 if (FAILED(rc))
10991 {
10992 delete pMediumLockList;
10993 throw rc;
10994 }
10995
10996 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10997 if (FAILED(rc))
10998 throw rc;
10999 }
11000 }
11001
11002 if (FAILED(rc))
11003 throw rc;
11004 } // end of offline
11005
11006 /* Lock lists are now up to date and include implicitly created media */
11007
11008 /* Go through remembered attachments and delete all implicitly created
11009 * diffs and fix up the attachment information */
11010 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11011 MediaData::AttachmentList implicitAtts;
11012 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11013 it != mMediaData->mAttachments.end();
11014 ++it)
11015 {
11016 ComObjPtr<MediumAttachment> pAtt = *it;
11017 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11018 if (pMedium.isNull())
11019 continue;
11020
11021 // Implicit attachments go on the list for deletion and back references are removed.
11022 if (pAtt->i_isImplicit())
11023 {
11024 /* Deassociate and mark for deletion */
11025 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11026 rc = pMedium->i_removeBackReference(mData->mUuid);
11027 if (FAILED(rc))
11028 throw rc;
11029 implicitAtts.push_back(pAtt);
11030 continue;
11031 }
11032
11033 /* Was this medium attached before? */
11034 if (!i_findAttachment(oldAtts, pMedium))
11035 {
11036 /* no: de-associate */
11037 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11038 rc = pMedium->i_removeBackReference(mData->mUuid);
11039 if (FAILED(rc))
11040 throw rc;
11041 continue;
11042 }
11043 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11044 }
11045
11046 /* If there are implicit attachments to delete, throw away the lock
11047 * map contents (which will unlock all media) since the medium
11048 * attachments will be rolled back. Below we need to completely
11049 * recreate the lock map anyway since it is infinitely complex to
11050 * do this incrementally (would need reconstructing each attachment
11051 * change, which would be extremely hairy). */
11052 if (implicitAtts.size() != 0)
11053 {
11054 ErrorInfoKeeper eik;
11055
11056 HRESULT rc1 = lockedMediaMap->Clear();
11057 AssertComRC(rc1);
11058 }
11059
11060 /* rollback hard disk changes */
11061 mMediaData.rollback();
11062
11063 MultiResult mrc(S_OK);
11064
11065 // Delete unused implicit diffs.
11066 if (implicitAtts.size() != 0)
11067 {
11068 alock.release();
11069
11070 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11071 {
11072 // Remove medium associated with this attachment.
11073 ComObjPtr<MediumAttachment> pAtt = *it;
11074 Assert(pAtt);
11075 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11076 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11077 Assert(pMedium);
11078
11079 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11080 // continue on delete failure, just collect error messages
11081 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11082 pMedium->i_getLocationFull().c_str() ));
11083 mrc = rc;
11084 }
11085 // Clear the list of deleted implicit attachments now, while not
11086 // holding the lock, as it will ultimately trigger Medium::uninit()
11087 // calls which assume that the media tree lock isn't held.
11088 implicitAtts.clear();
11089
11090 alock.acquire();
11091
11092 /* if there is a VM recreate media lock map as mentioned above,
11093 * otherwise it is a waste of time and we leave things unlocked */
11094 if (aOnline)
11095 {
11096 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11097 /* must never be NULL, but better safe than sorry */
11098 if (!pMachine.isNull())
11099 {
11100 alock.release();
11101 rc = mData->mSession.mMachine->i_lockMedia();
11102 alock.acquire();
11103 if (FAILED(rc))
11104 throw rc;
11105 }
11106 }
11107 }
11108 }
11109 catch (HRESULT aRC) {rc = aRC;}
11110
11111 if (mData->mMachineState == MachineState_SettingUp)
11112 i_setMachineState(oldState);
11113
11114 /* unlock all hard disks we locked when there is no VM */
11115 if (!aOnline)
11116 {
11117 ErrorInfoKeeper eik;
11118
11119 HRESULT rc1 = lockedMediaMap->Clear();
11120 AssertComRC(rc1);
11121 }
11122
11123 return rc;
11124}
11125
11126
11127/**
11128 * Looks through the given list of media attachments for one with the given parameters
11129 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11130 * can be searched as well if needed.
11131 *
11132 * @param list
11133 * @param aControllerName
11134 * @param aControllerPort
11135 * @param aDevice
11136 * @return
11137 */
11138MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11139 const Utf8Str &aControllerName,
11140 LONG aControllerPort,
11141 LONG aDevice)
11142{
11143 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11144 {
11145 MediumAttachment *pAttach = *it;
11146 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11147 return pAttach;
11148 }
11149
11150 return NULL;
11151}
11152
11153/**
11154 * Looks through the given list of media attachments for one with the given parameters
11155 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11156 * can be searched as well if needed.
11157 *
11158 * @param list
11159 * @param aControllerName
11160 * @param aControllerPort
11161 * @param aDevice
11162 * @return
11163 */
11164MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11165 ComObjPtr<Medium> pMedium)
11166{
11167 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11168 {
11169 MediumAttachment *pAttach = *it;
11170 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11171 if (pMediumThis == pMedium)
11172 return pAttach;
11173 }
11174
11175 return NULL;
11176}
11177
11178/**
11179 * Looks through the given list of media attachments for one with the given parameters
11180 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11181 * can be searched as well if needed.
11182 *
11183 * @param list
11184 * @param aControllerName
11185 * @param aControllerPort
11186 * @param aDevice
11187 * @return
11188 */
11189MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11190 Guid &id)
11191{
11192 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11193 {
11194 MediumAttachment *pAttach = *it;
11195 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11196 if (pMediumThis->i_getId() == id)
11197 return pAttach;
11198 }
11199
11200 return NULL;
11201}
11202
11203/**
11204 * Main implementation for Machine::DetachDevice. This also gets called
11205 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11206 *
11207 * @param pAttach Medium attachment to detach.
11208 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11209 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11210 * SnapshotMachine, and this must be its snapshot.
11211 * @return
11212 */
11213HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11214 AutoWriteLock &writeLock,
11215 Snapshot *pSnapshot)
11216{
11217 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11218 DeviceType_T mediumType = pAttach->i_getType();
11219
11220 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11221
11222 if (pAttach->i_isImplicit())
11223 {
11224 /* attempt to implicitly delete the implicitly created diff */
11225
11226 /// @todo move the implicit flag from MediumAttachment to Medium
11227 /// and forbid any hard disk operation when it is implicit. Or maybe
11228 /// a special media state for it to make it even more simple.
11229
11230 Assert(mMediaData.isBackedUp());
11231
11232 /* will release the lock before the potentially lengthy operation, so
11233 * protect with the special state */
11234 MachineState_T oldState = mData->mMachineState;
11235 i_setMachineState(MachineState_SettingUp);
11236
11237 writeLock.release();
11238
11239 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11240 true /*aWait*/);
11241
11242 writeLock.acquire();
11243
11244 i_setMachineState(oldState);
11245
11246 if (FAILED(rc)) return rc;
11247 }
11248
11249 i_setModified(IsModified_Storage);
11250 mMediaData.backup();
11251 mMediaData->mAttachments.remove(pAttach);
11252
11253 if (!oldmedium.isNull())
11254 {
11255 // if this is from a snapshot, do not defer detachment to commitMedia()
11256 if (pSnapshot)
11257 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11258 // else if non-hard disk media, do not defer detachment to commitMedia() either
11259 else if (mediumType != DeviceType_HardDisk)
11260 oldmedium->i_removeBackReference(mData->mUuid);
11261 }
11262
11263 return S_OK;
11264}
11265
11266/**
11267 * Goes thru all media of the given list and
11268 *
11269 * 1) calls i_detachDevice() on each of them for this machine and
11270 * 2) adds all Medium objects found in the process to the given list,
11271 * depending on cleanupMode.
11272 *
11273 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11274 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11275 * media to the list.
11276 *
11277 * This gets called from Machine::Unregister, both for the actual Machine and
11278 * the SnapshotMachine objects that might be found in the snapshots.
11279 *
11280 * Requires caller and locking. The machine lock must be passed in because it
11281 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11282 *
11283 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11284 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11285 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11286 * Full, then all media get added;
11287 * otherwise no media get added.
11288 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11289 * @return
11290 */
11291HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11292 Snapshot *pSnapshot,
11293 CleanupMode_T cleanupMode,
11294 MediaList &llMedia)
11295{
11296 Assert(isWriteLockOnCurrentThread());
11297
11298 HRESULT rc;
11299
11300 // make a temporary list because i_detachDevice invalidates iterators into
11301 // mMediaData->mAttachments
11302 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11303
11304 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11305 {
11306 ComObjPtr<MediumAttachment> &pAttach = *it;
11307 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11308
11309 if (!pMedium.isNull())
11310 {
11311 AutoCaller mac(pMedium);
11312 if (FAILED(mac.rc())) return mac.rc();
11313 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11314 DeviceType_T devType = pMedium->i_getDeviceType();
11315 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11316 && devType == DeviceType_HardDisk)
11317 || (cleanupMode == CleanupMode_Full)
11318 )
11319 {
11320 llMedia.push_back(pMedium);
11321 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11322 /* Not allowed to keep this lock as below we need the parent
11323 * medium lock, and the lock order is parent to child. */
11324 lock.release();
11325 /*
11326 * Search for medias which are not attached to any machine, but
11327 * in the chain to an attached disk. Mediums are only consided
11328 * if they are:
11329 * - have only one child
11330 * - no references to any machines
11331 * - are of normal medium type
11332 */
11333 while (!pParent.isNull())
11334 {
11335 AutoCaller mac1(pParent);
11336 if (FAILED(mac1.rc())) return mac1.rc();
11337 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11338 if (pParent->i_getChildren().size() == 1)
11339 {
11340 if ( pParent->i_getMachineBackRefCount() == 0
11341 && pParent->i_getType() == MediumType_Normal
11342 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11343 llMedia.push_back(pParent);
11344 }
11345 else
11346 break;
11347 pParent = pParent->i_getParent();
11348 }
11349 }
11350 }
11351
11352 // real machine: then we need to use the proper method
11353 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11354
11355 if (FAILED(rc))
11356 return rc;
11357 }
11358
11359 return S_OK;
11360}
11361
11362/**
11363 * Perform deferred hard disk detachments.
11364 *
11365 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11366 * backed up).
11367 *
11368 * If @a aOnline is @c true then this method will also unlock the old hard disks
11369 * for which the new implicit diffs were created and will lock these new diffs for
11370 * writing.
11371 *
11372 * @param aOnline Whether the VM was online prior to this operation.
11373 *
11374 * @note Locks this object for writing!
11375 */
11376void Machine::i_commitMedia(bool aOnline /*= false*/)
11377{
11378 AutoCaller autoCaller(this);
11379 AssertComRCReturnVoid(autoCaller.rc());
11380
11381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11382
11383 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11384
11385 HRESULT rc = S_OK;
11386
11387 /* no attach/detach operations -- nothing to do */
11388 if (!mMediaData.isBackedUp())
11389 return;
11390
11391 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11392 bool fMediaNeedsLocking = false;
11393
11394 /* enumerate new attachments */
11395 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11396 it != mMediaData->mAttachments.end();
11397 ++it)
11398 {
11399 MediumAttachment *pAttach = *it;
11400
11401 pAttach->i_commit();
11402
11403 Medium* pMedium = pAttach->i_getMedium();
11404 bool fImplicit = pAttach->i_isImplicit();
11405
11406 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11407 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11408 fImplicit));
11409
11410 /** @todo convert all this Machine-based voodoo to MediumAttachment
11411 * based commit logic. */
11412 if (fImplicit)
11413 {
11414 /* convert implicit attachment to normal */
11415 pAttach->i_setImplicit(false);
11416
11417 if ( aOnline
11418 && pMedium
11419 && pAttach->i_getType() == DeviceType_HardDisk
11420 )
11421 {
11422 /* update the appropriate lock list */
11423 MediumLockList *pMediumLockList;
11424 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11425 AssertComRC(rc);
11426 if (pMediumLockList)
11427 {
11428 /* unlock if there's a need to change the locking */
11429 if (!fMediaNeedsLocking)
11430 {
11431 rc = mData->mSession.mLockedMedia.Unlock();
11432 AssertComRC(rc);
11433 fMediaNeedsLocking = true;
11434 }
11435 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11436 AssertComRC(rc);
11437 rc = pMediumLockList->Append(pMedium, true);
11438 AssertComRC(rc);
11439 }
11440 }
11441
11442 continue;
11443 }
11444
11445 if (pMedium)
11446 {
11447 /* was this medium attached before? */
11448 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11449 {
11450 MediumAttachment *pOldAttach = *oldIt;
11451 if (pOldAttach->i_getMedium() == pMedium)
11452 {
11453 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11454
11455 /* yes: remove from old to avoid de-association */
11456 oldAtts.erase(oldIt);
11457 break;
11458 }
11459 }
11460 }
11461 }
11462
11463 /* enumerate remaining old attachments and de-associate from the
11464 * current machine state */
11465 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11466 {
11467 MediumAttachment *pAttach = *it;
11468 Medium* pMedium = pAttach->i_getMedium();
11469
11470 /* Detach only hard disks, since DVD/floppy media is detached
11471 * instantly in MountMedium. */
11472 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11473 {
11474 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11475
11476 /* now de-associate from the current machine state */
11477 rc = pMedium->i_removeBackReference(mData->mUuid);
11478 AssertComRC(rc);
11479
11480 if (aOnline)
11481 {
11482 /* unlock since medium is not used anymore */
11483 MediumLockList *pMediumLockList;
11484 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11485 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11486 {
11487 /* this happens for online snapshots, there the attachment
11488 * is changing, but only to a diff image created under
11489 * the old one, so there is no separate lock list */
11490 Assert(!pMediumLockList);
11491 }
11492 else
11493 {
11494 AssertComRC(rc);
11495 if (pMediumLockList)
11496 {
11497 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11498 AssertComRC(rc);
11499 }
11500 }
11501 }
11502 }
11503 }
11504
11505 /* take media locks again so that the locking state is consistent */
11506 if (fMediaNeedsLocking)
11507 {
11508 Assert(aOnline);
11509 rc = mData->mSession.mLockedMedia.Lock();
11510 AssertComRC(rc);
11511 }
11512
11513 /* commit the hard disk changes */
11514 mMediaData.commit();
11515
11516 if (i_isSessionMachine())
11517 {
11518 /*
11519 * Update the parent machine to point to the new owner.
11520 * This is necessary because the stored parent will point to the
11521 * session machine otherwise and cause crashes or errors later
11522 * when the session machine gets invalid.
11523 */
11524 /** @todo Change the MediumAttachment class to behave like any other
11525 * class in this regard by creating peer MediumAttachment
11526 * objects for session machines and share the data with the peer
11527 * machine.
11528 */
11529 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11530 it != mMediaData->mAttachments.end();
11531 ++it)
11532 (*it)->i_updateParentMachine(mPeer);
11533
11534 /* attach new data to the primary machine and reshare it */
11535 mPeer->mMediaData.attach(mMediaData);
11536 }
11537
11538 return;
11539}
11540
11541/**
11542 * Perform deferred deletion of implicitly created diffs.
11543 *
11544 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11545 * backed up).
11546 *
11547 * @note Locks this object for writing!
11548 */
11549void Machine::i_rollbackMedia()
11550{
11551 AutoCaller autoCaller(this);
11552 AssertComRCReturnVoid(autoCaller.rc());
11553
11554 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11555 LogFlowThisFunc(("Entering rollbackMedia\n"));
11556
11557 HRESULT rc = S_OK;
11558
11559 /* no attach/detach operations -- nothing to do */
11560 if (!mMediaData.isBackedUp())
11561 return;
11562
11563 /* enumerate new attachments */
11564 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11565 it != mMediaData->mAttachments.end();
11566 ++it)
11567 {
11568 MediumAttachment *pAttach = *it;
11569 /* Fix up the backrefs for DVD/floppy media. */
11570 if (pAttach->i_getType() != DeviceType_HardDisk)
11571 {
11572 Medium* pMedium = pAttach->i_getMedium();
11573 if (pMedium)
11574 {
11575 rc = pMedium->i_removeBackReference(mData->mUuid);
11576 AssertComRC(rc);
11577 }
11578 }
11579
11580 (*it)->i_rollback();
11581
11582 pAttach = *it;
11583 /* Fix up the backrefs for DVD/floppy media. */
11584 if (pAttach->i_getType() != DeviceType_HardDisk)
11585 {
11586 Medium* pMedium = pAttach->i_getMedium();
11587 if (pMedium)
11588 {
11589 rc = pMedium->i_addBackReference(mData->mUuid);
11590 AssertComRC(rc);
11591 }
11592 }
11593 }
11594
11595 /** @todo convert all this Machine-based voodoo to MediumAttachment
11596 * based rollback logic. */
11597 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11598
11599 return;
11600}
11601
11602/**
11603 * Returns true if the settings file is located in the directory named exactly
11604 * as the machine; this means, among other things, that the machine directory
11605 * should be auto-renamed.
11606 *
11607 * @param aSettingsDir if not NULL, the full machine settings file directory
11608 * name will be assigned there.
11609 *
11610 * @note Doesn't lock anything.
11611 * @note Not thread safe (must be called from this object's lock).
11612 */
11613bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11614{
11615 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11616 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11617 if (aSettingsDir)
11618 *aSettingsDir = strMachineDirName;
11619 strMachineDirName.stripPath(); // vmname
11620 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11621 strConfigFileOnly.stripPath() // vmname.vbox
11622 .stripSuffix(); // vmname
11623 /** @todo hack, make somehow use of ComposeMachineFilename */
11624 if (mUserData->s.fDirectoryIncludesUUID)
11625 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11626
11627 AssertReturn(!strMachineDirName.isEmpty(), false);
11628 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11629
11630 return strMachineDirName == strConfigFileOnly;
11631}
11632
11633/**
11634 * Discards all changes to machine settings.
11635 *
11636 * @param aNotify Whether to notify the direct session about changes or not.
11637 *
11638 * @note Locks objects for writing!
11639 */
11640void Machine::i_rollback(bool aNotify)
11641{
11642 AutoCaller autoCaller(this);
11643 AssertComRCReturn(autoCaller.rc(), (void)0);
11644
11645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11646
11647 if (!mStorageControllers.isNull())
11648 {
11649 if (mStorageControllers.isBackedUp())
11650 {
11651 /* unitialize all new devices (absent in the backed up list). */
11652 StorageControllerList::const_iterator it = mStorageControllers->begin();
11653 StorageControllerList *backedList = mStorageControllers.backedUpData();
11654 while (it != mStorageControllers->end())
11655 {
11656 if ( std::find(backedList->begin(), backedList->end(), *it)
11657 == backedList->end()
11658 )
11659 {
11660 (*it)->uninit();
11661 }
11662 ++it;
11663 }
11664
11665 /* restore the list */
11666 mStorageControllers.rollback();
11667 }
11668
11669 /* rollback any changes to devices after restoring the list */
11670 if (mData->flModifications & IsModified_Storage)
11671 {
11672 StorageControllerList::const_iterator it = mStorageControllers->begin();
11673 while (it != mStorageControllers->end())
11674 {
11675 (*it)->i_rollback();
11676 ++it;
11677 }
11678 }
11679 }
11680
11681 if (!mUSBControllers.isNull())
11682 {
11683 if (mUSBControllers.isBackedUp())
11684 {
11685 /* unitialize all new devices (absent in the backed up list). */
11686 USBControllerList::const_iterator it = mUSBControllers->begin();
11687 USBControllerList *backedList = mUSBControllers.backedUpData();
11688 while (it != mUSBControllers->end())
11689 {
11690 if ( std::find(backedList->begin(), backedList->end(), *it)
11691 == backedList->end()
11692 )
11693 {
11694 (*it)->uninit();
11695 }
11696 ++it;
11697 }
11698
11699 /* restore the list */
11700 mUSBControllers.rollback();
11701 }
11702
11703 /* rollback any changes to devices after restoring the list */
11704 if (mData->flModifications & IsModified_USB)
11705 {
11706 USBControllerList::const_iterator it = mUSBControllers->begin();
11707 while (it != mUSBControllers->end())
11708 {
11709 (*it)->i_rollback();
11710 ++it;
11711 }
11712 }
11713 }
11714
11715 mUserData.rollback();
11716
11717 mHWData.rollback();
11718
11719 if (mData->flModifications & IsModified_Storage)
11720 i_rollbackMedia();
11721
11722 if (mBIOSSettings)
11723 mBIOSSettings->i_rollback();
11724
11725 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11726 mVRDEServer->i_rollback();
11727
11728 if (mAudioAdapter)
11729 mAudioAdapter->i_rollback();
11730
11731 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11732 mUSBDeviceFilters->i_rollback();
11733
11734 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11735 mBandwidthControl->i_rollback();
11736
11737 if (!mHWData.isNull())
11738 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11739 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11740 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11741 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11742
11743 if (mData->flModifications & IsModified_NetworkAdapters)
11744 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11745 if ( mNetworkAdapters[slot]
11746 && mNetworkAdapters[slot]->i_isModified())
11747 {
11748 mNetworkAdapters[slot]->i_rollback();
11749 networkAdapters[slot] = mNetworkAdapters[slot];
11750 }
11751
11752 if (mData->flModifications & IsModified_SerialPorts)
11753 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11754 if ( mSerialPorts[slot]
11755 && mSerialPorts[slot]->i_isModified())
11756 {
11757 mSerialPorts[slot]->i_rollback();
11758 serialPorts[slot] = mSerialPorts[slot];
11759 }
11760
11761 if (mData->flModifications & IsModified_ParallelPorts)
11762 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11763 if ( mParallelPorts[slot]
11764 && mParallelPorts[slot]->i_isModified())
11765 {
11766 mParallelPorts[slot]->i_rollback();
11767 parallelPorts[slot] = mParallelPorts[slot];
11768 }
11769
11770 if (aNotify)
11771 {
11772 /* inform the direct session about changes */
11773
11774 ComObjPtr<Machine> that = this;
11775 uint32_t flModifications = mData->flModifications;
11776 alock.release();
11777
11778 if (flModifications & IsModified_SharedFolders)
11779 that->i_onSharedFolderChange();
11780
11781 if (flModifications & IsModified_VRDEServer)
11782 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11783 if (flModifications & IsModified_USB)
11784 that->i_onUSBControllerChange();
11785
11786 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11787 if (networkAdapters[slot])
11788 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11789 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11790 if (serialPorts[slot])
11791 that->i_onSerialPortChange(serialPorts[slot]);
11792 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11793 if (parallelPorts[slot])
11794 that->i_onParallelPortChange(parallelPorts[slot]);
11795
11796 if (flModifications & IsModified_Storage)
11797 that->i_onStorageControllerChange();
11798
11799#if 0
11800 if (flModifications & IsModified_BandwidthControl)
11801 that->onBandwidthControlChange();
11802#endif
11803 }
11804}
11805
11806/**
11807 * Commits all the changes to machine settings.
11808 *
11809 * Note that this operation is supposed to never fail.
11810 *
11811 * @note Locks this object and children for writing.
11812 */
11813void Machine::i_commit()
11814{
11815 AutoCaller autoCaller(this);
11816 AssertComRCReturnVoid(autoCaller.rc());
11817
11818 AutoCaller peerCaller(mPeer);
11819 AssertComRCReturnVoid(peerCaller.rc());
11820
11821 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11822
11823 /*
11824 * use safe commit to ensure Snapshot machines (that share mUserData)
11825 * will still refer to a valid memory location
11826 */
11827 mUserData.commitCopy();
11828
11829 mHWData.commit();
11830
11831 if (mMediaData.isBackedUp())
11832 i_commitMedia(Global::IsOnline(mData->mMachineState));
11833
11834 mBIOSSettings->i_commit();
11835 mVRDEServer->i_commit();
11836 mAudioAdapter->i_commit();
11837 mUSBDeviceFilters->i_commit();
11838 mBandwidthControl->i_commit();
11839
11840 /* Since mNetworkAdapters is a list which might have been changed (resized)
11841 * without using the Backupable<> template we need to handle the copying
11842 * of the list entries manually, including the creation of peers for the
11843 * new objects. */
11844 bool commitNetworkAdapters = false;
11845 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11846 if (mPeer)
11847 {
11848 /* commit everything, even the ones which will go away */
11849 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11850 mNetworkAdapters[slot]->i_commit();
11851 /* copy over the new entries, creating a peer and uninit the original */
11852 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11853 for (size_t slot = 0; slot < newSize; slot++)
11854 {
11855 /* look if this adapter has a peer device */
11856 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11857 if (!peer)
11858 {
11859 /* no peer means the adapter is a newly created one;
11860 * create a peer owning data this data share it with */
11861 peer.createObject();
11862 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11863 }
11864 mPeer->mNetworkAdapters[slot] = peer;
11865 }
11866 /* uninit any no longer needed network adapters */
11867 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11868 mNetworkAdapters[slot]->uninit();
11869 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11870 {
11871 if (mPeer->mNetworkAdapters[slot])
11872 mPeer->mNetworkAdapters[slot]->uninit();
11873 }
11874 /* Keep the original network adapter count until this point, so that
11875 * discarding a chipset type change will not lose settings. */
11876 mNetworkAdapters.resize(newSize);
11877 mPeer->mNetworkAdapters.resize(newSize);
11878 }
11879 else
11880 {
11881 /* we have no peer (our parent is the newly created machine);
11882 * just commit changes to the network adapters */
11883 commitNetworkAdapters = true;
11884 }
11885 if (commitNetworkAdapters)
11886 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11887 mNetworkAdapters[slot]->i_commit();
11888
11889 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11890 mSerialPorts[slot]->i_commit();
11891 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11892 mParallelPorts[slot]->i_commit();
11893
11894 bool commitStorageControllers = false;
11895
11896 if (mStorageControllers.isBackedUp())
11897 {
11898 mStorageControllers.commit();
11899
11900 if (mPeer)
11901 {
11902 /* Commit all changes to new controllers (this will reshare data with
11903 * peers for those who have peers) */
11904 StorageControllerList *newList = new StorageControllerList();
11905 StorageControllerList::const_iterator it = mStorageControllers->begin();
11906 while (it != mStorageControllers->end())
11907 {
11908 (*it)->i_commit();
11909
11910 /* look if this controller has a peer device */
11911 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11912 if (!peer)
11913 {
11914 /* no peer means the device is a newly created one;
11915 * create a peer owning data this device share it with */
11916 peer.createObject();
11917 peer->init(mPeer, *it, true /* aReshare */);
11918 }
11919 else
11920 {
11921 /* remove peer from the old list */
11922 mPeer->mStorageControllers->remove(peer);
11923 }
11924 /* and add it to the new list */
11925 newList->push_back(peer);
11926
11927 ++it;
11928 }
11929
11930 /* uninit old peer's controllers that are left */
11931 it = mPeer->mStorageControllers->begin();
11932 while (it != mPeer->mStorageControllers->end())
11933 {
11934 (*it)->uninit();
11935 ++it;
11936 }
11937
11938 /* attach new list of controllers to our peer */
11939 mPeer->mStorageControllers.attach(newList);
11940 }
11941 else
11942 {
11943 /* we have no peer (our parent is the newly created machine);
11944 * just commit changes to devices */
11945 commitStorageControllers = true;
11946 }
11947 }
11948 else
11949 {
11950 /* the list of controllers itself is not changed,
11951 * just commit changes to controllers themselves */
11952 commitStorageControllers = true;
11953 }
11954
11955 if (commitStorageControllers)
11956 {
11957 StorageControllerList::const_iterator it = mStorageControllers->begin();
11958 while (it != mStorageControllers->end())
11959 {
11960 (*it)->i_commit();
11961 ++it;
11962 }
11963 }
11964
11965 bool commitUSBControllers = false;
11966
11967 if (mUSBControllers.isBackedUp())
11968 {
11969 mUSBControllers.commit();
11970
11971 if (mPeer)
11972 {
11973 /* Commit all changes to new controllers (this will reshare data with
11974 * peers for those who have peers) */
11975 USBControllerList *newList = new USBControllerList();
11976 USBControllerList::const_iterator it = mUSBControllers->begin();
11977 while (it != mUSBControllers->end())
11978 {
11979 (*it)->i_commit();
11980
11981 /* look if this controller has a peer device */
11982 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11983 if (!peer)
11984 {
11985 /* no peer means the device is a newly created one;
11986 * create a peer owning data this device share it with */
11987 peer.createObject();
11988 peer->init(mPeer, *it, true /* aReshare */);
11989 }
11990 else
11991 {
11992 /* remove peer from the old list */
11993 mPeer->mUSBControllers->remove(peer);
11994 }
11995 /* and add it to the new list */
11996 newList->push_back(peer);
11997
11998 ++it;
11999 }
12000
12001 /* uninit old peer's controllers that are left */
12002 it = mPeer->mUSBControllers->begin();
12003 while (it != mPeer->mUSBControllers->end())
12004 {
12005 (*it)->uninit();
12006 ++it;
12007 }
12008
12009 /* attach new list of controllers to our peer */
12010 mPeer->mUSBControllers.attach(newList);
12011 }
12012 else
12013 {
12014 /* we have no peer (our parent is the newly created machine);
12015 * just commit changes to devices */
12016 commitUSBControllers = true;
12017 }
12018 }
12019 else
12020 {
12021 /* the list of controllers itself is not changed,
12022 * just commit changes to controllers themselves */
12023 commitUSBControllers = true;
12024 }
12025
12026 if (commitUSBControllers)
12027 {
12028 USBControllerList::const_iterator it = mUSBControllers->begin();
12029 while (it != mUSBControllers->end())
12030 {
12031 (*it)->i_commit();
12032 ++it;
12033 }
12034 }
12035
12036 if (i_isSessionMachine())
12037 {
12038 /* attach new data to the primary machine and reshare it */
12039 mPeer->mUserData.attach(mUserData);
12040 mPeer->mHWData.attach(mHWData);
12041 /* mMediaData is reshared by fixupMedia */
12042 // mPeer->mMediaData.attach(mMediaData);
12043 Assert(mPeer->mMediaData.data() == mMediaData.data());
12044 }
12045}
12046
12047/**
12048 * Copies all the hardware data from the given machine.
12049 *
12050 * Currently, only called when the VM is being restored from a snapshot. In
12051 * particular, this implies that the VM is not running during this method's
12052 * call.
12053 *
12054 * @note This method must be called from under this object's lock.
12055 *
12056 * @note This method doesn't call #commit(), so all data remains backed up and
12057 * unsaved.
12058 */
12059void Machine::i_copyFrom(Machine *aThat)
12060{
12061 AssertReturnVoid(!i_isSnapshotMachine());
12062 AssertReturnVoid(aThat->i_isSnapshotMachine());
12063
12064 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12065
12066 mHWData.assignCopy(aThat->mHWData);
12067
12068 // create copies of all shared folders (mHWData after attaching a copy
12069 // contains just references to original objects)
12070 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12071 it != mHWData->mSharedFolders.end();
12072 ++it)
12073 {
12074 ComObjPtr<SharedFolder> folder;
12075 folder.createObject();
12076 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12077 AssertComRC(rc);
12078 *it = folder;
12079 }
12080
12081 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12082 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12083 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12084 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12085 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12086
12087 /* create private copies of all controllers */
12088 mStorageControllers.backup();
12089 mStorageControllers->clear();
12090 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12091 it != aThat->mStorageControllers->end();
12092 ++it)
12093 {
12094 ComObjPtr<StorageController> ctrl;
12095 ctrl.createObject();
12096 ctrl->initCopy(this, *it);
12097 mStorageControllers->push_back(ctrl);
12098 }
12099
12100 /* create private copies of all USB controllers */
12101 mUSBControllers.backup();
12102 mUSBControllers->clear();
12103 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12104 it != aThat->mUSBControllers->end();
12105 ++it)
12106 {
12107 ComObjPtr<USBController> ctrl;
12108 ctrl.createObject();
12109 ctrl->initCopy(this, *it);
12110 mUSBControllers->push_back(ctrl);
12111 }
12112
12113 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12114 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12115 {
12116 if (mNetworkAdapters[slot].isNotNull())
12117 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12118 else
12119 {
12120 unconst(mNetworkAdapters[slot]).createObject();
12121 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12122 }
12123 }
12124 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12125 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12126 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12127 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12128}
12129
12130/**
12131 * Returns whether the given storage controller is hotplug capable.
12132 *
12133 * @returns true if the controller supports hotplugging
12134 * false otherwise.
12135 * @param enmCtrlType The controller type to check for.
12136 */
12137bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12138{
12139 ComPtr<ISystemProperties> systemProperties;
12140 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12141 if (FAILED(rc))
12142 return false;
12143
12144 BOOL aHotplugCapable = FALSE;
12145 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12146
12147 return RT_BOOL(aHotplugCapable);
12148}
12149
12150#ifdef VBOX_WITH_RESOURCE_USAGE_API
12151
12152void Machine::i_getDiskList(MediaList &list)
12153{
12154 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12155 it != mMediaData->mAttachments.end();
12156 ++it)
12157 {
12158 MediumAttachment* pAttach = *it;
12159 /* just in case */
12160 AssertContinue(pAttach);
12161
12162 AutoCaller localAutoCallerA(pAttach);
12163 if (FAILED(localAutoCallerA.rc())) continue;
12164
12165 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12166
12167 if (pAttach->i_getType() == DeviceType_HardDisk)
12168 list.push_back(pAttach->i_getMedium());
12169 }
12170}
12171
12172void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12173{
12174 AssertReturnVoid(isWriteLockOnCurrentThread());
12175 AssertPtrReturnVoid(aCollector);
12176
12177 pm::CollectorHAL *hal = aCollector->getHAL();
12178 /* Create sub metrics */
12179 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12180 "Percentage of processor time spent in user mode by the VM process.");
12181 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12182 "Percentage of processor time spent in kernel mode by the VM process.");
12183 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12184 "Size of resident portion of VM process in memory.");
12185 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12186 "Actual size of all VM disks combined.");
12187 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12188 "Network receive rate.");
12189 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12190 "Network transmit rate.");
12191 /* Create and register base metrics */
12192 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12193 cpuLoadUser, cpuLoadKernel);
12194 aCollector->registerBaseMetric(cpuLoad);
12195 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12196 ramUsageUsed);
12197 aCollector->registerBaseMetric(ramUsage);
12198 MediaList disks;
12199 i_getDiskList(disks);
12200 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12201 diskUsageUsed);
12202 aCollector->registerBaseMetric(diskUsage);
12203
12204 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12205 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12206 new pm::AggregateAvg()));
12207 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12208 new pm::AggregateMin()));
12209 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12210 new pm::AggregateMax()));
12211 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12212 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12213 new pm::AggregateAvg()));
12214 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12215 new pm::AggregateMin()));
12216 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12217 new pm::AggregateMax()));
12218
12219 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12220 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12221 new pm::AggregateAvg()));
12222 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12223 new pm::AggregateMin()));
12224 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12225 new pm::AggregateMax()));
12226
12227 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12228 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12229 new pm::AggregateAvg()));
12230 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12231 new pm::AggregateMin()));
12232 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12233 new pm::AggregateMax()));
12234
12235
12236 /* Guest metrics collector */
12237 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12238 aCollector->registerGuest(mCollectorGuest);
12239 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12240
12241 /* Create sub metrics */
12242 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12243 "Percentage of processor time spent in user mode as seen by the guest.");
12244 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12245 "Percentage of processor time spent in kernel mode as seen by the guest.");
12246 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12247 "Percentage of processor time spent idling as seen by the guest.");
12248
12249 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12250 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12251 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12252 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12253 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12254 pm::SubMetric *guestMemCache = new pm::SubMetric(
12255 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12256
12257 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12258 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12259
12260 /* Create and register base metrics */
12261 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12262 machineNetRx, machineNetTx);
12263 aCollector->registerBaseMetric(machineNetRate);
12264
12265 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12266 guestLoadUser, guestLoadKernel, guestLoadIdle);
12267 aCollector->registerBaseMetric(guestCpuLoad);
12268
12269 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12270 guestMemTotal, guestMemFree,
12271 guestMemBalloon, guestMemShared,
12272 guestMemCache, guestPagedTotal);
12273 aCollector->registerBaseMetric(guestCpuMem);
12274
12275 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12276 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12277 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12278 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12279
12280 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12281 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12282 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12283 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12284
12285 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12286 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12287 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12288 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12289
12290 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12291 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12292 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12293 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12294
12295 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12296 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12297 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12298 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12299
12300 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12301 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12302 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12303 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12304
12305 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12306 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12307 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12308 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12309
12310 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12311 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12312 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12313 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12314
12315 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12316 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12317 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12318 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12319
12320 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12321 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12322 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12323 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12324
12325 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12326 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12327 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12328 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12329}
12330
12331void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12332{
12333 AssertReturnVoid(isWriteLockOnCurrentThread());
12334
12335 if (aCollector)
12336 {
12337 aCollector->unregisterMetricsFor(aMachine);
12338 aCollector->unregisterBaseMetricsFor(aMachine);
12339 }
12340}
12341
12342#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12343
12344
12345////////////////////////////////////////////////////////////////////////////////
12346
12347DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12348
12349HRESULT SessionMachine::FinalConstruct()
12350{
12351 LogFlowThisFunc(("\n"));
12352
12353 mClientToken = NULL;
12354
12355 return BaseFinalConstruct();
12356}
12357
12358void SessionMachine::FinalRelease()
12359{
12360 LogFlowThisFunc(("\n"));
12361
12362 Assert(!mClientToken);
12363 /* paranoia, should not hang around any more */
12364 if (mClientToken)
12365 {
12366 delete mClientToken;
12367 mClientToken = NULL;
12368 }
12369
12370 uninit(Uninit::Unexpected);
12371
12372 BaseFinalRelease();
12373}
12374
12375/**
12376 * @note Must be called only by Machine::LockMachine() from its own write lock.
12377 */
12378HRESULT SessionMachine::init(Machine *aMachine)
12379{
12380 LogFlowThisFuncEnter();
12381 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12382
12383 AssertReturn(aMachine, E_INVALIDARG);
12384
12385 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12386
12387 /* Enclose the state transition NotReady->InInit->Ready */
12388 AutoInitSpan autoInitSpan(this);
12389 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12390
12391 HRESULT rc = S_OK;
12392
12393 RT_ZERO(mAuthLibCtx);
12394
12395 /* create the machine client token */
12396 try
12397 {
12398 mClientToken = new ClientToken(aMachine, this);
12399 if (!mClientToken->isReady())
12400 {
12401 delete mClientToken;
12402 mClientToken = NULL;
12403 rc = E_FAIL;
12404 }
12405 }
12406 catch (std::bad_alloc &)
12407 {
12408 rc = E_OUTOFMEMORY;
12409 }
12410 if (FAILED(rc))
12411 return rc;
12412
12413 /* memorize the peer Machine */
12414 unconst(mPeer) = aMachine;
12415 /* share the parent pointer */
12416 unconst(mParent) = aMachine->mParent;
12417
12418 /* take the pointers to data to share */
12419 mData.share(aMachine->mData);
12420 mSSData.share(aMachine->mSSData);
12421
12422 mUserData.share(aMachine->mUserData);
12423 mHWData.share(aMachine->mHWData);
12424 mMediaData.share(aMachine->mMediaData);
12425
12426 mStorageControllers.allocate();
12427 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12428 it != aMachine->mStorageControllers->end();
12429 ++it)
12430 {
12431 ComObjPtr<StorageController> ctl;
12432 ctl.createObject();
12433 ctl->init(this, *it);
12434 mStorageControllers->push_back(ctl);
12435 }
12436
12437 mUSBControllers.allocate();
12438 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12439 it != aMachine->mUSBControllers->end();
12440 ++it)
12441 {
12442 ComObjPtr<USBController> ctl;
12443 ctl.createObject();
12444 ctl->init(this, *it);
12445 mUSBControllers->push_back(ctl);
12446 }
12447
12448 unconst(mBIOSSettings).createObject();
12449 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12450 /* create another VRDEServer object that will be mutable */
12451 unconst(mVRDEServer).createObject();
12452 mVRDEServer->init(this, aMachine->mVRDEServer);
12453 /* create another audio adapter object that will be mutable */
12454 unconst(mAudioAdapter).createObject();
12455 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12456 /* create a list of serial ports that will be mutable */
12457 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12458 {
12459 unconst(mSerialPorts[slot]).createObject();
12460 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12461 }
12462 /* create a list of parallel ports that will be mutable */
12463 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12464 {
12465 unconst(mParallelPorts[slot]).createObject();
12466 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12467 }
12468
12469 /* create another USB device filters object that will be mutable */
12470 unconst(mUSBDeviceFilters).createObject();
12471 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12472
12473 /* create a list of network adapters that will be mutable */
12474 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12475 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12476 {
12477 unconst(mNetworkAdapters[slot]).createObject();
12478 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12479 }
12480
12481 /* create another bandwidth control object that will be mutable */
12482 unconst(mBandwidthControl).createObject();
12483 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12484
12485 /* default is to delete saved state on Saved -> PoweredOff transition */
12486 mRemoveSavedState = true;
12487
12488 /* Confirm a successful initialization when it's the case */
12489 autoInitSpan.setSucceeded();
12490
12491 miNATNetworksStarted = 0;
12492
12493 LogFlowThisFuncLeave();
12494 return rc;
12495}
12496
12497/**
12498 * Uninitializes this session object. If the reason is other than
12499 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12500 * or the client watcher code.
12501 *
12502 * @param aReason uninitialization reason
12503 *
12504 * @note Locks mParent + this object for writing.
12505 */
12506void SessionMachine::uninit(Uninit::Reason aReason)
12507{
12508 LogFlowThisFuncEnter();
12509 LogFlowThisFunc(("reason=%d\n", aReason));
12510
12511 /*
12512 * Strongly reference ourselves to prevent this object deletion after
12513 * mData->mSession.mMachine.setNull() below (which can release the last
12514 * reference and call the destructor). Important: this must be done before
12515 * accessing any members (and before AutoUninitSpan that does it as well).
12516 * This self reference will be released as the very last step on return.
12517 */
12518 ComObjPtr<SessionMachine> selfRef = this;
12519
12520 /* Enclose the state transition Ready->InUninit->NotReady */
12521 AutoUninitSpan autoUninitSpan(this);
12522 if (autoUninitSpan.uninitDone())
12523 {
12524 LogFlowThisFunc(("Already uninitialized\n"));
12525 LogFlowThisFuncLeave();
12526 return;
12527 }
12528
12529 if (autoUninitSpan.initFailed())
12530 {
12531 /* We've been called by init() because it's failed. It's not really
12532 * necessary (nor it's safe) to perform the regular uninit sequence
12533 * below, the following is enough.
12534 */
12535 LogFlowThisFunc(("Initialization failed.\n"));
12536 /* destroy the machine client token */
12537 if (mClientToken)
12538 {
12539 delete mClientToken;
12540 mClientToken = NULL;
12541 }
12542 uninitDataAndChildObjects();
12543 mData.free();
12544 unconst(mParent) = NULL;
12545 unconst(mPeer) = NULL;
12546 LogFlowThisFuncLeave();
12547 return;
12548 }
12549
12550 MachineState_T lastState;
12551 {
12552 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12553 lastState = mData->mMachineState;
12554 }
12555 NOREF(lastState);
12556
12557#ifdef VBOX_WITH_USB
12558 // release all captured USB devices, but do this before requesting the locks below
12559 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12560 {
12561 /* Console::captureUSBDevices() is called in the VM process only after
12562 * setting the machine state to Starting or Restoring.
12563 * Console::detachAllUSBDevices() will be called upon successful
12564 * termination. So, we need to release USB devices only if there was
12565 * an abnormal termination of a running VM.
12566 *
12567 * This is identical to SessionMachine::DetachAllUSBDevices except
12568 * for the aAbnormal argument. */
12569 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12570 AssertComRC(rc);
12571 NOREF(rc);
12572
12573 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12574 if (service)
12575 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12576 }
12577#endif /* VBOX_WITH_USB */
12578
12579 // we need to lock this object in uninit() because the lock is shared
12580 // with mPeer (as well as data we modify below). mParent lock is needed
12581 // by several calls to it, and USB needs host lock.
12582 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12583
12584#ifdef VBOX_WITH_RESOURCE_USAGE_API
12585 /*
12586 * It is safe to call Machine::i_unregisterMetrics() here because
12587 * PerformanceCollector::samplerCallback no longer accesses guest methods
12588 * holding the lock.
12589 */
12590 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12591 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12592 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12593 if (mCollectorGuest)
12594 {
12595 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12596 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12597 mCollectorGuest = NULL;
12598 }
12599#endif
12600
12601 if (aReason == Uninit::Abnormal)
12602 {
12603 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12604
12605 /* reset the state to Aborted */
12606 if (mData->mMachineState != MachineState_Aborted)
12607 i_setMachineState(MachineState_Aborted);
12608 }
12609
12610 // any machine settings modified?
12611 if (mData->flModifications)
12612 {
12613 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12614 i_rollback(false /* aNotify */);
12615 }
12616
12617 mData->mSession.mPID = NIL_RTPROCESS;
12618
12619 if (aReason == Uninit::Unexpected)
12620 {
12621 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12622 * client watcher thread to update the set of machines that have open
12623 * sessions. */
12624 mParent->i_updateClientWatcher();
12625 }
12626
12627 /* uninitialize all remote controls */
12628 if (mData->mSession.mRemoteControls.size())
12629 {
12630 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12631 mData->mSession.mRemoteControls.size()));
12632
12633 Data::Session::RemoteControlList::iterator it =
12634 mData->mSession.mRemoteControls.begin();
12635 while (it != mData->mSession.mRemoteControls.end())
12636 {
12637 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12638 HRESULT rc = (*it)->Uninitialize();
12639 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12640 if (FAILED(rc))
12641 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12642 ++it;
12643 }
12644 mData->mSession.mRemoteControls.clear();
12645 }
12646
12647 /* Remove all references to the NAT network service. The service will stop
12648 * if all references (also from other VMs) are removed. */
12649 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12650 {
12651 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12652 {
12653 BOOL enabled;
12654 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12655 if ( FAILED(hrc)
12656 || !enabled)
12657 continue;
12658
12659 NetworkAttachmentType_T type;
12660 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12661 if ( SUCCEEDED(hrc)
12662 && type == NetworkAttachmentType_NATNetwork)
12663 {
12664 Bstr name;
12665 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12666 if (SUCCEEDED(hrc))
12667 {
12668 multilock.release();
12669 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12670 mUserData->s.strName.c_str(), name.raw()));
12671 mParent->i_natNetworkRefDec(name.raw());
12672 multilock.acquire();
12673 }
12674 }
12675 }
12676 }
12677
12678 /*
12679 * An expected uninitialization can come only from #i_checkForDeath().
12680 * Otherwise it means that something's gone really wrong (for example,
12681 * the Session implementation has released the VirtualBox reference
12682 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12683 * etc). However, it's also possible, that the client releases the IPC
12684 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12685 * but the VirtualBox release event comes first to the server process.
12686 * This case is practically possible, so we should not assert on an
12687 * unexpected uninit, just log a warning.
12688 */
12689
12690 if ((aReason == Uninit::Unexpected))
12691 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12692
12693 if (aReason != Uninit::Normal)
12694 {
12695 mData->mSession.mDirectControl.setNull();
12696 }
12697 else
12698 {
12699 /* this must be null here (see #OnSessionEnd()) */
12700 Assert(mData->mSession.mDirectControl.isNull());
12701 Assert(mData->mSession.mState == SessionState_Unlocking);
12702 Assert(!mData->mSession.mProgress.isNull());
12703 }
12704 if (mData->mSession.mProgress)
12705 {
12706 if (aReason == Uninit::Normal)
12707 mData->mSession.mProgress->i_notifyComplete(S_OK);
12708 else
12709 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12710 COM_IIDOF(ISession),
12711 getComponentName(),
12712 tr("The VM session was aborted"));
12713 mData->mSession.mProgress.setNull();
12714 }
12715
12716 if (mConsoleTaskData.mProgress)
12717 {
12718 Assert(aReason == Uninit::Abnormal);
12719 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12720 COM_IIDOF(ISession),
12721 getComponentName(),
12722 tr("The VM session was aborted"));
12723 mConsoleTaskData.mProgress.setNull();
12724 }
12725
12726 /* remove the association between the peer machine and this session machine */
12727 Assert( (SessionMachine*)mData->mSession.mMachine == this
12728 || aReason == Uninit::Unexpected);
12729
12730 /* reset the rest of session data */
12731 mData->mSession.mLockType = LockType_Null;
12732 mData->mSession.mMachine.setNull();
12733 mData->mSession.mState = SessionState_Unlocked;
12734 mData->mSession.mName.setNull();
12735
12736 /* destroy the machine client token before leaving the exclusive lock */
12737 if (mClientToken)
12738 {
12739 delete mClientToken;
12740 mClientToken = NULL;
12741 }
12742
12743 /* fire an event */
12744 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12745
12746 uninitDataAndChildObjects();
12747
12748 /* free the essential data structure last */
12749 mData.free();
12750
12751 /* release the exclusive lock before setting the below two to NULL */
12752 multilock.release();
12753
12754 unconst(mParent) = NULL;
12755 unconst(mPeer) = NULL;
12756
12757 AuthLibUnload(&mAuthLibCtx);
12758
12759 LogFlowThisFuncLeave();
12760}
12761
12762// util::Lockable interface
12763////////////////////////////////////////////////////////////////////////////////
12764
12765/**
12766 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12767 * with the primary Machine instance (mPeer).
12768 */
12769RWLockHandle *SessionMachine::lockHandle() const
12770{
12771 AssertReturn(mPeer != NULL, NULL);
12772 return mPeer->lockHandle();
12773}
12774
12775// IInternalMachineControl methods
12776////////////////////////////////////////////////////////////////////////////////
12777
12778/**
12779 * Passes collected guest statistics to performance collector object
12780 */
12781HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12782 ULONG aCpuKernel, ULONG aCpuIdle,
12783 ULONG aMemTotal, ULONG aMemFree,
12784 ULONG aMemBalloon, ULONG aMemShared,
12785 ULONG aMemCache, ULONG aPageTotal,
12786 ULONG aAllocVMM, ULONG aFreeVMM,
12787 ULONG aBalloonedVMM, ULONG aSharedVMM,
12788 ULONG aVmNetRx, ULONG aVmNetTx)
12789{
12790#ifdef VBOX_WITH_RESOURCE_USAGE_API
12791 if (mCollectorGuest)
12792 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12793 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12794 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12795 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12796
12797 return S_OK;
12798#else
12799 NOREF(aValidStats);
12800 NOREF(aCpuUser);
12801 NOREF(aCpuKernel);
12802 NOREF(aCpuIdle);
12803 NOREF(aMemTotal);
12804 NOREF(aMemFree);
12805 NOREF(aMemBalloon);
12806 NOREF(aMemShared);
12807 NOREF(aMemCache);
12808 NOREF(aPageTotal);
12809 NOREF(aAllocVMM);
12810 NOREF(aFreeVMM);
12811 NOREF(aBalloonedVMM);
12812 NOREF(aSharedVMM);
12813 NOREF(aVmNetRx);
12814 NOREF(aVmNetTx);
12815 return E_NOTIMPL;
12816#endif
12817}
12818
12819////////////////////////////////////////////////////////////////////////////////
12820//
12821// SessionMachine task records
12822//
12823////////////////////////////////////////////////////////////////////////////////
12824
12825/**
12826 * Task record for saving the machine state.
12827 */
12828struct SessionMachine::SaveStateTask
12829 : public Machine::Task
12830{
12831 SaveStateTask(SessionMachine *m,
12832 Progress *p,
12833 const Utf8Str &t,
12834 Reason_T enmReason,
12835 const Utf8Str &strStateFilePath)
12836 : Task(m, p, t),
12837 m_enmReason(enmReason),
12838 m_strStateFilePath(strStateFilePath)
12839 {}
12840
12841 void handler()
12842 {
12843 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12844 }
12845
12846 Reason_T m_enmReason;
12847 Utf8Str m_strStateFilePath;
12848};
12849
12850/**
12851 * Task thread implementation for SessionMachine::SaveState(), called from
12852 * SessionMachine::taskHandler().
12853 *
12854 * @note Locks this object for writing.
12855 *
12856 * @param task
12857 * @return
12858 */
12859void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12860{
12861 LogFlowThisFuncEnter();
12862
12863 AutoCaller autoCaller(this);
12864 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12865 if (FAILED(autoCaller.rc()))
12866 {
12867 /* we might have been uninitialized because the session was accidentally
12868 * closed by the client, so don't assert */
12869 HRESULT rc = setError(E_FAIL,
12870 tr("The session has been accidentally closed"));
12871 task.m_pProgress->i_notifyComplete(rc);
12872 LogFlowThisFuncLeave();
12873 return;
12874 }
12875
12876 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12877
12878 HRESULT rc = S_OK;
12879
12880 try
12881 {
12882 ComPtr<IInternalSessionControl> directControl;
12883 if (mData->mSession.mLockType == LockType_VM)
12884 directControl = mData->mSession.mDirectControl;
12885 if (directControl.isNull())
12886 throw setError(VBOX_E_INVALID_VM_STATE,
12887 tr("Trying to save state without a running VM"));
12888 alock.release();
12889 BOOL fSuspendedBySave;
12890 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12891 Assert(!fSuspendedBySave);
12892 alock.acquire();
12893
12894 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12895 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12896 throw E_FAIL);
12897
12898 if (SUCCEEDED(rc))
12899 {
12900 mSSData->strStateFilePath = task.m_strStateFilePath;
12901
12902 /* save all VM settings */
12903 rc = i_saveSettings(NULL);
12904 // no need to check whether VirtualBox.xml needs saving also since
12905 // we can't have a name change pending at this point
12906 }
12907 else
12908 {
12909 // On failure, set the state to the state we had at the beginning.
12910 i_setMachineState(task.m_machineStateBackup);
12911 i_updateMachineStateOnClient();
12912
12913 // Delete the saved state file (might have been already created).
12914 // No need to check whether this is shared with a snapshot here
12915 // because we certainly created a fresh saved state file here.
12916 RTFileDelete(task.m_strStateFilePath.c_str());
12917 }
12918 }
12919 catch (HRESULT aRC) { rc = aRC; }
12920
12921 task.m_pProgress->i_notifyComplete(rc);
12922
12923 LogFlowThisFuncLeave();
12924}
12925
12926/**
12927 * @note Locks this object for writing.
12928 */
12929HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12930{
12931 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12932}
12933
12934HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12935{
12936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12937
12938 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12939 if (FAILED(rc)) return rc;
12940
12941 if ( mData->mMachineState != MachineState_Running
12942 && mData->mMachineState != MachineState_Paused
12943 )
12944 return setError(VBOX_E_INVALID_VM_STATE,
12945 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12946 Global::stringifyMachineState(mData->mMachineState));
12947
12948 ComObjPtr<Progress> pProgress;
12949 pProgress.createObject();
12950 rc = pProgress->init(i_getVirtualBox(),
12951 static_cast<IMachine *>(this) /* aInitiator */,
12952 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12953 FALSE /* aCancelable */);
12954 if (FAILED(rc))
12955 return rc;
12956
12957 Utf8Str strStateFilePath;
12958 i_composeSavedStateFilename(strStateFilePath);
12959
12960 /* create and start the task on a separate thread (note that it will not
12961 * start working until we release alock) */
12962 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12963 rc = pTask->createThread();
12964 if (FAILED(rc))
12965 return rc;
12966
12967 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12968 i_setMachineState(MachineState_Saving);
12969 i_updateMachineStateOnClient();
12970
12971 pProgress.queryInterfaceTo(aProgress.asOutParam());
12972
12973 return S_OK;
12974}
12975
12976/**
12977 * @note Locks this object for writing.
12978 */
12979HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12980{
12981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12982
12983 HRESULT rc = i_checkStateDependency(MutableStateDep);
12984 if (FAILED(rc)) return rc;
12985
12986 if ( mData->mMachineState != MachineState_PoweredOff
12987 && mData->mMachineState != MachineState_Teleported
12988 && mData->mMachineState != MachineState_Aborted
12989 )
12990 return setError(VBOX_E_INVALID_VM_STATE,
12991 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12992 Global::stringifyMachineState(mData->mMachineState));
12993
12994 com::Utf8Str stateFilePathFull;
12995 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12996 if (RT_FAILURE(vrc))
12997 return setError(VBOX_E_FILE_ERROR,
12998 tr("Invalid saved state file path '%s' (%Rrc)"),
12999 aSavedStateFile.c_str(),
13000 vrc);
13001
13002 mSSData->strStateFilePath = stateFilePathFull;
13003
13004 /* The below i_setMachineState() will detect the state transition and will
13005 * update the settings file */
13006
13007 return i_setMachineState(MachineState_Saved);
13008}
13009
13010/**
13011 * @note Locks this object for writing.
13012 */
13013HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13014{
13015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13016
13017 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13018 if (FAILED(rc)) return rc;
13019
13020 if (mData->mMachineState != MachineState_Saved)
13021 return setError(VBOX_E_INVALID_VM_STATE,
13022 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13023 Global::stringifyMachineState(mData->mMachineState));
13024
13025 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13026
13027 /*
13028 * Saved -> PoweredOff transition will be detected in the SessionMachine
13029 * and properly handled.
13030 */
13031 rc = i_setMachineState(MachineState_PoweredOff);
13032 return rc;
13033}
13034
13035
13036/**
13037 * @note Locks the same as #i_setMachineState() does.
13038 */
13039HRESULT SessionMachine::updateState(MachineState_T aState)
13040{
13041 return i_setMachineState(aState);
13042}
13043
13044/**
13045 * @note Locks this object for writing.
13046 */
13047HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13048{
13049 IProgress* pProgress(aProgress);
13050
13051 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13052
13053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13054
13055 if (mData->mSession.mState != SessionState_Locked)
13056 return VBOX_E_INVALID_OBJECT_STATE;
13057
13058 if (!mData->mSession.mProgress.isNull())
13059 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13060
13061 /* If we didn't reference the NAT network service yet, add a reference to
13062 * force a start */
13063 if (miNATNetworksStarted < 1)
13064 {
13065 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13066 {
13067 BOOL enabled;
13068 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13069 if ( FAILED(hrc)
13070 || !enabled)
13071 continue;
13072
13073 NetworkAttachmentType_T type;
13074 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13075 if ( SUCCEEDED(hrc)
13076 && type == NetworkAttachmentType_NATNetwork)
13077 {
13078 Bstr name;
13079 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13080 if (SUCCEEDED(hrc))
13081 {
13082 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13083 mUserData->s.strName.c_str(), name.raw()));
13084 mPeer->lockHandle()->unlockWrite();
13085 mParent->i_natNetworkRefInc(name.raw());
13086#ifdef RT_LOCK_STRICT
13087 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13088#else
13089 mPeer->lockHandle()->lockWrite();
13090#endif
13091 }
13092 }
13093 }
13094 miNATNetworksStarted++;
13095 }
13096
13097 LogFlowThisFunc(("returns S_OK.\n"));
13098 return S_OK;
13099}
13100
13101/**
13102 * @note Locks this object for writing.
13103 */
13104HRESULT SessionMachine::endPowerUp(LONG aResult)
13105{
13106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13107
13108 if (mData->mSession.mState != SessionState_Locked)
13109 return VBOX_E_INVALID_OBJECT_STATE;
13110
13111 /* Finalize the LaunchVMProcess progress object. */
13112 if (mData->mSession.mProgress)
13113 {
13114 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13115 mData->mSession.mProgress.setNull();
13116 }
13117
13118 if (SUCCEEDED((HRESULT)aResult))
13119 {
13120#ifdef VBOX_WITH_RESOURCE_USAGE_API
13121 /* The VM has been powered up successfully, so it makes sense
13122 * now to offer the performance metrics for a running machine
13123 * object. Doing it earlier wouldn't be safe. */
13124 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13125 mData->mSession.mPID);
13126#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13127 }
13128
13129 return S_OK;
13130}
13131
13132/**
13133 * @note Locks this object for writing.
13134 */
13135HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13136{
13137 LogFlowThisFuncEnter();
13138
13139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13140
13141 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13142 E_FAIL);
13143
13144 /* create a progress object to track operation completion */
13145 ComObjPtr<Progress> pProgress;
13146 pProgress.createObject();
13147 pProgress->init(i_getVirtualBox(),
13148 static_cast<IMachine *>(this) /* aInitiator */,
13149 Bstr(tr("Stopping the virtual machine")).raw(),
13150 FALSE /* aCancelable */);
13151
13152 /* fill in the console task data */
13153 mConsoleTaskData.mLastState = mData->mMachineState;
13154 mConsoleTaskData.mProgress = pProgress;
13155
13156 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13157 i_setMachineState(MachineState_Stopping);
13158
13159 pProgress.queryInterfaceTo(aProgress.asOutParam());
13160
13161 return S_OK;
13162}
13163
13164/**
13165 * @note Locks this object for writing.
13166 */
13167HRESULT SessionMachine::endPoweringDown(LONG aResult,
13168 const com::Utf8Str &aErrMsg)
13169{
13170 LogFlowThisFuncEnter();
13171
13172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13173
13174 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13175 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13176 && mConsoleTaskData.mLastState != MachineState_Null,
13177 E_FAIL);
13178
13179 /*
13180 * On failure, set the state to the state we had when BeginPoweringDown()
13181 * was called (this is expected by Console::PowerDown() and the associated
13182 * task). On success the VM process already changed the state to
13183 * MachineState_PoweredOff, so no need to do anything.
13184 */
13185 if (FAILED(aResult))
13186 i_setMachineState(mConsoleTaskData.mLastState);
13187
13188 /* notify the progress object about operation completion */
13189 Assert(mConsoleTaskData.mProgress);
13190 if (SUCCEEDED(aResult))
13191 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13192 else
13193 {
13194 if (aErrMsg.length())
13195 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13196 COM_IIDOF(ISession),
13197 getComponentName(),
13198 aErrMsg.c_str());
13199 else
13200 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13201 }
13202
13203 /* clear out the temporary saved state data */
13204 mConsoleTaskData.mLastState = MachineState_Null;
13205 mConsoleTaskData.mProgress.setNull();
13206
13207 LogFlowThisFuncLeave();
13208 return S_OK;
13209}
13210
13211
13212/**
13213 * Goes through the USB filters of the given machine to see if the given
13214 * device matches any filter or not.
13215 *
13216 * @note Locks the same as USBController::hasMatchingFilter() does.
13217 */
13218HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13219 BOOL *aMatched,
13220 ULONG *aMaskedInterfaces)
13221{
13222 LogFlowThisFunc(("\n"));
13223
13224#ifdef VBOX_WITH_USB
13225 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13226#else
13227 NOREF(aDevice);
13228 NOREF(aMaskedInterfaces);
13229 *aMatched = FALSE;
13230#endif
13231
13232 return S_OK;
13233}
13234
13235/**
13236 * @note Locks the same as Host::captureUSBDevice() does.
13237 */
13238HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13239{
13240 LogFlowThisFunc(("\n"));
13241
13242#ifdef VBOX_WITH_USB
13243 /* if captureDeviceForVM() fails, it must have set extended error info */
13244 clearError();
13245 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13246 if (FAILED(rc)) return rc;
13247
13248 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13249 AssertReturn(service, E_FAIL);
13250 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13251#else
13252 NOREF(aId);
13253 return E_NOTIMPL;
13254#endif
13255}
13256
13257/**
13258 * @note Locks the same as Host::detachUSBDevice() does.
13259 */
13260HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13261 BOOL aDone)
13262{
13263 LogFlowThisFunc(("\n"));
13264
13265#ifdef VBOX_WITH_USB
13266 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13267 AssertReturn(service, E_FAIL);
13268 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13269#else
13270 NOREF(aId);
13271 NOREF(aDone);
13272 return E_NOTIMPL;
13273#endif
13274}
13275
13276/**
13277 * Inserts all machine filters to the USB proxy service and then calls
13278 * Host::autoCaptureUSBDevices().
13279 *
13280 * Called by Console from the VM process upon VM startup.
13281 *
13282 * @note Locks what called methods lock.
13283 */
13284HRESULT SessionMachine::autoCaptureUSBDevices()
13285{
13286 LogFlowThisFunc(("\n"));
13287
13288#ifdef VBOX_WITH_USB
13289 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13290 AssertComRC(rc);
13291 NOREF(rc);
13292
13293 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13294 AssertReturn(service, E_FAIL);
13295 return service->autoCaptureDevicesForVM(this);
13296#else
13297 return S_OK;
13298#endif
13299}
13300
13301/**
13302 * Removes all machine filters from the USB proxy service and then calls
13303 * Host::detachAllUSBDevices().
13304 *
13305 * Called by Console from the VM process upon normal VM termination or by
13306 * SessionMachine::uninit() upon abnormal VM termination (from under the
13307 * Machine/SessionMachine lock).
13308 *
13309 * @note Locks what called methods lock.
13310 */
13311HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13312{
13313 LogFlowThisFunc(("\n"));
13314
13315#ifdef VBOX_WITH_USB
13316 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13317 AssertComRC(rc);
13318 NOREF(rc);
13319
13320 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13321 AssertReturn(service, E_FAIL);
13322 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13323#else
13324 NOREF(aDone);
13325 return S_OK;
13326#endif
13327}
13328
13329/**
13330 * @note Locks this object for writing.
13331 */
13332HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13333 ComPtr<IProgress> &aProgress)
13334{
13335 LogFlowThisFuncEnter();
13336
13337 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13338 /*
13339 * We don't assert below because it might happen that a non-direct session
13340 * informs us it is closed right after we've been uninitialized -- it's ok.
13341 */
13342
13343 /* get IInternalSessionControl interface */
13344 ComPtr<IInternalSessionControl> control(aSession);
13345
13346 ComAssertRet(!control.isNull(), E_INVALIDARG);
13347
13348 /* Creating a Progress object requires the VirtualBox lock, and
13349 * thus locking it here is required by the lock order rules. */
13350 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13351
13352 if (control == mData->mSession.mDirectControl)
13353 {
13354 /* The direct session is being normally closed by the client process
13355 * ----------------------------------------------------------------- */
13356
13357 /* go to the closing state (essential for all open*Session() calls and
13358 * for #i_checkForDeath()) */
13359 Assert(mData->mSession.mState == SessionState_Locked);
13360 mData->mSession.mState = SessionState_Unlocking;
13361
13362 /* set direct control to NULL to release the remote instance */
13363 mData->mSession.mDirectControl.setNull();
13364 LogFlowThisFunc(("Direct control is set to NULL\n"));
13365
13366 if (mData->mSession.mProgress)
13367 {
13368 /* finalize the progress, someone might wait if a frontend
13369 * closes the session before powering on the VM. */
13370 mData->mSession.mProgress->notifyComplete(E_FAIL,
13371 COM_IIDOF(ISession),
13372 getComponentName(),
13373 tr("The VM session was closed before any attempt to power it on"));
13374 mData->mSession.mProgress.setNull();
13375 }
13376
13377 /* Create the progress object the client will use to wait until
13378 * #i_checkForDeath() is called to uninitialize this session object after
13379 * it releases the IPC semaphore.
13380 * Note! Because we're "reusing" mProgress here, this must be a proxy
13381 * object just like for LaunchVMProcess. */
13382 Assert(mData->mSession.mProgress.isNull());
13383 ComObjPtr<ProgressProxy> progress;
13384 progress.createObject();
13385 ComPtr<IUnknown> pPeer(mPeer);
13386 progress->init(mParent, pPeer,
13387 Bstr(tr("Closing session")).raw(),
13388 FALSE /* aCancelable */);
13389 progress.queryInterfaceTo(aProgress.asOutParam());
13390 mData->mSession.mProgress = progress;
13391 }
13392 else
13393 {
13394 /* the remote session is being normally closed */
13395 Data::Session::RemoteControlList::iterator it =
13396 mData->mSession.mRemoteControls.begin();
13397 while (it != mData->mSession.mRemoteControls.end())
13398 {
13399 if (control == *it)
13400 break;
13401 ++it;
13402 }
13403 BOOL found = it != mData->mSession.mRemoteControls.end();
13404 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13405 E_INVALIDARG);
13406 // This MUST be erase(it), not remove(*it) as the latter triggers a
13407 // very nasty use after free due to the place where the value "lives".
13408 mData->mSession.mRemoteControls.erase(it);
13409 }
13410
13411 /* signal the client watcher thread, because the client is going away */
13412 mParent->i_updateClientWatcher();
13413
13414 LogFlowThisFuncLeave();
13415 return S_OK;
13416}
13417
13418HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13419 std::vector<com::Utf8Str> &aValues,
13420 std::vector<LONG64> &aTimestamps,
13421 std::vector<com::Utf8Str> &aFlags)
13422{
13423 LogFlowThisFunc(("\n"));
13424
13425#ifdef VBOX_WITH_GUEST_PROPS
13426 using namespace guestProp;
13427
13428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13429
13430 size_t cEntries = mHWData->mGuestProperties.size();
13431 aNames.resize(cEntries);
13432 aValues.resize(cEntries);
13433 aTimestamps.resize(cEntries);
13434 aFlags.resize(cEntries);
13435
13436 size_t i = 0;
13437 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13438 it != mHWData->mGuestProperties.end();
13439 ++it, ++i)
13440 {
13441 char szFlags[MAX_FLAGS_LEN + 1];
13442 aNames[i] = it->first;
13443 aValues[i] = it->second.strValue;
13444 aTimestamps[i] = it->second.mTimestamp;
13445
13446 /* If it is NULL, keep it NULL. */
13447 if (it->second.mFlags)
13448 {
13449 writeFlags(it->second.mFlags, szFlags);
13450 aFlags[i] = szFlags;
13451 }
13452 else
13453 aFlags[i] = "";
13454 }
13455 return S_OK;
13456#else
13457 ReturnComNotImplemented();
13458#endif
13459}
13460
13461HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13462 const com::Utf8Str &aValue,
13463 LONG64 aTimestamp,
13464 const com::Utf8Str &aFlags)
13465{
13466 LogFlowThisFunc(("\n"));
13467
13468#ifdef VBOX_WITH_GUEST_PROPS
13469 using namespace guestProp;
13470
13471 try
13472 {
13473 /*
13474 * Convert input up front.
13475 */
13476 uint32_t fFlags = NILFLAG;
13477 if (aFlags.length())
13478 {
13479 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13480 AssertRCReturn(vrc, E_INVALIDARG);
13481 }
13482
13483 /*
13484 * Now grab the object lock, validate the state and do the update.
13485 */
13486
13487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13488
13489 if (!Global::IsOnline(mData->mMachineState))
13490 {
13491 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13492 VBOX_E_INVALID_VM_STATE);
13493 }
13494
13495 i_setModified(IsModified_MachineData);
13496 mHWData.backup();
13497
13498 bool fDelete = !aValue.length();
13499 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13500 if (it != mHWData->mGuestProperties.end())
13501 {
13502 if (!fDelete)
13503 {
13504 it->second.strValue = aValue;
13505 it->second.mTimestamp = aTimestamp;
13506 it->second.mFlags = fFlags;
13507 }
13508 else
13509 mHWData->mGuestProperties.erase(it);
13510
13511 mData->mGuestPropertiesModified = TRUE;
13512 }
13513 else if (!fDelete)
13514 {
13515 HWData::GuestProperty prop;
13516 prop.strValue = aValue;
13517 prop.mTimestamp = aTimestamp;
13518 prop.mFlags = fFlags;
13519
13520 mHWData->mGuestProperties[aName] = prop;
13521 mData->mGuestPropertiesModified = TRUE;
13522 }
13523
13524 alock.release();
13525
13526 mParent->i_onGuestPropertyChange(mData->mUuid,
13527 Bstr(aName).raw(),
13528 Bstr(aValue).raw(),
13529 Bstr(aFlags).raw());
13530 }
13531 catch (...)
13532 {
13533 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13534 }
13535 return S_OK;
13536#else
13537 ReturnComNotImplemented();
13538#endif
13539}
13540
13541
13542HRESULT SessionMachine::lockMedia()
13543{
13544 AutoMultiWriteLock2 alock(this->lockHandle(),
13545 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13546
13547 AssertReturn( mData->mMachineState == MachineState_Starting
13548 || mData->mMachineState == MachineState_Restoring
13549 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13550
13551 clearError();
13552 alock.release();
13553 return i_lockMedia();
13554}
13555
13556HRESULT SessionMachine::unlockMedia()
13557{
13558 HRESULT hrc = i_unlockMedia();
13559 return hrc;
13560}
13561
13562HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13563 ComPtr<IMediumAttachment> &aNewAttachment)
13564{
13565 // request the host lock first, since might be calling Host methods for getting host drives;
13566 // next, protect the media tree all the while we're in here, as well as our member variables
13567 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13568 this->lockHandle(),
13569 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13570
13571 IMediumAttachment *iAttach = aAttachment;
13572 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13573
13574 Bstr ctrlName;
13575 LONG lPort;
13576 LONG lDevice;
13577 bool fTempEject;
13578 {
13579 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13580
13581 /* Need to query the details first, as the IMediumAttachment reference
13582 * might be to the original settings, which we are going to change. */
13583 ctrlName = pAttach->i_getControllerName();
13584 lPort = pAttach->i_getPort();
13585 lDevice = pAttach->i_getDevice();
13586 fTempEject = pAttach->i_getTempEject();
13587 }
13588
13589 if (!fTempEject)
13590 {
13591 /* Remember previously mounted medium. The medium before taking the
13592 * backup is not necessarily the same thing. */
13593 ComObjPtr<Medium> oldmedium;
13594 oldmedium = pAttach->i_getMedium();
13595
13596 i_setModified(IsModified_Storage);
13597 mMediaData.backup();
13598
13599 // The backup operation makes the pAttach reference point to the
13600 // old settings. Re-get the correct reference.
13601 pAttach = i_findAttachment(mMediaData->mAttachments,
13602 ctrlName.raw(),
13603 lPort,
13604 lDevice);
13605
13606 {
13607 AutoCaller autoAttachCaller(this);
13608 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13609
13610 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13611 if (!oldmedium.isNull())
13612 oldmedium->i_removeBackReference(mData->mUuid);
13613
13614 pAttach->i_updateMedium(NULL);
13615 pAttach->i_updateEjected();
13616 }
13617
13618 i_setModified(IsModified_Storage);
13619 }
13620 else
13621 {
13622 {
13623 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13624 pAttach->i_updateEjected();
13625 }
13626 }
13627
13628 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13629
13630 return S_OK;
13631}
13632
13633HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13634 com::Utf8Str &aResult)
13635{
13636 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13637
13638 HRESULT hr = S_OK;
13639
13640 if (!mAuthLibCtx.hAuthLibrary)
13641 {
13642 /* Load the external authentication library. */
13643 Bstr authLibrary;
13644 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13645
13646 Utf8Str filename = authLibrary;
13647
13648 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13649 if (RT_FAILURE(rc))
13650 {
13651 hr = setError(E_FAIL,
13652 tr("Could not load the external authentication library '%s' (%Rrc)"),
13653 filename.c_str(), rc);
13654 }
13655 }
13656
13657 /* The auth library might need the machine lock. */
13658 alock.release();
13659
13660 if (FAILED(hr))
13661 return hr;
13662
13663 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13664 {
13665 enum VRDEAuthParams
13666 {
13667 parmUuid = 1,
13668 parmGuestJudgement,
13669 parmUser,
13670 parmPassword,
13671 parmDomain,
13672 parmClientId
13673 };
13674
13675 AuthResult result = AuthResultAccessDenied;
13676
13677 Guid uuid(aAuthParams[parmUuid]);
13678 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13679 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13680
13681 result = AuthLibAuthenticate(&mAuthLibCtx,
13682 uuid.raw(), guestJudgement,
13683 aAuthParams[parmUser].c_str(),
13684 aAuthParams[parmPassword].c_str(),
13685 aAuthParams[parmDomain].c_str(),
13686 u32ClientId);
13687
13688 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13689 size_t cbPassword = aAuthParams[parmPassword].length();
13690 if (cbPassword)
13691 {
13692 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13693 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13694 }
13695
13696 if (result == AuthResultAccessGranted)
13697 aResult = "granted";
13698 else
13699 aResult = "denied";
13700
13701 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13702 aAuthParams[parmUser].c_str(), aResult.c_str()));
13703 }
13704 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13705 {
13706 enum VRDEAuthDisconnectParams
13707 {
13708 parmUuid = 1,
13709 parmClientId
13710 };
13711
13712 Guid uuid(aAuthParams[parmUuid]);
13713 uint32_t u32ClientId = 0;
13714 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13715 }
13716 else
13717 {
13718 hr = E_INVALIDARG;
13719 }
13720
13721 return hr;
13722}
13723
13724// public methods only for internal purposes
13725/////////////////////////////////////////////////////////////////////////////
13726
13727#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13728/**
13729 * Called from the client watcher thread to check for expected or unexpected
13730 * death of the client process that has a direct session to this machine.
13731 *
13732 * On Win32 and on OS/2, this method is called only when we've got the
13733 * mutex (i.e. the client has either died or terminated normally) so it always
13734 * returns @c true (the client is terminated, the session machine is
13735 * uninitialized).
13736 *
13737 * On other platforms, the method returns @c true if the client process has
13738 * terminated normally or abnormally and the session machine was uninitialized,
13739 * and @c false if the client process is still alive.
13740 *
13741 * @note Locks this object for writing.
13742 */
13743bool SessionMachine::i_checkForDeath()
13744{
13745 Uninit::Reason reason;
13746 bool terminated = false;
13747
13748 /* Enclose autoCaller with a block because calling uninit() from under it
13749 * will deadlock. */
13750 {
13751 AutoCaller autoCaller(this);
13752 if (!autoCaller.isOk())
13753 {
13754 /* return true if not ready, to cause the client watcher to exclude
13755 * the corresponding session from watching */
13756 LogFlowThisFunc(("Already uninitialized!\n"));
13757 return true;
13758 }
13759
13760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13761
13762 /* Determine the reason of death: if the session state is Closing here,
13763 * everything is fine. Otherwise it means that the client did not call
13764 * OnSessionEnd() before it released the IPC semaphore. This may happen
13765 * either because the client process has abnormally terminated, or
13766 * because it simply forgot to call ISession::Close() before exiting. We
13767 * threat the latter also as an abnormal termination (see
13768 * Session::uninit() for details). */
13769 reason = mData->mSession.mState == SessionState_Unlocking ?
13770 Uninit::Normal :
13771 Uninit::Abnormal;
13772
13773 if (mClientToken)
13774 terminated = mClientToken->release();
13775 } /* AutoCaller block */
13776
13777 if (terminated)
13778 uninit(reason);
13779
13780 return terminated;
13781}
13782
13783void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13784{
13785 LogFlowThisFunc(("\n"));
13786
13787 strTokenId.setNull();
13788
13789 AutoCaller autoCaller(this);
13790 AssertComRCReturnVoid(autoCaller.rc());
13791
13792 Assert(mClientToken);
13793 if (mClientToken)
13794 mClientToken->getId(strTokenId);
13795}
13796#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13797IToken *SessionMachine::i_getToken()
13798{
13799 LogFlowThisFunc(("\n"));
13800
13801 AutoCaller autoCaller(this);
13802 AssertComRCReturn(autoCaller.rc(), NULL);
13803
13804 Assert(mClientToken);
13805 if (mClientToken)
13806 return mClientToken->getToken();
13807 else
13808 return NULL;
13809}
13810#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13811
13812Machine::ClientToken *SessionMachine::i_getClientToken()
13813{
13814 LogFlowThisFunc(("\n"));
13815
13816 AutoCaller autoCaller(this);
13817 AssertComRCReturn(autoCaller.rc(), NULL);
13818
13819 return mClientToken;
13820}
13821
13822
13823/**
13824 * @note Locks this object for reading.
13825 */
13826HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13827{
13828 LogFlowThisFunc(("\n"));
13829
13830 AutoCaller autoCaller(this);
13831 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13832
13833 ComPtr<IInternalSessionControl> directControl;
13834 {
13835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13836 if (mData->mSession.mLockType == LockType_VM)
13837 directControl = mData->mSession.mDirectControl;
13838 }
13839
13840 /* ignore notifications sent after #OnSessionEnd() is called */
13841 if (!directControl)
13842 return S_OK;
13843
13844 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13845}
13846
13847/**
13848 * @note Locks this object for reading.
13849 */
13850HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13851 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13852 IN_BSTR aGuestIp, LONG aGuestPort)
13853{
13854 LogFlowThisFunc(("\n"));
13855
13856 AutoCaller autoCaller(this);
13857 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13858
13859 ComPtr<IInternalSessionControl> directControl;
13860 {
13861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13862 if (mData->mSession.mLockType == LockType_VM)
13863 directControl = mData->mSession.mDirectControl;
13864 }
13865
13866 /* ignore notifications sent after #OnSessionEnd() is called */
13867 if (!directControl)
13868 return S_OK;
13869 /*
13870 * instead acting like callback we ask IVirtualBox deliver corresponding event
13871 */
13872
13873 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13874 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13875 return S_OK;
13876}
13877
13878/**
13879 * @note Locks this object for reading.
13880 */
13881HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13882{
13883 LogFlowThisFunc(("\n"));
13884
13885 AutoCaller autoCaller(this);
13886 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13887
13888 ComPtr<IInternalSessionControl> directControl;
13889 {
13890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13891 if (mData->mSession.mLockType == LockType_VM)
13892 directControl = mData->mSession.mDirectControl;
13893 }
13894
13895 /* ignore notifications sent after #OnSessionEnd() is called */
13896 if (!directControl)
13897 return S_OK;
13898
13899 return directControl->OnSerialPortChange(serialPort);
13900}
13901
13902/**
13903 * @note Locks this object for reading.
13904 */
13905HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13906{
13907 LogFlowThisFunc(("\n"));
13908
13909 AutoCaller autoCaller(this);
13910 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13911
13912 ComPtr<IInternalSessionControl> directControl;
13913 {
13914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13915 if (mData->mSession.mLockType == LockType_VM)
13916 directControl = mData->mSession.mDirectControl;
13917 }
13918
13919 /* ignore notifications sent after #OnSessionEnd() is called */
13920 if (!directControl)
13921 return S_OK;
13922
13923 return directControl->OnParallelPortChange(parallelPort);
13924}
13925
13926/**
13927 * @note Locks this object for reading.
13928 */
13929HRESULT SessionMachine::i_onStorageControllerChange()
13930{
13931 LogFlowThisFunc(("\n"));
13932
13933 AutoCaller autoCaller(this);
13934 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13935
13936 ComPtr<IInternalSessionControl> directControl;
13937 {
13938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13939 if (mData->mSession.mLockType == LockType_VM)
13940 directControl = mData->mSession.mDirectControl;
13941 }
13942
13943 /* ignore notifications sent after #OnSessionEnd() is called */
13944 if (!directControl)
13945 return S_OK;
13946
13947 return directControl->OnStorageControllerChange();
13948}
13949
13950/**
13951 * @note Locks this object for reading.
13952 */
13953HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13954{
13955 LogFlowThisFunc(("\n"));
13956
13957 AutoCaller autoCaller(this);
13958 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13959
13960 ComPtr<IInternalSessionControl> directControl;
13961 {
13962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13963 if (mData->mSession.mLockType == LockType_VM)
13964 directControl = mData->mSession.mDirectControl;
13965 }
13966
13967 /* ignore notifications sent after #OnSessionEnd() is called */
13968 if (!directControl)
13969 return S_OK;
13970
13971 return directControl->OnMediumChange(aAttachment, aForce);
13972}
13973
13974/**
13975 * @note Locks this object for reading.
13976 */
13977HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13978{
13979 LogFlowThisFunc(("\n"));
13980
13981 AutoCaller autoCaller(this);
13982 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13983
13984 ComPtr<IInternalSessionControl> directControl;
13985 {
13986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13987 if (mData->mSession.mLockType == LockType_VM)
13988 directControl = mData->mSession.mDirectControl;
13989 }
13990
13991 /* ignore notifications sent after #OnSessionEnd() is called */
13992 if (!directControl)
13993 return S_OK;
13994
13995 return directControl->OnCPUChange(aCPU, aRemove);
13996}
13997
13998HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13999{
14000 LogFlowThisFunc(("\n"));
14001
14002 AutoCaller autoCaller(this);
14003 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14004
14005 ComPtr<IInternalSessionControl> directControl;
14006 {
14007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14008 if (mData->mSession.mLockType == LockType_VM)
14009 directControl = mData->mSession.mDirectControl;
14010 }
14011
14012 /* ignore notifications sent after #OnSessionEnd() is called */
14013 if (!directControl)
14014 return S_OK;
14015
14016 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14017}
14018
14019/**
14020 * @note Locks this object for reading.
14021 */
14022HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14023{
14024 LogFlowThisFunc(("\n"));
14025
14026 AutoCaller autoCaller(this);
14027 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14028
14029 ComPtr<IInternalSessionControl> directControl;
14030 {
14031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14032 if (mData->mSession.mLockType == LockType_VM)
14033 directControl = mData->mSession.mDirectControl;
14034 }
14035
14036 /* ignore notifications sent after #OnSessionEnd() is called */
14037 if (!directControl)
14038 return S_OK;
14039
14040 return directControl->OnVRDEServerChange(aRestart);
14041}
14042
14043/**
14044 * @note Locks this object for reading.
14045 */
14046HRESULT SessionMachine::i_onVideoCaptureChange()
14047{
14048 LogFlowThisFunc(("\n"));
14049
14050 AutoCaller autoCaller(this);
14051 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14052
14053 ComPtr<IInternalSessionControl> directControl;
14054 {
14055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14056 if (mData->mSession.mLockType == LockType_VM)
14057 directControl = mData->mSession.mDirectControl;
14058 }
14059
14060 /* ignore notifications sent after #OnSessionEnd() is called */
14061 if (!directControl)
14062 return S_OK;
14063
14064 return directControl->OnVideoCaptureChange();
14065}
14066
14067/**
14068 * @note Locks this object for reading.
14069 */
14070HRESULT SessionMachine::i_onUSBControllerChange()
14071{
14072 LogFlowThisFunc(("\n"));
14073
14074 AutoCaller autoCaller(this);
14075 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14076
14077 ComPtr<IInternalSessionControl> directControl;
14078 {
14079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14080 if (mData->mSession.mLockType == LockType_VM)
14081 directControl = mData->mSession.mDirectControl;
14082 }
14083
14084 /* ignore notifications sent after #OnSessionEnd() is called */
14085 if (!directControl)
14086 return S_OK;
14087
14088 return directControl->OnUSBControllerChange();
14089}
14090
14091/**
14092 * @note Locks this object for reading.
14093 */
14094HRESULT SessionMachine::i_onSharedFolderChange()
14095{
14096 LogFlowThisFunc(("\n"));
14097
14098 AutoCaller autoCaller(this);
14099 AssertComRCReturnRC(autoCaller.rc());
14100
14101 ComPtr<IInternalSessionControl> directControl;
14102 {
14103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14104 if (mData->mSession.mLockType == LockType_VM)
14105 directControl = mData->mSession.mDirectControl;
14106 }
14107
14108 /* ignore notifications sent after #OnSessionEnd() is called */
14109 if (!directControl)
14110 return S_OK;
14111
14112 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14113}
14114
14115/**
14116 * @note Locks this object for reading.
14117 */
14118HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14119{
14120 LogFlowThisFunc(("\n"));
14121
14122 AutoCaller autoCaller(this);
14123 AssertComRCReturnRC(autoCaller.rc());
14124
14125 ComPtr<IInternalSessionControl> directControl;
14126 {
14127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14128 if (mData->mSession.mLockType == LockType_VM)
14129 directControl = mData->mSession.mDirectControl;
14130 }
14131
14132 /* ignore notifications sent after #OnSessionEnd() is called */
14133 if (!directControl)
14134 return S_OK;
14135
14136 return directControl->OnClipboardModeChange(aClipboardMode);
14137}
14138
14139/**
14140 * @note Locks this object for reading.
14141 */
14142HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14143{
14144 LogFlowThisFunc(("\n"));
14145
14146 AutoCaller autoCaller(this);
14147 AssertComRCReturnRC(autoCaller.rc());
14148
14149 ComPtr<IInternalSessionControl> directControl;
14150 {
14151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14152 if (mData->mSession.mLockType == LockType_VM)
14153 directControl = mData->mSession.mDirectControl;
14154 }
14155
14156 /* ignore notifications sent after #OnSessionEnd() is called */
14157 if (!directControl)
14158 return S_OK;
14159
14160 return directControl->OnDnDModeChange(aDnDMode);
14161}
14162
14163/**
14164 * @note Locks this object for reading.
14165 */
14166HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14167{
14168 LogFlowThisFunc(("\n"));
14169
14170 AutoCaller autoCaller(this);
14171 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14172
14173 ComPtr<IInternalSessionControl> directControl;
14174 {
14175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14176 if (mData->mSession.mLockType == LockType_VM)
14177 directControl = mData->mSession.mDirectControl;
14178 }
14179
14180 /* ignore notifications sent after #OnSessionEnd() is called */
14181 if (!directControl)
14182 return S_OK;
14183
14184 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14185}
14186
14187/**
14188 * @note Locks this object for reading.
14189 */
14190HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14191{
14192 LogFlowThisFunc(("\n"));
14193
14194 AutoCaller autoCaller(this);
14195 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14196
14197 ComPtr<IInternalSessionControl> directControl;
14198 {
14199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14200 if (mData->mSession.mLockType == LockType_VM)
14201 directControl = mData->mSession.mDirectControl;
14202 }
14203
14204 /* ignore notifications sent after #OnSessionEnd() is called */
14205 if (!directControl)
14206 return S_OK;
14207
14208 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14209}
14210
14211/**
14212 * Returns @c true if this machine's USB controller reports it has a matching
14213 * filter for the given USB device and @c false otherwise.
14214 *
14215 * @note locks this object for reading.
14216 */
14217bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14218{
14219 AutoCaller autoCaller(this);
14220 /* silently return if not ready -- this method may be called after the
14221 * direct machine session has been called */
14222 if (!autoCaller.isOk())
14223 return false;
14224
14225#ifdef VBOX_WITH_USB
14226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14227
14228 switch (mData->mMachineState)
14229 {
14230 case MachineState_Starting:
14231 case MachineState_Restoring:
14232 case MachineState_TeleportingIn:
14233 case MachineState_Paused:
14234 case MachineState_Running:
14235 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14236 * elsewhere... */
14237 alock.release();
14238 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14239 default: break;
14240 }
14241#else
14242 NOREF(aDevice);
14243 NOREF(aMaskedIfs);
14244#endif
14245 return false;
14246}
14247
14248/**
14249 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14250 */
14251HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14252 IVirtualBoxErrorInfo *aError,
14253 ULONG aMaskedIfs,
14254 const com::Utf8Str &aCaptureFilename)
14255{
14256 LogFlowThisFunc(("\n"));
14257
14258 AutoCaller autoCaller(this);
14259
14260 /* This notification may happen after the machine object has been
14261 * uninitialized (the session was closed), so don't assert. */
14262 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14263
14264 ComPtr<IInternalSessionControl> directControl;
14265 {
14266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14267 if (mData->mSession.mLockType == LockType_VM)
14268 directControl = mData->mSession.mDirectControl;
14269 }
14270
14271 /* fail on notifications sent after #OnSessionEnd() is called, it is
14272 * expected by the caller */
14273 if (!directControl)
14274 return E_FAIL;
14275
14276 /* No locks should be held at this point. */
14277 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14278 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14279
14280 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14281}
14282
14283/**
14284 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14285 */
14286HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14287 IVirtualBoxErrorInfo *aError)
14288{
14289 LogFlowThisFunc(("\n"));
14290
14291 AutoCaller autoCaller(this);
14292
14293 /* This notification may happen after the machine object has been
14294 * uninitialized (the session was closed), so don't assert. */
14295 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14296
14297 ComPtr<IInternalSessionControl> directControl;
14298 {
14299 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14300 if (mData->mSession.mLockType == LockType_VM)
14301 directControl = mData->mSession.mDirectControl;
14302 }
14303
14304 /* fail on notifications sent after #OnSessionEnd() is called, it is
14305 * expected by the caller */
14306 if (!directControl)
14307 return E_FAIL;
14308
14309 /* No locks should be held at this point. */
14310 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14311 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14312
14313 return directControl->OnUSBDeviceDetach(aId, aError);
14314}
14315
14316// protected methods
14317/////////////////////////////////////////////////////////////////////////////
14318
14319/**
14320 * Deletes the given file if it is no longer in use by either the current machine state
14321 * (if the machine is "saved") or any of the machine's snapshots.
14322 *
14323 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14324 * but is different for each SnapshotMachine. When calling this, the order of calling this
14325 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14326 * is therefore critical. I know, it's all rather messy.
14327 *
14328 * @param strStateFile
14329 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14330 * the test for whether the saved state file is in use.
14331 */
14332void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14333 Snapshot *pSnapshotToIgnore)
14334{
14335 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14336 if ( (strStateFile.isNotEmpty())
14337 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14338 )
14339 // ... and it must also not be shared with other snapshots
14340 if ( !mData->mFirstSnapshot
14341 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14342 // this checks the SnapshotMachine's state file paths
14343 )
14344 RTFileDelete(strStateFile.c_str());
14345}
14346
14347/**
14348 * Locks the attached media.
14349 *
14350 * All attached hard disks are locked for writing and DVD/floppy are locked for
14351 * reading. Parents of attached hard disks (if any) are locked for reading.
14352 *
14353 * This method also performs accessibility check of all media it locks: if some
14354 * media is inaccessible, the method will return a failure and a bunch of
14355 * extended error info objects per each inaccessible medium.
14356 *
14357 * Note that this method is atomic: if it returns a success, all media are
14358 * locked as described above; on failure no media is locked at all (all
14359 * succeeded individual locks will be undone).
14360 *
14361 * The caller is responsible for doing the necessary state sanity checks.
14362 *
14363 * The locks made by this method must be undone by calling #unlockMedia() when
14364 * no more needed.
14365 */
14366HRESULT SessionMachine::i_lockMedia()
14367{
14368 AutoCaller autoCaller(this);
14369 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14370
14371 AutoMultiWriteLock2 alock(this->lockHandle(),
14372 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14373
14374 /* bail out if trying to lock things with already set up locking */
14375 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14376
14377 MultiResult mrc(S_OK);
14378
14379 /* Collect locking information for all medium objects attached to the VM. */
14380 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14381 it != mMediaData->mAttachments.end();
14382 ++it)
14383 {
14384 MediumAttachment* pAtt = *it;
14385 DeviceType_T devType = pAtt->i_getType();
14386 Medium *pMedium = pAtt->i_getMedium();
14387
14388 MediumLockList *pMediumLockList(new MediumLockList());
14389 // There can be attachments without a medium (floppy/dvd), and thus
14390 // it's impossible to create a medium lock list. It still makes sense
14391 // to have the empty medium lock list in the map in case a medium is
14392 // attached later.
14393 if (pMedium != NULL)
14394 {
14395 MediumType_T mediumType = pMedium->i_getType();
14396 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14397 || mediumType == MediumType_Shareable;
14398 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14399
14400 alock.release();
14401 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14402 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14403 false /* fMediumLockWriteAll */,
14404 NULL,
14405 *pMediumLockList);
14406 alock.acquire();
14407 if (FAILED(mrc))
14408 {
14409 delete pMediumLockList;
14410 mData->mSession.mLockedMedia.Clear();
14411 break;
14412 }
14413 }
14414
14415 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14416 if (FAILED(rc))
14417 {
14418 mData->mSession.mLockedMedia.Clear();
14419 mrc = setError(rc,
14420 tr("Collecting locking information for all attached media failed"));
14421 break;
14422 }
14423 }
14424
14425 if (SUCCEEDED(mrc))
14426 {
14427 /* Now lock all media. If this fails, nothing is locked. */
14428 alock.release();
14429 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14430 alock.acquire();
14431 if (FAILED(rc))
14432 {
14433 mrc = setError(rc,
14434 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14435 }
14436 }
14437
14438 return mrc;
14439}
14440
14441/**
14442 * Undoes the locks made by by #lockMedia().
14443 */
14444HRESULT SessionMachine::i_unlockMedia()
14445{
14446 AutoCaller autoCaller(this);
14447 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14448
14449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14450
14451 /* we may be holding important error info on the current thread;
14452 * preserve it */
14453 ErrorInfoKeeper eik;
14454
14455 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14456 AssertComRC(rc);
14457 return rc;
14458}
14459
14460/**
14461 * Helper to change the machine state (reimplementation).
14462 *
14463 * @note Locks this object for writing.
14464 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14465 * it can cause crashes in random places due to unexpectedly committing
14466 * the current settings. The caller is responsible for that. The call
14467 * to saveStateSettings is fine, because this method does not commit.
14468 */
14469HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14470{
14471 LogFlowThisFuncEnter();
14472 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14473
14474 AutoCaller autoCaller(this);
14475 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14476
14477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14478
14479 MachineState_T oldMachineState = mData->mMachineState;
14480
14481 AssertMsgReturn(oldMachineState != aMachineState,
14482 ("oldMachineState=%s, aMachineState=%s\n",
14483 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14484 E_FAIL);
14485
14486 HRESULT rc = S_OK;
14487
14488 int stsFlags = 0;
14489 bool deleteSavedState = false;
14490
14491 /* detect some state transitions */
14492
14493 if ( ( oldMachineState == MachineState_Saved
14494 && aMachineState == MachineState_Restoring)
14495 || ( ( oldMachineState == MachineState_PoweredOff
14496 || oldMachineState == MachineState_Teleported
14497 || oldMachineState == MachineState_Aborted
14498 )
14499 && ( aMachineState == MachineState_TeleportingIn
14500 || aMachineState == MachineState_Starting
14501 )
14502 )
14503 )
14504 {
14505 /* The EMT thread is about to start */
14506
14507 /* Nothing to do here for now... */
14508
14509 /// @todo NEWMEDIA don't let mDVDDrive and other children
14510 /// change anything when in the Starting/Restoring state
14511 }
14512 else if ( ( oldMachineState == MachineState_Running
14513 || oldMachineState == MachineState_Paused
14514 || oldMachineState == MachineState_Teleporting
14515 || oldMachineState == MachineState_OnlineSnapshotting
14516 || oldMachineState == MachineState_LiveSnapshotting
14517 || oldMachineState == MachineState_Stuck
14518 || oldMachineState == MachineState_Starting
14519 || oldMachineState == MachineState_Stopping
14520 || oldMachineState == MachineState_Saving
14521 || oldMachineState == MachineState_Restoring
14522 || oldMachineState == MachineState_TeleportingPausedVM
14523 || oldMachineState == MachineState_TeleportingIn
14524 )
14525 && ( aMachineState == MachineState_PoweredOff
14526 || aMachineState == MachineState_Saved
14527 || aMachineState == MachineState_Teleported
14528 || aMachineState == MachineState_Aborted
14529 )
14530 )
14531 {
14532 /* The EMT thread has just stopped, unlock attached media. Note that as
14533 * opposed to locking that is done from Console, we do unlocking here
14534 * because the VM process may have aborted before having a chance to
14535 * properly unlock all media it locked. */
14536
14537 unlockMedia();
14538 }
14539
14540 if (oldMachineState == MachineState_Restoring)
14541 {
14542 if (aMachineState != MachineState_Saved)
14543 {
14544 /*
14545 * delete the saved state file once the machine has finished
14546 * restoring from it (note that Console sets the state from
14547 * Restoring to Saved if the VM couldn't restore successfully,
14548 * to give the user an ability to fix an error and retry --
14549 * we keep the saved state file in this case)
14550 */
14551 deleteSavedState = true;
14552 }
14553 }
14554 else if ( oldMachineState == MachineState_Saved
14555 && ( aMachineState == MachineState_PoweredOff
14556 || aMachineState == MachineState_Aborted
14557 || aMachineState == MachineState_Teleported
14558 )
14559 )
14560 {
14561 /*
14562 * delete the saved state after SessionMachine::ForgetSavedState() is called
14563 * or if the VM process (owning a direct VM session) crashed while the
14564 * VM was Saved
14565 */
14566
14567 /// @todo (dmik)
14568 // Not sure that deleting the saved state file just because of the
14569 // client death before it attempted to restore the VM is a good
14570 // thing. But when it crashes we need to go to the Aborted state
14571 // which cannot have the saved state file associated... The only
14572 // way to fix this is to make the Aborted condition not a VM state
14573 // but a bool flag: i.e., when a crash occurs, set it to true and
14574 // change the state to PoweredOff or Saved depending on the
14575 // saved state presence.
14576
14577 deleteSavedState = true;
14578 mData->mCurrentStateModified = TRUE;
14579 stsFlags |= SaveSTS_CurStateModified;
14580 }
14581
14582 if ( aMachineState == MachineState_Starting
14583 || aMachineState == MachineState_Restoring
14584 || aMachineState == MachineState_TeleportingIn
14585 )
14586 {
14587 /* set the current state modified flag to indicate that the current
14588 * state is no more identical to the state in the
14589 * current snapshot */
14590 if (!mData->mCurrentSnapshot.isNull())
14591 {
14592 mData->mCurrentStateModified = TRUE;
14593 stsFlags |= SaveSTS_CurStateModified;
14594 }
14595 }
14596
14597 if (deleteSavedState)
14598 {
14599 if (mRemoveSavedState)
14600 {
14601 Assert(!mSSData->strStateFilePath.isEmpty());
14602
14603 // it is safe to delete the saved state file if ...
14604 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14605 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14606 // ... none of the snapshots share the saved state file
14607 )
14608 RTFileDelete(mSSData->strStateFilePath.c_str());
14609 }
14610
14611 mSSData->strStateFilePath.setNull();
14612 stsFlags |= SaveSTS_StateFilePath;
14613 }
14614
14615 /* redirect to the underlying peer machine */
14616 mPeer->i_setMachineState(aMachineState);
14617
14618 if ( oldMachineState != MachineState_RestoringSnapshot
14619 && ( aMachineState == MachineState_PoweredOff
14620 || aMachineState == MachineState_Teleported
14621 || aMachineState == MachineState_Aborted
14622 || aMachineState == MachineState_Saved))
14623 {
14624 /* the machine has stopped execution
14625 * (or the saved state file was adopted) */
14626 stsFlags |= SaveSTS_StateTimeStamp;
14627 }
14628
14629 if ( ( oldMachineState == MachineState_PoweredOff
14630 || oldMachineState == MachineState_Aborted
14631 || oldMachineState == MachineState_Teleported
14632 )
14633 && aMachineState == MachineState_Saved)
14634 {
14635 /* the saved state file was adopted */
14636 Assert(!mSSData->strStateFilePath.isEmpty());
14637 stsFlags |= SaveSTS_StateFilePath;
14638 }
14639
14640#ifdef VBOX_WITH_GUEST_PROPS
14641 if ( aMachineState == MachineState_PoweredOff
14642 || aMachineState == MachineState_Aborted
14643 || aMachineState == MachineState_Teleported)
14644 {
14645 /* Make sure any transient guest properties get removed from the
14646 * property store on shutdown. */
14647 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14648
14649 /* remove it from the settings representation */
14650 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14651 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14652 it != llGuestProperties.end();
14653 /*nothing*/)
14654 {
14655 const settings::GuestProperty &prop = *it;
14656 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14657 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14658 {
14659 it = llGuestProperties.erase(it);
14660 fNeedsSaving = true;
14661 }
14662 else
14663 {
14664 ++it;
14665 }
14666 }
14667
14668 /* Additionally remove it from the HWData representation. Required to
14669 * keep everything in sync, as this is what the API keeps using. */
14670 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14671 for (HWData::GuestPropertyMap::iterator it = llHWGuestProperties.begin();
14672 it != llHWGuestProperties.end();
14673 /*nothing*/)
14674 {
14675 uint32_t fFlags = it->second.mFlags;
14676 if ( fFlags & guestProp::TRANSIENT
14677 || fFlags & guestProp::TRANSRESET)
14678 {
14679 /* iterator where we need to continue after the erase call
14680 * (C++03 is a fact still, and it doesn't return the iterator
14681 * which would allow continuing) */
14682 HWData::GuestPropertyMap::iterator it2 = it;
14683 ++it2;
14684 llHWGuestProperties.erase(it);
14685 it = it2;
14686 fNeedsSaving = true;
14687 }
14688 else
14689 {
14690 ++it;
14691 }
14692 }
14693
14694 if (fNeedsSaving)
14695 {
14696 mData->mCurrentStateModified = TRUE;
14697 stsFlags |= SaveSTS_CurStateModified;
14698 }
14699 }
14700#endif /* VBOX_WITH_GUEST_PROPS */
14701
14702 rc = i_saveStateSettings(stsFlags);
14703
14704 if ( ( oldMachineState != MachineState_PoweredOff
14705 && oldMachineState != MachineState_Aborted
14706 && oldMachineState != MachineState_Teleported
14707 )
14708 && ( aMachineState == MachineState_PoweredOff
14709 || aMachineState == MachineState_Aborted
14710 || aMachineState == MachineState_Teleported
14711 )
14712 )
14713 {
14714 /* we've been shut down for any reason */
14715 /* no special action so far */
14716 }
14717
14718 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14719 LogFlowThisFuncLeave();
14720 return rc;
14721}
14722
14723/**
14724 * Sends the current machine state value to the VM process.
14725 *
14726 * @note Locks this object for reading, then calls a client process.
14727 */
14728HRESULT SessionMachine::i_updateMachineStateOnClient()
14729{
14730 AutoCaller autoCaller(this);
14731 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14732
14733 ComPtr<IInternalSessionControl> directControl;
14734 {
14735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14736 AssertReturn(!!mData, E_FAIL);
14737 if (mData->mSession.mLockType == LockType_VM)
14738 directControl = mData->mSession.mDirectControl;
14739
14740 /* directControl may be already set to NULL here in #OnSessionEnd()
14741 * called too early by the direct session process while there is still
14742 * some operation (like deleting the snapshot) in progress. The client
14743 * process in this case is waiting inside Session::close() for the
14744 * "end session" process object to complete, while #uninit() called by
14745 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14746 * operation to complete. For now, we accept this inconsistent behavior
14747 * and simply do nothing here. */
14748
14749 if (mData->mSession.mState == SessionState_Unlocking)
14750 return S_OK;
14751 }
14752
14753 /* ignore notifications sent after #OnSessionEnd() is called */
14754 if (!directControl)
14755 return S_OK;
14756
14757 return directControl->UpdateMachineState(mData->mMachineState);
14758}
14759
14760
14761/**
14762 * Static Machine method that can get passed to RTThreadCreate to
14763 * have a thread started for a Task. See Machine::Task.
14764 */
14765/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14766{
14767 AssertReturn(pvUser, VERR_INVALID_POINTER);
14768
14769 Task *pTask = static_cast<Task *>(pvUser);
14770 pTask->handler();
14771 /** @todo r=klaus it would be safer to update the progress object here,
14772 * as it avoids possible races due to scoping issues/tricks in the handler */
14773 // it's our responsibility to delete the task
14774 delete pTask;
14775
14776 return 0;
14777}
14778
14779/*static*/
14780HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14781{
14782 va_list args;
14783 va_start(args, pcszMsg);
14784 HRESULT rc = setErrorInternal(aResultCode,
14785 getStaticClassIID(),
14786 getStaticComponentName(),
14787 Utf8Str(pcszMsg, args),
14788 false /* aWarning */,
14789 true /* aLogIt */);
14790 va_end(args);
14791 return rc;
14792}
14793
14794
14795HRESULT Machine::updateState(MachineState_T aState)
14796{
14797 NOREF(aState);
14798 ReturnComNotImplemented();
14799}
14800
14801HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14802{
14803 NOREF(aProgress);
14804 ReturnComNotImplemented();
14805}
14806
14807HRESULT Machine::endPowerUp(LONG aResult)
14808{
14809 NOREF(aResult);
14810 ReturnComNotImplemented();
14811}
14812
14813HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14814{
14815 NOREF(aProgress);
14816 ReturnComNotImplemented();
14817}
14818
14819HRESULT Machine::endPoweringDown(LONG aResult,
14820 const com::Utf8Str &aErrMsg)
14821{
14822 NOREF(aResult);
14823 NOREF(aErrMsg);
14824 ReturnComNotImplemented();
14825}
14826
14827HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14828 BOOL *aMatched,
14829 ULONG *aMaskedInterfaces)
14830{
14831 NOREF(aDevice);
14832 NOREF(aMatched);
14833 NOREF(aMaskedInterfaces);
14834 ReturnComNotImplemented();
14835
14836}
14837
14838HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14839{
14840 NOREF(aId); NOREF(aCaptureFilename);
14841 ReturnComNotImplemented();
14842}
14843
14844HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14845 BOOL aDone)
14846{
14847 NOREF(aId);
14848 NOREF(aDone);
14849 ReturnComNotImplemented();
14850}
14851
14852HRESULT Machine::autoCaptureUSBDevices()
14853{
14854 ReturnComNotImplemented();
14855}
14856
14857HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14858{
14859 NOREF(aDone);
14860 ReturnComNotImplemented();
14861}
14862
14863HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14864 ComPtr<IProgress> &aProgress)
14865{
14866 NOREF(aSession);
14867 NOREF(aProgress);
14868 ReturnComNotImplemented();
14869}
14870
14871HRESULT Machine::finishOnlineMergeMedium()
14872{
14873 ReturnComNotImplemented();
14874}
14875
14876HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14877 std::vector<com::Utf8Str> &aValues,
14878 std::vector<LONG64> &aTimestamps,
14879 std::vector<com::Utf8Str> &aFlags)
14880{
14881 NOREF(aNames);
14882 NOREF(aValues);
14883 NOREF(aTimestamps);
14884 NOREF(aFlags);
14885 ReturnComNotImplemented();
14886}
14887
14888HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14889 const com::Utf8Str &aValue,
14890 LONG64 aTimestamp,
14891 const com::Utf8Str &aFlags)
14892{
14893 NOREF(aName);
14894 NOREF(aValue);
14895 NOREF(aTimestamp);
14896 NOREF(aFlags);
14897 ReturnComNotImplemented();
14898}
14899
14900HRESULT Machine::lockMedia()
14901{
14902 ReturnComNotImplemented();
14903}
14904
14905HRESULT Machine::unlockMedia()
14906{
14907 ReturnComNotImplemented();
14908}
14909
14910HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14911 ComPtr<IMediumAttachment> &aNewAttachment)
14912{
14913 NOREF(aAttachment);
14914 NOREF(aNewAttachment);
14915 ReturnComNotImplemented();
14916}
14917
14918HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14919 ULONG aCpuUser,
14920 ULONG aCpuKernel,
14921 ULONG aCpuIdle,
14922 ULONG aMemTotal,
14923 ULONG aMemFree,
14924 ULONG aMemBalloon,
14925 ULONG aMemShared,
14926 ULONG aMemCache,
14927 ULONG aPagedTotal,
14928 ULONG aMemAllocTotal,
14929 ULONG aMemFreeTotal,
14930 ULONG aMemBalloonTotal,
14931 ULONG aMemSharedTotal,
14932 ULONG aVmNetRx,
14933 ULONG aVmNetTx)
14934{
14935 NOREF(aValidStats);
14936 NOREF(aCpuUser);
14937 NOREF(aCpuKernel);
14938 NOREF(aCpuIdle);
14939 NOREF(aMemTotal);
14940 NOREF(aMemFree);
14941 NOREF(aMemBalloon);
14942 NOREF(aMemShared);
14943 NOREF(aMemCache);
14944 NOREF(aPagedTotal);
14945 NOREF(aMemAllocTotal);
14946 NOREF(aMemFreeTotal);
14947 NOREF(aMemBalloonTotal);
14948 NOREF(aMemSharedTotal);
14949 NOREF(aVmNetRx);
14950 NOREF(aVmNetTx);
14951 ReturnComNotImplemented();
14952}
14953
14954HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14955 com::Utf8Str &aResult)
14956{
14957 NOREF(aAuthParams);
14958 NOREF(aResult);
14959 ReturnComNotImplemented();
14960}
14961
14962HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
14963{
14964 NOREF(aFlags);
14965 ReturnComNotImplemented();
14966}
14967
14968/* This isn't handled entirely by the wrapper generator yet. */
14969#ifdef VBOX_WITH_XPCOM
14970NS_DECL_CLASSINFO(SessionMachine)
14971NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
14972
14973NS_DECL_CLASSINFO(SnapshotMachine)
14974NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
14975#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