VirtualBox

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

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

Temporarily disabled x2APIC for new VMs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 515.5 KB
Line 
1/* $Id: MachineImpl.cpp 61758 2016-06-18 08:26:53Z 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.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47
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 /* Let the OS type enable the X2APIC */
363// not yet
364// mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
365 }
366
367 /* Apply parallel port defaults */
368 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
369 mParallelPorts[slot]->i_applyDefaults();
370
371 /* At this point the changing of the current state modification
372 * flag is allowed. */
373 i_allowStateModification();
374
375 /* commit all changes made during the initialization */
376 i_commit();
377 }
378
379 /* Confirm a successful initialization when it's the case */
380 if (SUCCEEDED(rc))
381 {
382 if (mData->mAccessible)
383 autoInitSpan.setSucceeded();
384 else
385 autoInitSpan.setLimited();
386 }
387
388 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
389 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
390 mData->mRegistered,
391 mData->mAccessible,
392 rc));
393
394 LogFlowThisFuncLeave();
395
396 return rc;
397}
398
399/**
400 * Initializes a new instance with data from machine XML (formerly Init_Registered).
401 * Gets called in two modes:
402 *
403 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
404 * UUID is specified and we mark the machine as "registered";
405 *
406 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
407 * and the machine remains unregistered until RegisterMachine() is called.
408 *
409 * @param aParent Associated parent object
410 * @param aConfigFile Local file system path to the VM settings file (can
411 * be relative to the VirtualBox config directory).
412 * @param aId UUID of the machine or NULL (see above).
413 *
414 * @return Success indicator. if not S_OK, the machine object is invalid
415 */
416HRESULT Machine::initFromSettings(VirtualBox *aParent,
417 const Utf8Str &strConfigFile,
418 const Guid *aId)
419{
420 LogFlowThisFuncEnter();
421 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
422
423 /* Enclose the state transition NotReady->InInit->Ready */
424 AutoInitSpan autoInitSpan(this);
425 AssertReturn(autoInitSpan.isOk(), E_FAIL);
426
427 HRESULT rc = initImpl(aParent, strConfigFile);
428 if (FAILED(rc)) return rc;
429
430 if (aId)
431 {
432 // loading a registered VM:
433 unconst(mData->mUuid) = *aId;
434 mData->mRegistered = TRUE;
435 // now load the settings from XML:
436 rc = i_registeredInit();
437 // this calls initDataAndChildObjects() and loadSettings()
438 }
439 else
440 {
441 // opening an unregistered VM (VirtualBox::OpenMachine()):
442 rc = initDataAndChildObjects();
443
444 if (SUCCEEDED(rc))
445 {
446 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
447 mData->mAccessible = TRUE;
448
449 try
450 {
451 // load and parse machine XML; this will throw on XML or logic errors
452 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
453
454 // reject VM UUID duplicates, they can happen if someone
455 // tries to register an already known VM config again
456 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
457 true /* fPermitInaccessible */,
458 false /* aDoSetError */,
459 NULL) != VBOX_E_OBJECT_NOT_FOUND)
460 {
461 throw setError(E_FAIL,
462 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
463 mData->m_strConfigFile.c_str());
464 }
465
466 // use UUID from machine config
467 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
468
469 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
470 NULL /* puuidRegistry */);
471 if (FAILED(rc)) throw rc;
472
473 /* At this point the changing of the current state modification
474 * flag is allowed. */
475 i_allowStateModification();
476
477 i_commit();
478 }
479 catch (HRESULT err)
480 {
481 /* we assume that error info is set by the thrower */
482 rc = err;
483 }
484 catch (...)
485 {
486 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
487 }
488 }
489 }
490
491 /* Confirm a successful initialization when it's the case */
492 if (SUCCEEDED(rc))
493 {
494 if (mData->mAccessible)
495 autoInitSpan.setSucceeded();
496 else
497 {
498 autoInitSpan.setLimited();
499
500 // uninit media from this machine's media registry, or else
501 // reloading the settings will fail
502 mParent->i_unregisterMachineMedia(i_getId());
503 }
504 }
505
506 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
507 "rc=%08X\n",
508 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
509 mData->mRegistered, mData->mAccessible, rc));
510
511 LogFlowThisFuncLeave();
512
513 return rc;
514}
515
516/**
517 * Initializes a new instance from a machine config that is already in memory
518 * (import OVF case). Since we are importing, the UUID in the machine
519 * config is ignored and we always generate a fresh one.
520 *
521 * @param strName Name for the new machine; this overrides what is specified in config and is used
522 * for the settings file as well.
523 * @param config Machine configuration loaded and parsed from XML.
524 *
525 * @return Success indicator. if not S_OK, the machine object is invalid
526 */
527HRESULT Machine::init(VirtualBox *aParent,
528 const Utf8Str &strName,
529 const settings::MachineConfigFile &config)
530{
531 LogFlowThisFuncEnter();
532
533 /* Enclose the state transition NotReady->InInit->Ready */
534 AutoInitSpan autoInitSpan(this);
535 AssertReturn(autoInitSpan.isOk(), E_FAIL);
536
537 Utf8Str strConfigFile;
538 aParent->i_getDefaultMachineFolder(strConfigFile);
539 strConfigFile.append(RTPATH_DELIMITER);
540 strConfigFile.append(strName);
541 strConfigFile.append(RTPATH_DELIMITER);
542 strConfigFile.append(strName);
543 strConfigFile.append(".vbox");
544
545 HRESULT rc = initImpl(aParent, strConfigFile);
546 if (FAILED(rc)) return rc;
547
548 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
549 if (FAILED(rc)) return rc;
550
551 rc = initDataAndChildObjects();
552
553 if (SUCCEEDED(rc))
554 {
555 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
556 mData->mAccessible = TRUE;
557
558 // create empty machine config for instance data
559 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
560
561 // generate fresh UUID, ignore machine config
562 unconst(mData->mUuid).create();
563
564 rc = i_loadMachineDataFromSettings(config,
565 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
566
567 // override VM name as well, it may be different
568 mUserData->s.strName = strName;
569
570 if (SUCCEEDED(rc))
571 {
572 /* At this point the changing of the current state modification
573 * flag is allowed. */
574 i_allowStateModification();
575
576 /* commit all changes made during the initialization */
577 i_commit();
578 }
579 }
580
581 /* Confirm a successful initialization when it's the case */
582 if (SUCCEEDED(rc))
583 {
584 if (mData->mAccessible)
585 autoInitSpan.setSucceeded();
586 else
587 {
588 /* Ignore all errors from unregistering, they would destroy
589- * the more interesting error information we already have,
590- * pinpointing the issue with the VM config. */
591 ErrorInfoKeeper eik;
592
593 autoInitSpan.setLimited();
594
595 // uninit media from this machine's media registry, or else
596 // reloading the settings will fail
597 mParent->i_unregisterMachineMedia(i_getId());
598 }
599 }
600
601 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
602 "rc=%08X\n",
603 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
604 mData->mRegistered, mData->mAccessible, rc));
605
606 LogFlowThisFuncLeave();
607
608 return rc;
609}
610
611/**
612 * Shared code between the various init() implementations.
613 * @param aParent
614 * @return
615 */
616HRESULT Machine::initImpl(VirtualBox *aParent,
617 const Utf8Str &strConfigFile)
618{
619 LogFlowThisFuncEnter();
620
621 AssertReturn(aParent, E_INVALIDARG);
622 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
623
624 HRESULT rc = S_OK;
625
626 /* share the parent weakly */
627 unconst(mParent) = aParent;
628
629 /* allocate the essential machine data structure (the rest will be
630 * allocated later by initDataAndChildObjects() */
631 mData.allocate();
632
633 /* memorize the config file name (as provided) */
634 mData->m_strConfigFile = strConfigFile;
635
636 /* get the full file name */
637 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
638 if (RT_FAILURE(vrc1))
639 return setError(VBOX_E_FILE_ERROR,
640 tr("Invalid machine settings file name '%s' (%Rrc)"),
641 strConfigFile.c_str(),
642 vrc1);
643
644 LogFlowThisFuncLeave();
645
646 return rc;
647}
648
649/**
650 * Tries to create a machine settings file in the path stored in the machine
651 * instance data. Used when a new machine is created to fail gracefully if
652 * the settings file could not be written (e.g. because machine dir is read-only).
653 * @return
654 */
655HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
656{
657 HRESULT rc = S_OK;
658
659 // when we create a new machine, we must be able to create the settings file
660 RTFILE f = NIL_RTFILE;
661 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
662 if ( RT_SUCCESS(vrc)
663 || vrc == VERR_SHARING_VIOLATION
664 )
665 {
666 if (RT_SUCCESS(vrc))
667 RTFileClose(f);
668 if (!fForceOverwrite)
669 rc = setError(VBOX_E_FILE_ERROR,
670 tr("Machine settings file '%s' already exists"),
671 mData->m_strConfigFileFull.c_str());
672 else
673 {
674 /* try to delete the config file, as otherwise the creation
675 * of a new settings file will fail. */
676 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
677 if (RT_FAILURE(vrc2))
678 rc = setError(VBOX_E_FILE_ERROR,
679 tr("Could not delete the existing settings file '%s' (%Rrc)"),
680 mData->m_strConfigFileFull.c_str(), vrc2);
681 }
682 }
683 else if ( vrc != VERR_FILE_NOT_FOUND
684 && vrc != VERR_PATH_NOT_FOUND
685 )
686 rc = setError(VBOX_E_FILE_ERROR,
687 tr("Invalid machine settings file name '%s' (%Rrc)"),
688 mData->m_strConfigFileFull.c_str(),
689 vrc);
690 return rc;
691}
692
693/**
694 * Initializes the registered machine by loading the settings file.
695 * This method is separated from #init() in order to make it possible to
696 * retry the operation after VirtualBox startup instead of refusing to
697 * startup the whole VirtualBox server in case if the settings file of some
698 * registered VM is invalid or inaccessible.
699 *
700 * @note Must be always called from this object's write lock
701 * (unless called from #init() that doesn't need any locking).
702 * @note Locks the mUSBController method for writing.
703 * @note Subclasses must not call this method.
704 */
705HRESULT Machine::i_registeredInit()
706{
707 AssertReturn(!i_isSessionMachine(), E_FAIL);
708 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
709 AssertReturn(mData->mUuid.isValid(), E_FAIL);
710 AssertReturn(!mData->mAccessible, E_FAIL);
711
712 HRESULT rc = initDataAndChildObjects();
713
714 if (SUCCEEDED(rc))
715 {
716 /* Temporarily reset the registered flag in order to let setters
717 * potentially called from loadSettings() succeed (isMutable() used in
718 * all setters will return FALSE for a Machine instance if mRegistered
719 * is TRUE). */
720 mData->mRegistered = FALSE;
721
722 try
723 {
724 // load and parse machine XML; this will throw on XML or logic errors
725 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
726
727 if (mData->mUuid != mData->pMachineConfigFile->uuid)
728 throw setError(E_FAIL,
729 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
730 mData->pMachineConfigFile->uuid.raw(),
731 mData->m_strConfigFileFull.c_str(),
732 mData->mUuid.toString().c_str(),
733 mParent->i_settingsFilePath().c_str());
734
735 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
736 NULL /* const Guid *puuidRegistry */);
737 if (FAILED(rc)) throw rc;
738 }
739 catch (HRESULT err)
740 {
741 /* we assume that error info is set by the thrower */
742 rc = err;
743 }
744 catch (...)
745 {
746 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
747 }
748
749 /* Restore the registered flag (even on failure) */
750 mData->mRegistered = TRUE;
751 }
752
753 if (SUCCEEDED(rc))
754 {
755 /* Set mAccessible to TRUE only if we successfully locked and loaded
756 * the settings file */
757 mData->mAccessible = TRUE;
758
759 /* commit all changes made during loading the settings file */
760 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
761 /// @todo r=klaus for some reason the settings loading logic backs up
762 // the settings, and therefore a commit is needed. Should probably be changed.
763 }
764 else
765 {
766 /* If the machine is registered, then, instead of returning a
767 * failure, we mark it as inaccessible and set the result to
768 * success to give it a try later */
769
770 /* fetch the current error info */
771 mData->mAccessError = com::ErrorInfo();
772 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
773
774 /* rollback all changes */
775 i_rollback(false /* aNotify */);
776
777 // uninit media from this machine's media registry, or else
778 // reloading the settings will fail
779 mParent->i_unregisterMachineMedia(i_getId());
780
781 /* uninitialize the common part to make sure all data is reset to
782 * default (null) values */
783 uninitDataAndChildObjects();
784
785 rc = S_OK;
786 }
787
788 return rc;
789}
790
791/**
792 * Uninitializes the instance.
793 * Called either from FinalRelease() or by the parent when it gets destroyed.
794 *
795 * @note The caller of this method must make sure that this object
796 * a) doesn't have active callers on the current thread and b) is not locked
797 * by the current thread; otherwise uninit() will hang either a) due to
798 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
799 * a dead-lock caused by this thread waiting for all callers on the other
800 * threads are done but preventing them from doing so by holding a lock.
801 */
802void Machine::uninit()
803{
804 LogFlowThisFuncEnter();
805
806 Assert(!isWriteLockOnCurrentThread());
807
808 Assert(!uRegistryNeedsSaving);
809 if (uRegistryNeedsSaving)
810 {
811 AutoCaller autoCaller(this);
812 if (SUCCEEDED(autoCaller.rc()))
813 {
814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
815 i_saveSettings(NULL, Machine::SaveS_Force);
816 }
817 }
818
819 /* Enclose the state transition Ready->InUninit->NotReady */
820 AutoUninitSpan autoUninitSpan(this);
821 if (autoUninitSpan.uninitDone())
822 return;
823
824 Assert(!i_isSnapshotMachine());
825 Assert(!i_isSessionMachine());
826 Assert(!!mData);
827
828 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
829 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
830
831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
832
833 if (!mData->mSession.mMachine.isNull())
834 {
835 /* Theoretically, this can only happen if the VirtualBox server has been
836 * terminated while there were clients running that owned open direct
837 * sessions. Since in this case we are definitely called by
838 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
839 * won't happen on the client watcher thread (because it has a
840 * VirtualBox caller for the duration of the
841 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
842 * cannot happen until the VirtualBox caller is released). This is
843 * important, because SessionMachine::uninit() cannot correctly operate
844 * after we return from this method (it expects the Machine instance is
845 * still valid). We'll call it ourselves below.
846 */
847 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
848 (SessionMachine*)mData->mSession.mMachine));
849
850 if (Global::IsOnlineOrTransient(mData->mMachineState))
851 {
852 Log1WarningThisFunc(("Setting state to Aborted!\n"));
853 /* set machine state using SessionMachine reimplementation */
854 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
855 }
856
857 /*
858 * Uninitialize SessionMachine using public uninit() to indicate
859 * an unexpected uninitialization.
860 */
861 mData->mSession.mMachine->uninit();
862 /* SessionMachine::uninit() must set mSession.mMachine to null */
863 Assert(mData->mSession.mMachine.isNull());
864 }
865
866 // uninit media from this machine's media registry, if they're still there
867 Guid uuidMachine(i_getId());
868
869 /* the lock is no more necessary (SessionMachine is uninitialized) */
870 alock.release();
871
872 /* XXX This will fail with
873 * "cannot be closed because it is still attached to 1 virtual machines"
874 * because at this point we did not call uninitDataAndChildObjects() yet
875 * and therefore also removeBackReference() for all these mediums was not called! */
876
877 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
878 mParent->i_unregisterMachineMedia(uuidMachine);
879
880 // has machine been modified?
881 if (mData->flModifications)
882 {
883 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
884 i_rollback(false /* aNotify */);
885 }
886
887 if (mData->mAccessible)
888 uninitDataAndChildObjects();
889
890 /* free the essential data structure last */
891 mData.free();
892
893 LogFlowThisFuncLeave();
894}
895
896// Wrapped IMachine properties
897/////////////////////////////////////////////////////////////////////////////
898HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
899{
900 /* mParent is constant during life time, no need to lock */
901 ComObjPtr<VirtualBox> pVirtualBox(mParent);
902 aParent = pVirtualBox;
903
904 return S_OK;
905}
906
907
908HRESULT Machine::getAccessible(BOOL *aAccessible)
909{
910 /* In some cases (medium registry related), it is necessary to be able to
911 * go through the list of all machines. Happens when an inaccessible VM
912 * has a sensible medium registry. */
913 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
915
916 HRESULT rc = S_OK;
917
918 if (!mData->mAccessible)
919 {
920 /* try to initialize the VM once more if not accessible */
921
922 AutoReinitSpan autoReinitSpan(this);
923 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
924
925#ifdef DEBUG
926 LogFlowThisFunc(("Dumping media backreferences\n"));
927 mParent->i_dumpAllBackRefs();
928#endif
929
930 if (mData->pMachineConfigFile)
931 {
932 // reset the XML file to force loadSettings() (called from registeredInit())
933 // to parse it again; the file might have changed
934 delete mData->pMachineConfigFile;
935 mData->pMachineConfigFile = NULL;
936 }
937
938 rc = i_registeredInit();
939
940 if (SUCCEEDED(rc) && mData->mAccessible)
941 {
942 autoReinitSpan.setSucceeded();
943
944 /* make sure interesting parties will notice the accessibility
945 * state change */
946 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
947 mParent->i_onMachineDataChange(mData->mUuid);
948 }
949 }
950
951 if (SUCCEEDED(rc))
952 *aAccessible = mData->mAccessible;
953
954 LogFlowThisFuncLeave();
955
956 return rc;
957}
958
959HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
960{
961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
962
963 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
964 {
965 /* return shortly */
966 aAccessError = NULL;
967 return S_OK;
968 }
969
970 HRESULT rc = S_OK;
971
972 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
973 rc = errorInfo.createObject();
974 if (SUCCEEDED(rc))
975 {
976 errorInfo->init(mData->mAccessError.getResultCode(),
977 mData->mAccessError.getInterfaceID().ref(),
978 Utf8Str(mData->mAccessError.getComponent()).c_str(),
979 Utf8Str(mData->mAccessError.getText()));
980 aAccessError = errorInfo;
981 }
982
983 return rc;
984}
985
986HRESULT Machine::getName(com::Utf8Str &aName)
987{
988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
989
990 aName = mUserData->s.strName;
991
992 return S_OK;
993}
994
995HRESULT Machine::setName(const com::Utf8Str &aName)
996{
997 // prohibit setting a UUID only as the machine name, or else it can
998 // never be found by findMachine()
999 Guid test(aName);
1000
1001 if (test.isValid())
1002 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1003
1004 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1005
1006 HRESULT rc = i_checkStateDependency(MutableStateDep);
1007 if (FAILED(rc)) return rc;
1008
1009 i_setModified(IsModified_MachineData);
1010 mUserData.backup();
1011 mUserData->s.strName = aName;
1012
1013 return S_OK;
1014}
1015
1016HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1017{
1018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1019
1020 aDescription = mUserData->s.strDescription;
1021
1022 return S_OK;
1023}
1024
1025HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1026{
1027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1028
1029 // this can be done in principle in any state as it doesn't affect the VM
1030 // significantly, but play safe by not messing around while complex
1031 // activities are going on
1032 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1033 if (FAILED(rc)) return rc;
1034
1035 i_setModified(IsModified_MachineData);
1036 mUserData.backup();
1037 mUserData->s.strDescription = aDescription;
1038
1039 return S_OK;
1040}
1041
1042HRESULT Machine::getId(com::Guid &aId)
1043{
1044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1045
1046 aId = mData->mUuid;
1047
1048 return S_OK;
1049}
1050
1051HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1052{
1053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1054 aGroups.resize(mUserData->s.llGroups.size());
1055 size_t i = 0;
1056 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1057 it != mUserData->s.llGroups.end(); ++it, ++i)
1058 aGroups[i] = (*it);
1059
1060 return S_OK;
1061}
1062
1063HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1064{
1065 StringsList llGroups;
1066 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1067 if (FAILED(rc))
1068 return rc;
1069
1070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1071
1072 rc = i_checkStateDependency(MutableOrSavedStateDep);
1073 if (FAILED(rc)) return rc;
1074
1075 i_setModified(IsModified_MachineData);
1076 mUserData.backup();
1077 mUserData->s.llGroups = llGroups;
1078
1079 return S_OK;
1080}
1081
1082HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1083{
1084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1085
1086 aOSTypeId = mUserData->s.strOsType;
1087
1088 return S_OK;
1089}
1090
1091HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1092{
1093 /* look up the object by Id to check it is valid */
1094 ComPtr<IGuestOSType> guestOSType;
1095 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1096 if (FAILED(rc)) return rc;
1097
1098 /* when setting, always use the "etalon" value for consistency -- lookup
1099 * by ID is case-insensitive and the input value may have different case */
1100 Bstr osTypeId;
1101 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1102 if (FAILED(rc)) return rc;
1103
1104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1105
1106 rc = i_checkStateDependency(MutableStateDep);
1107 if (FAILED(rc)) return rc;
1108
1109 i_setModified(IsModified_MachineData);
1110 mUserData.backup();
1111 mUserData->s.strOsType = osTypeId;
1112
1113 return S_OK;
1114}
1115
1116HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1117{
1118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1119
1120 *aFirmwareType = mHWData->mFirmwareType;
1121
1122 return S_OK;
1123}
1124
1125HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1126{
1127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1128
1129 HRESULT rc = i_checkStateDependency(MutableStateDep);
1130 if (FAILED(rc)) return rc;
1131
1132 i_setModified(IsModified_MachineData);
1133 mHWData.backup();
1134 mHWData->mFirmwareType = aFirmwareType;
1135
1136 return S_OK;
1137}
1138
1139HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1140{
1141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1142
1143 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1144
1145 return S_OK;
1146}
1147
1148HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1149{
1150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1151
1152 HRESULT rc = i_checkStateDependency(MutableStateDep);
1153 if (FAILED(rc)) return rc;
1154
1155 i_setModified(IsModified_MachineData);
1156 mHWData.backup();
1157 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1158
1159 return S_OK;
1160}
1161
1162HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1163{
1164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1165
1166 *aPointingHIDType = mHWData->mPointingHIDType;
1167
1168 return S_OK;
1169}
1170
1171HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1172{
1173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1174
1175 HRESULT rc = i_checkStateDependency(MutableStateDep);
1176 if (FAILED(rc)) return rc;
1177
1178 i_setModified(IsModified_MachineData);
1179 mHWData.backup();
1180 mHWData->mPointingHIDType = aPointingHIDType;
1181
1182 return S_OK;
1183}
1184
1185HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1186{
1187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1188
1189 *aChipsetType = mHWData->mChipsetType;
1190
1191 return S_OK;
1192}
1193
1194HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1195{
1196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1197
1198 HRESULT rc = i_checkStateDependency(MutableStateDep);
1199 if (FAILED(rc)) return rc;
1200
1201 if (aChipsetType != mHWData->mChipsetType)
1202 {
1203 i_setModified(IsModified_MachineData);
1204 mHWData.backup();
1205 mHWData->mChipsetType = aChipsetType;
1206
1207 // Resize network adapter array, to be finalized on commit/rollback.
1208 // We must not throw away entries yet, otherwise settings are lost
1209 // without a way to roll back.
1210 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1211 size_t oldCount = mNetworkAdapters.size();
1212 if (newCount > oldCount)
1213 {
1214 mNetworkAdapters.resize(newCount);
1215 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1216 {
1217 unconst(mNetworkAdapters[slot]).createObject();
1218 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1219 }
1220 }
1221 }
1222
1223 return S_OK;
1224}
1225
1226HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1227{
1228 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1229
1230 aParavirtDebug = mHWData->mParavirtDebug;
1231 return S_OK;
1232}
1233
1234HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1235{
1236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1237
1238 HRESULT rc = i_checkStateDependency(MutableStateDep);
1239 if (FAILED(rc)) return rc;
1240
1241 /** @todo Parse/validate options? */
1242 if (aParavirtDebug != mHWData->mParavirtDebug)
1243 {
1244 i_setModified(IsModified_MachineData);
1245 mHWData.backup();
1246 mHWData->mParavirtDebug = aParavirtDebug;
1247 }
1248
1249 return S_OK;
1250}
1251
1252HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1253{
1254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1255
1256 *aParavirtProvider = mHWData->mParavirtProvider;
1257
1258 return S_OK;
1259}
1260
1261HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1262{
1263 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1264
1265 HRESULT rc = i_checkStateDependency(MutableStateDep);
1266 if (FAILED(rc)) return rc;
1267
1268 if (aParavirtProvider != mHWData->mParavirtProvider)
1269 {
1270 i_setModified(IsModified_MachineData);
1271 mHWData.backup();
1272 mHWData->mParavirtProvider = aParavirtProvider;
1273 }
1274
1275 return S_OK;
1276}
1277
1278HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1279{
1280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1281
1282 *aParavirtProvider = mHWData->mParavirtProvider;
1283 switch (mHWData->mParavirtProvider)
1284 {
1285 case ParavirtProvider_None:
1286 case ParavirtProvider_HyperV:
1287 case ParavirtProvider_KVM:
1288 case ParavirtProvider_Minimal:
1289 break;
1290
1291 /* Resolve dynamic provider types to the effective types. */
1292 default:
1293 {
1294 ComPtr<IGuestOSType> ptrGuestOSType;
1295 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1296 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1297
1298 Bstr guestTypeFamilyId;
1299 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1300 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1301 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1302
1303 switch (mHWData->mParavirtProvider)
1304 {
1305 case ParavirtProvider_Legacy:
1306 {
1307 if (fOsXGuest)
1308 *aParavirtProvider = ParavirtProvider_Minimal;
1309 else
1310 *aParavirtProvider = ParavirtProvider_None;
1311 break;
1312 }
1313
1314 case ParavirtProvider_Default:
1315 {
1316 if (fOsXGuest)
1317 *aParavirtProvider = ParavirtProvider_Minimal;
1318 else if ( mUserData->s.strOsType == "Windows10"
1319 || mUserData->s.strOsType == "Windows10_64"
1320 || mUserData->s.strOsType == "Windows81"
1321 || mUserData->s.strOsType == "Windows81_64"
1322 || mUserData->s.strOsType == "Windows8"
1323 || mUserData->s.strOsType == "Windows8_64"
1324 || mUserData->s.strOsType == "Windows7"
1325 || mUserData->s.strOsType == "Windows7_64"
1326 || mUserData->s.strOsType == "WindowsVista"
1327 || mUserData->s.strOsType == "WindowsVista_64"
1328 || mUserData->s.strOsType == "Windows2012"
1329 || mUserData->s.strOsType == "Windows2012_64"
1330 || mUserData->s.strOsType == "Windows2008"
1331 || mUserData->s.strOsType == "Windows2008_64")
1332 {
1333 *aParavirtProvider = ParavirtProvider_HyperV;
1334 }
1335 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1336 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1337 || mUserData->s.strOsType == "Linux"
1338 || mUserData->s.strOsType == "Linux_64"
1339 || mUserData->s.strOsType == "ArchLinux"
1340 || mUserData->s.strOsType == "ArchLinux_64"
1341 || mUserData->s.strOsType == "Debian"
1342 || mUserData->s.strOsType == "Debian_64"
1343 || mUserData->s.strOsType == "Fedora"
1344 || mUserData->s.strOsType == "Fedora_64"
1345 || mUserData->s.strOsType == "Gentoo"
1346 || mUserData->s.strOsType == "Gentoo_64"
1347 || mUserData->s.strOsType == "Mandriva"
1348 || mUserData->s.strOsType == "Mandriva_64"
1349 || mUserData->s.strOsType == "OpenSUSE"
1350 || mUserData->s.strOsType == "OpenSUSE_64"
1351 || mUserData->s.strOsType == "Oracle"
1352 || mUserData->s.strOsType == "Oracle_64"
1353 || mUserData->s.strOsType == "RedHat"
1354 || mUserData->s.strOsType == "RedHat_64"
1355 || mUserData->s.strOsType == "Turbolinux"
1356 || mUserData->s.strOsType == "Turbolinux_64"
1357 || mUserData->s.strOsType == "Ubuntu"
1358 || mUserData->s.strOsType == "Ubuntu_64"
1359 || mUserData->s.strOsType == "Xandros"
1360 || mUserData->s.strOsType == "Xandros_64")
1361 {
1362 *aParavirtProvider = ParavirtProvider_KVM;
1363 }
1364 else
1365 *aParavirtProvider = ParavirtProvider_None;
1366 break;
1367 }
1368 }
1369 break;
1370 }
1371 }
1372
1373 Assert( *aParavirtProvider == ParavirtProvider_None
1374 || *aParavirtProvider == ParavirtProvider_Minimal
1375 || *aParavirtProvider == ParavirtProvider_HyperV
1376 || *aParavirtProvider == ParavirtProvider_KVM);
1377 return S_OK;
1378}
1379
1380HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1381{
1382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1383
1384 aHardwareVersion = mHWData->mHWVersion;
1385
1386 return S_OK;
1387}
1388
1389HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1390{
1391 /* check known version */
1392 Utf8Str hwVersion = aHardwareVersion;
1393 if ( hwVersion.compare("1") != 0
1394 && hwVersion.compare("2") != 0)
1395 return setError(E_INVALIDARG,
1396 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1397
1398 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1399
1400 HRESULT rc = i_checkStateDependency(MutableStateDep);
1401 if (FAILED(rc)) return rc;
1402
1403 i_setModified(IsModified_MachineData);
1404 mHWData.backup();
1405 mHWData->mHWVersion = aHardwareVersion;
1406
1407 return S_OK;
1408}
1409
1410HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1411{
1412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1413
1414 if (!mHWData->mHardwareUUID.isZero())
1415 aHardwareUUID = mHWData->mHardwareUUID;
1416 else
1417 aHardwareUUID = mData->mUuid;
1418
1419 return S_OK;
1420}
1421
1422HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1423{
1424 if (!aHardwareUUID.isValid())
1425 return E_INVALIDARG;
1426
1427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1428
1429 HRESULT rc = i_checkStateDependency(MutableStateDep);
1430 if (FAILED(rc)) return rc;
1431
1432 i_setModified(IsModified_MachineData);
1433 mHWData.backup();
1434 if (aHardwareUUID == mData->mUuid)
1435 mHWData->mHardwareUUID.clear();
1436 else
1437 mHWData->mHardwareUUID = aHardwareUUID;
1438
1439 return S_OK;
1440}
1441
1442HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1443{
1444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1445
1446 *aMemorySize = mHWData->mMemorySize;
1447
1448 return S_OK;
1449}
1450
1451HRESULT Machine::setMemorySize(ULONG aMemorySize)
1452{
1453 /* check RAM limits */
1454 if ( aMemorySize < MM_RAM_MIN_IN_MB
1455 || aMemorySize > MM_RAM_MAX_IN_MB
1456 )
1457 return setError(E_INVALIDARG,
1458 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1459 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1460
1461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1462
1463 HRESULT rc = i_checkStateDependency(MutableStateDep);
1464 if (FAILED(rc)) return rc;
1465
1466 i_setModified(IsModified_MachineData);
1467 mHWData.backup();
1468 mHWData->mMemorySize = aMemorySize;
1469
1470 return S_OK;
1471}
1472
1473HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1474{
1475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1476
1477 *aCPUCount = mHWData->mCPUCount;
1478
1479 return S_OK;
1480}
1481
1482HRESULT Machine::setCPUCount(ULONG aCPUCount)
1483{
1484 /* check CPU limits */
1485 if ( aCPUCount < SchemaDefs::MinCPUCount
1486 || aCPUCount > SchemaDefs::MaxCPUCount
1487 )
1488 return setError(E_INVALIDARG,
1489 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1490 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1491
1492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1493
1494 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1495 if (mHWData->mCPUHotPlugEnabled)
1496 {
1497 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1498 {
1499 if (mHWData->mCPUAttached[idx])
1500 return setError(E_INVALIDARG,
1501 tr("There is still a CPU attached to socket %lu."
1502 "Detach the CPU before removing the socket"),
1503 aCPUCount, idx+1);
1504 }
1505 }
1506
1507 HRESULT rc = i_checkStateDependency(MutableStateDep);
1508 if (FAILED(rc)) return rc;
1509
1510 i_setModified(IsModified_MachineData);
1511 mHWData.backup();
1512 mHWData->mCPUCount = aCPUCount;
1513
1514 return S_OK;
1515}
1516
1517HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1518{
1519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1520
1521 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1522
1523 return S_OK;
1524}
1525
1526HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1527{
1528 HRESULT rc = S_OK;
1529
1530 /* check throttle limits */
1531 if ( aCPUExecutionCap < 1
1532 || aCPUExecutionCap > 100
1533 )
1534 return setError(E_INVALIDARG,
1535 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1536 aCPUExecutionCap, 1, 100);
1537
1538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1539
1540 alock.release();
1541 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1542 alock.acquire();
1543 if (FAILED(rc)) return rc;
1544
1545 i_setModified(IsModified_MachineData);
1546 mHWData.backup();
1547 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1548
1549 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1550 if (Global::IsOnline(mData->mMachineState))
1551 i_saveSettings(NULL);
1552
1553 return S_OK;
1554}
1555
1556HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1557{
1558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1559
1560 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1561
1562 return S_OK;
1563}
1564
1565HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1566{
1567 HRESULT rc = S_OK;
1568
1569 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1570
1571 rc = i_checkStateDependency(MutableStateDep);
1572 if (FAILED(rc)) return rc;
1573
1574 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1575 {
1576 if (aCPUHotPlugEnabled)
1577 {
1578 i_setModified(IsModified_MachineData);
1579 mHWData.backup();
1580
1581 /* Add the amount of CPUs currently attached */
1582 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1583 mHWData->mCPUAttached[i] = true;
1584 }
1585 else
1586 {
1587 /*
1588 * We can disable hotplug only if the amount of maximum CPUs is equal
1589 * to the amount of attached CPUs
1590 */
1591 unsigned cCpusAttached = 0;
1592 unsigned iHighestId = 0;
1593
1594 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1595 {
1596 if (mHWData->mCPUAttached[i])
1597 {
1598 cCpusAttached++;
1599 iHighestId = i;
1600 }
1601 }
1602
1603 if ( (cCpusAttached != mHWData->mCPUCount)
1604 || (iHighestId >= mHWData->mCPUCount))
1605 return setError(E_INVALIDARG,
1606 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1607
1608 i_setModified(IsModified_MachineData);
1609 mHWData.backup();
1610 }
1611 }
1612
1613 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1614
1615 return rc;
1616}
1617
1618HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1619{
1620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1621
1622 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1623
1624 return S_OK;
1625}
1626
1627HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1628{
1629 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1630
1631 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1632 if (SUCCEEDED(hrc))
1633 {
1634 i_setModified(IsModified_MachineData);
1635 mHWData.backup();
1636 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1637 }
1638 return hrc;
1639}
1640
1641HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1642{
1643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1644 aCPUProfile = mHWData->mCpuProfile;
1645 return S_OK;
1646}
1647
1648HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1649{
1650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1651 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1652 if (SUCCEEDED(hrc))
1653 {
1654 i_setModified(IsModified_MachineData);
1655 mHWData.backup();
1656 /* Empty equals 'host'. */
1657 if (aCPUProfile.isNotEmpty())
1658 mHWData->mCpuProfile = aCPUProfile;
1659 else
1660 mHWData->mCpuProfile = "host";
1661 }
1662 return hrc;
1663}
1664
1665HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1666{
1667#ifdef VBOX_WITH_USB_CARDREADER
1668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1669
1670 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1671
1672 return S_OK;
1673#else
1674 NOREF(aEmulatedUSBCardReaderEnabled);
1675 return E_NOTIMPL;
1676#endif
1677}
1678
1679HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1680{
1681#ifdef VBOX_WITH_USB_CARDREADER
1682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1683
1684 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1685 if (FAILED(rc)) return rc;
1686
1687 i_setModified(IsModified_MachineData);
1688 mHWData.backup();
1689 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1690
1691 return S_OK;
1692#else
1693 NOREF(aEmulatedUSBCardReaderEnabled);
1694 return E_NOTIMPL;
1695#endif
1696}
1697
1698HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1699{
1700 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1701
1702 *aHPETEnabled = mHWData->mHPETEnabled;
1703
1704 return S_OK;
1705}
1706
1707HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1708{
1709 HRESULT rc = S_OK;
1710
1711 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1712
1713 rc = i_checkStateDependency(MutableStateDep);
1714 if (FAILED(rc)) return rc;
1715
1716 i_setModified(IsModified_MachineData);
1717 mHWData.backup();
1718
1719 mHWData->mHPETEnabled = aHPETEnabled;
1720
1721 return rc;
1722}
1723
1724HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1725{
1726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1727
1728 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1729 return S_OK;
1730}
1731
1732HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1733{
1734 HRESULT rc = S_OK;
1735
1736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1737
1738 i_setModified(IsModified_MachineData);
1739 mHWData.backup();
1740 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1741
1742 alock.release();
1743 rc = i_onVideoCaptureChange();
1744 alock.acquire();
1745 if (FAILED(rc))
1746 {
1747 /*
1748 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1749 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1750 * determine if it should start or stop capturing. Therefore we need to manually
1751 * undo change.
1752 */
1753 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1754 return rc;
1755 }
1756
1757 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1758 if (Global::IsOnline(mData->mMachineState))
1759 i_saveSettings(NULL);
1760
1761 return rc;
1762}
1763
1764HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1765{
1766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1767 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1768 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1769 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1770 return S_OK;
1771}
1772
1773HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1774{
1775 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1776 bool fChanged = false;
1777
1778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1779
1780 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1781 {
1782 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1783 {
1784 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1785 fChanged = true;
1786 }
1787 }
1788 if (fChanged)
1789 {
1790 alock.release();
1791 HRESULT rc = i_onVideoCaptureChange();
1792 alock.acquire();
1793 if (FAILED(rc)) return rc;
1794 i_setModified(IsModified_MachineData);
1795
1796 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1797 if (Global::IsOnline(mData->mMachineState))
1798 i_saveSettings(NULL);
1799 }
1800
1801 return S_OK;
1802}
1803
1804HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1805{
1806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1807 if (mHWData->mVideoCaptureFile.isEmpty())
1808 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1809 else
1810 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1811 return S_OK;
1812}
1813
1814HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1815{
1816 Utf8Str strFile(aVideoCaptureFile);
1817 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1818
1819 if ( Global::IsOnline(mData->mMachineState)
1820 && mHWData->mVideoCaptureEnabled)
1821 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1822
1823 if (!RTPathStartsWithRoot(strFile.c_str()))
1824 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1825
1826 if (!strFile.isEmpty())
1827 {
1828 Utf8Str defaultFile;
1829 i_getDefaultVideoCaptureFile(defaultFile);
1830 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1831 strFile.setNull();
1832 }
1833
1834 i_setModified(IsModified_MachineData);
1835 mHWData.backup();
1836 mHWData->mVideoCaptureFile = strFile;
1837
1838 return S_OK;
1839}
1840
1841HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1842{
1843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1844 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1845 return S_OK;
1846}
1847
1848HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1849{
1850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1851
1852 if ( Global::IsOnline(mData->mMachineState)
1853 && mHWData->mVideoCaptureEnabled)
1854 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1855
1856 i_setModified(IsModified_MachineData);
1857 mHWData.backup();
1858 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1859
1860 return S_OK;
1861}
1862
1863HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1864{
1865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1866 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1867 return S_OK;
1868}
1869
1870HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1871{
1872 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1873
1874 if ( Global::IsOnline(mData->mMachineState)
1875 && mHWData->mVideoCaptureEnabled)
1876 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1877
1878 i_setModified(IsModified_MachineData);
1879 mHWData.backup();
1880 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1881
1882 return S_OK;
1883}
1884
1885HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1886{
1887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1888 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1889 return S_OK;
1890}
1891
1892HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1893{
1894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1895
1896 if ( Global::IsOnline(mData->mMachineState)
1897 && mHWData->mVideoCaptureEnabled)
1898 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1899
1900 i_setModified(IsModified_MachineData);
1901 mHWData.backup();
1902 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1903
1904 return S_OK;
1905}
1906
1907HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1908{
1909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1910 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1911 return S_OK;
1912}
1913
1914HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1915{
1916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1917
1918 if ( Global::IsOnline(mData->mMachineState)
1919 && mHWData->mVideoCaptureEnabled)
1920 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1921
1922 i_setModified(IsModified_MachineData);
1923 mHWData.backup();
1924 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1925
1926 return S_OK;
1927}
1928
1929HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1930{
1931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1932 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1933 return S_OK;
1934}
1935
1936HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1937{
1938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1939
1940 if ( Global::IsOnline(mData->mMachineState)
1941 && mHWData->mVideoCaptureEnabled)
1942 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1943
1944 i_setModified(IsModified_MachineData);
1945 mHWData.backup();
1946 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1947
1948 return S_OK;
1949}
1950
1951HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1952{
1953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1954 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1955 return S_OK;
1956}
1957
1958HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1959{
1960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1961
1962 if ( Global::IsOnline(mData->mMachineState)
1963 && mHWData->mVideoCaptureEnabled)
1964 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1965
1966 i_setModified(IsModified_MachineData);
1967 mHWData.backup();
1968 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1969
1970 return S_OK;
1971}
1972
1973HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1974{
1975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1976
1977 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1978 return S_OK;
1979}
1980
1981HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1982{
1983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1984
1985 if ( Global::IsOnline(mData->mMachineState)
1986 && mHWData->mVideoCaptureEnabled)
1987 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1988
1989 i_setModified(IsModified_MachineData);
1990 mHWData.backup();
1991 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1992
1993 return S_OK;
1994}
1995
1996HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1997{
1998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1999
2000 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
2001
2002 return S_OK;
2003}
2004
2005HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2006{
2007 switch (aGraphicsControllerType)
2008 {
2009 case GraphicsControllerType_Null:
2010 case GraphicsControllerType_VBoxVGA:
2011#ifdef VBOX_WITH_VMSVGA
2012 case GraphicsControllerType_VMSVGA:
2013#endif
2014 break;
2015 default:
2016 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2017 }
2018
2019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2020
2021 HRESULT rc = i_checkStateDependency(MutableStateDep);
2022 if (FAILED(rc)) return rc;
2023
2024 i_setModified(IsModified_MachineData);
2025 mHWData.backup();
2026 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2027
2028 return S_OK;
2029}
2030
2031HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2032{
2033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2034
2035 *aVRAMSize = mHWData->mVRAMSize;
2036
2037 return S_OK;
2038}
2039
2040HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2041{
2042 /* check VRAM limits */
2043 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2044 return setError(E_INVALIDARG,
2045 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2046 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2047
2048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2049
2050 HRESULT rc = i_checkStateDependency(MutableStateDep);
2051 if (FAILED(rc)) return rc;
2052
2053 i_setModified(IsModified_MachineData);
2054 mHWData.backup();
2055 mHWData->mVRAMSize = aVRAMSize;
2056
2057 return S_OK;
2058}
2059
2060/** @todo this method should not be public */
2061HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2062{
2063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2064
2065 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2066
2067 return S_OK;
2068}
2069
2070/**
2071 * Set the memory balloon size.
2072 *
2073 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2074 * we have to make sure that we never call IGuest from here.
2075 */
2076HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2077{
2078 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2079#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2080 /* check limits */
2081 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2082 return setError(E_INVALIDARG,
2083 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2084 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2085
2086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2087
2088 i_setModified(IsModified_MachineData);
2089 mHWData.backup();
2090 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2091
2092 return S_OK;
2093#else
2094 NOREF(aMemoryBalloonSize);
2095 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2096#endif
2097}
2098
2099HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2100{
2101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2102
2103 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2104 return S_OK;
2105}
2106
2107HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2108{
2109#ifdef VBOX_WITH_PAGE_SHARING
2110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2111
2112 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2113 i_setModified(IsModified_MachineData);
2114 mHWData.backup();
2115 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2116 return S_OK;
2117#else
2118 NOREF(aPageFusionEnabled);
2119 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2120#endif
2121}
2122
2123HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2124{
2125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2126
2127 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2128
2129 return S_OK;
2130}
2131
2132HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2133{
2134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2135
2136 HRESULT rc = i_checkStateDependency(MutableStateDep);
2137 if (FAILED(rc)) return rc;
2138
2139 /** @todo check validity! */
2140
2141 i_setModified(IsModified_MachineData);
2142 mHWData.backup();
2143 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2144
2145 return S_OK;
2146}
2147
2148
2149HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2150{
2151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2152
2153 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2154
2155 return S_OK;
2156}
2157
2158HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2159{
2160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2161
2162 HRESULT rc = i_checkStateDependency(MutableStateDep);
2163 if (FAILED(rc)) return rc;
2164
2165 /** @todo check validity! */
2166 i_setModified(IsModified_MachineData);
2167 mHWData.backup();
2168 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2169
2170 return S_OK;
2171}
2172
2173HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2174{
2175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2176
2177 *aMonitorCount = mHWData->mMonitorCount;
2178
2179 return S_OK;
2180}
2181
2182HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2183{
2184 /* make sure monitor count is a sensible number */
2185 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2186 return setError(E_INVALIDARG,
2187 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2188 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2189
2190 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2191
2192 HRESULT rc = i_checkStateDependency(MutableStateDep);
2193 if (FAILED(rc)) return rc;
2194
2195 i_setModified(IsModified_MachineData);
2196 mHWData.backup();
2197 mHWData->mMonitorCount = aMonitorCount;
2198
2199 return S_OK;
2200}
2201
2202HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2203{
2204 /* mBIOSSettings is constant during life time, no need to lock */
2205 aBIOSSettings = mBIOSSettings;
2206
2207 return S_OK;
2208}
2209
2210HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2211{
2212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2213
2214 switch (aProperty)
2215 {
2216 case CPUPropertyType_PAE:
2217 *aValue = mHWData->mPAEEnabled;
2218 break;
2219
2220 case CPUPropertyType_LongMode:
2221 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2222 *aValue = TRUE;
2223 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2224 *aValue = FALSE;
2225#if HC_ARCH_BITS == 64
2226 else
2227 *aValue = TRUE;
2228#else
2229 else
2230 {
2231 *aValue = FALSE;
2232
2233 ComPtr<IGuestOSType> ptrGuestOSType;
2234 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2235 if (SUCCEEDED(hrc2))
2236 {
2237 BOOL fIs64Bit = FALSE;
2238 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2239 if (SUCCEEDED(hrc2) && fIs64Bit)
2240 {
2241 ComObjPtr<Host> ptrHost = mParent->i_host();
2242 alock.release();
2243
2244 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2245 if (FAILED(hrc2))
2246 *aValue = FALSE;
2247 }
2248 }
2249 }
2250#endif
2251 break;
2252
2253 case CPUPropertyType_TripleFaultReset:
2254 *aValue = mHWData->mTripleFaultReset;
2255 break;
2256
2257 case CPUPropertyType_APIC:
2258 *aValue = mHWData->mAPIC;
2259 break;
2260
2261 case CPUPropertyType_X2APIC:
2262 *aValue = mHWData->mX2APIC;
2263 break;
2264
2265 default:
2266 return E_INVALIDARG;
2267 }
2268 return S_OK;
2269}
2270
2271HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2272{
2273 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2274
2275 HRESULT rc = i_checkStateDependency(MutableStateDep);
2276 if (FAILED(rc)) return rc;
2277
2278 switch (aProperty)
2279 {
2280 case CPUPropertyType_PAE:
2281 i_setModified(IsModified_MachineData);
2282 mHWData.backup();
2283 mHWData->mPAEEnabled = !!aValue;
2284 break;
2285
2286 case CPUPropertyType_LongMode:
2287 i_setModified(IsModified_MachineData);
2288 mHWData.backup();
2289 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2290 break;
2291
2292 case CPUPropertyType_TripleFaultReset:
2293 i_setModified(IsModified_MachineData);
2294 mHWData.backup();
2295 mHWData->mTripleFaultReset = !!aValue;
2296 break;
2297
2298 case CPUPropertyType_APIC:
2299 if (mHWData->mX2APIC)
2300 aValue = TRUE;
2301 i_setModified(IsModified_MachineData);
2302 mHWData.backup();
2303 mHWData->mAPIC = !!aValue;
2304 break;
2305
2306 case CPUPropertyType_X2APIC:
2307 i_setModified(IsModified_MachineData);
2308 mHWData.backup();
2309 mHWData->mX2APIC = !!aValue;
2310 if (aValue)
2311 mHWData->mAPIC = !!aValue;
2312 break;
2313
2314 default:
2315 return E_INVALIDARG;
2316 }
2317 return S_OK;
2318}
2319
2320HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2321{
2322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2323
2324 switch(aId)
2325 {
2326 case 0x0:
2327 case 0x1:
2328 case 0x2:
2329 case 0x3:
2330 case 0x4:
2331 case 0x5:
2332 case 0x6:
2333 case 0x7:
2334 case 0x8:
2335 case 0x9:
2336 case 0xA:
2337 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2338 return E_INVALIDARG;
2339
2340 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2341 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2342 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2343 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2344 break;
2345
2346 case 0x80000000:
2347 case 0x80000001:
2348 case 0x80000002:
2349 case 0x80000003:
2350 case 0x80000004:
2351 case 0x80000005:
2352 case 0x80000006:
2353 case 0x80000007:
2354 case 0x80000008:
2355 case 0x80000009:
2356 case 0x8000000A:
2357 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2358 return E_INVALIDARG;
2359
2360 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2361 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2362 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2363 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2364 break;
2365
2366 default:
2367 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2368 }
2369 return S_OK;
2370}
2371
2372
2373HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2374{
2375 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2376
2377 HRESULT rc = i_checkStateDependency(MutableStateDep);
2378 if (FAILED(rc)) return rc;
2379
2380 switch(aId)
2381 {
2382 case 0x0:
2383 case 0x1:
2384 case 0x2:
2385 case 0x3:
2386 case 0x4:
2387 case 0x5:
2388 case 0x6:
2389 case 0x7:
2390 case 0x8:
2391 case 0x9:
2392 case 0xA:
2393 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2394 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2395 i_setModified(IsModified_MachineData);
2396 mHWData.backup();
2397 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2398 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2399 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2400 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2401 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2402 break;
2403
2404 case 0x80000000:
2405 case 0x80000001:
2406 case 0x80000002:
2407 case 0x80000003:
2408 case 0x80000004:
2409 case 0x80000005:
2410 case 0x80000006:
2411 case 0x80000007:
2412 case 0x80000008:
2413 case 0x80000009:
2414 case 0x8000000A:
2415 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2416 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2417 i_setModified(IsModified_MachineData);
2418 mHWData.backup();
2419 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2420 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2421 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2422 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2423 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2424 break;
2425
2426 default:
2427 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2428 }
2429 return S_OK;
2430}
2431
2432HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2433{
2434 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2435
2436 HRESULT rc = i_checkStateDependency(MutableStateDep);
2437 if (FAILED(rc)) return rc;
2438
2439 switch(aId)
2440 {
2441 case 0x0:
2442 case 0x1:
2443 case 0x2:
2444 case 0x3:
2445 case 0x4:
2446 case 0x5:
2447 case 0x6:
2448 case 0x7:
2449 case 0x8:
2450 case 0x9:
2451 case 0xA:
2452 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2453 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2454 i_setModified(IsModified_MachineData);
2455 mHWData.backup();
2456 /* Invalidate leaf. */
2457 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2458 break;
2459
2460 case 0x80000000:
2461 case 0x80000001:
2462 case 0x80000002:
2463 case 0x80000003:
2464 case 0x80000004:
2465 case 0x80000005:
2466 case 0x80000006:
2467 case 0x80000007:
2468 case 0x80000008:
2469 case 0x80000009:
2470 case 0x8000000A:
2471 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2472 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2473 i_setModified(IsModified_MachineData);
2474 mHWData.backup();
2475 /* Invalidate leaf. */
2476 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2477 break;
2478
2479 default:
2480 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2481 }
2482 return S_OK;
2483}
2484
2485HRESULT Machine::removeAllCPUIDLeaves()
2486{
2487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2488
2489 HRESULT rc = i_checkStateDependency(MutableStateDep);
2490 if (FAILED(rc)) return rc;
2491
2492 i_setModified(IsModified_MachineData);
2493 mHWData.backup();
2494
2495 /* Invalidate all standard leafs. */
2496 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2497 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2498
2499 /* Invalidate all extended leafs. */
2500 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2501 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2502
2503 return S_OK;
2504}
2505HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2506{
2507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2508
2509 switch(aProperty)
2510 {
2511 case HWVirtExPropertyType_Enabled:
2512 *aValue = mHWData->mHWVirtExEnabled;
2513 break;
2514
2515 case HWVirtExPropertyType_VPID:
2516 *aValue = mHWData->mHWVirtExVPIDEnabled;
2517 break;
2518
2519 case HWVirtExPropertyType_NestedPaging:
2520 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2521 break;
2522
2523 case HWVirtExPropertyType_UnrestrictedExecution:
2524 *aValue = mHWData->mHWVirtExUXEnabled;
2525 break;
2526
2527 case HWVirtExPropertyType_LargePages:
2528 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2529#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2530 *aValue = FALSE;
2531#endif
2532 break;
2533
2534 case HWVirtExPropertyType_Force:
2535 *aValue = mHWData->mHWVirtExForceEnabled;
2536 break;
2537
2538 default:
2539 return E_INVALIDARG;
2540 }
2541 return S_OK;
2542}
2543
2544HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2545{
2546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2547
2548 HRESULT rc = i_checkStateDependency(MutableStateDep);
2549 if (FAILED(rc)) return rc;
2550
2551 switch(aProperty)
2552 {
2553 case HWVirtExPropertyType_Enabled:
2554 i_setModified(IsModified_MachineData);
2555 mHWData.backup();
2556 mHWData->mHWVirtExEnabled = !!aValue;
2557 break;
2558
2559 case HWVirtExPropertyType_VPID:
2560 i_setModified(IsModified_MachineData);
2561 mHWData.backup();
2562 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2563 break;
2564
2565 case HWVirtExPropertyType_NestedPaging:
2566 i_setModified(IsModified_MachineData);
2567 mHWData.backup();
2568 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2569 break;
2570
2571 case HWVirtExPropertyType_UnrestrictedExecution:
2572 i_setModified(IsModified_MachineData);
2573 mHWData.backup();
2574 mHWData->mHWVirtExUXEnabled = !!aValue;
2575 break;
2576
2577 case HWVirtExPropertyType_LargePages:
2578 i_setModified(IsModified_MachineData);
2579 mHWData.backup();
2580 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2581 break;
2582
2583 case HWVirtExPropertyType_Force:
2584 i_setModified(IsModified_MachineData);
2585 mHWData.backup();
2586 mHWData->mHWVirtExForceEnabled = !!aValue;
2587 break;
2588
2589 default:
2590 return E_INVALIDARG;
2591 }
2592
2593 return S_OK;
2594}
2595
2596HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2597{
2598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2599
2600 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2601
2602 return S_OK;
2603}
2604
2605HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2606{
2607 /* @todo (r=dmik):
2608 * 1. Allow to change the name of the snapshot folder containing snapshots
2609 * 2. Rename the folder on disk instead of just changing the property
2610 * value (to be smart and not to leave garbage). Note that it cannot be
2611 * done here because the change may be rolled back. Thus, the right
2612 * place is #saveSettings().
2613 */
2614
2615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2616
2617 HRESULT rc = i_checkStateDependency(MutableStateDep);
2618 if (FAILED(rc)) return rc;
2619
2620 if (!mData->mCurrentSnapshot.isNull())
2621 return setError(E_FAIL,
2622 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2623
2624 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2625
2626 if (strSnapshotFolder.isEmpty())
2627 strSnapshotFolder = "Snapshots";
2628 int vrc = i_calculateFullPath(strSnapshotFolder,
2629 strSnapshotFolder);
2630 if (RT_FAILURE(vrc))
2631 return setError(E_FAIL,
2632 tr("Invalid snapshot folder '%s' (%Rrc)"),
2633 strSnapshotFolder.c_str(), vrc);
2634
2635 i_setModified(IsModified_MachineData);
2636 mUserData.backup();
2637
2638 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2639
2640 return S_OK;
2641}
2642
2643HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2644{
2645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2646
2647 aMediumAttachments.resize(mMediaData->mAttachments.size());
2648 size_t i = 0;
2649 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2650 it != mMediaData->mAttachments.end(); ++it, ++i)
2651 aMediumAttachments[i] = *it;
2652
2653 return S_OK;
2654}
2655
2656HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2657{
2658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2659
2660 Assert(!!mVRDEServer);
2661
2662 aVRDEServer = mVRDEServer;
2663
2664 return S_OK;
2665}
2666
2667HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2668{
2669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2670
2671 aAudioAdapter = mAudioAdapter;
2672
2673 return S_OK;
2674}
2675
2676HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2677{
2678#ifdef VBOX_WITH_VUSB
2679 clearError();
2680 MultiResult rc(S_OK);
2681
2682# ifdef VBOX_WITH_USB
2683 rc = mParent->i_host()->i_checkUSBProxyService();
2684 if (FAILED(rc)) return rc;
2685# endif
2686
2687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2688
2689 USBControllerList data = *mUSBControllers.data();
2690 aUSBControllers.resize(data.size());
2691 size_t i = 0;
2692 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2693 aUSBControllers[i] = *it;
2694
2695 return S_OK;
2696#else
2697 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2698 * extended error info to indicate that USB is simply not available
2699 * (w/o treating it as a failure), for example, as in OSE */
2700 NOREF(aUSBControllers);
2701 ReturnComNotImplemented();
2702#endif /* VBOX_WITH_VUSB */
2703}
2704
2705HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2706{
2707#ifdef VBOX_WITH_VUSB
2708 clearError();
2709 MultiResult rc(S_OK);
2710
2711# ifdef VBOX_WITH_USB
2712 rc = mParent->i_host()->i_checkUSBProxyService();
2713 if (FAILED(rc)) return rc;
2714# endif
2715
2716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2717
2718 aUSBDeviceFilters = mUSBDeviceFilters;
2719 return rc;
2720#else
2721 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2722 * extended error info to indicate that USB is simply not available
2723 * (w/o treating it as a failure), for example, as in OSE */
2724 NOREF(aUSBDeviceFilters);
2725 ReturnComNotImplemented();
2726#endif /* VBOX_WITH_VUSB */
2727}
2728
2729HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2730{
2731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2732
2733 aSettingsFilePath = mData->m_strConfigFileFull;
2734
2735 return S_OK;
2736}
2737
2738HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2739{
2740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2741
2742 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2743 if (FAILED(rc)) return rc;
2744
2745 if (!mData->pMachineConfigFile->fileExists())
2746 // this is a new machine, and no config file exists yet:
2747 *aSettingsModified = TRUE;
2748 else
2749 *aSettingsModified = (mData->flModifications != 0);
2750
2751 return S_OK;
2752}
2753
2754HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2755{
2756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2757
2758 *aSessionState = mData->mSession.mState;
2759
2760 return S_OK;
2761}
2762
2763HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2764{
2765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2766
2767 aSessionName = mData->mSession.mName;
2768
2769 return S_OK;
2770}
2771
2772HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2773{
2774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2775
2776 *aSessionPID = mData->mSession.mPID;
2777
2778 return S_OK;
2779}
2780
2781HRESULT Machine::getState(MachineState_T *aState)
2782{
2783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2784
2785 *aState = mData->mMachineState;
2786 Assert(mData->mMachineState != MachineState_Null);
2787
2788 return S_OK;
2789}
2790
2791HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2792{
2793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2794
2795 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2796
2797 return S_OK;
2798}
2799
2800HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2801{
2802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2803
2804 aStateFilePath = mSSData->strStateFilePath;
2805
2806 return S_OK;
2807}
2808
2809HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2810{
2811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2812
2813 i_getLogFolder(aLogFolder);
2814
2815 return S_OK;
2816}
2817
2818HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2819{
2820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 aCurrentSnapshot = mData->mCurrentSnapshot;
2823
2824 return S_OK;
2825}
2826
2827HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2828{
2829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2830
2831 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2832 ? 0
2833 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2834
2835 return S_OK;
2836}
2837
2838HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2839{
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 /* Note: for machines with no snapshots, we always return FALSE
2843 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2844 * reasons :) */
2845
2846 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2847 ? FALSE
2848 : mData->mCurrentStateModified;
2849
2850 return S_OK;
2851}
2852
2853HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2854{
2855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2856
2857 aSharedFolders.resize(mHWData->mSharedFolders.size());
2858 size_t i = 0;
2859 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2860 it != mHWData->mSharedFolders.end(); ++i, ++it)
2861 aSharedFolders[i] = *it;
2862
2863 return S_OK;
2864}
2865
2866HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2867{
2868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2869
2870 *aClipboardMode = mHWData->mClipboardMode;
2871
2872 return S_OK;
2873}
2874
2875HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2876{
2877 HRESULT rc = S_OK;
2878
2879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 alock.release();
2882 rc = i_onClipboardModeChange(aClipboardMode);
2883 alock.acquire();
2884 if (FAILED(rc)) return rc;
2885
2886 i_setModified(IsModified_MachineData);
2887 mHWData.backup();
2888 mHWData->mClipboardMode = aClipboardMode;
2889
2890 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2891 if (Global::IsOnline(mData->mMachineState))
2892 i_saveSettings(NULL);
2893
2894 return S_OK;
2895}
2896
2897HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2898{
2899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2900
2901 *aDnDMode = mHWData->mDnDMode;
2902
2903 return S_OK;
2904}
2905
2906HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2907{
2908 HRESULT rc = S_OK;
2909
2910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2911
2912 alock.release();
2913 rc = i_onDnDModeChange(aDnDMode);
2914
2915 alock.acquire();
2916 if (FAILED(rc)) return rc;
2917
2918 i_setModified(IsModified_MachineData);
2919 mHWData.backup();
2920 mHWData->mDnDMode = aDnDMode;
2921
2922 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2923 if (Global::IsOnline(mData->mMachineState))
2924 i_saveSettings(NULL);
2925
2926 return S_OK;
2927}
2928
2929HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2930{
2931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2932 StorageControllerList data = *mStorageControllers.data();
2933 size_t i = 0;
2934 aStorageControllers.resize(data.size());
2935 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2936 aStorageControllers[i] = *it;
2937 return S_OK;
2938}
2939
2940HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2941{
2942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2943
2944 *aEnabled = mUserData->s.fTeleporterEnabled;
2945
2946 return S_OK;
2947}
2948
2949HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2950{
2951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2952
2953 /* Only allow it to be set to true when PoweredOff or Aborted.
2954 (Clearing it is always permitted.) */
2955 if ( aTeleporterEnabled
2956 && mData->mRegistered
2957 && ( !i_isSessionMachine()
2958 || ( mData->mMachineState != MachineState_PoweredOff
2959 && mData->mMachineState != MachineState_Teleported
2960 && mData->mMachineState != MachineState_Aborted
2961 )
2962 )
2963 )
2964 return setError(VBOX_E_INVALID_VM_STATE,
2965 tr("The machine is not powered off (state is %s)"),
2966 Global::stringifyMachineState(mData->mMachineState));
2967
2968 i_setModified(IsModified_MachineData);
2969 mUserData.backup();
2970 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2971
2972 return S_OK;
2973}
2974
2975HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2976{
2977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2978
2979 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2980
2981 return S_OK;
2982}
2983
2984HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2985{
2986 if (aTeleporterPort >= _64K)
2987 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2988
2989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2990
2991 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2992 if (FAILED(rc)) return rc;
2993
2994 i_setModified(IsModified_MachineData);
2995 mUserData.backup();
2996 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2997
2998 return S_OK;
2999}
3000
3001HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3002{
3003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3004
3005 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3006
3007 return S_OK;
3008}
3009
3010HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3011{
3012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3013
3014 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3015 if (FAILED(rc)) return rc;
3016
3017 i_setModified(IsModified_MachineData);
3018 mUserData.backup();
3019 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3020
3021 return S_OK;
3022}
3023
3024HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3025{
3026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3027 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3028
3029 return S_OK;
3030}
3031
3032HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3033{
3034 /*
3035 * Hash the password first.
3036 */
3037 com::Utf8Str aT = aTeleporterPassword;
3038
3039 if (!aT.isEmpty())
3040 {
3041 if (VBoxIsPasswordHashed(&aT))
3042 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3043 VBoxHashPassword(&aT);
3044 }
3045
3046 /*
3047 * Do the update.
3048 */
3049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3050 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3051 if (SUCCEEDED(hrc))
3052 {
3053 i_setModified(IsModified_MachineData);
3054 mUserData.backup();
3055 mUserData->s.strTeleporterPassword = aT;
3056 }
3057
3058 return hrc;
3059}
3060
3061HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3062{
3063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3064
3065 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3066 return S_OK;
3067}
3068
3069HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3070{
3071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3072
3073 /* @todo deal with running state change. */
3074 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3075 if (FAILED(rc)) return rc;
3076
3077 i_setModified(IsModified_MachineData);
3078 mUserData.backup();
3079 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3080 return S_OK;
3081}
3082
3083HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3084{
3085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3086
3087 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3088 return S_OK;
3089}
3090
3091HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3092{
3093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3094
3095 /* @todo deal with running state change. */
3096 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3097 if (FAILED(rc)) return rc;
3098
3099 i_setModified(IsModified_MachineData);
3100 mUserData.backup();
3101 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3102 return S_OK;
3103}
3104
3105HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3106{
3107 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3108
3109 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3110 return S_OK;
3111}
3112
3113HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3114{
3115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3116
3117 /* @todo deal with running state change. */
3118 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3119 if (FAILED(rc)) return rc;
3120
3121 i_setModified(IsModified_MachineData);
3122 mUserData.backup();
3123 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3124 return S_OK;
3125}
3126
3127HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3128{
3129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3130
3131 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3132
3133 return S_OK;
3134}
3135
3136HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3137{
3138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3139
3140 /* @todo deal with running state change. */
3141 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3142 if (FAILED(rc)) return rc;
3143
3144 i_setModified(IsModified_MachineData);
3145 mUserData.backup();
3146 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3147
3148 return S_OK;
3149}
3150
3151HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3152{
3153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3154
3155 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3156 return S_OK;
3157}
3158
3159HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3160{
3161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3162
3163 /* @todo deal with running state change. */
3164 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3165 if (FAILED(rc)) return rc;
3166
3167 i_setModified(IsModified_MachineData);
3168 mUserData.backup();
3169 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3170 return S_OK;
3171}
3172
3173HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3174{
3175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3176
3177 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3178
3179 return S_OK;
3180}
3181
3182HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3183{
3184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3185
3186 /* Only allow it to be set to true when PoweredOff or Aborted.
3187 (Clearing it is always permitted.) */
3188 if ( aRTCUseUTC
3189 && mData->mRegistered
3190 && ( !i_isSessionMachine()
3191 || ( mData->mMachineState != MachineState_PoweredOff
3192 && mData->mMachineState != MachineState_Teleported
3193 && mData->mMachineState != MachineState_Aborted
3194 )
3195 )
3196 )
3197 return setError(VBOX_E_INVALID_VM_STATE,
3198 tr("The machine is not powered off (state is %s)"),
3199 Global::stringifyMachineState(mData->mMachineState));
3200
3201 i_setModified(IsModified_MachineData);
3202 mUserData.backup();
3203 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3204
3205 return S_OK;
3206}
3207
3208HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3209{
3210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3211
3212 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3213
3214 return S_OK;
3215}
3216
3217HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3218{
3219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3220
3221 HRESULT rc = i_checkStateDependency(MutableStateDep);
3222 if (FAILED(rc)) return rc;
3223
3224 i_setModified(IsModified_MachineData);
3225 mHWData.backup();
3226 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3227
3228 return S_OK;
3229}
3230
3231HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3232{
3233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3234
3235 *aIOCacheSize = mHWData->mIOCacheSize;
3236
3237 return S_OK;
3238}
3239
3240HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3241{
3242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3243
3244 HRESULT rc = i_checkStateDependency(MutableStateDep);
3245 if (FAILED(rc)) return rc;
3246
3247 i_setModified(IsModified_MachineData);
3248 mHWData.backup();
3249 mHWData->mIOCacheSize = aIOCacheSize;
3250
3251 return S_OK;
3252}
3253
3254
3255/**
3256 * @note Locks objects!
3257 */
3258HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3259 LockType_T aLockType)
3260{
3261 /* check the session state */
3262 SessionState_T state;
3263 HRESULT rc = aSession->COMGETTER(State)(&state);
3264 if (FAILED(rc)) return rc;
3265
3266 if (state != SessionState_Unlocked)
3267 return setError(VBOX_E_INVALID_OBJECT_STATE,
3268 tr("The given session is busy"));
3269
3270 // get the client's IInternalSessionControl interface
3271 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3272 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3273 E_INVALIDARG);
3274
3275 // session name (only used in some code paths)
3276 Utf8Str strSessionName;
3277
3278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3279
3280 if (!mData->mRegistered)
3281 return setError(E_UNEXPECTED,
3282 tr("The machine '%s' is not registered"),
3283 mUserData->s.strName.c_str());
3284
3285 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3286
3287 SessionState_T oldState = mData->mSession.mState;
3288 /* Hack: in case the session is closing and there is a progress object
3289 * which allows waiting for the session to be closed, take the opportunity
3290 * and do a limited wait (max. 1 second). This helps a lot when the system
3291 * is busy and thus session closing can take a little while. */
3292 if ( mData->mSession.mState == SessionState_Unlocking
3293 && mData->mSession.mProgress)
3294 {
3295 alock.release();
3296 mData->mSession.mProgress->WaitForCompletion(1000);
3297 alock.acquire();
3298 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3299 }
3300
3301 // try again now
3302 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3303 // (i.e. session machine exists)
3304 && (aLockType == LockType_Shared) // caller wants a shared link to the
3305 // existing session that holds the write lock:
3306 )
3307 {
3308 // OK, share the session... we are now dealing with three processes:
3309 // 1) VBoxSVC (where this code runs);
3310 // 2) process C: the caller's client process (who wants a shared session);
3311 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3312
3313 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3314 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3315 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3316 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3317 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3318
3319 /*
3320 * Release the lock before calling the client process. It's safe here
3321 * since the only thing to do after we get the lock again is to add
3322 * the remote control to the list (which doesn't directly influence
3323 * anything).
3324 */
3325 alock.release();
3326
3327 // get the console of the session holding the write lock (this is a remote call)
3328 ComPtr<IConsole> pConsoleW;
3329 if (mData->mSession.mLockType == LockType_VM)
3330 {
3331 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3332 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3333 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3334 if (FAILED(rc))
3335 // the failure may occur w/o any error info (from RPC), so provide one
3336 return setError(VBOX_E_VM_ERROR,
3337 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3338 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3339 }
3340
3341 // share the session machine and W's console with the caller's session
3342 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3343 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3344 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3345
3346 if (FAILED(rc))
3347 // the failure may occur w/o any error info (from RPC), so provide one
3348 return setError(VBOX_E_VM_ERROR,
3349 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3350 alock.acquire();
3351
3352 // need to revalidate the state after acquiring the lock again
3353 if (mData->mSession.mState != SessionState_Locked)
3354 {
3355 pSessionControl->Uninitialize();
3356 return setError(VBOX_E_INVALID_SESSION_STATE,
3357 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3358 mUserData->s.strName.c_str());
3359 }
3360
3361 // add the caller's session to the list
3362 mData->mSession.mRemoteControls.push_back(pSessionControl);
3363 }
3364 else if ( mData->mSession.mState == SessionState_Locked
3365 || mData->mSession.mState == SessionState_Unlocking
3366 )
3367 {
3368 // sharing not permitted, or machine still unlocking:
3369 return setError(VBOX_E_INVALID_OBJECT_STATE,
3370 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3371 mUserData->s.strName.c_str());
3372 }
3373 else
3374 {
3375 // machine is not locked: then write-lock the machine (create the session machine)
3376
3377 // must not be busy
3378 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3379
3380 // get the caller's session PID
3381 RTPROCESS pid = NIL_RTPROCESS;
3382 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3383 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3384 Assert(pid != NIL_RTPROCESS);
3385
3386 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3387
3388 if (fLaunchingVMProcess)
3389 {
3390 if (mData->mSession.mPID == NIL_RTPROCESS)
3391 {
3392 // two or more clients racing for a lock, the one which set the
3393 // session state to Spawning will win, the others will get an
3394 // error as we can't decide here if waiting a little would help
3395 // (only for shared locks this would avoid an error)
3396 return setError(VBOX_E_INVALID_OBJECT_STATE,
3397 tr("The machine '%s' already has a lock request pending"),
3398 mUserData->s.strName.c_str());
3399 }
3400
3401 // this machine is awaiting for a spawning session to be opened:
3402 // then the calling process must be the one that got started by
3403 // LaunchVMProcess()
3404
3405 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3406 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3407
3408#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3409 /* Hardened windows builds spawns three processes when a VM is
3410 launched, the 3rd one is the one that will end up here. */
3411 RTPROCESS ppid;
3412 int rc = RTProcQueryParent(pid, &ppid);
3413 if (RT_SUCCESS(rc))
3414 rc = RTProcQueryParent(ppid, &ppid);
3415 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3416 || rc == VERR_ACCESS_DENIED)
3417 {
3418 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3419 mData->mSession.mPID = pid;
3420 }
3421#endif
3422
3423 if (mData->mSession.mPID != pid)
3424 return setError(E_ACCESSDENIED,
3425 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3426 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3427 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3428 }
3429
3430 // create the mutable SessionMachine from the current machine
3431 ComObjPtr<SessionMachine> sessionMachine;
3432 sessionMachine.createObject();
3433 rc = sessionMachine->init(this);
3434 AssertComRC(rc);
3435
3436 /* NOTE: doing return from this function after this point but
3437 * before the end is forbidden since it may call SessionMachine::uninit()
3438 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3439 * lock while still holding the Machine lock in alock so that a deadlock
3440 * is possible due to the wrong lock order. */
3441
3442 if (SUCCEEDED(rc))
3443 {
3444 /*
3445 * Set the session state to Spawning to protect against subsequent
3446 * attempts to open a session and to unregister the machine after
3447 * we release the lock.
3448 */
3449 SessionState_T origState = mData->mSession.mState;
3450 mData->mSession.mState = SessionState_Spawning;
3451
3452#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3453 /* Get the client token ID to be passed to the client process */
3454 Utf8Str strTokenId;
3455 sessionMachine->i_getTokenId(strTokenId);
3456 Assert(!strTokenId.isEmpty());
3457#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3458 /* Get the client token to be passed to the client process */
3459 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3460 /* The token is now "owned" by pToken, fix refcount */
3461 if (!pToken.isNull())
3462 pToken->Release();
3463#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3464
3465 /*
3466 * Release the lock before calling the client process -- it will call
3467 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3468 * because the state is Spawning, so that LaunchVMProcess() and
3469 * LockMachine() calls will fail. This method, called before we
3470 * acquire the lock again, will fail because of the wrong PID.
3471 *
3472 * Note that mData->mSession.mRemoteControls accessed outside
3473 * the lock may not be modified when state is Spawning, so it's safe.
3474 */
3475 alock.release();
3476
3477 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3478#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3479 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3480#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3481 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3482 /* Now the token is owned by the client process. */
3483 pToken.setNull();
3484#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3485 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3486
3487 /* The failure may occur w/o any error info (from RPC), so provide one */
3488 if (FAILED(rc))
3489 setError(VBOX_E_VM_ERROR,
3490 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3491
3492 // get session name, either to remember or to compare against
3493 // the already known session name.
3494 {
3495 Bstr bstrSessionName;
3496 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3497 if (SUCCEEDED(rc2))
3498 strSessionName = bstrSessionName;
3499 }
3500
3501 if ( SUCCEEDED(rc)
3502 && fLaunchingVMProcess
3503 )
3504 {
3505 /* complete the remote session initialization */
3506
3507 /* get the console from the direct session */
3508 ComPtr<IConsole> console;
3509 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3510 ComAssertComRC(rc);
3511
3512 if (SUCCEEDED(rc) && !console)
3513 {
3514 ComAssert(!!console);
3515 rc = E_FAIL;
3516 }
3517
3518 /* assign machine & console to the remote session */
3519 if (SUCCEEDED(rc))
3520 {
3521 /*
3522 * after LaunchVMProcess(), the first and the only
3523 * entry in remoteControls is that remote session
3524 */
3525 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3526 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3527 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3528
3529 /* The failure may occur w/o any error info (from RPC), so provide one */
3530 if (FAILED(rc))
3531 setError(VBOX_E_VM_ERROR,
3532 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3533 }
3534
3535 if (FAILED(rc))
3536 pSessionControl->Uninitialize();
3537 }
3538
3539 /* acquire the lock again */
3540 alock.acquire();
3541
3542 /* Restore the session state */
3543 mData->mSession.mState = origState;
3544 }
3545
3546 // finalize spawning anyway (this is why we don't return on errors above)
3547 if (fLaunchingVMProcess)
3548 {
3549 Assert(mData->mSession.mName == strSessionName);
3550 /* Note that the progress object is finalized later */
3551 /** @todo Consider checking mData->mSession.mProgress for cancellation
3552 * around here. */
3553
3554 /* We don't reset mSession.mPID here because it is necessary for
3555 * SessionMachine::uninit() to reap the child process later. */
3556
3557 if (FAILED(rc))
3558 {
3559 /* Close the remote session, remove the remote control from the list
3560 * and reset session state to Closed (@note keep the code in sync
3561 * with the relevant part in checkForSpawnFailure()). */
3562
3563 Assert(mData->mSession.mRemoteControls.size() == 1);
3564 if (mData->mSession.mRemoteControls.size() == 1)
3565 {
3566 ErrorInfoKeeper eik;
3567 mData->mSession.mRemoteControls.front()->Uninitialize();
3568 }
3569
3570 mData->mSession.mRemoteControls.clear();
3571 mData->mSession.mState = SessionState_Unlocked;
3572 }
3573 }
3574 else
3575 {
3576 /* memorize PID of the directly opened session */
3577 if (SUCCEEDED(rc))
3578 mData->mSession.mPID = pid;
3579 }
3580
3581 if (SUCCEEDED(rc))
3582 {
3583 mData->mSession.mLockType = aLockType;
3584 /* memorize the direct session control and cache IUnknown for it */
3585 mData->mSession.mDirectControl = pSessionControl;
3586 mData->mSession.mState = SessionState_Locked;
3587 if (!fLaunchingVMProcess)
3588 mData->mSession.mName = strSessionName;
3589 /* associate the SessionMachine with this Machine */
3590 mData->mSession.mMachine = sessionMachine;
3591
3592 /* request an IUnknown pointer early from the remote party for later
3593 * identity checks (it will be internally cached within mDirectControl
3594 * at least on XPCOM) */
3595 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3596 NOREF(unk);
3597 }
3598
3599 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3600 * would break the lock order */
3601 alock.release();
3602
3603 /* uninitialize the created session machine on failure */
3604 if (FAILED(rc))
3605 sessionMachine->uninit();
3606 }
3607
3608 if (SUCCEEDED(rc))
3609 {
3610 /*
3611 * tell the client watcher thread to update the set of
3612 * machines that have open sessions
3613 */
3614 mParent->i_updateClientWatcher();
3615
3616 if (oldState != SessionState_Locked)
3617 /* fire an event */
3618 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3619 }
3620
3621 return rc;
3622}
3623
3624/**
3625 * @note Locks objects!
3626 */
3627HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3628 const com::Utf8Str &aName,
3629 const com::Utf8Str &aEnvironment,
3630 ComPtr<IProgress> &aProgress)
3631{
3632 Utf8Str strFrontend(aName);
3633 /* "emergencystop" doesn't need the session, so skip the checks/interface
3634 * retrieval. This code doesn't quite fit in here, but introducing a
3635 * special API method would be even more effort, and would require explicit
3636 * support by every API client. It's better to hide the feature a bit. */
3637 if (strFrontend != "emergencystop")
3638 CheckComArgNotNull(aSession);
3639
3640 HRESULT rc = S_OK;
3641 if (strFrontend.isEmpty())
3642 {
3643 Bstr bstrFrontend;
3644 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3645 if (FAILED(rc))
3646 return rc;
3647 strFrontend = bstrFrontend;
3648 if (strFrontend.isEmpty())
3649 {
3650 ComPtr<ISystemProperties> systemProperties;
3651 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3652 if (FAILED(rc))
3653 return rc;
3654 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3655 if (FAILED(rc))
3656 return rc;
3657 strFrontend = bstrFrontend;
3658 }
3659 /* paranoia - emergencystop is not a valid default */
3660 if (strFrontend == "emergencystop")
3661 strFrontend = Utf8Str::Empty;
3662 }
3663 /* default frontend: Qt GUI */
3664 if (strFrontend.isEmpty())
3665 strFrontend = "GUI/Qt";
3666
3667 if (strFrontend != "emergencystop")
3668 {
3669 /* check the session state */
3670 SessionState_T state;
3671 rc = aSession->COMGETTER(State)(&state);
3672 if (FAILED(rc))
3673 return rc;
3674
3675 if (state != SessionState_Unlocked)
3676 return setError(VBOX_E_INVALID_OBJECT_STATE,
3677 tr("The given session is busy"));
3678
3679 /* get the IInternalSessionControl interface */
3680 ComPtr<IInternalSessionControl> control(aSession);
3681 ComAssertMsgRet(!control.isNull(),
3682 ("No IInternalSessionControl interface"),
3683 E_INVALIDARG);
3684
3685 /* get the teleporter enable state for the progress object init. */
3686 BOOL fTeleporterEnabled;
3687 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3688 if (FAILED(rc))
3689 return rc;
3690
3691 /* create a progress object */
3692 ComObjPtr<ProgressProxy> progress;
3693 progress.createObject();
3694 rc = progress->init(mParent,
3695 static_cast<IMachine*>(this),
3696 Bstr(tr("Starting VM")).raw(),
3697 TRUE /* aCancelable */,
3698 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3699 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3700 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3701 2 /* uFirstOperationWeight */,
3702 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3703
3704 if (SUCCEEDED(rc))
3705 {
3706 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3707 if (SUCCEEDED(rc))
3708 {
3709 aProgress = progress;
3710
3711 /* signal the client watcher thread */
3712 mParent->i_updateClientWatcher();
3713
3714 /* fire an event */
3715 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3716 }
3717 }
3718 }
3719 else
3720 {
3721 /* no progress object - either instant success or failure */
3722 aProgress = NULL;
3723
3724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3725
3726 if (mData->mSession.mState != SessionState_Locked)
3727 return setError(VBOX_E_INVALID_OBJECT_STATE,
3728 tr("The machine '%s' is not locked by a session"),
3729 mUserData->s.strName.c_str());
3730
3731 /* must have a VM process associated - do not kill normal API clients
3732 * with an open session */
3733 if (!Global::IsOnline(mData->mMachineState))
3734 return setError(VBOX_E_INVALID_OBJECT_STATE,
3735 tr("The machine '%s' does not have a VM process"),
3736 mUserData->s.strName.c_str());
3737
3738 /* forcibly terminate the VM process */
3739 if (mData->mSession.mPID != NIL_RTPROCESS)
3740 RTProcTerminate(mData->mSession.mPID);
3741
3742 /* signal the client watcher thread, as most likely the client has
3743 * been terminated */
3744 mParent->i_updateClientWatcher();
3745 }
3746
3747 return rc;
3748}
3749
3750HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3751{
3752 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3753 return setError(E_INVALIDARG,
3754 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3755 aPosition, SchemaDefs::MaxBootPosition);
3756
3757 if (aDevice == DeviceType_USB)
3758 return setError(E_NOTIMPL,
3759 tr("Booting from USB device is currently not supported"));
3760
3761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3762
3763 HRESULT rc = i_checkStateDependency(MutableStateDep);
3764 if (FAILED(rc)) return rc;
3765
3766 i_setModified(IsModified_MachineData);
3767 mHWData.backup();
3768 mHWData->mBootOrder[aPosition - 1] = aDevice;
3769
3770 return S_OK;
3771}
3772
3773HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3774{
3775 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3776 return setError(E_INVALIDARG,
3777 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3778 aPosition, SchemaDefs::MaxBootPosition);
3779
3780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3781
3782 *aDevice = mHWData->mBootOrder[aPosition - 1];
3783
3784 return S_OK;
3785}
3786
3787HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3788 LONG aControllerPort,
3789 LONG aDevice,
3790 DeviceType_T aType,
3791 const ComPtr<IMedium> &aMedium)
3792{
3793 IMedium *aM = aMedium;
3794 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3795 aName.c_str(), aControllerPort, aDevice, aType, aM));
3796
3797 // request the host lock first, since might be calling Host methods for getting host drives;
3798 // next, protect the media tree all the while we're in here, as well as our member variables
3799 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3800 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3801
3802 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3803 if (FAILED(rc)) return rc;
3804
3805 /// @todo NEWMEDIA implicit machine registration
3806 if (!mData->mRegistered)
3807 return setError(VBOX_E_INVALID_OBJECT_STATE,
3808 tr("Cannot attach storage devices to an unregistered machine"));
3809
3810 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3811
3812 /* Check for an existing controller. */
3813 ComObjPtr<StorageController> ctl;
3814 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3815 if (FAILED(rc)) return rc;
3816
3817 StorageControllerType_T ctrlType;
3818 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3819 if (FAILED(rc))
3820 return setError(E_FAIL,
3821 tr("Could not get type of controller '%s'"),
3822 aName.c_str());
3823
3824 bool fSilent = false;
3825 Utf8Str strReconfig;
3826
3827 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3828 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3829 if ( mData->mMachineState == MachineState_Paused
3830 && strReconfig == "1")
3831 fSilent = true;
3832
3833 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3834 bool fHotplug = false;
3835 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3836 fHotplug = true;
3837
3838 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3839 return setError(VBOX_E_INVALID_VM_STATE,
3840 tr("Controller '%s' does not support hotplugging"),
3841 aName.c_str());
3842
3843 // check that the port and device are not out of range
3844 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3845 if (FAILED(rc)) return rc;
3846
3847 /* check if the device slot is already busy */
3848 MediumAttachment *pAttachTemp;
3849 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3850 Bstr(aName).raw(),
3851 aControllerPort,
3852 aDevice)))
3853 {
3854 Medium *pMedium = pAttachTemp->i_getMedium();
3855 if (pMedium)
3856 {
3857 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3858 return setError(VBOX_E_OBJECT_IN_USE,
3859 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3860 pMedium->i_getLocationFull().c_str(),
3861 aControllerPort,
3862 aDevice,
3863 aName.c_str());
3864 }
3865 else
3866 return setError(VBOX_E_OBJECT_IN_USE,
3867 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3868 aControllerPort, aDevice, aName.c_str());
3869 }
3870
3871 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3872 if (aMedium && medium.isNull())
3873 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3874
3875 AutoCaller mediumCaller(medium);
3876 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3877
3878 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3879
3880 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3881 && !medium.isNull()
3882 )
3883 return setError(VBOX_E_OBJECT_IN_USE,
3884 tr("Medium '%s' is already attached to this virtual machine"),
3885 medium->i_getLocationFull().c_str());
3886
3887 if (!medium.isNull())
3888 {
3889 MediumType_T mtype = medium->i_getType();
3890 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3891 // For DVDs it's not written to the config file, so needs no global config
3892 // version bump. For floppies it's a new attribute "type", which is ignored
3893 // by older VirtualBox version, so needs no global config version bump either.
3894 // For hard disks this type is not accepted.
3895 if (mtype == MediumType_MultiAttach)
3896 {
3897 // This type is new with VirtualBox 4.0 and therefore requires settings
3898 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3899 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3900 // two reasons: The medium type is a property of the media registry tree, which
3901 // can reside in the global config file (for pre-4.0 media); we would therefore
3902 // possibly need to bump the global config version. We don't want to do that though
3903 // because that might make downgrading to pre-4.0 impossible.
3904 // As a result, we can only use these two new types if the medium is NOT in the
3905 // global registry:
3906 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3907 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3908 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3909 )
3910 return setError(VBOX_E_INVALID_OBJECT_STATE,
3911 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3912 "to machines that were created with VirtualBox 4.0 or later"),
3913 medium->i_getLocationFull().c_str());
3914 }
3915 }
3916
3917 bool fIndirect = false;
3918 if (!medium.isNull())
3919 fIndirect = medium->i_isReadOnly();
3920 bool associate = true;
3921
3922 do
3923 {
3924 if ( aType == DeviceType_HardDisk
3925 && mMediaData.isBackedUp())
3926 {
3927 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3928
3929 /* check if the medium was attached to the VM before we started
3930 * changing attachments in which case the attachment just needs to
3931 * be restored */
3932 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3933 {
3934 AssertReturn(!fIndirect, E_FAIL);
3935
3936 /* see if it's the same bus/channel/device */
3937 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3938 {
3939 /* the simplest case: restore the whole attachment
3940 * and return, nothing else to do */
3941 mMediaData->mAttachments.push_back(pAttachTemp);
3942
3943 /* Reattach the medium to the VM. */
3944 if (fHotplug || fSilent)
3945 {
3946 mediumLock.release();
3947 treeLock.release();
3948 alock.release();
3949
3950 MediumLockList *pMediumLockList(new MediumLockList());
3951
3952 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3953 medium /* pToLockWrite */,
3954 false /* fMediumLockWriteAll */,
3955 NULL,
3956 *pMediumLockList);
3957 alock.acquire();
3958 if (FAILED(rc))
3959 delete pMediumLockList;
3960 else
3961 {
3962 mData->mSession.mLockedMedia.Unlock();
3963 alock.release();
3964 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3965 mData->mSession.mLockedMedia.Lock();
3966 alock.acquire();
3967 }
3968 alock.release();
3969
3970 if (SUCCEEDED(rc))
3971 {
3972 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3973 /* Remove lock list in case of error. */
3974 if (FAILED(rc))
3975 {
3976 mData->mSession.mLockedMedia.Unlock();
3977 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3978 mData->mSession.mLockedMedia.Lock();
3979 }
3980 }
3981 }
3982
3983 return S_OK;
3984 }
3985
3986 /* bus/channel/device differ; we need a new attachment object,
3987 * but don't try to associate it again */
3988 associate = false;
3989 break;
3990 }
3991 }
3992
3993 /* go further only if the attachment is to be indirect */
3994 if (!fIndirect)
3995 break;
3996
3997 /* perform the so called smart attachment logic for indirect
3998 * attachments. Note that smart attachment is only applicable to base
3999 * hard disks. */
4000
4001 if (medium->i_getParent().isNull())
4002 {
4003 /* first, investigate the backup copy of the current hard disk
4004 * attachments to make it possible to re-attach existing diffs to
4005 * another device slot w/o losing their contents */
4006 if (mMediaData.isBackedUp())
4007 {
4008 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4009
4010 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4011 uint32_t foundLevel = 0;
4012
4013 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
4014 {
4015 uint32_t level = 0;
4016 MediumAttachment *pAttach = *it;
4017 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4018 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4019 if (pMedium.isNull())
4020 continue;
4021
4022 if (pMedium->i_getBase(&level) == medium)
4023 {
4024 /* skip the hard disk if its currently attached (we
4025 * cannot attach the same hard disk twice) */
4026 if (i_findAttachment(mMediaData->mAttachments,
4027 pMedium))
4028 continue;
4029
4030 /* matched device, channel and bus (i.e. attached to the
4031 * same place) will win and immediately stop the search;
4032 * otherwise the attachment that has the youngest
4033 * descendant of medium will be used
4034 */
4035 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
4036 {
4037 /* the simplest case: restore the whole attachment
4038 * and return, nothing else to do */
4039 mMediaData->mAttachments.push_back(*it);
4040
4041 /* Reattach the medium to the VM. */
4042 if (fHotplug || fSilent)
4043 {
4044 mediumLock.release();
4045 treeLock.release();
4046 alock.release();
4047
4048 MediumLockList *pMediumLockList(new MediumLockList());
4049
4050 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4051 medium /* pToLockWrite */,
4052 false /* fMediumLockWriteAll */,
4053 NULL,
4054 *pMediumLockList);
4055 alock.acquire();
4056 if (FAILED(rc))
4057 delete pMediumLockList;
4058 else
4059 {
4060 mData->mSession.mLockedMedia.Unlock();
4061 alock.release();
4062 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4063 mData->mSession.mLockedMedia.Lock();
4064 alock.acquire();
4065 }
4066 alock.release();
4067
4068 if (SUCCEEDED(rc))
4069 {
4070 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4071 /* Remove lock list in case of error. */
4072 if (FAILED(rc))
4073 {
4074 mData->mSession.mLockedMedia.Unlock();
4075 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4076 mData->mSession.mLockedMedia.Lock();
4077 }
4078 }
4079 }
4080
4081 return S_OK;
4082 }
4083 else if ( foundIt == oldAtts.end()
4084 || level > foundLevel /* prefer younger */
4085 )
4086 {
4087 foundIt = it;
4088 foundLevel = level;
4089 }
4090 }
4091 }
4092
4093 if (foundIt != oldAtts.end())
4094 {
4095 /* use the previously attached hard disk */
4096 medium = (*foundIt)->i_getMedium();
4097 mediumCaller.attach(medium);
4098 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4099 mediumLock.attach(medium);
4100 /* not implicit, doesn't require association with this VM */
4101 fIndirect = false;
4102 associate = false;
4103 /* go right to the MediumAttachment creation */
4104 break;
4105 }
4106 }
4107
4108 /* must give up the medium lock and medium tree lock as below we
4109 * go over snapshots, which needs a lock with higher lock order. */
4110 mediumLock.release();
4111 treeLock.release();
4112
4113 /* then, search through snapshots for the best diff in the given
4114 * hard disk's chain to base the new diff on */
4115
4116 ComObjPtr<Medium> base;
4117 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4118 while (snap)
4119 {
4120 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4121
4122 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4123
4124 MediumAttachment *pAttachFound = NULL;
4125 uint32_t foundLevel = 0;
4126
4127 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4128 {
4129 MediumAttachment *pAttach = *it;
4130 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4131 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4132 if (pMedium.isNull())
4133 continue;
4134
4135 uint32_t level = 0;
4136 if (pMedium->i_getBase(&level) == medium)
4137 {
4138 /* matched device, channel and bus (i.e. attached to the
4139 * same place) will win and immediately stop the search;
4140 * otherwise the attachment that has the youngest
4141 * descendant of medium will be used
4142 */
4143 if ( pAttach->i_getDevice() == aDevice
4144 && pAttach->i_getPort() == aControllerPort
4145 && pAttach->i_getControllerName() == aName
4146 )
4147 {
4148 pAttachFound = pAttach;
4149 break;
4150 }
4151 else if ( !pAttachFound
4152 || level > foundLevel /* prefer younger */
4153 )
4154 {
4155 pAttachFound = pAttach;
4156 foundLevel = level;
4157 }
4158 }
4159 }
4160
4161 if (pAttachFound)
4162 {
4163 base = pAttachFound->i_getMedium();
4164 break;
4165 }
4166
4167 snap = snap->i_getParent();
4168 }
4169
4170 /* re-lock medium tree and the medium, as we need it below */
4171 treeLock.acquire();
4172 mediumLock.acquire();
4173
4174 /* found a suitable diff, use it as a base */
4175 if (!base.isNull())
4176 {
4177 medium = base;
4178 mediumCaller.attach(medium);
4179 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4180 mediumLock.attach(medium);
4181 }
4182 }
4183
4184 Utf8Str strFullSnapshotFolder;
4185 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4186
4187 ComObjPtr<Medium> diff;
4188 diff.createObject();
4189 // store this diff in the same registry as the parent
4190 Guid uuidRegistryParent;
4191 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4192 {
4193 // parent image has no registry: this can happen if we're attaching a new immutable
4194 // image that has not yet been attached (medium then points to the base and we're
4195 // creating the diff image for the immutable, and the parent is not yet registered);
4196 // put the parent in the machine registry then
4197 mediumLock.release();
4198 treeLock.release();
4199 alock.release();
4200 i_addMediumToRegistry(medium);
4201 alock.acquire();
4202 treeLock.acquire();
4203 mediumLock.acquire();
4204 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4205 }
4206 rc = diff->init(mParent,
4207 medium->i_getPreferredDiffFormat(),
4208 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4209 uuidRegistryParent,
4210 DeviceType_HardDisk);
4211 if (FAILED(rc)) return rc;
4212
4213 /* Apply the normal locking logic to the entire chain. */
4214 MediumLockList *pMediumLockList(new MediumLockList());
4215 mediumLock.release();
4216 treeLock.release();
4217 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4218 diff /* pToLockWrite */,
4219 false /* fMediumLockWriteAll */,
4220 medium,
4221 *pMediumLockList);
4222 treeLock.acquire();
4223 mediumLock.acquire();
4224 if (SUCCEEDED(rc))
4225 {
4226 mediumLock.release();
4227 treeLock.release();
4228 rc = pMediumLockList->Lock();
4229 treeLock.acquire();
4230 mediumLock.acquire();
4231 if (FAILED(rc))
4232 setError(rc,
4233 tr("Could not lock medium when creating diff '%s'"),
4234 diff->i_getLocationFull().c_str());
4235 else
4236 {
4237 /* will release the lock before the potentially lengthy
4238 * operation, so protect with the special state */
4239 MachineState_T oldState = mData->mMachineState;
4240 i_setMachineState(MachineState_SettingUp);
4241
4242 mediumLock.release();
4243 treeLock.release();
4244 alock.release();
4245
4246 rc = medium->i_createDiffStorage(diff,
4247 medium->i_getPreferredDiffVariant(),
4248 pMediumLockList,
4249 NULL /* aProgress */,
4250 true /* aWait */);
4251
4252 alock.acquire();
4253 treeLock.acquire();
4254 mediumLock.acquire();
4255
4256 i_setMachineState(oldState);
4257 }
4258 }
4259
4260 /* Unlock the media and free the associated memory. */
4261 delete pMediumLockList;
4262
4263 if (FAILED(rc)) return rc;
4264
4265 /* use the created diff for the actual attachment */
4266 medium = diff;
4267 mediumCaller.attach(medium);
4268 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4269 mediumLock.attach(medium);
4270 }
4271 while (0);
4272
4273 ComObjPtr<MediumAttachment> attachment;
4274 attachment.createObject();
4275 rc = attachment->init(this,
4276 medium,
4277 aName,
4278 aControllerPort,
4279 aDevice,
4280 aType,
4281 fIndirect,
4282 false /* fPassthrough */,
4283 false /* fTempEject */,
4284 false /* fNonRotational */,
4285 false /* fDiscard */,
4286 fHotplug /* fHotPluggable */,
4287 Utf8Str::Empty);
4288 if (FAILED(rc)) return rc;
4289
4290 if (associate && !medium.isNull())
4291 {
4292 // as the last step, associate the medium to the VM
4293 rc = medium->i_addBackReference(mData->mUuid);
4294 // here we can fail because of Deleting, or being in process of creating a Diff
4295 if (FAILED(rc)) return rc;
4296
4297 mediumLock.release();
4298 treeLock.release();
4299 alock.release();
4300 i_addMediumToRegistry(medium);
4301 alock.acquire();
4302 treeLock.acquire();
4303 mediumLock.acquire();
4304 }
4305
4306 /* success: finally remember the attachment */
4307 i_setModified(IsModified_Storage);
4308 mMediaData.backup();
4309 mMediaData->mAttachments.push_back(attachment);
4310
4311 mediumLock.release();
4312 treeLock.release();
4313 alock.release();
4314
4315 if (fHotplug || fSilent)
4316 {
4317 if (!medium.isNull())
4318 {
4319 MediumLockList *pMediumLockList(new MediumLockList());
4320
4321 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4322 medium /* pToLockWrite */,
4323 false /* fMediumLockWriteAll */,
4324 NULL,
4325 *pMediumLockList);
4326 alock.acquire();
4327 if (FAILED(rc))
4328 delete pMediumLockList;
4329 else
4330 {
4331 mData->mSession.mLockedMedia.Unlock();
4332 alock.release();
4333 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4334 mData->mSession.mLockedMedia.Lock();
4335 alock.acquire();
4336 }
4337 alock.release();
4338 }
4339
4340 if (SUCCEEDED(rc))
4341 {
4342 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4343 /* Remove lock list in case of error. */
4344 if (FAILED(rc))
4345 {
4346 mData->mSession.mLockedMedia.Unlock();
4347 mData->mSession.mLockedMedia.Remove(attachment);
4348 mData->mSession.mLockedMedia.Lock();
4349 }
4350 }
4351 }
4352
4353 /* Save modified registries, but skip this machine as it's the caller's
4354 * job to save its settings like all other settings changes. */
4355 mParent->i_unmarkRegistryModified(i_getId());
4356 mParent->i_saveModifiedRegistries();
4357
4358 return rc;
4359}
4360
4361HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4362 LONG aDevice)
4363{
4364 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4365 aName.c_str(), aControllerPort, aDevice));
4366
4367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4368
4369 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4370 if (FAILED(rc)) return rc;
4371
4372 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4373
4374 /* Check for an existing controller. */
4375 ComObjPtr<StorageController> ctl;
4376 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4377 if (FAILED(rc)) return rc;
4378
4379 StorageControllerType_T ctrlType;
4380 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4381 if (FAILED(rc))
4382 return setError(E_FAIL,
4383 tr("Could not get type of controller '%s'"),
4384 aName.c_str());
4385
4386 bool fSilent = false;
4387 Utf8Str strReconfig;
4388
4389 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4390 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4391 if ( mData->mMachineState == MachineState_Paused
4392 && strReconfig == "1")
4393 fSilent = true;
4394
4395 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4396 bool fHotplug = false;
4397 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4398 fHotplug = true;
4399
4400 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4401 return setError(VBOX_E_INVALID_VM_STATE,
4402 tr("Controller '%s' does not support hotplugging"),
4403 aName.c_str());
4404
4405 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4406 Bstr(aName).raw(),
4407 aControllerPort,
4408 aDevice);
4409 if (!pAttach)
4410 return setError(VBOX_E_OBJECT_NOT_FOUND,
4411 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4412 aDevice, aControllerPort, aName.c_str());
4413
4414 if (fHotplug && !pAttach->i_getHotPluggable())
4415 return setError(VBOX_E_NOT_SUPPORTED,
4416 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4417 aDevice, aControllerPort, aName.c_str());
4418
4419 /*
4420 * The VM has to detach the device before we delete any implicit diffs.
4421 * If this fails we can roll back without loosing data.
4422 */
4423 if (fHotplug || fSilent)
4424 {
4425 alock.release();
4426 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4427 alock.acquire();
4428 }
4429 if (FAILED(rc)) return rc;
4430
4431 /* If we are here everything went well and we can delete the implicit now. */
4432 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4433
4434 alock.release();
4435
4436 /* Save modified registries, but skip this machine as it's the caller's
4437 * job to save its settings like all other settings changes. */
4438 mParent->i_unmarkRegistryModified(i_getId());
4439 mParent->i_saveModifiedRegistries();
4440
4441 return rc;
4442}
4443
4444HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4445 LONG aDevice, BOOL aPassthrough)
4446{
4447 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4448 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4449
4450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4451
4452 HRESULT rc = i_checkStateDependency(MutableStateDep);
4453 if (FAILED(rc)) return rc;
4454
4455 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4456
4457 if (Global::IsOnlineOrTransient(mData->mMachineState))
4458 return setError(VBOX_E_INVALID_VM_STATE,
4459 tr("Invalid machine state: %s"),
4460 Global::stringifyMachineState(mData->mMachineState));
4461
4462 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4463 Bstr(aName).raw(),
4464 aControllerPort,
4465 aDevice);
4466 if (!pAttach)
4467 return setError(VBOX_E_OBJECT_NOT_FOUND,
4468 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4469 aDevice, aControllerPort, aName.c_str());
4470
4471
4472 i_setModified(IsModified_Storage);
4473 mMediaData.backup();
4474
4475 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4476
4477 if (pAttach->i_getType() != DeviceType_DVD)
4478 return setError(E_INVALIDARG,
4479 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4480 aDevice, aControllerPort, aName.c_str());
4481 pAttach->i_updatePassthrough(!!aPassthrough);
4482
4483 return S_OK;
4484}
4485
4486HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4487 LONG aDevice, BOOL aTemporaryEject)
4488{
4489
4490 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4491 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4492
4493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4494
4495 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4496 if (FAILED(rc)) return rc;
4497
4498 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4499 Bstr(aName).raw(),
4500 aControllerPort,
4501 aDevice);
4502 if (!pAttach)
4503 return setError(VBOX_E_OBJECT_NOT_FOUND,
4504 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4505 aDevice, aControllerPort, aName.c_str());
4506
4507
4508 i_setModified(IsModified_Storage);
4509 mMediaData.backup();
4510
4511 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4512
4513 if (pAttach->i_getType() != DeviceType_DVD)
4514 return setError(E_INVALIDARG,
4515 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4516 aDevice, aControllerPort, aName.c_str());
4517 pAttach->i_updateTempEject(!!aTemporaryEject);
4518
4519 return S_OK;
4520}
4521
4522HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4523 LONG aDevice, BOOL aNonRotational)
4524{
4525
4526 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4527 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4528
4529 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4530
4531 HRESULT rc = i_checkStateDependency(MutableStateDep);
4532 if (FAILED(rc)) return rc;
4533
4534 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4535
4536 if (Global::IsOnlineOrTransient(mData->mMachineState))
4537 return setError(VBOX_E_INVALID_VM_STATE,
4538 tr("Invalid machine state: %s"),
4539 Global::stringifyMachineState(mData->mMachineState));
4540
4541 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4542 Bstr(aName).raw(),
4543 aControllerPort,
4544 aDevice);
4545 if (!pAttach)
4546 return setError(VBOX_E_OBJECT_NOT_FOUND,
4547 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4548 aDevice, aControllerPort, aName.c_str());
4549
4550
4551 i_setModified(IsModified_Storage);
4552 mMediaData.backup();
4553
4554 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4555
4556 if (pAttach->i_getType() != DeviceType_HardDisk)
4557 return setError(E_INVALIDARG,
4558 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"),
4559 aDevice, aControllerPort, aName.c_str());
4560 pAttach->i_updateNonRotational(!!aNonRotational);
4561
4562 return S_OK;
4563}
4564
4565HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4566 LONG aDevice, BOOL aDiscard)
4567{
4568
4569 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4570 aName.c_str(), aControllerPort, aDevice, aDiscard));
4571
4572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4573
4574 HRESULT rc = i_checkStateDependency(MutableStateDep);
4575 if (FAILED(rc)) return rc;
4576
4577 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4578
4579 if (Global::IsOnlineOrTransient(mData->mMachineState))
4580 return setError(VBOX_E_INVALID_VM_STATE,
4581 tr("Invalid machine state: %s"),
4582 Global::stringifyMachineState(mData->mMachineState));
4583
4584 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4585 Bstr(aName).raw(),
4586 aControllerPort,
4587 aDevice);
4588 if (!pAttach)
4589 return setError(VBOX_E_OBJECT_NOT_FOUND,
4590 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4591 aDevice, aControllerPort, aName.c_str());
4592
4593
4594 i_setModified(IsModified_Storage);
4595 mMediaData.backup();
4596
4597 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4598
4599 if (pAttach->i_getType() != DeviceType_HardDisk)
4600 return setError(E_INVALIDARG,
4601 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"),
4602 aDevice, aControllerPort, aName.c_str());
4603 pAttach->i_updateDiscard(!!aDiscard);
4604
4605 return S_OK;
4606}
4607
4608HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4609 LONG aDevice, BOOL aHotPluggable)
4610{
4611 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4612 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4613
4614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4615
4616 HRESULT rc = i_checkStateDependency(MutableStateDep);
4617 if (FAILED(rc)) return rc;
4618
4619 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4620
4621 if (Global::IsOnlineOrTransient(mData->mMachineState))
4622 return setError(VBOX_E_INVALID_VM_STATE,
4623 tr("Invalid machine state: %s"),
4624 Global::stringifyMachineState(mData->mMachineState));
4625
4626 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4627 Bstr(aName).raw(),
4628 aControllerPort,
4629 aDevice);
4630 if (!pAttach)
4631 return setError(VBOX_E_OBJECT_NOT_FOUND,
4632 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4633 aDevice, aControllerPort, aName.c_str());
4634
4635 /* Check for an existing controller. */
4636 ComObjPtr<StorageController> ctl;
4637 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4638 if (FAILED(rc)) return rc;
4639
4640 StorageControllerType_T ctrlType;
4641 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4642 if (FAILED(rc))
4643 return setError(E_FAIL,
4644 tr("Could not get type of controller '%s'"),
4645 aName.c_str());
4646
4647 if (!i_isControllerHotplugCapable(ctrlType))
4648 return setError(VBOX_E_NOT_SUPPORTED,
4649 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4650 aName.c_str());
4651
4652 i_setModified(IsModified_Storage);
4653 mMediaData.backup();
4654
4655 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4656
4657 if (pAttach->i_getType() == DeviceType_Floppy)
4658 return setError(E_INVALIDARG,
4659 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"),
4660 aDevice, aControllerPort, aName.c_str());
4661 pAttach->i_updateHotPluggable(!!aHotPluggable);
4662
4663 return S_OK;
4664}
4665
4666HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4667 LONG aDevice)
4668{
4669 int rc = S_OK;
4670 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4671 aName.c_str(), aControllerPort, aDevice));
4672
4673 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4674
4675 return rc;
4676}
4677
4678HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4679 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4680{
4681 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4682 aName.c_str(), aControllerPort, aDevice));
4683
4684 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4685
4686 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4687 if (FAILED(rc)) return rc;
4688
4689 if (Global::IsOnlineOrTransient(mData->mMachineState))
4690 return setError(VBOX_E_INVALID_VM_STATE,
4691 tr("Invalid machine state: %s"),
4692 Global::stringifyMachineState(mData->mMachineState));
4693
4694 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4695 Bstr(aName).raw(),
4696 aControllerPort,
4697 aDevice);
4698 if (!pAttach)
4699 return setError(VBOX_E_OBJECT_NOT_FOUND,
4700 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4701 aDevice, aControllerPort, aName.c_str());
4702
4703
4704 i_setModified(IsModified_Storage);
4705 mMediaData.backup();
4706
4707 IBandwidthGroup *iB = aBandwidthGroup;
4708 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4709 if (aBandwidthGroup && group.isNull())
4710 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4711
4712 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4713
4714 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4715 if (strBandwidthGroupOld.isNotEmpty())
4716 {
4717 /* Get the bandwidth group object and release it - this must not fail. */
4718 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4719 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4720 Assert(SUCCEEDED(rc));
4721
4722 pBandwidthGroupOld->i_release();
4723 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4724 }
4725
4726 if (!group.isNull())
4727 {
4728 group->i_reference();
4729 pAttach->i_updateBandwidthGroup(group->i_getName());
4730 }
4731
4732 return S_OK;
4733}
4734
4735HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4736 LONG aControllerPort,
4737 LONG aDevice,
4738 DeviceType_T aType)
4739{
4740 HRESULT rc = S_OK;
4741
4742 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4743 aName.c_str(), aControllerPort, aDevice, aType));
4744
4745 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4746
4747 return rc;
4748}
4749
4750
4751HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4752 LONG aControllerPort,
4753 LONG aDevice,
4754 BOOL aForce)
4755{
4756 int rc = S_OK;
4757 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4758 aName.c_str(), aControllerPort, aForce));
4759
4760 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4761
4762 return rc;
4763}
4764
4765HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4766 LONG aControllerPort,
4767 LONG aDevice,
4768 const ComPtr<IMedium> &aMedium,
4769 BOOL aForce)
4770{
4771 int rc = S_OK;
4772 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4773 aName.c_str(), aControllerPort, aDevice, aForce));
4774
4775 // request the host lock first, since might be calling Host methods for getting host drives;
4776 // next, protect the media tree all the while we're in here, as well as our member variables
4777 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4778 this->lockHandle(),
4779 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4780
4781 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4782 Bstr(aName).raw(),
4783 aControllerPort,
4784 aDevice);
4785 if (pAttach.isNull())
4786 return setError(VBOX_E_OBJECT_NOT_FOUND,
4787 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4788 aDevice, aControllerPort, aName.c_str());
4789
4790 /* Remember previously mounted medium. The medium before taking the
4791 * backup is not necessarily the same thing. */
4792 ComObjPtr<Medium> oldmedium;
4793 oldmedium = pAttach->i_getMedium();
4794
4795 IMedium *iM = aMedium;
4796 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4797 if (aMedium && pMedium.isNull())
4798 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4799
4800 AutoCaller mediumCaller(pMedium);
4801 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4802
4803 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4804 if (pMedium)
4805 {
4806 DeviceType_T mediumType = pAttach->i_getType();
4807 switch (mediumType)
4808 {
4809 case DeviceType_DVD:
4810 case DeviceType_Floppy:
4811 break;
4812
4813 default:
4814 return setError(VBOX_E_INVALID_OBJECT_STATE,
4815 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4816 aControllerPort,
4817 aDevice,
4818 aName.c_str());
4819 }
4820 }
4821
4822 i_setModified(IsModified_Storage);
4823 mMediaData.backup();
4824
4825 {
4826 // The backup operation makes the pAttach reference point to the
4827 // old settings. Re-get the correct reference.
4828 pAttach = i_findAttachment(mMediaData->mAttachments,
4829 Bstr(aName).raw(),
4830 aControllerPort,
4831 aDevice);
4832 if (!oldmedium.isNull())
4833 oldmedium->i_removeBackReference(mData->mUuid);
4834 if (!pMedium.isNull())
4835 {
4836 pMedium->i_addBackReference(mData->mUuid);
4837
4838 mediumLock.release();
4839 multiLock.release();
4840 i_addMediumToRegistry(pMedium);
4841 multiLock.acquire();
4842 mediumLock.acquire();
4843 }
4844
4845 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4846 pAttach->i_updateMedium(pMedium);
4847 }
4848
4849 i_setModified(IsModified_Storage);
4850
4851 mediumLock.release();
4852 multiLock.release();
4853 rc = i_onMediumChange(pAttach, aForce);
4854 multiLock.acquire();
4855 mediumLock.acquire();
4856
4857 /* On error roll back this change only. */
4858 if (FAILED(rc))
4859 {
4860 if (!pMedium.isNull())
4861 pMedium->i_removeBackReference(mData->mUuid);
4862 pAttach = i_findAttachment(mMediaData->mAttachments,
4863 Bstr(aName).raw(),
4864 aControllerPort,
4865 aDevice);
4866 /* If the attachment is gone in the meantime, bail out. */
4867 if (pAttach.isNull())
4868 return rc;
4869 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4870 if (!oldmedium.isNull())
4871 oldmedium->i_addBackReference(mData->mUuid);
4872 pAttach->i_updateMedium(oldmedium);
4873 }
4874
4875 mediumLock.release();
4876 multiLock.release();
4877
4878 /* Save modified registries, but skip this machine as it's the caller's
4879 * job to save its settings like all other settings changes. */
4880 mParent->i_unmarkRegistryModified(i_getId());
4881 mParent->i_saveModifiedRegistries();
4882
4883 return rc;
4884}
4885HRESULT Machine::getMedium(const com::Utf8Str &aName,
4886 LONG aControllerPort,
4887 LONG aDevice,
4888 ComPtr<IMedium> &aMedium)
4889{
4890 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4891 aName.c_str(), aControllerPort, aDevice));
4892
4893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4894
4895 aMedium = NULL;
4896
4897 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4898 Bstr(aName).raw(),
4899 aControllerPort,
4900 aDevice);
4901 if (pAttach.isNull())
4902 return setError(VBOX_E_OBJECT_NOT_FOUND,
4903 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4904 aDevice, aControllerPort, aName.c_str());
4905
4906 aMedium = pAttach->i_getMedium();
4907
4908 return S_OK;
4909}
4910
4911HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4912{
4913
4914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4915
4916 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4917
4918 return S_OK;
4919}
4920
4921HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4922{
4923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4924
4925 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4926
4927 return S_OK;
4928}
4929
4930HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4931{
4932 /* Do not assert if slot is out of range, just return the advertised
4933 status. testdriver/vbox.py triggers this in logVmInfo. */
4934 if (aSlot >= mNetworkAdapters.size())
4935 return setError(E_INVALIDARG,
4936 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4937 aSlot, mNetworkAdapters.size());
4938
4939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4940
4941 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4942
4943 return S_OK;
4944}
4945
4946HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4947{
4948 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4949
4950 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4951 size_t i = 0;
4952 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4953 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4954 ++it, ++i)
4955 aKeys[i] = it->first;
4956
4957 return S_OK;
4958}
4959
4960 /**
4961 * @note Locks this object for reading.
4962 */
4963HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4964 com::Utf8Str &aValue)
4965{
4966 /* start with nothing found */
4967 aValue = "";
4968
4969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4970
4971 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4972 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4973 // found:
4974 aValue = it->second; // source is a Utf8Str
4975
4976 /* return the result to caller (may be empty) */
4977 return S_OK;
4978}
4979
4980 /**
4981 * @note Locks mParent for writing + this object for writing.
4982 */
4983HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4984{
4985 Utf8Str strOldValue; // empty
4986
4987 // locking note: we only hold the read lock briefly to look up the old value,
4988 // then release it and call the onExtraCanChange callbacks. There is a small
4989 // chance of a race insofar as the callback might be called twice if two callers
4990 // change the same key at the same time, but that's a much better solution
4991 // than the deadlock we had here before. The actual changing of the extradata
4992 // is then performed under the write lock and race-free.
4993
4994 // look up the old value first; if nothing has changed then we need not do anything
4995 {
4996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4997
4998 // For snapshots don't even think about allowing changes, extradata
4999 // is global for a machine, so there is nothing snapshot specific.
5000 if (i_isSnapshotMachine())
5001 return setError(VBOX_E_INVALID_VM_STATE,
5002 tr("Cannot set extradata for a snapshot"));
5003
5004 // check if the right IMachine instance is used
5005 if (mData->mRegistered && !i_isSessionMachine())
5006 return setError(VBOX_E_INVALID_VM_STATE,
5007 tr("Cannot set extradata for an immutable machine"));
5008
5009 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5010 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5011 strOldValue = it->second;
5012 }
5013
5014 bool fChanged;
5015 if ((fChanged = (strOldValue != aValue)))
5016 {
5017 // ask for permission from all listeners outside the locks;
5018 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5019 // lock to copy the list of callbacks to invoke
5020 Bstr error;
5021 Bstr bstrValue(aValue);
5022
5023 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5024 {
5025 const char *sep = error.isEmpty() ? "" : ": ";
5026 CBSTR err = error.raw();
5027 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5028 return setError(E_ACCESSDENIED,
5029 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5030 aKey.c_str(),
5031 aValue.c_str(),
5032 sep,
5033 err);
5034 }
5035
5036 // data is changing and change not vetoed: then write it out under the lock
5037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5038
5039 if (aValue.isEmpty())
5040 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5041 else
5042 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5043 // creates a new key if needed
5044
5045 bool fNeedsGlobalSaveSettings = false;
5046 // This saving of settings is tricky: there is no "old state" for the
5047 // extradata items at all (unlike all other settings), so the old/new
5048 // settings comparison would give a wrong result!
5049 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5050
5051 if (fNeedsGlobalSaveSettings)
5052 {
5053 // save the global settings; for that we should hold only the VirtualBox lock
5054 alock.release();
5055 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5056 mParent->i_saveSettings();
5057 }
5058 }
5059
5060 // fire notification outside the lock
5061 if (fChanged)
5062 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5063
5064 return S_OK;
5065}
5066
5067HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5068{
5069 aProgress = NULL;
5070 NOREF(aSettingsFilePath);
5071 ReturnComNotImplemented();
5072}
5073
5074HRESULT Machine::saveSettings()
5075{
5076 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5077
5078 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5079 if (FAILED(rc)) return rc;
5080
5081 /* the settings file path may never be null */
5082 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5083
5084 /* save all VM data excluding snapshots */
5085 bool fNeedsGlobalSaveSettings = false;
5086 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5087 mlock.release();
5088
5089 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5090 {
5091 // save the global settings; for that we should hold only the VirtualBox lock
5092 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5093 rc = mParent->i_saveSettings();
5094 }
5095
5096 return rc;
5097}
5098
5099
5100HRESULT Machine::discardSettings()
5101{
5102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5103
5104 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5105 if (FAILED(rc)) return rc;
5106
5107 /*
5108 * during this rollback, the session will be notified if data has
5109 * been actually changed
5110 */
5111 i_rollback(true /* aNotify */);
5112
5113 return S_OK;
5114}
5115
5116/** @note Locks objects! */
5117HRESULT Machine::unregister(AutoCaller &autoCaller,
5118 CleanupMode_T aCleanupMode,
5119 std::vector<ComPtr<IMedium> > &aMedia)
5120{
5121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5122
5123 Guid id(i_getId());
5124
5125 if (mData->mSession.mState != SessionState_Unlocked)
5126 return setError(VBOX_E_INVALID_OBJECT_STATE,
5127 tr("Cannot unregister the machine '%s' while it is locked"),
5128 mUserData->s.strName.c_str());
5129
5130 // wait for state dependents to drop to zero
5131 i_ensureNoStateDependencies();
5132
5133 if (!mData->mAccessible)
5134 {
5135 // inaccessible maschines can only be unregistered; uninitialize ourselves
5136 // here because currently there may be no unregistered that are inaccessible
5137 // (this state combination is not supported). Note releasing the caller and
5138 // leaving the lock before calling uninit()
5139 alock.release();
5140 autoCaller.release();
5141
5142 uninit();
5143
5144 mParent->i_unregisterMachine(this, id);
5145 // calls VirtualBox::i_saveSettings()
5146
5147 return S_OK;
5148 }
5149
5150 HRESULT rc = S_OK;
5151
5152 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5153 // discard saved state
5154 if (mData->mMachineState == MachineState_Saved)
5155 {
5156 // add the saved state file to the list of files the caller should delete
5157 Assert(!mSSData->strStateFilePath.isEmpty());
5158 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5159
5160 mSSData->strStateFilePath.setNull();
5161
5162 // unconditionally set the machine state to powered off, we now
5163 // know no session has locked the machine
5164 mData->mMachineState = MachineState_PoweredOff;
5165 }
5166
5167 size_t cSnapshots = 0;
5168 if (mData->mFirstSnapshot)
5169 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5170 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5171 // fail now before we start detaching media
5172 return setError(VBOX_E_INVALID_OBJECT_STATE,
5173 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5174 mUserData->s.strName.c_str(), cSnapshots);
5175
5176 // This list collects the medium objects from all medium attachments
5177 // which we will detach from the machine and its snapshots, in a specific
5178 // order which allows for closing all media without getting "media in use"
5179 // errors, simply by going through the list from the front to the back:
5180 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5181 // and must be closed before the parent media from the snapshots, or closing the parents
5182 // will fail because they still have children);
5183 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5184 // the root ("first") snapshot of the machine.
5185 MediaList llMedia;
5186
5187 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5188 && mMediaData->mAttachments.size()
5189 )
5190 {
5191 // we have media attachments: detach them all and add the Medium objects to our list
5192 if (aCleanupMode != CleanupMode_UnregisterOnly)
5193 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5194 else
5195 return setError(VBOX_E_INVALID_OBJECT_STATE,
5196 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5197 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5198 }
5199
5200 if (cSnapshots)
5201 {
5202 // add the media from the medium attachments of the snapshots to llMedia
5203 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5204 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5205 // into the children first
5206
5207 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5208 MachineState_T oldState = mData->mMachineState;
5209 mData->mMachineState = MachineState_DeletingSnapshot;
5210
5211 // make a copy of the first snapshot so the refcount does not drop to 0
5212 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5213 // because of the AutoCaller voodoo)
5214 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5215
5216 // GO!
5217 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5218
5219 mData->mMachineState = oldState;
5220 }
5221
5222 if (FAILED(rc))
5223 {
5224 i_rollbackMedia();
5225 return rc;
5226 }
5227
5228 // commit all the media changes made above
5229 i_commitMedia();
5230
5231 mData->mRegistered = false;
5232
5233 // machine lock no longer needed
5234 alock.release();
5235
5236 // return media to caller
5237 size_t i = 0;
5238 aMedia.resize(llMedia.size());
5239 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5240 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5241
5242 mParent->i_unregisterMachine(this, id);
5243 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5244
5245 return S_OK;
5246}
5247
5248/**
5249 * Task record for deleting a machine config.
5250 */
5251struct Machine::DeleteConfigTask
5252 : public Machine::Task
5253{
5254 DeleteConfigTask(Machine *m,
5255 Progress *p,
5256 const Utf8Str &t,
5257 const RTCList<ComPtr<IMedium> > &llMediums,
5258 const StringsList &llFilesToDelete)
5259 : Task(m, p, t),
5260 m_llMediums(llMediums),
5261 m_llFilesToDelete(llFilesToDelete)
5262 {}
5263
5264 void handler()
5265 {
5266 m_pMachine->i_deleteConfigHandler(*this);
5267 }
5268
5269 RTCList<ComPtr<IMedium> > m_llMediums;
5270 StringsList m_llFilesToDelete;
5271};
5272
5273/**
5274 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5275 * SessionMachine::taskHandler().
5276 *
5277 * @note Locks this object for writing.
5278 *
5279 * @param task
5280 * @return
5281 */
5282void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5283{
5284 LogFlowThisFuncEnter();
5285
5286 AutoCaller autoCaller(this);
5287 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5288 if (FAILED(autoCaller.rc()))
5289 {
5290 /* we might have been uninitialized because the session was accidentally
5291 * closed by the client, so don't assert */
5292 HRESULT rc = setError(E_FAIL,
5293 tr("The session has been accidentally closed"));
5294 task.m_pProgress->i_notifyComplete(rc);
5295 LogFlowThisFuncLeave();
5296 return;
5297 }
5298
5299 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5300
5301 HRESULT rc = S_OK;
5302
5303 try
5304 {
5305 ULONG uLogHistoryCount = 3;
5306 ComPtr<ISystemProperties> systemProperties;
5307 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5308 if (FAILED(rc)) throw rc;
5309
5310 if (!systemProperties.isNull())
5311 {
5312 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5313 if (FAILED(rc)) throw rc;
5314 }
5315
5316 MachineState_T oldState = mData->mMachineState;
5317 i_setMachineState(MachineState_SettingUp);
5318 alock.release();
5319 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5320 {
5321 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5322 {
5323 AutoCaller mac(pMedium);
5324 if (FAILED(mac.rc())) throw mac.rc();
5325 Utf8Str strLocation = pMedium->i_getLocationFull();
5326 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5327 if (FAILED(rc)) throw rc;
5328 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5329 }
5330 if (pMedium->i_isMediumFormatFile())
5331 {
5332 ComPtr<IProgress> pProgress2;
5333 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5334 if (FAILED(rc)) throw rc;
5335 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5336 if (FAILED(rc)) throw rc;
5337 /* Check the result of the asynchronous process. */
5338 LONG iRc;
5339 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5340 if (FAILED(rc)) throw rc;
5341 /* If the thread of the progress object has an error, then
5342 * retrieve the error info from there, or it'll be lost. */
5343 if (FAILED(iRc))
5344 throw setError(ProgressErrorInfo(pProgress2));
5345 }
5346
5347 /* Close the medium, deliberately without checking the return
5348 * code, and without leaving any trace in the error info, as
5349 * a failure here is a very minor issue, which shouldn't happen
5350 * as above we even managed to delete the medium. */
5351 {
5352 ErrorInfoKeeper eik;
5353 pMedium->Close();
5354 }
5355 }
5356 i_setMachineState(oldState);
5357 alock.acquire();
5358
5359 // delete the files pushed on the task list by Machine::Delete()
5360 // (this includes saved states of the machine and snapshots and
5361 // medium storage files from the IMedium list passed in, and the
5362 // machine XML file)
5363 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5364 while (it != task.m_llFilesToDelete.end())
5365 {
5366 const Utf8Str &strFile = *it;
5367 LogFunc(("Deleting file %s\n", strFile.c_str()));
5368 int vrc = RTFileDelete(strFile.c_str());
5369 if (RT_FAILURE(vrc))
5370 throw setError(VBOX_E_IPRT_ERROR,
5371 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5372
5373 ++it;
5374 if (it == task.m_llFilesToDelete.end())
5375 {
5376 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5377 if (FAILED(rc)) throw rc;
5378 break;
5379 }
5380
5381 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5382 if (FAILED(rc)) throw rc;
5383 }
5384
5385 /* delete the settings only when the file actually exists */
5386 if (mData->pMachineConfigFile->fileExists())
5387 {
5388 /* Delete any backup or uncommitted XML files. Ignore failures.
5389 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5390 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5391 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5392 RTFileDelete(otherXml.c_str());
5393 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5394 RTFileDelete(otherXml.c_str());
5395
5396 /* delete the Logs folder, nothing important should be left
5397 * there (we don't check for errors because the user might have
5398 * some private files there that we don't want to delete) */
5399 Utf8Str logFolder;
5400 getLogFolder(logFolder);
5401 Assert(logFolder.length());
5402 if (RTDirExists(logFolder.c_str()))
5403 {
5404 /* Delete all VBox.log[.N] files from the Logs folder
5405 * (this must be in sync with the rotation logic in
5406 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5407 * files that may have been created by the GUI. */
5408 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5409 logFolder.c_str(), RTPATH_DELIMITER);
5410 RTFileDelete(log.c_str());
5411 log = Utf8StrFmt("%s%cVBox.png",
5412 logFolder.c_str(), RTPATH_DELIMITER);
5413 RTFileDelete(log.c_str());
5414 for (int i = uLogHistoryCount; i > 0; i--)
5415 {
5416 log = Utf8StrFmt("%s%cVBox.log.%d",
5417 logFolder.c_str(), RTPATH_DELIMITER, i);
5418 RTFileDelete(log.c_str());
5419 log = Utf8StrFmt("%s%cVBox.png.%d",
5420 logFolder.c_str(), RTPATH_DELIMITER, i);
5421 RTFileDelete(log.c_str());
5422 }
5423#if defined(RT_OS_WINDOWS)
5424 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5425 RTFileDelete(log.c_str());
5426 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5427 RTFileDelete(log.c_str());
5428#endif
5429
5430 RTDirRemove(logFolder.c_str());
5431 }
5432
5433 /* delete the Snapshots folder, nothing important should be left
5434 * there (we don't check for errors because the user might have
5435 * some private files there that we don't want to delete) */
5436 Utf8Str strFullSnapshotFolder;
5437 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5438 Assert(!strFullSnapshotFolder.isEmpty());
5439 if (RTDirExists(strFullSnapshotFolder.c_str()))
5440 RTDirRemove(strFullSnapshotFolder.c_str());
5441
5442 // delete the directory that contains the settings file, but only
5443 // if it matches the VM name
5444 Utf8Str settingsDir;
5445 if (i_isInOwnDir(&settingsDir))
5446 RTDirRemove(settingsDir.c_str());
5447 }
5448
5449 alock.release();
5450
5451 mParent->i_saveModifiedRegistries();
5452 }
5453 catch (HRESULT aRC) { rc = aRC; }
5454
5455 task.m_pProgress->i_notifyComplete(rc);
5456
5457 LogFlowThisFuncLeave();
5458}
5459
5460HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5461{
5462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5463
5464 HRESULT rc = i_checkStateDependency(MutableStateDep);
5465 if (FAILED(rc)) return rc;
5466
5467 if (mData->mRegistered)
5468 return setError(VBOX_E_INVALID_VM_STATE,
5469 tr("Cannot delete settings of a registered machine"));
5470
5471 // collect files to delete
5472 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5473 if (mData->pMachineConfigFile->fileExists())
5474 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5475
5476 RTCList<ComPtr<IMedium> > llMediums;
5477 for (size_t i = 0; i < aMedia.size(); ++i)
5478 {
5479 IMedium *pIMedium(aMedia[i]);
5480 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5481 if (pMedium.isNull())
5482 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5483 SafeArray<BSTR> ids;
5484 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5485 if (FAILED(rc)) return rc;
5486 /* At this point the medium should not have any back references
5487 * anymore. If it has it is attached to another VM and *must* not
5488 * deleted. */
5489 if (ids.size() < 1)
5490 llMediums.append(pMedium);
5491 }
5492
5493 ComObjPtr<Progress> pProgress;
5494 pProgress.createObject();
5495 rc = pProgress->init(i_getVirtualBox(),
5496 static_cast<IMachine*>(this) /* aInitiator */,
5497 Bstr(tr("Deleting files")).raw(),
5498 true /* fCancellable */,
5499 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5500 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5501 if (FAILED(rc))
5502 return rc;
5503
5504 /* create and start the task on a separate thread (note that it will not
5505 * start working until we release alock) */
5506 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5507 rc = pTask->createThread();
5508 if (FAILED(rc))
5509 return rc;
5510
5511 pProgress.queryInterfaceTo(aProgress.asOutParam());
5512
5513 LogFlowFuncLeave();
5514
5515 return S_OK;
5516}
5517
5518HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5519{
5520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5521
5522 ComObjPtr<Snapshot> pSnapshot;
5523 HRESULT rc;
5524
5525 if (aNameOrId.isEmpty())
5526 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5527 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5528 else
5529 {
5530 Guid uuid(aNameOrId);
5531 if (uuid.isValid())
5532 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5533 else
5534 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5535 }
5536 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5537
5538 return rc;
5539}
5540
5541HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5542{
5543 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5544
5545 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5546 if (FAILED(rc)) return rc;
5547
5548 ComObjPtr<SharedFolder> sharedFolder;
5549 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5550 if (SUCCEEDED(rc))
5551 return setError(VBOX_E_OBJECT_IN_USE,
5552 tr("Shared folder named '%s' already exists"),
5553 aName.c_str());
5554
5555 sharedFolder.createObject();
5556 rc = sharedFolder->init(i_getMachine(),
5557 aName,
5558 aHostPath,
5559 !!aWritable,
5560 !!aAutomount,
5561 true /* fFailOnError */);
5562 if (FAILED(rc)) return rc;
5563
5564 i_setModified(IsModified_SharedFolders);
5565 mHWData.backup();
5566 mHWData->mSharedFolders.push_back(sharedFolder);
5567
5568 /* inform the direct session if any */
5569 alock.release();
5570 i_onSharedFolderChange();
5571
5572 return S_OK;
5573}
5574
5575HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5576{
5577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5578
5579 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5580 if (FAILED(rc)) return rc;
5581
5582 ComObjPtr<SharedFolder> sharedFolder;
5583 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5584 if (FAILED(rc)) return rc;
5585
5586 i_setModified(IsModified_SharedFolders);
5587 mHWData.backup();
5588 mHWData->mSharedFolders.remove(sharedFolder);
5589
5590 /* inform the direct session if any */
5591 alock.release();
5592 i_onSharedFolderChange();
5593
5594 return S_OK;
5595}
5596
5597HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5598{
5599 /* start with No */
5600 *aCanShow = FALSE;
5601
5602 ComPtr<IInternalSessionControl> directControl;
5603 {
5604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5605
5606 if (mData->mSession.mState != SessionState_Locked)
5607 return setError(VBOX_E_INVALID_VM_STATE,
5608 tr("Machine is not locked for session (session state: %s)"),
5609 Global::stringifySessionState(mData->mSession.mState));
5610
5611 if (mData->mSession.mLockType == LockType_VM)
5612 directControl = mData->mSession.mDirectControl;
5613 }
5614
5615 /* ignore calls made after #OnSessionEnd() is called */
5616 if (!directControl)
5617 return S_OK;
5618
5619 LONG64 dummy;
5620 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5621}
5622
5623HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5624{
5625 ComPtr<IInternalSessionControl> directControl;
5626 {
5627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5628
5629 if (mData->mSession.mState != SessionState_Locked)
5630 return setError(E_FAIL,
5631 tr("Machine is not locked for session (session state: %s)"),
5632 Global::stringifySessionState(mData->mSession.mState));
5633
5634 if (mData->mSession.mLockType == LockType_VM)
5635 directControl = mData->mSession.mDirectControl;
5636 }
5637
5638 /* ignore calls made after #OnSessionEnd() is called */
5639 if (!directControl)
5640 return S_OK;
5641
5642 BOOL dummy;
5643 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5644}
5645
5646#ifdef VBOX_WITH_GUEST_PROPS
5647/**
5648 * Look up a guest property in VBoxSVC's internal structures.
5649 */
5650HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5651 com::Utf8Str &aValue,
5652 LONG64 *aTimestamp,
5653 com::Utf8Str &aFlags) const
5654{
5655 using namespace guestProp;
5656
5657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5658 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5659
5660 if (it != mHWData->mGuestProperties.end())
5661 {
5662 char szFlags[MAX_FLAGS_LEN + 1];
5663 aValue = it->second.strValue;
5664 *aTimestamp = it->second.mTimestamp;
5665 writeFlags(it->second.mFlags, szFlags);
5666 aFlags = Utf8Str(szFlags);
5667 }
5668
5669 return S_OK;
5670}
5671
5672/**
5673 * Query the VM that a guest property belongs to for the property.
5674 * @returns E_ACCESSDENIED if the VM process is not available or not
5675 * currently handling queries and the lookup should then be done in
5676 * VBoxSVC.
5677 */
5678HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5679 com::Utf8Str &aValue,
5680 LONG64 *aTimestamp,
5681 com::Utf8Str &aFlags) const
5682{
5683 HRESULT rc = S_OK;
5684 BSTR bValue = NULL;
5685 BSTR bFlags = NULL;
5686
5687 ComPtr<IInternalSessionControl> directControl;
5688 {
5689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5690 if (mData->mSession.mLockType == LockType_VM)
5691 directControl = mData->mSession.mDirectControl;
5692 }
5693
5694 /* ignore calls made after #OnSessionEnd() is called */
5695 if (!directControl)
5696 rc = E_ACCESSDENIED;
5697 else
5698 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5699 0 /* accessMode */,
5700 &bValue, aTimestamp, &bFlags);
5701
5702 aValue = bValue;
5703 aFlags = bFlags;
5704
5705 return rc;
5706}
5707#endif // VBOX_WITH_GUEST_PROPS
5708
5709HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5710 com::Utf8Str &aValue,
5711 LONG64 *aTimestamp,
5712 com::Utf8Str &aFlags)
5713{
5714#ifndef VBOX_WITH_GUEST_PROPS
5715 ReturnComNotImplemented();
5716#else // VBOX_WITH_GUEST_PROPS
5717
5718 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5719
5720 if (rc == E_ACCESSDENIED)
5721 /* The VM is not running or the service is not (yet) accessible */
5722 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5723 return rc;
5724#endif // VBOX_WITH_GUEST_PROPS
5725}
5726
5727HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5728{
5729 LONG64 dummyTimestamp;
5730 com::Utf8Str dummyFlags;
5731 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5732 return rc;
5733
5734}
5735HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5736{
5737 com::Utf8Str dummyFlags;
5738 com::Utf8Str dummyValue;
5739 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5740 return rc;
5741}
5742
5743#ifdef VBOX_WITH_GUEST_PROPS
5744/**
5745 * Set a guest property in VBoxSVC's internal structures.
5746 */
5747HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5748 const com::Utf8Str &aFlags, bool fDelete)
5749{
5750 using namespace guestProp;
5751
5752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5753 HRESULT rc = S_OK;
5754
5755 rc = i_checkStateDependency(MutableOrSavedStateDep);
5756 if (FAILED(rc)) return rc;
5757
5758 try
5759 {
5760 uint32_t fFlags = NILFLAG;
5761 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5762 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5763
5764 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5765 if (it == mHWData->mGuestProperties.end())
5766 {
5767 if (!fDelete)
5768 {
5769 i_setModified(IsModified_MachineData);
5770 mHWData.backupEx();
5771
5772 RTTIMESPEC time;
5773 HWData::GuestProperty prop;
5774 prop.strValue = Bstr(aValue).raw();
5775 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5776 prop.mFlags = fFlags;
5777 mHWData->mGuestProperties[aName] = prop;
5778 }
5779 }
5780 else
5781 {
5782 if (it->second.mFlags & (RDONLYHOST))
5783 {
5784 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5785 }
5786 else
5787 {
5788 i_setModified(IsModified_MachineData);
5789 mHWData.backupEx();
5790
5791 /* The backupEx() operation invalidates our iterator,
5792 * so get a new one. */
5793 it = mHWData->mGuestProperties.find(aName);
5794 Assert(it != mHWData->mGuestProperties.end());
5795
5796 if (!fDelete)
5797 {
5798 RTTIMESPEC time;
5799 it->second.strValue = aValue;
5800 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5801 it->second.mFlags = fFlags;
5802 }
5803 else
5804 mHWData->mGuestProperties.erase(it);
5805 }
5806 }
5807
5808 if (SUCCEEDED(rc))
5809 {
5810 alock.release();
5811
5812 mParent->i_onGuestPropertyChange(mData->mUuid,
5813 Bstr(aName).raw(),
5814 Bstr(aValue).raw(),
5815 Bstr(aFlags).raw());
5816 }
5817 }
5818 catch (std::bad_alloc &)
5819 {
5820 rc = E_OUTOFMEMORY;
5821 }
5822
5823 return rc;
5824}
5825
5826/**
5827 * Set a property on the VM that that property belongs to.
5828 * @returns E_ACCESSDENIED if the VM process is not available or not
5829 * currently handling queries and the setting should then be done in
5830 * VBoxSVC.
5831 */
5832HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5833 const com::Utf8Str &aFlags, bool fDelete)
5834{
5835 HRESULT rc;
5836
5837 try
5838 {
5839 ComPtr<IInternalSessionControl> directControl;
5840 {
5841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5842 if (mData->mSession.mLockType == LockType_VM)
5843 directControl = mData->mSession.mDirectControl;
5844 }
5845
5846 BSTR dummy = NULL; /* will not be changed (setter) */
5847 LONG64 dummy64;
5848 if (!directControl)
5849 rc = E_ACCESSDENIED;
5850 else
5851 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5852 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5853 fDelete? 2: 1 /* accessMode */,
5854 &dummy, &dummy64, &dummy);
5855 }
5856 catch (std::bad_alloc &)
5857 {
5858 rc = E_OUTOFMEMORY;
5859 }
5860
5861 return rc;
5862}
5863#endif // VBOX_WITH_GUEST_PROPS
5864
5865HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5866 const com::Utf8Str &aFlags)
5867{
5868#ifndef VBOX_WITH_GUEST_PROPS
5869 ReturnComNotImplemented();
5870#else // VBOX_WITH_GUEST_PROPS
5871 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5872 if (rc == E_ACCESSDENIED)
5873 /* The VM is not running or the service is not (yet) accessible */
5874 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5875 return rc;
5876#endif // VBOX_WITH_GUEST_PROPS
5877}
5878
5879HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5880{
5881 return setGuestProperty(aProperty, aValue, "");
5882}
5883
5884HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5885{
5886#ifndef VBOX_WITH_GUEST_PROPS
5887 ReturnComNotImplemented();
5888#else // VBOX_WITH_GUEST_PROPS
5889 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5890 if (rc == E_ACCESSDENIED)
5891 /* The VM is not running or the service is not (yet) accessible */
5892 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5893 return rc;
5894#endif // VBOX_WITH_GUEST_PROPS
5895}
5896
5897#ifdef VBOX_WITH_GUEST_PROPS
5898/**
5899 * Enumerate the guest properties in VBoxSVC's internal structures.
5900 */
5901HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5902 std::vector<com::Utf8Str> &aNames,
5903 std::vector<com::Utf8Str> &aValues,
5904 std::vector<LONG64> &aTimestamps,
5905 std::vector<com::Utf8Str> &aFlags)
5906{
5907 using namespace guestProp;
5908
5909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5910 Utf8Str strPatterns(aPatterns);
5911
5912 HWData::GuestPropertyMap propMap;
5913
5914 /*
5915 * Look for matching patterns and build up a list.
5916 */
5917 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5918 while (it != mHWData->mGuestProperties.end())
5919 {
5920 if ( strPatterns.isEmpty()
5921 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5922 RTSTR_MAX,
5923 it->first.c_str(),
5924 RTSTR_MAX,
5925 NULL)
5926 )
5927 propMap.insert(*it);
5928 ++it;
5929 }
5930
5931 alock.release();
5932
5933 /*
5934 * And build up the arrays for returning the property information.
5935 */
5936 size_t cEntries = propMap.size();
5937
5938 aNames.resize(cEntries);
5939 aValues.resize(cEntries);
5940 aTimestamps.resize(cEntries);
5941 aFlags.resize(cEntries);
5942
5943 char szFlags[MAX_FLAGS_LEN + 1];
5944 size_t i= 0;
5945 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5946 {
5947 aNames[i] = it->first;
5948 aValues[i] = it->second.strValue;
5949 aTimestamps[i] = it->second.mTimestamp;
5950 writeFlags(it->second.mFlags, szFlags);
5951 aFlags[i] = Utf8Str(szFlags);
5952 }
5953
5954 return S_OK;
5955}
5956
5957/**
5958 * Enumerate the properties managed by a VM.
5959 * @returns E_ACCESSDENIED if the VM process is not available or not
5960 * currently handling queries and the setting should then be done in
5961 * VBoxSVC.
5962 */
5963HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5964 std::vector<com::Utf8Str> &aNames,
5965 std::vector<com::Utf8Str> &aValues,
5966 std::vector<LONG64> &aTimestamps,
5967 std::vector<com::Utf8Str> &aFlags)
5968{
5969 HRESULT rc;
5970 ComPtr<IInternalSessionControl> directControl;
5971 {
5972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5973 if (mData->mSession.mLockType == LockType_VM)
5974 directControl = mData->mSession.mDirectControl;
5975 }
5976
5977 com::SafeArray<BSTR> bNames;
5978 com::SafeArray<BSTR> bValues;
5979 com::SafeArray<LONG64> bTimestamps;
5980 com::SafeArray<BSTR> bFlags;
5981
5982 if (!directControl)
5983 rc = E_ACCESSDENIED;
5984 else
5985 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5986 ComSafeArrayAsOutParam(bNames),
5987 ComSafeArrayAsOutParam(bValues),
5988 ComSafeArrayAsOutParam(bTimestamps),
5989 ComSafeArrayAsOutParam(bFlags));
5990 size_t i;
5991 aNames.resize(bNames.size());
5992 for (i = 0; i < bNames.size(); ++i)
5993 aNames[i] = Utf8Str(bNames[i]);
5994 aValues.resize(bValues.size());
5995 for (i = 0; i < bValues.size(); ++i)
5996 aValues[i] = Utf8Str(bValues[i]);
5997 aTimestamps.resize(bTimestamps.size());
5998 for (i = 0; i < bTimestamps.size(); ++i)
5999 aTimestamps[i] = bTimestamps[i];
6000 aFlags.resize(bFlags.size());
6001 for (i = 0; i < bFlags.size(); ++i)
6002 aFlags[i] = Utf8Str(bFlags[i]);
6003
6004 return rc;
6005}
6006#endif // VBOX_WITH_GUEST_PROPS
6007HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6008 std::vector<com::Utf8Str> &aNames,
6009 std::vector<com::Utf8Str> &aValues,
6010 std::vector<LONG64> &aTimestamps,
6011 std::vector<com::Utf8Str> &aFlags)
6012{
6013#ifndef VBOX_WITH_GUEST_PROPS
6014 ReturnComNotImplemented();
6015#else // VBOX_WITH_GUEST_PROPS
6016
6017 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6018
6019 if (rc == E_ACCESSDENIED)
6020 /* The VM is not running or the service is not (yet) accessible */
6021 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6022 return rc;
6023#endif // VBOX_WITH_GUEST_PROPS
6024}
6025
6026HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6027 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6028{
6029 MediaData::AttachmentList atts;
6030
6031 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6032 if (FAILED(rc)) return rc;
6033
6034 size_t i = 0;
6035 aMediumAttachments.resize(atts.size());
6036 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
6037 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6038
6039 return S_OK;
6040}
6041
6042HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6043 LONG aControllerPort,
6044 LONG aDevice,
6045 ComPtr<IMediumAttachment> &aAttachment)
6046{
6047 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6048 aName.c_str(), aControllerPort, aDevice));
6049
6050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6051
6052 aAttachment = NULL;
6053
6054 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
6055 Bstr(aName).raw(),
6056 aControllerPort,
6057 aDevice);
6058 if (pAttach.isNull())
6059 return setError(VBOX_E_OBJECT_NOT_FOUND,
6060 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6061 aDevice, aControllerPort, aName.c_str());
6062
6063 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6064
6065 return S_OK;
6066}
6067
6068
6069HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6070 StorageBus_T aConnectionType,
6071 ComPtr<IStorageController> &aController)
6072{
6073 if ( (aConnectionType <= StorageBus_Null)
6074 || (aConnectionType > StorageBus_USB))
6075 return setError(E_INVALIDARG,
6076 tr("Invalid connection type: %d"),
6077 aConnectionType);
6078
6079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6080
6081 HRESULT rc = i_checkStateDependency(MutableStateDep);
6082 if (FAILED(rc)) return rc;
6083
6084 /* try to find one with the name first. */
6085 ComObjPtr<StorageController> ctrl;
6086
6087 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6088 if (SUCCEEDED(rc))
6089 return setError(VBOX_E_OBJECT_IN_USE,
6090 tr("Storage controller named '%s' already exists"),
6091 aName.c_str());
6092
6093 ctrl.createObject();
6094
6095 /* get a new instance number for the storage controller */
6096 ULONG ulInstance = 0;
6097 bool fBootable = true;
6098 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6099 it != mStorageControllers->end();
6100 ++it)
6101 {
6102 if ((*it)->i_getStorageBus() == aConnectionType)
6103 {
6104 ULONG ulCurInst = (*it)->i_getInstance();
6105
6106 if (ulCurInst >= ulInstance)
6107 ulInstance = ulCurInst + 1;
6108
6109 /* Only one controller of each type can be marked as bootable. */
6110 if ((*it)->i_getBootable())
6111 fBootable = false;
6112 }
6113 }
6114
6115 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6116 if (FAILED(rc)) return rc;
6117
6118 i_setModified(IsModified_Storage);
6119 mStorageControllers.backup();
6120 mStorageControllers->push_back(ctrl);
6121
6122 ctrl.queryInterfaceTo(aController.asOutParam());
6123
6124 /* inform the direct session if any */
6125 alock.release();
6126 i_onStorageControllerChange();
6127
6128 return S_OK;
6129}
6130
6131HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6132 ComPtr<IStorageController> &aStorageController)
6133{
6134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6135
6136 ComObjPtr<StorageController> ctrl;
6137
6138 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6139 if (SUCCEEDED(rc))
6140 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6141
6142 return rc;
6143}
6144
6145HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6146 ULONG aInstance,
6147 ComPtr<IStorageController> &aStorageController)
6148{
6149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6150
6151 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6152 it != mStorageControllers->end();
6153 ++it)
6154 {
6155 if ( (*it)->i_getStorageBus() == aConnectionType
6156 && (*it)->i_getInstance() == aInstance)
6157 {
6158 (*it).queryInterfaceTo(aStorageController.asOutParam());
6159 return S_OK;
6160 }
6161 }
6162
6163 return setError(VBOX_E_OBJECT_NOT_FOUND,
6164 tr("Could not find a storage controller with instance number '%lu'"),
6165 aInstance);
6166}
6167
6168HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6169{
6170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6171
6172 HRESULT rc = i_checkStateDependency(MutableStateDep);
6173 if (FAILED(rc)) return rc;
6174
6175 ComObjPtr<StorageController> ctrl;
6176
6177 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6178 if (SUCCEEDED(rc))
6179 {
6180 /* Ensure that only one controller of each type is marked as bootable. */
6181 if (aBootable == TRUE)
6182 {
6183 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6184 it != mStorageControllers->end();
6185 ++it)
6186 {
6187 ComObjPtr<StorageController> aCtrl = (*it);
6188
6189 if ( (aCtrl->i_getName() != aName)
6190 && aCtrl->i_getBootable() == TRUE
6191 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6192 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6193 {
6194 aCtrl->i_setBootable(FALSE);
6195 break;
6196 }
6197 }
6198 }
6199
6200 if (SUCCEEDED(rc))
6201 {
6202 ctrl->i_setBootable(aBootable);
6203 i_setModified(IsModified_Storage);
6204 }
6205 }
6206
6207 if (SUCCEEDED(rc))
6208 {
6209 /* inform the direct session if any */
6210 alock.release();
6211 i_onStorageControllerChange();
6212 }
6213
6214 return rc;
6215}
6216
6217HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6218{
6219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6220
6221 HRESULT rc = i_checkStateDependency(MutableStateDep);
6222 if (FAILED(rc)) return rc;
6223
6224 ComObjPtr<StorageController> ctrl;
6225 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6226 if (FAILED(rc)) return rc;
6227
6228 {
6229 /* find all attached devices to the appropriate storage controller and detach them all */
6230 // make a temporary list because detachDevice invalidates iterators into
6231 // mMediaData->mAttachments
6232 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6233
6234 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6235 it != llAttachments2.end();
6236 ++it)
6237 {
6238 MediumAttachment *pAttachTemp = *it;
6239
6240 AutoCaller localAutoCaller(pAttachTemp);
6241 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6242
6243 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6244
6245 if (pAttachTemp->i_getControllerName() == aName)
6246 {
6247 rc = i_detachDevice(pAttachTemp, alock, NULL);
6248 if (FAILED(rc)) return rc;
6249 }
6250 }
6251 }
6252
6253 /* We can remove it now. */
6254 i_setModified(IsModified_Storage);
6255 mStorageControllers.backup();
6256
6257 ctrl->i_unshare();
6258
6259 mStorageControllers->remove(ctrl);
6260
6261 /* inform the direct session if any */
6262 alock.release();
6263 i_onStorageControllerChange();
6264
6265 return S_OK;
6266}
6267
6268HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6269 ComPtr<IUSBController> &aController)
6270{
6271 if ( (aType <= USBControllerType_Null)
6272 || (aType >= USBControllerType_Last))
6273 return setError(E_INVALIDARG,
6274 tr("Invalid USB controller type: %d"),
6275 aType);
6276
6277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6278
6279 HRESULT rc = i_checkStateDependency(MutableStateDep);
6280 if (FAILED(rc)) return rc;
6281
6282 /* try to find one with the same type first. */
6283 ComObjPtr<USBController> ctrl;
6284
6285 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6286 if (SUCCEEDED(rc))
6287 return setError(VBOX_E_OBJECT_IN_USE,
6288 tr("USB controller named '%s' already exists"),
6289 aName.c_str());
6290
6291 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6292 ULONG maxInstances;
6293 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6294 if (FAILED(rc))
6295 return rc;
6296
6297 ULONG cInstances = i_getUSBControllerCountByType(aType);
6298 if (cInstances >= maxInstances)
6299 return setError(E_INVALIDARG,
6300 tr("Too many USB controllers of this type"));
6301
6302 ctrl.createObject();
6303
6304 rc = ctrl->init(this, aName, aType);
6305 if (FAILED(rc)) return rc;
6306
6307 i_setModified(IsModified_USB);
6308 mUSBControllers.backup();
6309 mUSBControllers->push_back(ctrl);
6310
6311 ctrl.queryInterfaceTo(aController.asOutParam());
6312
6313 /* inform the direct session if any */
6314 alock.release();
6315 i_onUSBControllerChange();
6316
6317 return S_OK;
6318}
6319
6320HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6321{
6322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6323
6324 ComObjPtr<USBController> ctrl;
6325
6326 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6327 if (SUCCEEDED(rc))
6328 ctrl.queryInterfaceTo(aController.asOutParam());
6329
6330 return rc;
6331}
6332
6333HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6334 ULONG *aControllers)
6335{
6336 if ( (aType <= USBControllerType_Null)
6337 || (aType >= USBControllerType_Last))
6338 return setError(E_INVALIDARG,
6339 tr("Invalid USB controller type: %d"),
6340 aType);
6341
6342 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6343
6344 ComObjPtr<USBController> ctrl;
6345
6346 *aControllers = i_getUSBControllerCountByType(aType);
6347
6348 return S_OK;
6349}
6350
6351HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6352{
6353
6354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6355
6356 HRESULT rc = i_checkStateDependency(MutableStateDep);
6357 if (FAILED(rc)) return rc;
6358
6359 ComObjPtr<USBController> ctrl;
6360 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6361 if (FAILED(rc)) return rc;
6362
6363 i_setModified(IsModified_USB);
6364 mUSBControllers.backup();
6365
6366 ctrl->i_unshare();
6367
6368 mUSBControllers->remove(ctrl);
6369
6370 /* inform the direct session if any */
6371 alock.release();
6372 i_onUSBControllerChange();
6373
6374 return S_OK;
6375}
6376
6377HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6378 ULONG *aOriginX,
6379 ULONG *aOriginY,
6380 ULONG *aWidth,
6381 ULONG *aHeight,
6382 BOOL *aEnabled)
6383{
6384 uint32_t u32OriginX= 0;
6385 uint32_t u32OriginY= 0;
6386 uint32_t u32Width = 0;
6387 uint32_t u32Height = 0;
6388 uint16_t u16Flags = 0;
6389
6390 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6391 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6392 if (RT_FAILURE(vrc))
6393 {
6394#ifdef RT_OS_WINDOWS
6395 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6396 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6397 * So just assign fEnable to TRUE again.
6398 * The right fix would be to change GUI API wrappers to make sure that parameters
6399 * are changed only if API succeeds.
6400 */
6401 *aEnabled = TRUE;
6402#endif
6403 return setError(VBOX_E_IPRT_ERROR,
6404 tr("Saved guest size is not available (%Rrc)"),
6405 vrc);
6406 }
6407
6408 *aOriginX = u32OriginX;
6409 *aOriginY = u32OriginY;
6410 *aWidth = u32Width;
6411 *aHeight = u32Height;
6412 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6413
6414 return S_OK;
6415}
6416
6417HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6418 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6419{
6420 if (aScreenId != 0)
6421 return E_NOTIMPL;
6422
6423 if ( aBitmapFormat != BitmapFormat_BGR0
6424 && aBitmapFormat != BitmapFormat_BGRA
6425 && aBitmapFormat != BitmapFormat_RGBA
6426 && aBitmapFormat != BitmapFormat_PNG)
6427 return setError(E_NOTIMPL,
6428 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6429
6430 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6431
6432 uint8_t *pu8Data = NULL;
6433 uint32_t cbData = 0;
6434 uint32_t u32Width = 0;
6435 uint32_t u32Height = 0;
6436
6437 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6438
6439 if (RT_FAILURE(vrc))
6440 return setError(VBOX_E_IPRT_ERROR,
6441 tr("Saved thumbnail data is not available (%Rrc)"),
6442 vrc);
6443
6444 HRESULT hr = S_OK;
6445
6446 *aWidth = u32Width;
6447 *aHeight = u32Height;
6448
6449 if (cbData > 0)
6450 {
6451 /* Convert pixels to the format expected by the API caller. */
6452 if (aBitmapFormat == BitmapFormat_BGR0)
6453 {
6454 /* [0] B, [1] G, [2] R, [3] 0. */
6455 aData.resize(cbData);
6456 memcpy(&aData.front(), pu8Data, cbData);
6457 }
6458 else if (aBitmapFormat == BitmapFormat_BGRA)
6459 {
6460 /* [0] B, [1] G, [2] R, [3] A. */
6461 aData.resize(cbData);
6462 for (uint32_t i = 0; i < cbData; i += 4)
6463 {
6464 aData[i] = pu8Data[i];
6465 aData[i + 1] = pu8Data[i + 1];
6466 aData[i + 2] = pu8Data[i + 2];
6467 aData[i + 3] = 0xff;
6468 }
6469 }
6470 else if (aBitmapFormat == BitmapFormat_RGBA)
6471 {
6472 /* [0] R, [1] G, [2] B, [3] A. */
6473 aData.resize(cbData);
6474 for (uint32_t i = 0; i < cbData; i += 4)
6475 {
6476 aData[i] = pu8Data[i + 2];
6477 aData[i + 1] = pu8Data[i + 1];
6478 aData[i + 2] = pu8Data[i];
6479 aData[i + 3] = 0xff;
6480 }
6481 }
6482 else if (aBitmapFormat == BitmapFormat_PNG)
6483 {
6484 uint8_t *pu8PNG = NULL;
6485 uint32_t cbPNG = 0;
6486 uint32_t cxPNG = 0;
6487 uint32_t cyPNG = 0;
6488
6489 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6490
6491 if (RT_SUCCESS(vrc))
6492 {
6493 aData.resize(cbPNG);
6494 if (cbPNG)
6495 memcpy(&aData.front(), pu8PNG, cbPNG);
6496 }
6497 else
6498 hr = setError(VBOX_E_IPRT_ERROR,
6499 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6500 vrc);
6501
6502 RTMemFree(pu8PNG);
6503 }
6504 }
6505
6506 freeSavedDisplayScreenshot(pu8Data);
6507
6508 return hr;
6509}
6510
6511HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6512 ULONG *aWidth,
6513 ULONG *aHeight,
6514 std::vector<BitmapFormat_T> &aBitmapFormats)
6515{
6516 if (aScreenId != 0)
6517 return E_NOTIMPL;
6518
6519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6520
6521 uint8_t *pu8Data = NULL;
6522 uint32_t cbData = 0;
6523 uint32_t u32Width = 0;
6524 uint32_t u32Height = 0;
6525
6526 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6527
6528 if (RT_FAILURE(vrc))
6529 return setError(VBOX_E_IPRT_ERROR,
6530 tr("Saved screenshot data is not available (%Rrc)"),
6531 vrc);
6532
6533 *aWidth = u32Width;
6534 *aHeight = u32Height;
6535 aBitmapFormats.resize(1);
6536 aBitmapFormats[0] = BitmapFormat_PNG;
6537
6538 freeSavedDisplayScreenshot(pu8Data);
6539
6540 return S_OK;
6541}
6542
6543HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6544 BitmapFormat_T aBitmapFormat,
6545 ULONG *aWidth,
6546 ULONG *aHeight,
6547 std::vector<BYTE> &aData)
6548{
6549 if (aScreenId != 0)
6550 return E_NOTIMPL;
6551
6552 if (aBitmapFormat != BitmapFormat_PNG)
6553 return E_NOTIMPL;
6554
6555 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6556
6557 uint8_t *pu8Data = NULL;
6558 uint32_t cbData = 0;
6559 uint32_t u32Width = 0;
6560 uint32_t u32Height = 0;
6561
6562 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6563
6564 if (RT_FAILURE(vrc))
6565 return setError(VBOX_E_IPRT_ERROR,
6566 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6567 vrc);
6568
6569 *aWidth = u32Width;
6570 *aHeight = u32Height;
6571
6572 aData.resize(cbData);
6573 if (cbData)
6574 memcpy(&aData.front(), pu8Data, cbData);
6575
6576 freeSavedDisplayScreenshot(pu8Data);
6577
6578 return S_OK;
6579}
6580
6581HRESULT Machine::hotPlugCPU(ULONG aCpu)
6582{
6583 HRESULT rc = S_OK;
6584 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6585
6586 if (!mHWData->mCPUHotPlugEnabled)
6587 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6588
6589 if (aCpu >= mHWData->mCPUCount)
6590 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6591
6592 if (mHWData->mCPUAttached[aCpu])
6593 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6594
6595 alock.release();
6596 rc = i_onCPUChange(aCpu, false);
6597 alock.acquire();
6598 if (FAILED(rc)) return rc;
6599
6600 i_setModified(IsModified_MachineData);
6601 mHWData.backup();
6602 mHWData->mCPUAttached[aCpu] = true;
6603
6604 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6605 if (Global::IsOnline(mData->mMachineState))
6606 i_saveSettings(NULL);
6607
6608 return S_OK;
6609}
6610
6611HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6612{
6613 HRESULT rc = S_OK;
6614
6615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6616
6617 if (!mHWData->mCPUHotPlugEnabled)
6618 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6619
6620 if (aCpu >= SchemaDefs::MaxCPUCount)
6621 return setError(E_INVALIDARG,
6622 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6623 SchemaDefs::MaxCPUCount);
6624
6625 if (!mHWData->mCPUAttached[aCpu])
6626 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6627
6628 /* CPU 0 can't be detached */
6629 if (aCpu == 0)
6630 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6631
6632 alock.release();
6633 rc = i_onCPUChange(aCpu, true);
6634 alock.acquire();
6635 if (FAILED(rc)) return rc;
6636
6637 i_setModified(IsModified_MachineData);
6638 mHWData.backup();
6639 mHWData->mCPUAttached[aCpu] = false;
6640
6641 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6642 if (Global::IsOnline(mData->mMachineState))
6643 i_saveSettings(NULL);
6644
6645 return S_OK;
6646}
6647
6648HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6649{
6650 *aAttached = false;
6651
6652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6653
6654 /* If hotplug is enabled the CPU is always enabled. */
6655 if (!mHWData->mCPUHotPlugEnabled)
6656 {
6657 if (aCpu < mHWData->mCPUCount)
6658 *aAttached = true;
6659 }
6660 else
6661 {
6662 if (aCpu < SchemaDefs::MaxCPUCount)
6663 *aAttached = mHWData->mCPUAttached[aCpu];
6664 }
6665
6666 return S_OK;
6667}
6668
6669HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6670{
6671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6672
6673 Utf8Str log = i_getLogFilename(aIdx);
6674 if (!RTFileExists(log.c_str()))
6675 log.setNull();
6676 aFilename = log;
6677
6678 return S_OK;
6679}
6680
6681HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6682{
6683 if (aSize < 0)
6684 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6685
6686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6687
6688 HRESULT rc = S_OK;
6689 Utf8Str log = i_getLogFilename(aIdx);
6690
6691 /* do not unnecessarily hold the lock while doing something which does
6692 * not need the lock and potentially takes a long time. */
6693 alock.release();
6694
6695 /* Limit the chunk size to 32K for now, as that gives better performance
6696 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6697 * One byte expands to approx. 25 bytes of breathtaking XML. */
6698 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6699 aData.resize(cbData);
6700
6701 RTFILE LogFile;
6702 int vrc = RTFileOpen(&LogFile, log.c_str(),
6703 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6704 if (RT_SUCCESS(vrc))
6705 {
6706 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6707 if (RT_SUCCESS(vrc))
6708 aData.resize(cbData);
6709 else
6710 rc = setError(VBOX_E_IPRT_ERROR,
6711 tr("Could not read log file '%s' (%Rrc)"),
6712 log.c_str(), vrc);
6713 RTFileClose(LogFile);
6714 }
6715 else
6716 rc = setError(VBOX_E_IPRT_ERROR,
6717 tr("Could not open log file '%s' (%Rrc)"),
6718 log.c_str(), vrc);
6719
6720 if (FAILED(rc))
6721 aData.resize(0);
6722
6723 return rc;
6724}
6725
6726
6727/**
6728 * Currently this method doesn't attach device to the running VM,
6729 * just makes sure it's plugged on next VM start.
6730 */
6731HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6732{
6733 // lock scope
6734 {
6735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6736
6737 HRESULT rc = i_checkStateDependency(MutableStateDep);
6738 if (FAILED(rc)) return rc;
6739
6740 ChipsetType_T aChipset = ChipsetType_PIIX3;
6741 COMGETTER(ChipsetType)(&aChipset);
6742
6743 if (aChipset != ChipsetType_ICH9)
6744 {
6745 return setError(E_INVALIDARG,
6746 tr("Host PCI attachment only supported with ICH9 chipset"));
6747 }
6748
6749 // check if device with this host PCI address already attached
6750 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6751 it != mHWData->mPCIDeviceAssignments.end();
6752 ++it)
6753 {
6754 LONG iHostAddress = -1;
6755 ComPtr<PCIDeviceAttachment> pAttach;
6756 pAttach = *it;
6757 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6758 if (iHostAddress == aHostAddress)
6759 return setError(E_INVALIDARG,
6760 tr("Device with host PCI address already attached to this VM"));
6761 }
6762
6763 ComObjPtr<PCIDeviceAttachment> pda;
6764 char name[32];
6765
6766 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6767 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6768 Bstr bname(name);
6769 pda.createObject();
6770 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6771 i_setModified(IsModified_MachineData);
6772 mHWData.backup();
6773 mHWData->mPCIDeviceAssignments.push_back(pda);
6774 }
6775
6776 return S_OK;
6777}
6778
6779/**
6780 * Currently this method doesn't detach device from the running VM,
6781 * just makes sure it's not plugged on next VM start.
6782 */
6783HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6784{
6785 ComObjPtr<PCIDeviceAttachment> pAttach;
6786 bool fRemoved = false;
6787 HRESULT rc;
6788
6789 // lock scope
6790 {
6791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6792
6793 rc = i_checkStateDependency(MutableStateDep);
6794 if (FAILED(rc)) return rc;
6795
6796 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6797 it != mHWData->mPCIDeviceAssignments.end();
6798 ++it)
6799 {
6800 LONG iHostAddress = -1;
6801 pAttach = *it;
6802 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6803 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6804 {
6805 i_setModified(IsModified_MachineData);
6806 mHWData.backup();
6807 mHWData->mPCIDeviceAssignments.remove(pAttach);
6808 fRemoved = true;
6809 break;
6810 }
6811 }
6812 }
6813
6814
6815 /* Fire event outside of the lock */
6816 if (fRemoved)
6817 {
6818 Assert(!pAttach.isNull());
6819 ComPtr<IEventSource> es;
6820 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6821 Assert(SUCCEEDED(rc));
6822 Bstr mid;
6823 rc = this->COMGETTER(Id)(mid.asOutParam());
6824 Assert(SUCCEEDED(rc));
6825 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6826 }
6827
6828 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6829 tr("No host PCI device %08x attached"),
6830 aHostAddress
6831 );
6832}
6833
6834HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6835{
6836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6837
6838 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6839
6840 size_t i = 0;
6841 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6842 it != mHWData->mPCIDeviceAssignments.end();
6843 ++i, ++it)
6844 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6845
6846 return S_OK;
6847}
6848
6849HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6850{
6851 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6852
6853 return S_OK;
6854}
6855
6856HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6857{
6858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6859
6860 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6861
6862 return S_OK;
6863}
6864
6865HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6866{
6867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6868 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6869 if (SUCCEEDED(hrc))
6870 {
6871 hrc = mHWData.backupEx();
6872 if (SUCCEEDED(hrc))
6873 {
6874 i_setModified(IsModified_MachineData);
6875 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6876 }
6877 }
6878 return hrc;
6879}
6880
6881HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6882{
6883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6884 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6885 return S_OK;
6886}
6887
6888HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6889{
6890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6891 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6892 if (SUCCEEDED(hrc))
6893 {
6894 hrc = mHWData.backupEx();
6895 if (SUCCEEDED(hrc))
6896 {
6897 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6898 if (SUCCEEDED(hrc))
6899 i_setModified(IsModified_MachineData);
6900 }
6901 }
6902 return hrc;
6903}
6904
6905HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6906{
6907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6908
6909 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6910
6911 return S_OK;
6912}
6913
6914HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6915{
6916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6917 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6918 if (SUCCEEDED(hrc))
6919 {
6920 hrc = mHWData.backupEx();
6921 if (SUCCEEDED(hrc))
6922 {
6923 i_setModified(IsModified_MachineData);
6924 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6925 }
6926 }
6927 return hrc;
6928}
6929
6930HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6931{
6932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6933
6934 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6935
6936 return S_OK;
6937}
6938
6939HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6940{
6941 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6942
6943 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6944 if ( SUCCEEDED(hrc)
6945 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6946 {
6947 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6948 int vrc;
6949
6950 if (aAutostartEnabled)
6951 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6952 else
6953 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6954
6955 if (RT_SUCCESS(vrc))
6956 {
6957 hrc = mHWData.backupEx();
6958 if (SUCCEEDED(hrc))
6959 {
6960 i_setModified(IsModified_MachineData);
6961 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6962 }
6963 }
6964 else if (vrc == VERR_NOT_SUPPORTED)
6965 hrc = setError(VBOX_E_NOT_SUPPORTED,
6966 tr("The VM autostart feature is not supported on this platform"));
6967 else if (vrc == VERR_PATH_NOT_FOUND)
6968 hrc = setError(E_FAIL,
6969 tr("The path to the autostart database is not set"));
6970 else
6971 hrc = setError(E_UNEXPECTED,
6972 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6973 aAutostartEnabled ? "Adding" : "Removing",
6974 mUserData->s.strName.c_str(), vrc);
6975 }
6976 return hrc;
6977}
6978
6979HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6980{
6981 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6982
6983 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6984
6985 return S_OK;
6986}
6987
6988HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6989{
6990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6991 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6992 if (SUCCEEDED(hrc))
6993 {
6994 hrc = mHWData.backupEx();
6995 if (SUCCEEDED(hrc))
6996 {
6997 i_setModified(IsModified_MachineData);
6998 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6999 }
7000 }
7001 return hrc;
7002}
7003
7004HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7005{
7006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7007
7008 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7009
7010 return S_OK;
7011}
7012
7013HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7014{
7015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7016 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7017 if ( SUCCEEDED(hrc)
7018 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7019 {
7020 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7021 int vrc;
7022
7023 if (aAutostopType != AutostopType_Disabled)
7024 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7025 else
7026 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7027
7028 if (RT_SUCCESS(vrc))
7029 {
7030 hrc = mHWData.backupEx();
7031 if (SUCCEEDED(hrc))
7032 {
7033 i_setModified(IsModified_MachineData);
7034 mHWData->mAutostart.enmAutostopType = aAutostopType;
7035 }
7036 }
7037 else if (vrc == VERR_NOT_SUPPORTED)
7038 hrc = setError(VBOX_E_NOT_SUPPORTED,
7039 tr("The VM autostop feature is not supported on this platform"));
7040 else if (vrc == VERR_PATH_NOT_FOUND)
7041 hrc = setError(E_FAIL,
7042 tr("The path to the autostart database is not set"));
7043 else
7044 hrc = setError(E_UNEXPECTED,
7045 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7046 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7047 mUserData->s.strName.c_str(), vrc);
7048 }
7049 return hrc;
7050}
7051
7052HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7053{
7054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7055
7056 aDefaultFrontend = mHWData->mDefaultFrontend;
7057
7058 return S_OK;
7059}
7060
7061HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7062{
7063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7064 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7065 if (SUCCEEDED(hrc))
7066 {
7067 hrc = mHWData.backupEx();
7068 if (SUCCEEDED(hrc))
7069 {
7070 i_setModified(IsModified_MachineData);
7071 mHWData->mDefaultFrontend = aDefaultFrontend;
7072 }
7073 }
7074 return hrc;
7075}
7076
7077HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7078{
7079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7080 size_t cbIcon = mUserData->s.ovIcon.size();
7081 aIcon.resize(cbIcon);
7082 if (cbIcon)
7083 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7084 return S_OK;
7085}
7086
7087HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7088{
7089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7090 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7091 if (SUCCEEDED(hrc))
7092 {
7093 i_setModified(IsModified_MachineData);
7094 mUserData.backup();
7095 size_t cbIcon = aIcon.size();
7096 mUserData->s.ovIcon.resize(cbIcon);
7097 if (cbIcon)
7098 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7099 }
7100 return hrc;
7101}
7102
7103HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7104{
7105#ifdef VBOX_WITH_USB
7106 *aUSBProxyAvailable = true;
7107#else
7108 *aUSBProxyAvailable = false;
7109#endif
7110 return S_OK;
7111}
7112
7113HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7114{
7115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7116
7117 aVMProcessPriority = mUserData->s.strVMPriority;
7118
7119 return S_OK;
7120}
7121
7122HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7123{
7124 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7125 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7126 if (SUCCEEDED(hrc))
7127 {
7128 /** @todo r=klaus: currently this is marked as not implemented, as
7129 * the code for setting the priority of the process is not there
7130 * (neither when starting the VM nor at runtime). */
7131 ReturnComNotImplemented();
7132 hrc = mUserData.backupEx();
7133 if (SUCCEEDED(hrc))
7134 {
7135 i_setModified(IsModified_MachineData);
7136 mUserData->s.strVMPriority = aVMProcessPriority;
7137 }
7138 }
7139 return hrc;
7140}
7141
7142HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7143 ComPtr<IProgress> &aProgress)
7144{
7145 ComObjPtr<Progress> pP;
7146 Progress *ppP = pP;
7147 IProgress *iP = static_cast<IProgress *>(ppP);
7148 IProgress **pProgress = &iP;
7149
7150 IMachine *pTarget = aTarget;
7151
7152 /* Convert the options. */
7153 RTCList<CloneOptions_T> optList;
7154 if (aOptions.size())
7155 for (size_t i = 0; i < aOptions.size(); ++i)
7156 optList.append(aOptions[i]);
7157
7158 if (optList.contains(CloneOptions_Link))
7159 {
7160 if (!i_isSnapshotMachine())
7161 return setError(E_INVALIDARG,
7162 tr("Linked clone can only be created from a snapshot"));
7163 if (aMode != CloneMode_MachineState)
7164 return setError(E_INVALIDARG,
7165 tr("Linked clone can only be created for a single machine state"));
7166 }
7167 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7168
7169 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7170
7171 HRESULT rc = pWorker->start(pProgress);
7172
7173 pP = static_cast<Progress *>(*pProgress);
7174 pP.queryInterfaceTo(aProgress.asOutParam());
7175
7176 return rc;
7177
7178}
7179
7180HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7181{
7182 NOREF(aProgress);
7183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7184
7185 // This check should always fail.
7186 HRESULT rc = i_checkStateDependency(MutableStateDep);
7187 if (FAILED(rc)) return rc;
7188
7189 AssertFailedReturn(E_NOTIMPL);
7190}
7191
7192HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7193{
7194 NOREF(aSavedStateFile);
7195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7196
7197 // This check should always fail.
7198 HRESULT rc = i_checkStateDependency(MutableStateDep);
7199 if (FAILED(rc)) return rc;
7200
7201 AssertFailedReturn(E_NOTIMPL);
7202}
7203
7204HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7205{
7206 NOREF(aFRemoveFile);
7207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7208
7209 // This check should always fail.
7210 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7211 if (FAILED(rc)) return rc;
7212
7213 AssertFailedReturn(E_NOTIMPL);
7214}
7215
7216// public methods for internal purposes
7217/////////////////////////////////////////////////////////////////////////////
7218
7219/**
7220 * Adds the given IsModified_* flag to the dirty flags of the machine.
7221 * This must be called either during i_loadSettings or under the machine write lock.
7222 * @param fl
7223 */
7224void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7225{
7226 mData->flModifications |= fl;
7227 if (fAllowStateModification && i_isStateModificationAllowed())
7228 mData->mCurrentStateModified = true;
7229}
7230
7231/**
7232 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7233 * care of the write locking.
7234 *
7235 * @param fModifications The flag to add.
7236 */
7237void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7238{
7239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7240 i_setModified(fModification, fAllowStateModification);
7241}
7242
7243/**
7244 * Saves the registry entry of this machine to the given configuration node.
7245 *
7246 * @param aEntryNode Node to save the registry entry to.
7247 *
7248 * @note locks this object for reading.
7249 */
7250HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7251{
7252 AutoLimitedCaller autoCaller(this);
7253 AssertComRCReturnRC(autoCaller.rc());
7254
7255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7256
7257 data.uuid = mData->mUuid;
7258 data.strSettingsFile = mData->m_strConfigFile;
7259
7260 return S_OK;
7261}
7262
7263/**
7264 * Calculates the absolute path of the given path taking the directory of the
7265 * machine settings file as the current directory.
7266 *
7267 * @param aPath Path to calculate the absolute path for.
7268 * @param aResult Where to put the result (used only on success, can be the
7269 * same Utf8Str instance as passed in @a aPath).
7270 * @return IPRT result.
7271 *
7272 * @note Locks this object for reading.
7273 */
7274int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7275{
7276 AutoCaller autoCaller(this);
7277 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7278
7279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7280
7281 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7282
7283 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7284
7285 strSettingsDir.stripFilename();
7286 char folder[RTPATH_MAX];
7287 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7288 if (RT_SUCCESS(vrc))
7289 aResult = folder;
7290
7291 return vrc;
7292}
7293
7294/**
7295 * Copies strSource to strTarget, making it relative to the machine folder
7296 * if it is a subdirectory thereof, or simply copying it otherwise.
7297 *
7298 * @param strSource Path to evaluate and copy.
7299 * @param strTarget Buffer to receive target path.
7300 *
7301 * @note Locks this object for reading.
7302 */
7303void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7304 Utf8Str &strTarget)
7305{
7306 AutoCaller autoCaller(this);
7307 AssertComRCReturn(autoCaller.rc(), (void)0);
7308
7309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7310
7311 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7312 // use strTarget as a temporary buffer to hold the machine settings dir
7313 strTarget = mData->m_strConfigFileFull;
7314 strTarget.stripFilename();
7315 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7316 {
7317 // is relative: then append what's left
7318 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7319 // for empty paths (only possible for subdirs) use "." to avoid
7320 // triggering default settings for not present config attributes.
7321 if (strTarget.isEmpty())
7322 strTarget = ".";
7323 }
7324 else
7325 // is not relative: then overwrite
7326 strTarget = strSource;
7327}
7328
7329/**
7330 * Returns the full path to the machine's log folder in the
7331 * \a aLogFolder argument.
7332 */
7333void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7334{
7335 AutoCaller autoCaller(this);
7336 AssertComRCReturnVoid(autoCaller.rc());
7337
7338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7339
7340 char szTmp[RTPATH_MAX];
7341 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7342 if (RT_SUCCESS(vrc))
7343 {
7344 if (szTmp[0] && !mUserData.isNull())
7345 {
7346 char szTmp2[RTPATH_MAX];
7347 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7348 if (RT_SUCCESS(vrc))
7349 aLogFolder = BstrFmt("%s%c%s",
7350 szTmp2,
7351 RTPATH_DELIMITER,
7352 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7353 }
7354 else
7355 vrc = VERR_PATH_IS_RELATIVE;
7356 }
7357
7358 if (RT_FAILURE(vrc))
7359 {
7360 // fallback if VBOX_USER_LOGHOME is not set or invalid
7361 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7362 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7363 aLogFolder.append(RTPATH_DELIMITER);
7364 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7365 }
7366}
7367
7368/**
7369 * Returns the full path to the machine's log file for an given index.
7370 */
7371Utf8Str Machine::i_getLogFilename(ULONG idx)
7372{
7373 Utf8Str logFolder;
7374 getLogFolder(logFolder);
7375 Assert(logFolder.length());
7376
7377 Utf8Str log;
7378 if (idx == 0)
7379 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7380#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7381 else if (idx == 1)
7382 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7383 else
7384 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7385#else
7386 else
7387 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7388#endif
7389 return log;
7390}
7391
7392/**
7393 * Returns the full path to the machine's hardened log file.
7394 */
7395Utf8Str Machine::i_getHardeningLogFilename(void)
7396{
7397 Utf8Str strFilename;
7398 getLogFolder(strFilename);
7399 Assert(strFilename.length());
7400 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7401 return strFilename;
7402}
7403
7404
7405/**
7406 * Composes a unique saved state filename based on the current system time. The filename is
7407 * granular to the second so this will work so long as no more than one snapshot is taken on
7408 * a machine per second.
7409 *
7410 * Before version 4.1, we used this formula for saved state files:
7411 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7412 * which no longer works because saved state files can now be shared between the saved state of the
7413 * "saved" machine and an online snapshot, and the following would cause problems:
7414 * 1) save machine
7415 * 2) create online snapshot from that machine state --> reusing saved state file
7416 * 3) save machine again --> filename would be reused, breaking the online snapshot
7417 *
7418 * So instead we now use a timestamp.
7419 *
7420 * @param str
7421 */
7422
7423void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7424{
7425 AutoCaller autoCaller(this);
7426 AssertComRCReturnVoid(autoCaller.rc());
7427
7428 {
7429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7430 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7431 }
7432
7433 RTTIMESPEC ts;
7434 RTTimeNow(&ts);
7435 RTTIME time;
7436 RTTimeExplode(&time, &ts);
7437
7438 strStateFilePath += RTPATH_DELIMITER;
7439 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7440 time.i32Year, time.u8Month, time.u8MonthDay,
7441 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7442}
7443
7444/**
7445 * Returns the full path to the default video capture file.
7446 */
7447void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7448{
7449 AutoCaller autoCaller(this);
7450 AssertComRCReturnVoid(autoCaller.rc());
7451
7452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7453
7454 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7455 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7456 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7457}
7458
7459/**
7460 * Returns whether at least one USB controller is present for the VM.
7461 */
7462bool Machine::i_isUSBControllerPresent()
7463{
7464 AutoCaller autoCaller(this);
7465 AssertComRCReturn(autoCaller.rc(), false);
7466
7467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7468
7469 return (mUSBControllers->size() > 0);
7470}
7471
7472/**
7473 * @note Locks this object for writing, calls the client process
7474 * (inside the lock).
7475 */
7476HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7477 const Utf8Str &strFrontend,
7478 const Utf8Str &strEnvironment,
7479 ProgressProxy *aProgress)
7480{
7481 LogFlowThisFuncEnter();
7482
7483 AssertReturn(aControl, E_FAIL);
7484 AssertReturn(aProgress, E_FAIL);
7485 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7486
7487 AutoCaller autoCaller(this);
7488 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7489
7490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7491
7492 if (!mData->mRegistered)
7493 return setError(E_UNEXPECTED,
7494 tr("The machine '%s' is not registered"),
7495 mUserData->s.strName.c_str());
7496
7497 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7498
7499 /* The process started when launching a VM with separate UI/VM processes is always
7500 * the UI process, i.e. needs special handling as it won't claim the session. */
7501 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7502
7503 if (fSeparate)
7504 {
7505 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7506 return setError(VBOX_E_INVALID_OBJECT_STATE,
7507 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7508 mUserData->s.strName.c_str());
7509 }
7510 else
7511 {
7512 if ( mData->mSession.mState == SessionState_Locked
7513 || mData->mSession.mState == SessionState_Spawning
7514 || mData->mSession.mState == SessionState_Unlocking)
7515 return setError(VBOX_E_INVALID_OBJECT_STATE,
7516 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7517 mUserData->s.strName.c_str());
7518
7519 /* may not be busy */
7520 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7521 }
7522
7523 /* get the path to the executable */
7524 char szPath[RTPATH_MAX];
7525 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7526 size_t cchBufLeft = strlen(szPath);
7527 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7528 szPath[cchBufLeft] = 0;
7529 char *pszNamePart = szPath + cchBufLeft;
7530 cchBufLeft = sizeof(szPath) - cchBufLeft;
7531
7532 int vrc = VINF_SUCCESS;
7533 RTPROCESS pid = NIL_RTPROCESS;
7534
7535 RTENV env = RTENV_DEFAULT;
7536
7537 if (!strEnvironment.isEmpty())
7538 {
7539 char *newEnvStr = NULL;
7540
7541 do
7542 {
7543 /* clone the current environment */
7544 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7545 AssertRCBreakStmt(vrc2, vrc = vrc2);
7546
7547 newEnvStr = RTStrDup(strEnvironment.c_str());
7548 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7549
7550 /* put new variables to the environment
7551 * (ignore empty variable names here since RTEnv API
7552 * intentionally doesn't do that) */
7553 char *var = newEnvStr;
7554 for (char *p = newEnvStr; *p; ++p)
7555 {
7556 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7557 {
7558 *p = '\0';
7559 if (*var)
7560 {
7561 char *val = strchr(var, '=');
7562 if (val)
7563 {
7564 *val++ = '\0';
7565 vrc2 = RTEnvSetEx(env, var, val);
7566 }
7567 else
7568 vrc2 = RTEnvUnsetEx(env, var);
7569 if (RT_FAILURE(vrc2))
7570 break;
7571 }
7572 var = p + 1;
7573 }
7574 }
7575 if (RT_SUCCESS(vrc2) && *var)
7576 vrc2 = RTEnvPutEx(env, var);
7577
7578 AssertRCBreakStmt(vrc2, vrc = vrc2);
7579 }
7580 while (0);
7581
7582 if (newEnvStr != NULL)
7583 RTStrFree(newEnvStr);
7584 }
7585
7586 /* Hardening logging */
7587#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7588 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7589 {
7590 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7591 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7592 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7593 {
7594 Utf8Str strStartupLogDir = strHardeningLogFile;
7595 strStartupLogDir.stripFilename();
7596 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7597 file without stripping the file. */
7598 }
7599 strSupHardeningLogArg.append(strHardeningLogFile);
7600
7601 /* Remove legacy log filename to avoid confusion. */
7602 Utf8Str strOldStartupLogFile;
7603 getLogFolder(strOldStartupLogFile);
7604 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7605 RTFileDelete(strOldStartupLogFile.c_str());
7606 }
7607 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7608#else
7609 const char *pszSupHardeningLogArg = NULL;
7610#endif
7611
7612 Utf8Str strCanonicalName;
7613
7614#ifdef VBOX_WITH_QTGUI
7615 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7616 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7617 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7618 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7619 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7620 {
7621 strCanonicalName = "GUI/Qt";
7622# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7623 /* Modify the base path so that we don't need to use ".." below. */
7624 RTPathStripTrailingSlash(szPath);
7625 RTPathStripFilename(szPath);
7626 cchBufLeft = strlen(szPath);
7627 pszNamePart = szPath + cchBufLeft;
7628 cchBufLeft = sizeof(szPath) - cchBufLeft;
7629
7630# define OSX_APP_NAME "VirtualBoxVM"
7631# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7632
7633 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7634 if ( strAppOverride.contains(".")
7635 || strAppOverride.contains("/")
7636 || strAppOverride.contains("\\")
7637 || strAppOverride.contains(":"))
7638 strAppOverride.setNull();
7639 Utf8Str strAppPath;
7640 if (!strAppOverride.isEmpty())
7641 {
7642 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7643 Utf8Str strFullPath(szPath);
7644 strFullPath.append(strAppPath);
7645 /* there is a race, but people using this deserve the failure */
7646 if (!RTFileExists(strFullPath.c_str()))
7647 strAppOverride.setNull();
7648 }
7649 if (strAppOverride.isEmpty())
7650 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7651 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7652 strcpy(pszNamePart, strAppPath.c_str());
7653# else
7654 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7655 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7656 strcpy(pszNamePart, s_szVirtualBox_exe);
7657# endif
7658
7659 Utf8Str idStr = mData->mUuid.toString();
7660 const char *apszArgs[] =
7661 {
7662 szPath,
7663 "--comment", mUserData->s.strName.c_str(),
7664 "--startvm", idStr.c_str(),
7665 "--no-startvm-errormsgbox",
7666 NULL, /* For "--separate". */
7667 NULL, /* For "--sup-startup-log". */
7668 NULL
7669 };
7670 unsigned iArg = 6;
7671 if (fSeparate)
7672 apszArgs[iArg++] = "--separate";
7673 apszArgs[iArg++] = pszSupHardeningLogArg;
7674
7675 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7676 }
7677#else /* !VBOX_WITH_QTGUI */
7678 if (0)
7679 ;
7680#endif /* VBOX_WITH_QTGUI */
7681
7682 else
7683
7684#ifdef VBOX_WITH_VBOXSDL
7685 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7686 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7687 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7688 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7689 {
7690 strCanonicalName = "GUI/SDL";
7691 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7692 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7693 strcpy(pszNamePart, s_szVBoxSDL_exe);
7694
7695 Utf8Str idStr = mData->mUuid.toString();
7696 const char *apszArgs[] =
7697 {
7698 szPath,
7699 "--comment", mUserData->s.strName.c_str(),
7700 "--startvm", idStr.c_str(),
7701 NULL, /* For "--separate". */
7702 NULL, /* For "--sup-startup-log". */
7703 NULL
7704 };
7705 unsigned iArg = 5;
7706 if (fSeparate)
7707 apszArgs[iArg++] = "--separate";
7708 apszArgs[iArg++] = pszSupHardeningLogArg;
7709
7710 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7711 }
7712#else /* !VBOX_WITH_VBOXSDL */
7713 if (0)
7714 ;
7715#endif /* !VBOX_WITH_VBOXSDL */
7716
7717 else
7718
7719#ifdef VBOX_WITH_HEADLESS
7720 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7721 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7722 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7723 )
7724 {
7725 strCanonicalName = "headless";
7726 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7727 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7728 * and a VM works even if the server has not been installed.
7729 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7730 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7731 * differently in 4.0 and 3.x.
7732 */
7733 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7734 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7735 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7736
7737 Utf8Str idStr = mData->mUuid.toString();
7738 const char *apszArgs[] =
7739 {
7740 szPath,
7741 "--comment", mUserData->s.strName.c_str(),
7742 "--startvm", idStr.c_str(),
7743 "--vrde", "config",
7744 NULL, /* For "--capture". */
7745 NULL, /* For "--sup-startup-log". */
7746 NULL
7747 };
7748 unsigned iArg = 7;
7749 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7750 apszArgs[iArg++] = "--capture";
7751 apszArgs[iArg++] = pszSupHardeningLogArg;
7752
7753# ifdef RT_OS_WINDOWS
7754 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7755# else
7756 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7757# endif
7758 }
7759#else /* !VBOX_WITH_HEADLESS */
7760 if (0)
7761 ;
7762#endif /* !VBOX_WITH_HEADLESS */
7763 else
7764 {
7765 RTEnvDestroy(env);
7766 return setError(E_INVALIDARG,
7767 tr("Invalid frontend name: '%s'"),
7768 strFrontend.c_str());
7769 }
7770
7771 RTEnvDestroy(env);
7772
7773 if (RT_FAILURE(vrc))
7774 return setError(VBOX_E_IPRT_ERROR,
7775 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7776 mUserData->s.strName.c_str(), vrc);
7777
7778 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7779
7780 if (!fSeparate)
7781 {
7782 /*
7783 * Note that we don't release the lock here before calling the client,
7784 * because it doesn't need to call us back if called with a NULL argument.
7785 * Releasing the lock here is dangerous because we didn't prepare the
7786 * launch data yet, but the client we've just started may happen to be
7787 * too fast and call LockMachine() that will fail (because of PID, etc.),
7788 * so that the Machine will never get out of the Spawning session state.
7789 */
7790
7791 /* inform the session that it will be a remote one */
7792 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7793#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7794 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7795#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7796 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7797#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7798 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7799
7800 if (FAILED(rc))
7801 {
7802 /* restore the session state */
7803 mData->mSession.mState = SessionState_Unlocked;
7804 alock.release();
7805 mParent->i_addProcessToReap(pid);
7806 /* The failure may occur w/o any error info (from RPC), so provide one */
7807 return setError(VBOX_E_VM_ERROR,
7808 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7809 }
7810
7811 /* attach launch data to the machine */
7812 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7813 mData->mSession.mRemoteControls.push_back(aControl);
7814 mData->mSession.mProgress = aProgress;
7815 mData->mSession.mPID = pid;
7816 mData->mSession.mState = SessionState_Spawning;
7817 Assert(strCanonicalName.isNotEmpty());
7818 mData->mSession.mName = strCanonicalName;
7819 }
7820 else
7821 {
7822 /* For separate UI process we declare the launch as completed instantly, as the
7823 * actual headless VM start may or may not come. No point in remembering anything
7824 * yet, as what matters for us is when the headless VM gets started. */
7825 aProgress->i_notifyComplete(S_OK);
7826 }
7827
7828 alock.release();
7829 mParent->i_addProcessToReap(pid);
7830
7831 LogFlowThisFuncLeave();
7832 return S_OK;
7833}
7834
7835/**
7836 * Returns @c true if the given session machine instance has an open direct
7837 * session (and optionally also for direct sessions which are closing) and
7838 * returns the session control machine instance if so.
7839 *
7840 * Note that when the method returns @c false, the arguments remain unchanged.
7841 *
7842 * @param aMachine Session machine object.
7843 * @param aControl Direct session control object (optional).
7844 * @param aRequireVM If true then only allow VM sessions.
7845 * @param aAllowClosing If true then additionally a session which is currently
7846 * being closed will also be allowed.
7847 *
7848 * @note locks this object for reading.
7849 */
7850bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7851 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7852 bool aRequireVM /*= false*/,
7853 bool aAllowClosing /*= false*/)
7854{
7855 AutoLimitedCaller autoCaller(this);
7856 AssertComRCReturn(autoCaller.rc(), false);
7857
7858 /* just return false for inaccessible machines */
7859 if (getObjectState().getState() != ObjectState::Ready)
7860 return false;
7861
7862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7863
7864 if ( ( mData->mSession.mState == SessionState_Locked
7865 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7866 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7867 )
7868 {
7869 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7870
7871 aMachine = mData->mSession.mMachine;
7872
7873 if (aControl != NULL)
7874 *aControl = mData->mSession.mDirectControl;
7875
7876 return true;
7877 }
7878
7879 return false;
7880}
7881
7882/**
7883 * Returns @c true if the given machine has an spawning direct session.
7884 *
7885 * @note locks this object for reading.
7886 */
7887bool Machine::i_isSessionSpawning()
7888{
7889 AutoLimitedCaller autoCaller(this);
7890 AssertComRCReturn(autoCaller.rc(), false);
7891
7892 /* just return false for inaccessible machines */
7893 if (getObjectState().getState() != ObjectState::Ready)
7894 return false;
7895
7896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7897
7898 if (mData->mSession.mState == SessionState_Spawning)
7899 return true;
7900
7901 return false;
7902}
7903
7904/**
7905 * Called from the client watcher thread to check for unexpected client process
7906 * death during Session_Spawning state (e.g. before it successfully opened a
7907 * direct session).
7908 *
7909 * On Win32 and on OS/2, this method is called only when we've got the
7910 * direct client's process termination notification, so it always returns @c
7911 * true.
7912 *
7913 * On other platforms, this method returns @c true if the client process is
7914 * terminated and @c false if it's still alive.
7915 *
7916 * @note Locks this object for writing.
7917 */
7918bool Machine::i_checkForSpawnFailure()
7919{
7920 AutoCaller autoCaller(this);
7921 if (!autoCaller.isOk())
7922 {
7923 /* nothing to do */
7924 LogFlowThisFunc(("Already uninitialized!\n"));
7925 return true;
7926 }
7927
7928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7929
7930 if (mData->mSession.mState != SessionState_Spawning)
7931 {
7932 /* nothing to do */
7933 LogFlowThisFunc(("Not spawning any more!\n"));
7934 return true;
7935 }
7936
7937 HRESULT rc = S_OK;
7938
7939 /* PID not yet initialized, skip check. */
7940 if (mData->mSession.mPID == NIL_RTPROCESS)
7941 return false;
7942
7943 RTPROCSTATUS status;
7944 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7945
7946 if (vrc != VERR_PROCESS_RUNNING)
7947 {
7948 Utf8Str strExtraInfo;
7949
7950#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7951 /* If the startup logfile exists and is of non-zero length, tell the
7952 user to look there for more details to encourage them to attach it
7953 when reporting startup issues. */
7954 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7955 uint64_t cbStartupLogFile = 0;
7956 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7957 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7958 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7959#endif
7960
7961 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7962 rc = setError(E_FAIL,
7963 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7964 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7965 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7966 rc = setError(E_FAIL,
7967 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7968 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7969 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7970 rc = setError(E_FAIL,
7971 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7972 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7973 else
7974 rc = setError(E_FAIL,
7975 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7976 i_getName().c_str(), vrc, strExtraInfo.c_str());
7977 }
7978
7979 if (FAILED(rc))
7980 {
7981 /* Close the remote session, remove the remote control from the list
7982 * and reset session state to Closed (@note keep the code in sync with
7983 * the relevant part in LockMachine()). */
7984
7985 Assert(mData->mSession.mRemoteControls.size() == 1);
7986 if (mData->mSession.mRemoteControls.size() == 1)
7987 {
7988 ErrorInfoKeeper eik;
7989 mData->mSession.mRemoteControls.front()->Uninitialize();
7990 }
7991
7992 mData->mSession.mRemoteControls.clear();
7993 mData->mSession.mState = SessionState_Unlocked;
7994
7995 /* finalize the progress after setting the state */
7996 if (!mData->mSession.mProgress.isNull())
7997 {
7998 mData->mSession.mProgress->notifyComplete(rc);
7999 mData->mSession.mProgress.setNull();
8000 }
8001
8002 mData->mSession.mPID = NIL_RTPROCESS;
8003
8004 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8005 return true;
8006 }
8007
8008 return false;
8009}
8010
8011/**
8012 * Checks whether the machine can be registered. If so, commits and saves
8013 * all settings.
8014 *
8015 * @note Must be called from mParent's write lock. Locks this object and
8016 * children for writing.
8017 */
8018HRESULT Machine::i_prepareRegister()
8019{
8020 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8021
8022 AutoLimitedCaller autoCaller(this);
8023 AssertComRCReturnRC(autoCaller.rc());
8024
8025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8026
8027 /* wait for state dependents to drop to zero */
8028 i_ensureNoStateDependencies();
8029
8030 if (!mData->mAccessible)
8031 return setError(VBOX_E_INVALID_OBJECT_STATE,
8032 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8033 mUserData->s.strName.c_str(),
8034 mData->mUuid.toString().c_str());
8035
8036 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8037
8038 if (mData->mRegistered)
8039 return setError(VBOX_E_INVALID_OBJECT_STATE,
8040 tr("The machine '%s' with UUID {%s} is already registered"),
8041 mUserData->s.strName.c_str(),
8042 mData->mUuid.toString().c_str());
8043
8044 HRESULT rc = S_OK;
8045
8046 // Ensure the settings are saved. If we are going to be registered and
8047 // no config file exists yet, create it by calling i_saveSettings() too.
8048 if ( (mData->flModifications)
8049 || (!mData->pMachineConfigFile->fileExists())
8050 )
8051 {
8052 rc = i_saveSettings(NULL);
8053 // no need to check whether VirtualBox.xml needs saving too since
8054 // we can't have a machine XML file rename pending
8055 if (FAILED(rc)) return rc;
8056 }
8057
8058 /* more config checking goes here */
8059
8060 if (SUCCEEDED(rc))
8061 {
8062 /* we may have had implicit modifications we want to fix on success */
8063 i_commit();
8064
8065 mData->mRegistered = true;
8066 }
8067 else
8068 {
8069 /* we may have had implicit modifications we want to cancel on failure*/
8070 i_rollback(false /* aNotify */);
8071 }
8072
8073 return rc;
8074}
8075
8076/**
8077 * Increases the number of objects dependent on the machine state or on the
8078 * registered state. Guarantees that these two states will not change at least
8079 * until #releaseStateDependency() is called.
8080 *
8081 * Depending on the @a aDepType value, additional state checks may be made.
8082 * These checks will set extended error info on failure. See
8083 * #checkStateDependency() for more info.
8084 *
8085 * If this method returns a failure, the dependency is not added and the caller
8086 * is not allowed to rely on any particular machine state or registration state
8087 * value and may return the failed result code to the upper level.
8088 *
8089 * @param aDepType Dependency type to add.
8090 * @param aState Current machine state (NULL if not interested).
8091 * @param aRegistered Current registered state (NULL if not interested).
8092 *
8093 * @note Locks this object for writing.
8094 */
8095HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8096 MachineState_T *aState /* = NULL */,
8097 BOOL *aRegistered /* = NULL */)
8098{
8099 AutoCaller autoCaller(this);
8100 AssertComRCReturnRC(autoCaller.rc());
8101
8102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8103
8104 HRESULT rc = i_checkStateDependency(aDepType);
8105 if (FAILED(rc)) return rc;
8106
8107 {
8108 if (mData->mMachineStateChangePending != 0)
8109 {
8110 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8111 * drop to zero so don't add more. It may make sense to wait a bit
8112 * and retry before reporting an error (since the pending state
8113 * transition should be really quick) but let's just assert for
8114 * now to see if it ever happens on practice. */
8115
8116 AssertFailed();
8117
8118 return setError(E_ACCESSDENIED,
8119 tr("Machine state change is in progress. Please retry the operation later."));
8120 }
8121
8122 ++mData->mMachineStateDeps;
8123 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8124 }
8125
8126 if (aState)
8127 *aState = mData->mMachineState;
8128 if (aRegistered)
8129 *aRegistered = mData->mRegistered;
8130
8131 return S_OK;
8132}
8133
8134/**
8135 * Decreases the number of objects dependent on the machine state.
8136 * Must always complete the #addStateDependency() call after the state
8137 * dependency is no more necessary.
8138 */
8139void Machine::i_releaseStateDependency()
8140{
8141 AutoCaller autoCaller(this);
8142 AssertComRCReturnVoid(autoCaller.rc());
8143
8144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8145
8146 /* releaseStateDependency() w/o addStateDependency()? */
8147 AssertReturnVoid(mData->mMachineStateDeps != 0);
8148 -- mData->mMachineStateDeps;
8149
8150 if (mData->mMachineStateDeps == 0)
8151 {
8152 /* inform i_ensureNoStateDependencies() that there are no more deps */
8153 if (mData->mMachineStateChangePending != 0)
8154 {
8155 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8156 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8157 }
8158 }
8159}
8160
8161Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8162{
8163 /* start with nothing found */
8164 Utf8Str strResult("");
8165
8166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8167
8168 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8169 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8170 // found:
8171 strResult = it->second; // source is a Utf8Str
8172
8173 return strResult;
8174}
8175
8176// protected methods
8177/////////////////////////////////////////////////////////////////////////////
8178
8179/**
8180 * Performs machine state checks based on the @a aDepType value. If a check
8181 * fails, this method will set extended error info, otherwise it will return
8182 * S_OK. It is supposed, that on failure, the caller will immediately return
8183 * the return value of this method to the upper level.
8184 *
8185 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8186 *
8187 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8188 * current state of this machine object allows to change settings of the
8189 * machine (i.e. the machine is not registered, or registered but not running
8190 * and not saved). It is useful to call this method from Machine setters
8191 * before performing any change.
8192 *
8193 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8194 * as for MutableStateDep except that if the machine is saved, S_OK is also
8195 * returned. This is useful in setters which allow changing machine
8196 * properties when it is in the saved state.
8197 *
8198 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8199 * if the current state of this machine object allows to change runtime
8200 * changeable settings of the machine (i.e. the machine is not registered, or
8201 * registered but either running or not running and not saved). It is useful
8202 * to call this method from Machine setters before performing any changes to
8203 * runtime changeable settings.
8204 *
8205 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8206 * the same as for MutableOrRunningStateDep except that if the machine is
8207 * saved, S_OK is also returned. This is useful in setters which allow
8208 * changing runtime and saved state changeable machine properties.
8209 *
8210 * @param aDepType Dependency type to check.
8211 *
8212 * @note Non Machine based classes should use #addStateDependency() and
8213 * #releaseStateDependency() methods or the smart AutoStateDependency
8214 * template.
8215 *
8216 * @note This method must be called from under this object's read or write
8217 * lock.
8218 */
8219HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8220{
8221 switch (aDepType)
8222 {
8223 case AnyStateDep:
8224 {
8225 break;
8226 }
8227 case MutableStateDep:
8228 {
8229 if ( mData->mRegistered
8230 && ( !i_isSessionMachine()
8231 || ( mData->mMachineState != MachineState_Aborted
8232 && mData->mMachineState != MachineState_Teleported
8233 && mData->mMachineState != MachineState_PoweredOff
8234 )
8235 )
8236 )
8237 return setError(VBOX_E_INVALID_VM_STATE,
8238 tr("The machine is not mutable (state is %s)"),
8239 Global::stringifyMachineState(mData->mMachineState));
8240 break;
8241 }
8242 case MutableOrSavedStateDep:
8243 {
8244 if ( mData->mRegistered
8245 && ( !i_isSessionMachine()
8246 || ( mData->mMachineState != MachineState_Aborted
8247 && mData->mMachineState != MachineState_Teleported
8248 && mData->mMachineState != MachineState_Saved
8249 && mData->mMachineState != MachineState_PoweredOff
8250 )
8251 )
8252 )
8253 return setError(VBOX_E_INVALID_VM_STATE,
8254 tr("The machine is not mutable or saved (state is %s)"),
8255 Global::stringifyMachineState(mData->mMachineState));
8256 break;
8257 }
8258 case MutableOrRunningStateDep:
8259 {
8260 if ( mData->mRegistered
8261 && ( !i_isSessionMachine()
8262 || ( mData->mMachineState != MachineState_Aborted
8263 && mData->mMachineState != MachineState_Teleported
8264 && mData->mMachineState != MachineState_PoweredOff
8265 && !Global::IsOnline(mData->mMachineState)
8266 )
8267 )
8268 )
8269 return setError(VBOX_E_INVALID_VM_STATE,
8270 tr("The machine is not mutable or running (state is %s)"),
8271 Global::stringifyMachineState(mData->mMachineState));
8272 break;
8273 }
8274 case MutableOrSavedOrRunningStateDep:
8275 {
8276 if ( mData->mRegistered
8277 && ( !i_isSessionMachine()
8278 || ( mData->mMachineState != MachineState_Aborted
8279 && mData->mMachineState != MachineState_Teleported
8280 && mData->mMachineState != MachineState_Saved
8281 && mData->mMachineState != MachineState_PoweredOff
8282 && !Global::IsOnline(mData->mMachineState)
8283 )
8284 )
8285 )
8286 return setError(VBOX_E_INVALID_VM_STATE,
8287 tr("The machine is not mutable, saved or running (state is %s)"),
8288 Global::stringifyMachineState(mData->mMachineState));
8289 break;
8290 }
8291 }
8292
8293 return S_OK;
8294}
8295
8296/**
8297 * Helper to initialize all associated child objects and allocate data
8298 * structures.
8299 *
8300 * This method must be called as a part of the object's initialization procedure
8301 * (usually done in the #init() method).
8302 *
8303 * @note Must be called only from #init() or from #registeredInit().
8304 */
8305HRESULT Machine::initDataAndChildObjects()
8306{
8307 AutoCaller autoCaller(this);
8308 AssertComRCReturnRC(autoCaller.rc());
8309 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8310 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8311
8312 AssertReturn(!mData->mAccessible, E_FAIL);
8313
8314 /* allocate data structures */
8315 mSSData.allocate();
8316 mUserData.allocate();
8317 mHWData.allocate();
8318 mMediaData.allocate();
8319 mStorageControllers.allocate();
8320 mUSBControllers.allocate();
8321
8322 /* initialize mOSTypeId */
8323 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8324
8325 /* create associated BIOS settings object */
8326 unconst(mBIOSSettings).createObject();
8327 mBIOSSettings->init(this);
8328
8329 /* create an associated VRDE object (default is disabled) */
8330 unconst(mVRDEServer).createObject();
8331 mVRDEServer->init(this);
8332
8333 /* create associated serial port objects */
8334 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8335 {
8336 unconst(mSerialPorts[slot]).createObject();
8337 mSerialPorts[slot]->init(this, slot);
8338 }
8339
8340 /* create associated parallel port objects */
8341 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8342 {
8343 unconst(mParallelPorts[slot]).createObject();
8344 mParallelPorts[slot]->init(this, slot);
8345 }
8346
8347 /* create the audio adapter object (always present, default is disabled) */
8348 unconst(mAudioAdapter).createObject();
8349 mAudioAdapter->init(this);
8350
8351 /* create the USB device filters object (always present) */
8352 unconst(mUSBDeviceFilters).createObject();
8353 mUSBDeviceFilters->init(this);
8354
8355 /* create associated network adapter objects */
8356 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8357 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8358 {
8359 unconst(mNetworkAdapters[slot]).createObject();
8360 mNetworkAdapters[slot]->init(this, slot);
8361 }
8362
8363 /* create the bandwidth control */
8364 unconst(mBandwidthControl).createObject();
8365 mBandwidthControl->init(this);
8366
8367 return S_OK;
8368}
8369
8370/**
8371 * Helper to uninitialize all associated child objects and to free all data
8372 * structures.
8373 *
8374 * This method must be called as a part of the object's uninitialization
8375 * procedure (usually done in the #uninit() method).
8376 *
8377 * @note Must be called only from #uninit() or from #registeredInit().
8378 */
8379void Machine::uninitDataAndChildObjects()
8380{
8381 AutoCaller autoCaller(this);
8382 AssertComRCReturnVoid(autoCaller.rc());
8383 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8384 || getObjectState().getState() == ObjectState::Limited);
8385
8386 /* tell all our other child objects we've been uninitialized */
8387 if (mBandwidthControl)
8388 {
8389 mBandwidthControl->uninit();
8390 unconst(mBandwidthControl).setNull();
8391 }
8392
8393 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8394 {
8395 if (mNetworkAdapters[slot])
8396 {
8397 mNetworkAdapters[slot]->uninit();
8398 unconst(mNetworkAdapters[slot]).setNull();
8399 }
8400 }
8401
8402 if (mUSBDeviceFilters)
8403 {
8404 mUSBDeviceFilters->uninit();
8405 unconst(mUSBDeviceFilters).setNull();
8406 }
8407
8408 if (mAudioAdapter)
8409 {
8410 mAudioAdapter->uninit();
8411 unconst(mAudioAdapter).setNull();
8412 }
8413
8414 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8415 {
8416 if (mParallelPorts[slot])
8417 {
8418 mParallelPorts[slot]->uninit();
8419 unconst(mParallelPorts[slot]).setNull();
8420 }
8421 }
8422
8423 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8424 {
8425 if (mSerialPorts[slot])
8426 {
8427 mSerialPorts[slot]->uninit();
8428 unconst(mSerialPorts[slot]).setNull();
8429 }
8430 }
8431
8432 if (mVRDEServer)
8433 {
8434 mVRDEServer->uninit();
8435 unconst(mVRDEServer).setNull();
8436 }
8437
8438 if (mBIOSSettings)
8439 {
8440 mBIOSSettings->uninit();
8441 unconst(mBIOSSettings).setNull();
8442 }
8443
8444 /* Deassociate media (only when a real Machine or a SnapshotMachine
8445 * instance is uninitialized; SessionMachine instances refer to real
8446 * Machine media). This is necessary for a clean re-initialization of
8447 * the VM after successfully re-checking the accessibility state. Note
8448 * that in case of normal Machine or SnapshotMachine uninitialization (as
8449 * a result of unregistering or deleting the snapshot), outdated media
8450 * attachments will already be uninitialized and deleted, so this
8451 * code will not affect them. */
8452 if ( !!mMediaData
8453 && (!i_isSessionMachine())
8454 )
8455 {
8456 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8457 it != mMediaData->mAttachments.end();
8458 ++it)
8459 {
8460 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8461 if (pMedium.isNull())
8462 continue;
8463 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8464 AssertComRC(rc);
8465 }
8466 }
8467
8468 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8469 {
8470 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8471 if (mData->mFirstSnapshot)
8472 {
8473 // snapshots tree is protected by machine write lock; strictly
8474 // this isn't necessary here since we're deleting the entire
8475 // machine, but otherwise we assert in Snapshot::uninit()
8476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8477 mData->mFirstSnapshot->uninit();
8478 mData->mFirstSnapshot.setNull();
8479 }
8480
8481 mData->mCurrentSnapshot.setNull();
8482 }
8483
8484 /* free data structures (the essential mData structure is not freed here
8485 * since it may be still in use) */
8486 mMediaData.free();
8487 mStorageControllers.free();
8488 mUSBControllers.free();
8489 mHWData.free();
8490 mUserData.free();
8491 mSSData.free();
8492}
8493
8494/**
8495 * Returns a pointer to the Machine object for this machine that acts like a
8496 * parent for complex machine data objects such as shared folders, etc.
8497 *
8498 * For primary Machine objects and for SnapshotMachine objects, returns this
8499 * object's pointer itself. For SessionMachine objects, returns the peer
8500 * (primary) machine pointer.
8501 */
8502Machine* Machine::i_getMachine()
8503{
8504 if (i_isSessionMachine())
8505 return (Machine*)mPeer;
8506 return this;
8507}
8508
8509/**
8510 * Makes sure that there are no machine state dependents. If necessary, waits
8511 * for the number of dependents to drop to zero.
8512 *
8513 * Make sure this method is called from under this object's write lock to
8514 * guarantee that no new dependents may be added when this method returns
8515 * control to the caller.
8516 *
8517 * @note Locks this object for writing. The lock will be released while waiting
8518 * (if necessary).
8519 *
8520 * @warning To be used only in methods that change the machine state!
8521 */
8522void Machine::i_ensureNoStateDependencies()
8523{
8524 AssertReturnVoid(isWriteLockOnCurrentThread());
8525
8526 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8527
8528 /* Wait for all state dependents if necessary */
8529 if (mData->mMachineStateDeps != 0)
8530 {
8531 /* lazy semaphore creation */
8532 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8533 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8534
8535 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8536 mData->mMachineStateDeps));
8537
8538 ++mData->mMachineStateChangePending;
8539
8540 /* reset the semaphore before waiting, the last dependent will signal
8541 * it */
8542 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8543
8544 alock.release();
8545
8546 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8547
8548 alock.acquire();
8549
8550 -- mData->mMachineStateChangePending;
8551 }
8552}
8553
8554/**
8555 * Changes the machine state and informs callbacks.
8556 *
8557 * This method is not intended to fail so it either returns S_OK or asserts (and
8558 * returns a failure).
8559 *
8560 * @note Locks this object for writing.
8561 */
8562HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8563{
8564 LogFlowThisFuncEnter();
8565 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8566 Assert(aMachineState != MachineState_Null);
8567
8568 AutoCaller autoCaller(this);
8569 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8570
8571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8572
8573 /* wait for state dependents to drop to zero */
8574 i_ensureNoStateDependencies();
8575
8576 MachineState_T const enmOldState = mData->mMachineState;
8577 if (enmOldState != aMachineState)
8578 {
8579 mData->mMachineState = aMachineState;
8580 RTTimeNow(&mData->mLastStateChange);
8581
8582#ifdef VBOX_WITH_DTRACE_R3_MAIN
8583 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8584#endif
8585 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8586 }
8587
8588 LogFlowThisFuncLeave();
8589 return S_OK;
8590}
8591
8592/**
8593 * Searches for a shared folder with the given logical name
8594 * in the collection of shared folders.
8595 *
8596 * @param aName logical name of the shared folder
8597 * @param aSharedFolder where to return the found object
8598 * @param aSetError whether to set the error info if the folder is
8599 * not found
8600 * @return
8601 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8602 *
8603 * @note
8604 * must be called from under the object's lock!
8605 */
8606HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8607 ComObjPtr<SharedFolder> &aSharedFolder,
8608 bool aSetError /* = false */)
8609{
8610 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8611 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8612 it != mHWData->mSharedFolders.end();
8613 ++it)
8614 {
8615 SharedFolder *pSF = *it;
8616 AutoCaller autoCaller(pSF);
8617 if (pSF->i_getName() == aName)
8618 {
8619 aSharedFolder = pSF;
8620 rc = S_OK;
8621 break;
8622 }
8623 }
8624
8625 if (aSetError && FAILED(rc))
8626 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8627
8628 return rc;
8629}
8630
8631/**
8632 * Initializes all machine instance data from the given settings structures
8633 * from XML. The exception is the machine UUID which needs special handling
8634 * depending on the caller's use case, so the caller needs to set that herself.
8635 *
8636 * This gets called in several contexts during machine initialization:
8637 *
8638 * -- When machine XML exists on disk already and needs to be loaded into memory,
8639 * for example, from registeredInit() to load all registered machines on
8640 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8641 * attached to the machine should be part of some media registry already.
8642 *
8643 * -- During OVF import, when a machine config has been constructed from an
8644 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8645 * ensure that the media listed as attachments in the config (which have
8646 * been imported from the OVF) receive the correct registry ID.
8647 *
8648 * -- During VM cloning.
8649 *
8650 * @param config Machine settings from XML.
8651 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8652 * for each attached medium in the config.
8653 * @return
8654 */
8655HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8656 const Guid *puuidRegistry)
8657{
8658 // copy name, description, OS type, teleporter, UTC etc.
8659 mUserData->s = config.machineUserData;
8660
8661 // look up the object by Id to check it is valid
8662 ComPtr<IGuestOSType> guestOSType;
8663 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8664 guestOSType.asOutParam());
8665 if (FAILED(rc)) return rc;
8666
8667 // stateFile (optional)
8668 if (config.strStateFile.isEmpty())
8669 mSSData->strStateFilePath.setNull();
8670 else
8671 {
8672 Utf8Str stateFilePathFull(config.strStateFile);
8673 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8674 if (RT_FAILURE(vrc))
8675 return setError(E_FAIL,
8676 tr("Invalid saved state file path '%s' (%Rrc)"),
8677 config.strStateFile.c_str(),
8678 vrc);
8679 mSSData->strStateFilePath = stateFilePathFull;
8680 }
8681
8682 // snapshot folder needs special processing so set it again
8683 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8684 if (FAILED(rc)) return rc;
8685
8686 /* Copy the extra data items (Not in any case config is already the same as
8687 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8688 * make sure the extra data map is copied). */
8689 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8690
8691 /* currentStateModified (optional, default is true) */
8692 mData->mCurrentStateModified = config.fCurrentStateModified;
8693
8694 mData->mLastStateChange = config.timeLastStateChange;
8695
8696 /*
8697 * note: all mUserData members must be assigned prior this point because
8698 * we need to commit changes in order to let mUserData be shared by all
8699 * snapshot machine instances.
8700 */
8701 mUserData.commitCopy();
8702
8703 // machine registry, if present (must be loaded before snapshots)
8704 if (config.canHaveOwnMediaRegistry())
8705 {
8706 // determine machine folder
8707 Utf8Str strMachineFolder = i_getSettingsFileFull();
8708 strMachineFolder.stripFilename();
8709 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8710 config.mediaRegistry,
8711 strMachineFolder);
8712 if (FAILED(rc)) return rc;
8713 }
8714
8715 /* Snapshot node (optional) */
8716 size_t cRootSnapshots;
8717 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8718 {
8719 // there must be only one root snapshot
8720 Assert(cRootSnapshots == 1);
8721
8722 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8723
8724 rc = i_loadSnapshot(snap,
8725 config.uuidCurrentSnapshot,
8726 NULL); // no parent == first snapshot
8727 if (FAILED(rc)) return rc;
8728 }
8729
8730 // hardware data
8731 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8732 if (FAILED(rc)) return rc;
8733
8734 /*
8735 * NOTE: the assignment below must be the last thing to do,
8736 * otherwise it will be not possible to change the settings
8737 * somewhere in the code above because all setters will be
8738 * blocked by i_checkStateDependency(MutableStateDep).
8739 */
8740
8741 /* set the machine state to Aborted or Saved when appropriate */
8742 if (config.fAborted)
8743 {
8744 mSSData->strStateFilePath.setNull();
8745
8746 /* no need to use i_setMachineState() during init() */
8747 mData->mMachineState = MachineState_Aborted;
8748 }
8749 else if (!mSSData->strStateFilePath.isEmpty())
8750 {
8751 /* no need to use i_setMachineState() during init() */
8752 mData->mMachineState = MachineState_Saved;
8753 }
8754
8755 // after loading settings, we are no longer different from the XML on disk
8756 mData->flModifications = 0;
8757
8758 return S_OK;
8759}
8760
8761/**
8762 * Recursively loads all snapshots starting from the given.
8763 *
8764 * @param aNode <Snapshot> node.
8765 * @param aCurSnapshotId Current snapshot ID from the settings file.
8766 * @param aParentSnapshot Parent snapshot.
8767 */
8768HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8769 const Guid &aCurSnapshotId,
8770 Snapshot *aParentSnapshot)
8771{
8772 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8773 AssertReturn(!i_isSessionMachine(), E_FAIL);
8774
8775 HRESULT rc = S_OK;
8776
8777 Utf8Str strStateFile;
8778 if (!data.strStateFile.isEmpty())
8779 {
8780 /* optional */
8781 strStateFile = data.strStateFile;
8782 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8783 if (RT_FAILURE(vrc))
8784 return setError(E_FAIL,
8785 tr("Invalid saved state file path '%s' (%Rrc)"),
8786 strStateFile.c_str(),
8787 vrc);
8788 }
8789
8790 /* create a snapshot machine object */
8791 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8792 pSnapshotMachine.createObject();
8793 rc = pSnapshotMachine->initFromSettings(this,
8794 data.hardware,
8795 &data.debugging,
8796 &data.autostart,
8797 data.uuid.ref(),
8798 strStateFile);
8799 if (FAILED(rc)) return rc;
8800
8801 /* create a snapshot object */
8802 ComObjPtr<Snapshot> pSnapshot;
8803 pSnapshot.createObject();
8804 /* initialize the snapshot */
8805 rc = pSnapshot->init(mParent, // VirtualBox object
8806 data.uuid,
8807 data.strName,
8808 data.strDescription,
8809 data.timestamp,
8810 pSnapshotMachine,
8811 aParentSnapshot);
8812 if (FAILED(rc)) return rc;
8813
8814 /* memorize the first snapshot if necessary */
8815 if (!mData->mFirstSnapshot)
8816 mData->mFirstSnapshot = pSnapshot;
8817
8818 /* memorize the current snapshot when appropriate */
8819 if ( !mData->mCurrentSnapshot
8820 && pSnapshot->i_getId() == aCurSnapshotId
8821 )
8822 mData->mCurrentSnapshot = pSnapshot;
8823
8824 // now create the children
8825 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8826 it != data.llChildSnapshots.end();
8827 ++it)
8828 {
8829 const settings::Snapshot &childData = *it;
8830 // recurse
8831 rc = i_loadSnapshot(childData,
8832 aCurSnapshotId,
8833 pSnapshot); // parent = the one we created above
8834 if (FAILED(rc)) return rc;
8835 }
8836
8837 return rc;
8838}
8839
8840/**
8841 * Loads settings into mHWData.
8842 *
8843 * @param data Reference to the hardware settings.
8844 * @param pDbg Pointer to the debugging settings.
8845 * @param pAutostart Pointer to the autostart settings.
8846 */
8847HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8848 const Guid *puuidSnapshot,
8849 const settings::Hardware &data,
8850 const settings::Debugging *pDbg,
8851 const settings::Autostart *pAutostart)
8852{
8853 AssertReturn(!i_isSessionMachine(), E_FAIL);
8854
8855 HRESULT rc = S_OK;
8856
8857 try
8858 {
8859 /* The hardware version attribute (optional). */
8860 mHWData->mHWVersion = data.strVersion;
8861 mHWData->mHardwareUUID = data.uuid;
8862
8863 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8864 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8865 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8866 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8867 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8868 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8869 mHWData->mPAEEnabled = data.fPAE;
8870 mHWData->mLongMode = data.enmLongMode;
8871 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8872 mHWData->mAPIC = data.fAPIC;
8873 mHWData->mX2APIC = data.fX2APIC;
8874 mHWData->mCPUCount = data.cCPUs;
8875 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8876 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8877 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8878 mHWData->mCpuProfile = data.strCpuProfile;
8879
8880 // cpu
8881 if (mHWData->mCPUHotPlugEnabled)
8882 {
8883 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8884 it != data.llCpus.end();
8885 ++it)
8886 {
8887 const settings::Cpu &cpu = *it;
8888
8889 mHWData->mCPUAttached[cpu.ulId] = true;
8890 }
8891 }
8892
8893 // cpuid leafs
8894 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8895 it != data.llCpuIdLeafs.end();
8896 ++it)
8897 {
8898 const settings::CpuIdLeaf &leaf = *it;
8899
8900 switch (leaf.ulId)
8901 {
8902 case 0x0:
8903 case 0x1:
8904 case 0x2:
8905 case 0x3:
8906 case 0x4:
8907 case 0x5:
8908 case 0x6:
8909 case 0x7:
8910 case 0x8:
8911 case 0x9:
8912 case 0xA:
8913 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8914 break;
8915
8916 case 0x80000000:
8917 case 0x80000001:
8918 case 0x80000002:
8919 case 0x80000003:
8920 case 0x80000004:
8921 case 0x80000005:
8922 case 0x80000006:
8923 case 0x80000007:
8924 case 0x80000008:
8925 case 0x80000009:
8926 case 0x8000000A:
8927 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8928 break;
8929
8930 default:
8931 /* just ignore */
8932 break;
8933 }
8934 }
8935
8936 mHWData->mMemorySize = data.ulMemorySizeMB;
8937 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8938
8939 // boot order
8940 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8941 {
8942 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8943 if (it == data.mapBootOrder.end())
8944 mHWData->mBootOrder[i] = DeviceType_Null;
8945 else
8946 mHWData->mBootOrder[i] = it->second;
8947 }
8948
8949 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8950 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8951 mHWData->mMonitorCount = data.cMonitors;
8952 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8953 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8954 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8955 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8956 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8957 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8958 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8959 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8960 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8961 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8962 if (!data.strVideoCaptureFile.isEmpty())
8963 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8964 else
8965 mHWData->mVideoCaptureFile.setNull();
8966 mHWData->mFirmwareType = data.firmwareType;
8967 mHWData->mPointingHIDType = data.pointingHIDType;
8968 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8969 mHWData->mChipsetType = data.chipsetType;
8970 mHWData->mParavirtProvider = data.paravirtProvider;
8971 mHWData->mParavirtDebug = data.strParavirtDebug;
8972 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8973 mHWData->mHPETEnabled = data.fHPETEnabled;
8974
8975 /* VRDEServer */
8976 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8977 if (FAILED(rc)) return rc;
8978
8979 /* BIOS */
8980 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8981 if (FAILED(rc)) return rc;
8982
8983 // Bandwidth control (must come before network adapters)
8984 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8985 if (FAILED(rc)) return rc;
8986
8987 /* Shared folders */
8988 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8989 it != data.usbSettings.llUSBControllers.end();
8990 ++it)
8991 {
8992 const settings::USBController &settingsCtrl = *it;
8993 ComObjPtr<USBController> newCtrl;
8994
8995 newCtrl.createObject();
8996 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8997 mUSBControllers->push_back(newCtrl);
8998 }
8999
9000 /* USB device filters */
9001 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9002 if (FAILED(rc)) return rc;
9003
9004 // network adapters
9005 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9006 size_t oldCount = mNetworkAdapters.size();
9007 if (newCount > oldCount)
9008 {
9009 mNetworkAdapters.resize(newCount);
9010 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9011 {
9012 unconst(mNetworkAdapters[slot]).createObject();
9013 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9014 }
9015 }
9016 else if (newCount < oldCount)
9017 mNetworkAdapters.resize(newCount);
9018 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9019 it != data.llNetworkAdapters.end();
9020 ++it)
9021 {
9022 const settings::NetworkAdapter &nic = *it;
9023
9024 /* slot unicity is guaranteed by XML Schema */
9025 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9026 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9027 if (FAILED(rc)) return rc;
9028 }
9029
9030 // serial ports
9031 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9032 it != data.llSerialPorts.end();
9033 ++it)
9034 {
9035 const settings::SerialPort &s = *it;
9036
9037 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9038 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9039 if (FAILED(rc)) return rc;
9040 }
9041
9042 // parallel ports (optional)
9043 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9044 it != data.llParallelPorts.end();
9045 ++it)
9046 {
9047 const settings::ParallelPort &p = *it;
9048
9049 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9050 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9051 if (FAILED(rc)) return rc;
9052 }
9053
9054 /* AudioAdapter */
9055 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9056 if (FAILED(rc)) return rc;
9057
9058 /* storage controllers */
9059 rc = i_loadStorageControllers(data.storage,
9060 puuidRegistry,
9061 puuidSnapshot);
9062 if (FAILED(rc)) return rc;
9063
9064 /* Shared folders */
9065 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9066 it != data.llSharedFolders.end();
9067 ++it)
9068 {
9069 const settings::SharedFolder &sf = *it;
9070
9071 ComObjPtr<SharedFolder> sharedFolder;
9072 /* Check for double entries. Not allowed! */
9073 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9074 if (SUCCEEDED(rc))
9075 return setError(VBOX_E_OBJECT_IN_USE,
9076 tr("Shared folder named '%s' already exists"),
9077 sf.strName.c_str());
9078
9079 /* Create the new shared folder. Don't break on error. This will be
9080 * reported when the machine starts. */
9081 sharedFolder.createObject();
9082 rc = sharedFolder->init(i_getMachine(),
9083 sf.strName,
9084 sf.strHostPath,
9085 RT_BOOL(sf.fWritable),
9086 RT_BOOL(sf.fAutoMount),
9087 false /* fFailOnError */);
9088 if (FAILED(rc)) return rc;
9089 mHWData->mSharedFolders.push_back(sharedFolder);
9090 }
9091
9092 // Clipboard
9093 mHWData->mClipboardMode = data.clipboardMode;
9094
9095 // drag'n'drop
9096 mHWData->mDnDMode = data.dndMode;
9097
9098 // guest settings
9099 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9100
9101 // IO settings
9102 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9103 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9104
9105 // Host PCI devices
9106 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9107 it != data.pciAttachments.end();
9108 ++it)
9109 {
9110 const settings::HostPCIDeviceAttachment &hpda = *it;
9111 ComObjPtr<PCIDeviceAttachment> pda;
9112
9113 pda.createObject();
9114 pda->i_loadSettings(this, hpda);
9115 mHWData->mPCIDeviceAssignments.push_back(pda);
9116 }
9117
9118 /*
9119 * (The following isn't really real hardware, but it lives in HWData
9120 * for reasons of convenience.)
9121 */
9122
9123#ifdef VBOX_WITH_GUEST_PROPS
9124 /* Guest properties (optional) */
9125
9126 /* Only load transient guest properties for configs which have saved
9127 * state, because there shouldn't be any for powered off VMs. The same
9128 * logic applies for snapshots, as offline snapshots shouldn't have
9129 * any such properties. They confuse the code in various places.
9130 * Note: can't rely on the machine state, as it isn't set yet. */
9131 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9132 /* apologies for the hacky unconst() usage, but this needs hacking
9133 * actually inconsistent settings into consistency, otherwise there
9134 * will be some corner cases where the inconsistency survives
9135 * surprisingly long without getting fixed, especially for snapshots
9136 * as there are no config changes. */
9137 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9138 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
9139 it != llGuestProperties.end();
9140 /*nothing*/)
9141 {
9142 const settings::GuestProperty &prop = *it;
9143 uint32_t fFlags = guestProp::NILFLAG;
9144 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9145 if ( fSkipTransientGuestProperties
9146 && ( fFlags & guestProp::TRANSIENT
9147 || fFlags & guestProp::TRANSRESET))
9148 {
9149 it = llGuestProperties.erase(it);
9150 continue;
9151 }
9152 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9153 mHWData->mGuestProperties[prop.strName] = property;
9154 ++it;
9155 }
9156#endif /* VBOX_WITH_GUEST_PROPS defined */
9157
9158 rc = i_loadDebugging(pDbg);
9159 if (FAILED(rc))
9160 return rc;
9161
9162 mHWData->mAutostart = *pAutostart;
9163
9164 /* default frontend */
9165 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9166 }
9167 catch(std::bad_alloc &)
9168 {
9169 return E_OUTOFMEMORY;
9170 }
9171
9172 AssertComRC(rc);
9173 return rc;
9174}
9175
9176/**
9177 * Called from Machine::loadHardware() to load the debugging settings of the
9178 * machine.
9179 *
9180 * @param pDbg Pointer to the settings.
9181 */
9182HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9183{
9184 mHWData->mDebugging = *pDbg;
9185 /* no more processing currently required, this will probably change. */
9186 return S_OK;
9187}
9188
9189/**
9190 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9191 *
9192 * @param data
9193 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9194 * @param puuidSnapshot
9195 * @return
9196 */
9197HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9198 const Guid *puuidRegistry,
9199 const Guid *puuidSnapshot)
9200{
9201 AssertReturn(!i_isSessionMachine(), E_FAIL);
9202
9203 HRESULT rc = S_OK;
9204
9205 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9206 it != data.llStorageControllers.end();
9207 ++it)
9208 {
9209 const settings::StorageController &ctlData = *it;
9210
9211 ComObjPtr<StorageController> pCtl;
9212 /* Try to find one with the name first. */
9213 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9214 if (SUCCEEDED(rc))
9215 return setError(VBOX_E_OBJECT_IN_USE,
9216 tr("Storage controller named '%s' already exists"),
9217 ctlData.strName.c_str());
9218
9219 pCtl.createObject();
9220 rc = pCtl->init(this,
9221 ctlData.strName,
9222 ctlData.storageBus,
9223 ctlData.ulInstance,
9224 ctlData.fBootable);
9225 if (FAILED(rc)) return rc;
9226
9227 mStorageControllers->push_back(pCtl);
9228
9229 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9230 if (FAILED(rc)) return rc;
9231
9232 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9233 if (FAILED(rc)) return rc;
9234
9235 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9236 if (FAILED(rc)) return rc;
9237
9238 /* Load the attached devices now. */
9239 rc = i_loadStorageDevices(pCtl,
9240 ctlData,
9241 puuidRegistry,
9242 puuidSnapshot);
9243 if (FAILED(rc)) return rc;
9244 }
9245
9246 return S_OK;
9247}
9248
9249/**
9250 * Called from i_loadStorageControllers for a controller's devices.
9251 *
9252 * @param aStorageController
9253 * @param data
9254 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9255 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9256 * @return
9257 */
9258HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9259 const settings::StorageController &data,
9260 const Guid *puuidRegistry,
9261 const Guid *puuidSnapshot)
9262{
9263 HRESULT rc = S_OK;
9264
9265 /* paranoia: detect duplicate attachments */
9266 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9267 it != data.llAttachedDevices.end();
9268 ++it)
9269 {
9270 const settings::AttachedDevice &ad = *it;
9271
9272 for (settings::AttachedDevicesList::const_iterator it2 = it;
9273 it2 != data.llAttachedDevices.end();
9274 ++it2)
9275 {
9276 if (it == it2)
9277 continue;
9278
9279 const settings::AttachedDevice &ad2 = *it2;
9280
9281 if ( ad.lPort == ad2.lPort
9282 && ad.lDevice == ad2.lDevice)
9283 {
9284 return setError(E_FAIL,
9285 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9286 aStorageController->i_getName().c_str(),
9287 ad.lPort,
9288 ad.lDevice,
9289 mUserData->s.strName.c_str());
9290 }
9291 }
9292 }
9293
9294 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9295 it != data.llAttachedDevices.end();
9296 ++it)
9297 {
9298 const settings::AttachedDevice &dev = *it;
9299 ComObjPtr<Medium> medium;
9300
9301 switch (dev.deviceType)
9302 {
9303 case DeviceType_Floppy:
9304 case DeviceType_DVD:
9305 if (dev.strHostDriveSrc.isNotEmpty())
9306 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9307 false /* fRefresh */, medium);
9308 else
9309 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9310 dev.uuid,
9311 false /* fRefresh */,
9312 false /* aSetError */,
9313 medium);
9314 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9315 // This is not an error. The host drive or UUID might have vanished, so just go
9316 // ahead without this removeable medium attachment
9317 rc = S_OK;
9318 break;
9319
9320 case DeviceType_HardDisk:
9321 {
9322 /* find a hard disk by UUID */
9323 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9324 if (FAILED(rc))
9325 {
9326 if (i_isSnapshotMachine())
9327 {
9328 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9329 // so the user knows that the bad disk is in a snapshot somewhere
9330 com::ErrorInfo info;
9331 return setError(E_FAIL,
9332 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9333 puuidSnapshot->raw(),
9334 info.getText().raw());
9335 }
9336 else
9337 return rc;
9338 }
9339
9340 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9341
9342 if (medium->i_getType() == MediumType_Immutable)
9343 {
9344 if (i_isSnapshotMachine())
9345 return setError(E_FAIL,
9346 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9347 "of the virtual machine '%s' ('%s')"),
9348 medium->i_getLocationFull().c_str(),
9349 dev.uuid.raw(),
9350 puuidSnapshot->raw(),
9351 mUserData->s.strName.c_str(),
9352 mData->m_strConfigFileFull.c_str());
9353
9354 return setError(E_FAIL,
9355 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9356 medium->i_getLocationFull().c_str(),
9357 dev.uuid.raw(),
9358 mUserData->s.strName.c_str(),
9359 mData->m_strConfigFileFull.c_str());
9360 }
9361
9362 if (medium->i_getType() == MediumType_MultiAttach)
9363 {
9364 if (i_isSnapshotMachine())
9365 return setError(E_FAIL,
9366 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9367 "of the virtual machine '%s' ('%s')"),
9368 medium->i_getLocationFull().c_str(),
9369 dev.uuid.raw(),
9370 puuidSnapshot->raw(),
9371 mUserData->s.strName.c_str(),
9372 mData->m_strConfigFileFull.c_str());
9373
9374 return setError(E_FAIL,
9375 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9376 medium->i_getLocationFull().c_str(),
9377 dev.uuid.raw(),
9378 mUserData->s.strName.c_str(),
9379 mData->m_strConfigFileFull.c_str());
9380 }
9381
9382 if ( !i_isSnapshotMachine()
9383 && medium->i_getChildren().size() != 0
9384 )
9385 return setError(E_FAIL,
9386 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9387 "because it has %d differencing child hard disks"),
9388 medium->i_getLocationFull().c_str(),
9389 dev.uuid.raw(),
9390 mUserData->s.strName.c_str(),
9391 mData->m_strConfigFileFull.c_str(),
9392 medium->i_getChildren().size());
9393
9394 if (i_findAttachment(mMediaData->mAttachments,
9395 medium))
9396 return setError(E_FAIL,
9397 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9398 medium->i_getLocationFull().c_str(),
9399 dev.uuid.raw(),
9400 mUserData->s.strName.c_str(),
9401 mData->m_strConfigFileFull.c_str());
9402
9403 break;
9404 }
9405
9406 default:
9407 return setError(E_FAIL,
9408 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9409 medium->i_getLocationFull().c_str(),
9410 mUserData->s.strName.c_str(),
9411 mData->m_strConfigFileFull.c_str());
9412 }
9413
9414 if (FAILED(rc))
9415 break;
9416
9417 /* Bandwidth groups are loaded at this point. */
9418 ComObjPtr<BandwidthGroup> pBwGroup;
9419
9420 if (!dev.strBwGroup.isEmpty())
9421 {
9422 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9423 if (FAILED(rc))
9424 return setError(E_FAIL,
9425 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9426 medium->i_getLocationFull().c_str(),
9427 dev.strBwGroup.c_str(),
9428 mUserData->s.strName.c_str(),
9429 mData->m_strConfigFileFull.c_str());
9430 pBwGroup->i_reference();
9431 }
9432
9433 const Bstr controllerName = aStorageController->i_getName();
9434 ComObjPtr<MediumAttachment> pAttachment;
9435 pAttachment.createObject();
9436 rc = pAttachment->init(this,
9437 medium,
9438 controllerName,
9439 dev.lPort,
9440 dev.lDevice,
9441 dev.deviceType,
9442 false,
9443 dev.fPassThrough,
9444 dev.fTempEject,
9445 dev.fNonRotational,
9446 dev.fDiscard,
9447 dev.fHotPluggable,
9448 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9449 if (FAILED(rc)) break;
9450
9451 /* associate the medium with this machine and snapshot */
9452 if (!medium.isNull())
9453 {
9454 AutoCaller medCaller(medium);
9455 if (FAILED(medCaller.rc())) return medCaller.rc();
9456 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9457
9458 if (i_isSnapshotMachine())
9459 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9460 else
9461 rc = medium->i_addBackReference(mData->mUuid);
9462 /* If the medium->addBackReference fails it sets an appropriate
9463 * error message, so no need to do any guesswork here. */
9464
9465 if (puuidRegistry)
9466 // caller wants registry ID to be set on all attached media (OVF import case)
9467 medium->i_addRegistry(*puuidRegistry);
9468 }
9469
9470 if (FAILED(rc))
9471 break;
9472
9473 /* back up mMediaData to let registeredInit() properly rollback on failure
9474 * (= limited accessibility) */
9475 i_setModified(IsModified_Storage);
9476 mMediaData.backup();
9477 mMediaData->mAttachments.push_back(pAttachment);
9478 }
9479
9480 return rc;
9481}
9482
9483/**
9484 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9485 *
9486 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9487 * @param aSnapshot where to return the found snapshot
9488 * @param aSetError true to set extended error info on failure
9489 */
9490HRESULT Machine::i_findSnapshotById(const Guid &aId,
9491 ComObjPtr<Snapshot> &aSnapshot,
9492 bool aSetError /* = false */)
9493{
9494 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9495
9496 if (!mData->mFirstSnapshot)
9497 {
9498 if (aSetError)
9499 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9500 return E_FAIL;
9501 }
9502
9503 if (aId.isZero())
9504 aSnapshot = mData->mFirstSnapshot;
9505 else
9506 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9507
9508 if (!aSnapshot)
9509 {
9510 if (aSetError)
9511 return setError(E_FAIL,
9512 tr("Could not find a snapshot with UUID {%s}"),
9513 aId.toString().c_str());
9514 return E_FAIL;
9515 }
9516
9517 return S_OK;
9518}
9519
9520/**
9521 * Returns the snapshot with the given name or fails of no such snapshot.
9522 *
9523 * @param aName snapshot name to find
9524 * @param aSnapshot where to return the found snapshot
9525 * @param aSetError true to set extended error info on failure
9526 */
9527HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9528 ComObjPtr<Snapshot> &aSnapshot,
9529 bool aSetError /* = false */)
9530{
9531 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9532
9533 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9534
9535 if (!mData->mFirstSnapshot)
9536 {
9537 if (aSetError)
9538 return setError(VBOX_E_OBJECT_NOT_FOUND,
9539 tr("This machine does not have any snapshots"));
9540 return VBOX_E_OBJECT_NOT_FOUND;
9541 }
9542
9543 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9544
9545 if (!aSnapshot)
9546 {
9547 if (aSetError)
9548 return setError(VBOX_E_OBJECT_NOT_FOUND,
9549 tr("Could not find a snapshot named '%s'"), strName.c_str());
9550 return VBOX_E_OBJECT_NOT_FOUND;
9551 }
9552
9553 return S_OK;
9554}
9555
9556/**
9557 * Returns a storage controller object with the given name.
9558 *
9559 * @param aName storage controller name to find
9560 * @param aStorageController where to return the found storage controller
9561 * @param aSetError true to set extended error info on failure
9562 */
9563HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9564 ComObjPtr<StorageController> &aStorageController,
9565 bool aSetError /* = false */)
9566{
9567 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9568
9569 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9570 it != mStorageControllers->end();
9571 ++it)
9572 {
9573 if ((*it)->i_getName() == aName)
9574 {
9575 aStorageController = (*it);
9576 return S_OK;
9577 }
9578 }
9579
9580 if (aSetError)
9581 return setError(VBOX_E_OBJECT_NOT_FOUND,
9582 tr("Could not find a storage controller named '%s'"),
9583 aName.c_str());
9584 return VBOX_E_OBJECT_NOT_FOUND;
9585}
9586
9587/**
9588 * Returns a USB controller object with the given name.
9589 *
9590 * @param aName USB controller name to find
9591 * @param aUSBController where to return the found USB controller
9592 * @param aSetError true to set extended error info on failure
9593 */
9594HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9595 ComObjPtr<USBController> &aUSBController,
9596 bool aSetError /* = false */)
9597{
9598 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9599
9600 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9601 it != mUSBControllers->end();
9602 ++it)
9603 {
9604 if ((*it)->i_getName() == aName)
9605 {
9606 aUSBController = (*it);
9607 return S_OK;
9608 }
9609 }
9610
9611 if (aSetError)
9612 return setError(VBOX_E_OBJECT_NOT_FOUND,
9613 tr("Could not find a storage controller named '%s'"),
9614 aName.c_str());
9615 return VBOX_E_OBJECT_NOT_FOUND;
9616}
9617
9618/**
9619 * Returns the number of USB controller instance of the given type.
9620 *
9621 * @param enmType USB controller type.
9622 */
9623ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9624{
9625 ULONG cCtrls = 0;
9626
9627 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9628 it != mUSBControllers->end();
9629 ++it)
9630 {
9631 if ((*it)->i_getControllerType() == enmType)
9632 cCtrls++;
9633 }
9634
9635 return cCtrls;
9636}
9637
9638HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9639 MediaData::AttachmentList &atts)
9640{
9641 AutoCaller autoCaller(this);
9642 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9643
9644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9645
9646 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9647 it != mMediaData->mAttachments.end();
9648 ++it)
9649 {
9650 const ComObjPtr<MediumAttachment> &pAtt = *it;
9651 // should never happen, but deal with NULL pointers in the list.
9652 AssertContinue(!pAtt.isNull());
9653
9654 // getControllerName() needs caller+read lock
9655 AutoCaller autoAttCaller(pAtt);
9656 if (FAILED(autoAttCaller.rc()))
9657 {
9658 atts.clear();
9659 return autoAttCaller.rc();
9660 }
9661 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9662
9663 if (pAtt->i_getControllerName() == aName)
9664 atts.push_back(pAtt);
9665 }
9666
9667 return S_OK;
9668}
9669
9670
9671/**
9672 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9673 * file if the machine name was changed and about creating a new settings file
9674 * if this is a new machine.
9675 *
9676 * @note Must be never called directly but only from #saveSettings().
9677 */
9678HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9679{
9680 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9681
9682 HRESULT rc = S_OK;
9683
9684 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9685
9686 /// @todo need to handle primary group change, too
9687
9688 /* attempt to rename the settings file if machine name is changed */
9689 if ( mUserData->s.fNameSync
9690 && mUserData.isBackedUp()
9691 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9692 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9693 )
9694 {
9695 bool dirRenamed = false;
9696 bool fileRenamed = false;
9697
9698 Utf8Str configFile, newConfigFile;
9699 Utf8Str configFilePrev, newConfigFilePrev;
9700 Utf8Str configDir, newConfigDir;
9701
9702 do
9703 {
9704 int vrc = VINF_SUCCESS;
9705
9706 Utf8Str name = mUserData.backedUpData()->s.strName;
9707 Utf8Str newName = mUserData->s.strName;
9708 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9709 if (group == "/")
9710 group.setNull();
9711 Utf8Str newGroup = mUserData->s.llGroups.front();
9712 if (newGroup == "/")
9713 newGroup.setNull();
9714
9715 configFile = mData->m_strConfigFileFull;
9716
9717 /* first, rename the directory if it matches the group and machine name */
9718 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9719 group.c_str(), RTPATH_DELIMITER, name.c_str());
9720 /** @todo hack, make somehow use of ComposeMachineFilename */
9721 if (mUserData->s.fDirectoryIncludesUUID)
9722 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9723 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9724 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9725 /** @todo hack, make somehow use of ComposeMachineFilename */
9726 if (mUserData->s.fDirectoryIncludesUUID)
9727 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9728 configDir = configFile;
9729 configDir.stripFilename();
9730 newConfigDir = configDir;
9731 if ( configDir.length() >= groupPlusName.length()
9732 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9733 groupPlusName.c_str()))
9734 {
9735 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9736 Utf8Str newConfigBaseDir(newConfigDir);
9737 newConfigDir.append(newGroupPlusName);
9738 /* consistency: use \ if appropriate on the platform */
9739 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9740 /* new dir and old dir cannot be equal here because of 'if'
9741 * above and because name != newName */
9742 Assert(configDir != newConfigDir);
9743 if (!fSettingsFileIsNew)
9744 {
9745 /* perform real rename only if the machine is not new */
9746 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9747 if ( vrc == VERR_FILE_NOT_FOUND
9748 || vrc == VERR_PATH_NOT_FOUND)
9749 {
9750 /* create the parent directory, then retry renaming */
9751 Utf8Str parent(newConfigDir);
9752 parent.stripFilename();
9753 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9754 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9755 }
9756 if (RT_FAILURE(vrc))
9757 {
9758 rc = setError(E_FAIL,
9759 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9760 configDir.c_str(),
9761 newConfigDir.c_str(),
9762 vrc);
9763 break;
9764 }
9765 /* delete subdirectories which are no longer needed */
9766 Utf8Str dir(configDir);
9767 dir.stripFilename();
9768 while (dir != newConfigBaseDir && dir != ".")
9769 {
9770 vrc = RTDirRemove(dir.c_str());
9771 if (RT_FAILURE(vrc))
9772 break;
9773 dir.stripFilename();
9774 }
9775 dirRenamed = true;
9776 }
9777 }
9778
9779 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9780 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9781
9782 /* then try to rename the settings file itself */
9783 if (newConfigFile != configFile)
9784 {
9785 /* get the path to old settings file in renamed directory */
9786 configFile = Utf8StrFmt("%s%c%s",
9787 newConfigDir.c_str(),
9788 RTPATH_DELIMITER,
9789 RTPathFilename(configFile.c_str()));
9790 if (!fSettingsFileIsNew)
9791 {
9792 /* perform real rename only if the machine is not new */
9793 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9794 if (RT_FAILURE(vrc))
9795 {
9796 rc = setError(E_FAIL,
9797 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9798 configFile.c_str(),
9799 newConfigFile.c_str(),
9800 vrc);
9801 break;
9802 }
9803 fileRenamed = true;
9804 configFilePrev = configFile;
9805 configFilePrev += "-prev";
9806 newConfigFilePrev = newConfigFile;
9807 newConfigFilePrev += "-prev";
9808 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9809 }
9810 }
9811
9812 // update m_strConfigFileFull amd mConfigFile
9813 mData->m_strConfigFileFull = newConfigFile;
9814 // compute the relative path too
9815 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9816
9817 // store the old and new so that VirtualBox::i_saveSettings() can update
9818 // the media registry
9819 if ( mData->mRegistered
9820 && (configDir != newConfigDir || configFile != newConfigFile))
9821 {
9822 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9823
9824 if (pfNeedsGlobalSaveSettings)
9825 *pfNeedsGlobalSaveSettings = true;
9826 }
9827
9828 // in the saved state file path, replace the old directory with the new directory
9829 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9830 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9831
9832 // and do the same thing for the saved state file paths of all the online snapshots
9833 if (mData->mFirstSnapshot)
9834 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9835 newConfigDir.c_str());
9836 }
9837 while (0);
9838
9839 if (FAILED(rc))
9840 {
9841 /* silently try to rename everything back */
9842 if (fileRenamed)
9843 {
9844 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9845 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9846 }
9847 if (dirRenamed)
9848 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9849 }
9850
9851 if (FAILED(rc)) return rc;
9852 }
9853
9854 if (fSettingsFileIsNew)
9855 {
9856 /* create a virgin config file */
9857 int vrc = VINF_SUCCESS;
9858
9859 /* ensure the settings directory exists */
9860 Utf8Str path(mData->m_strConfigFileFull);
9861 path.stripFilename();
9862 if (!RTDirExists(path.c_str()))
9863 {
9864 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9865 if (RT_FAILURE(vrc))
9866 {
9867 return setError(E_FAIL,
9868 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9869 path.c_str(),
9870 vrc);
9871 }
9872 }
9873
9874 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9875 path = Utf8Str(mData->m_strConfigFileFull);
9876 RTFILE f = NIL_RTFILE;
9877 vrc = RTFileOpen(&f, path.c_str(),
9878 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9879 if (RT_FAILURE(vrc))
9880 return setError(E_FAIL,
9881 tr("Could not create the settings file '%s' (%Rrc)"),
9882 path.c_str(),
9883 vrc);
9884 RTFileClose(f);
9885 }
9886
9887 return rc;
9888}
9889
9890/**
9891 * Saves and commits machine data, user data and hardware data.
9892 *
9893 * Note that on failure, the data remains uncommitted.
9894 *
9895 * @a aFlags may combine the following flags:
9896 *
9897 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9898 * Used when saving settings after an operation that makes them 100%
9899 * correspond to the settings from the current snapshot.
9900 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9901 * #isReallyModified() returns false. This is necessary for cases when we
9902 * change machine data directly, not through the backup()/commit() mechanism.
9903 * - SaveS_Force: settings will be saved without doing a deep compare of the
9904 * settings structures. This is used when this is called because snapshots
9905 * have changed to avoid the overhead of the deep compare.
9906 *
9907 * @note Must be called from under this object's write lock. Locks children for
9908 * writing.
9909 *
9910 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9911 * initialized to false and that will be set to true by this function if
9912 * the caller must invoke VirtualBox::i_saveSettings() because the global
9913 * settings have changed. This will happen if a machine rename has been
9914 * saved and the global machine and media registries will therefore need
9915 * updating.
9916 */
9917HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9918 int aFlags /*= 0*/)
9919{
9920 LogFlowThisFuncEnter();
9921
9922 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9923
9924 /* make sure child objects are unable to modify the settings while we are
9925 * saving them */
9926 i_ensureNoStateDependencies();
9927
9928 AssertReturn(!i_isSnapshotMachine(),
9929 E_FAIL);
9930
9931 HRESULT rc = S_OK;
9932 bool fNeedsWrite = false;
9933
9934 /* First, prepare to save settings. It will care about renaming the
9935 * settings directory and file if the machine name was changed and about
9936 * creating a new settings file if this is a new machine. */
9937 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9938 if (FAILED(rc)) return rc;
9939
9940 // keep a pointer to the current settings structures
9941 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9942 settings::MachineConfigFile *pNewConfig = NULL;
9943
9944 try
9945 {
9946 // make a fresh one to have everyone write stuff into
9947 pNewConfig = new settings::MachineConfigFile(NULL);
9948 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9949
9950 // now go and copy all the settings data from COM to the settings structures
9951 // (this calles i_saveSettings() on all the COM objects in the machine)
9952 i_copyMachineDataToSettings(*pNewConfig);
9953
9954 if (aFlags & SaveS_ResetCurStateModified)
9955 {
9956 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9957 mData->mCurrentStateModified = FALSE;
9958 fNeedsWrite = true; // always, no need to compare
9959 }
9960 else if (aFlags & SaveS_Force)
9961 {
9962 fNeedsWrite = true; // always, no need to compare
9963 }
9964 else
9965 {
9966 if (!mData->mCurrentStateModified)
9967 {
9968 // do a deep compare of the settings that we just saved with the settings
9969 // previously stored in the config file; this invokes MachineConfigFile::operator==
9970 // which does a deep compare of all the settings, which is expensive but less expensive
9971 // than writing out XML in vain
9972 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9973
9974 // could still be modified if any settings changed
9975 mData->mCurrentStateModified = fAnySettingsChanged;
9976
9977 fNeedsWrite = fAnySettingsChanged;
9978 }
9979 else
9980 fNeedsWrite = true;
9981 }
9982
9983 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9984
9985 if (fNeedsWrite)
9986 // now spit it all out!
9987 pNewConfig->write(mData->m_strConfigFileFull);
9988
9989 mData->pMachineConfigFile = pNewConfig;
9990 delete pOldConfig;
9991 i_commit();
9992
9993 // after saving settings, we are no longer different from the XML on disk
9994 mData->flModifications = 0;
9995 }
9996 catch (HRESULT err)
9997 {
9998 // we assume that error info is set by the thrower
9999 rc = err;
10000
10001 // restore old config
10002 delete pNewConfig;
10003 mData->pMachineConfigFile = pOldConfig;
10004 }
10005 catch (...)
10006 {
10007 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10008 }
10009
10010 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10011 {
10012 /* Fire the data change event, even on failure (since we've already
10013 * committed all data). This is done only for SessionMachines because
10014 * mutable Machine instances are always not registered (i.e. private
10015 * to the client process that creates them) and thus don't need to
10016 * inform callbacks. */
10017 if (i_isSessionMachine())
10018 mParent->i_onMachineDataChange(mData->mUuid);
10019 }
10020
10021 LogFlowThisFunc(("rc=%08X\n", rc));
10022 LogFlowThisFuncLeave();
10023 return rc;
10024}
10025
10026/**
10027 * Implementation for saving the machine settings into the given
10028 * settings::MachineConfigFile instance. This copies machine extradata
10029 * from the previous machine config file in the instance data, if any.
10030 *
10031 * This gets called from two locations:
10032 *
10033 * -- Machine::i_saveSettings(), during the regular XML writing;
10034 *
10035 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10036 * exported to OVF and we write the VirtualBox proprietary XML
10037 * into a <vbox:Machine> tag.
10038 *
10039 * This routine fills all the fields in there, including snapshots, *except*
10040 * for the following:
10041 *
10042 * -- fCurrentStateModified. There is some special logic associated with that.
10043 *
10044 * The caller can then call MachineConfigFile::write() or do something else
10045 * with it.
10046 *
10047 * Caller must hold the machine lock!
10048 *
10049 * This throws XML errors and HRESULT, so the caller must have a catch block!
10050 */
10051void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10052{
10053 // deep copy extradata
10054 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10055
10056 config.uuid = mData->mUuid;
10057
10058 // copy name, description, OS type, teleport, UTC etc.
10059 config.machineUserData = mUserData->s;
10060
10061 if ( mData->mMachineState == MachineState_Saved
10062 || mData->mMachineState == MachineState_Restoring
10063 // when doing certain snapshot operations we may or may not have
10064 // a saved state in the current state, so keep everything as is
10065 || ( ( mData->mMachineState == MachineState_Snapshotting
10066 || mData->mMachineState == MachineState_DeletingSnapshot
10067 || mData->mMachineState == MachineState_RestoringSnapshot)
10068 && (!mSSData->strStateFilePath.isEmpty())
10069 )
10070 )
10071 {
10072 Assert(!mSSData->strStateFilePath.isEmpty());
10073 /* try to make the file name relative to the settings file dir */
10074 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10075 }
10076 else
10077 {
10078 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10079 config.strStateFile.setNull();
10080 }
10081
10082 if (mData->mCurrentSnapshot)
10083 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10084 else
10085 config.uuidCurrentSnapshot.clear();
10086
10087 config.timeLastStateChange = mData->mLastStateChange;
10088 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10089 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10090
10091 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10092 if (FAILED(rc)) throw rc;
10093
10094 // save machine's media registry if this is VirtualBox 4.0 or later
10095 if (config.canHaveOwnMediaRegistry())
10096 {
10097 // determine machine folder
10098 Utf8Str strMachineFolder = i_getSettingsFileFull();
10099 strMachineFolder.stripFilename();
10100 mParent->i_saveMediaRegistry(config.mediaRegistry,
10101 i_getId(), // only media with registry ID == machine UUID
10102 strMachineFolder);
10103 // this throws HRESULT
10104 }
10105
10106 // save snapshots
10107 rc = i_saveAllSnapshots(config);
10108 if (FAILED(rc)) throw rc;
10109}
10110
10111/**
10112 * Saves all snapshots of the machine into the given machine config file. Called
10113 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10114 * @param config
10115 * @return
10116 */
10117HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10118{
10119 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10120
10121 HRESULT rc = S_OK;
10122
10123 try
10124 {
10125 config.llFirstSnapshot.clear();
10126
10127 if (mData->mFirstSnapshot)
10128 {
10129 // the settings use a list for "the first snapshot"
10130 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10131
10132 // get reference to the snapshot on the list and work on that
10133 // element straight in the list to avoid excessive copying later
10134 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10135 if (FAILED(rc)) throw rc;
10136 }
10137
10138// if (mType == IsSessionMachine)
10139// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10140
10141 }
10142 catch (HRESULT err)
10143 {
10144 /* we assume that error info is set by the thrower */
10145 rc = err;
10146 }
10147 catch (...)
10148 {
10149 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10150 }
10151
10152 return rc;
10153}
10154
10155/**
10156 * Saves the VM hardware configuration. It is assumed that the
10157 * given node is empty.
10158 *
10159 * @param data Reference to the settings object for the hardware config.
10160 * @param pDbg Pointer to the settings object for the debugging config
10161 * which happens to live in mHWData.
10162 * @param pAutostart Pointer to the settings object for the autostart config
10163 * which happens to live in mHWData.
10164 */
10165HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10166 settings::Autostart *pAutostart)
10167{
10168 HRESULT rc = S_OK;
10169
10170 try
10171 {
10172 /* The hardware version attribute (optional).
10173 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10174 if ( mHWData->mHWVersion == "1"
10175 && mSSData->strStateFilePath.isEmpty()
10176 )
10177 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10178 other point needs to be found where this can be done. */
10179
10180 data.strVersion = mHWData->mHWVersion;
10181 data.uuid = mHWData->mHardwareUUID;
10182
10183 // CPU
10184 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10185 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10186 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10187 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10188 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10189 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10190 data.fPAE = !!mHWData->mPAEEnabled;
10191 data.enmLongMode = mHWData->mLongMode;
10192 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10193 data.fAPIC = !!mHWData->mAPIC;
10194 data.fX2APIC = !!mHWData->mX2APIC;
10195 data.cCPUs = mHWData->mCPUCount;
10196 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10197 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10198 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10199 data.strCpuProfile = mHWData->mCpuProfile;
10200
10201 data.llCpus.clear();
10202 if (data.fCpuHotPlug)
10203 {
10204 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10205 {
10206 if (mHWData->mCPUAttached[idx])
10207 {
10208 settings::Cpu cpu;
10209 cpu.ulId = idx;
10210 data.llCpus.push_back(cpu);
10211 }
10212 }
10213 }
10214
10215 /* Standard and Extended CPUID leafs. */
10216 data.llCpuIdLeafs.clear();
10217 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10218 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10219 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10220 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10221 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10222 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10223
10224 // memory
10225 data.ulMemorySizeMB = mHWData->mMemorySize;
10226 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10227
10228 // firmware
10229 data.firmwareType = mHWData->mFirmwareType;
10230
10231 // HID
10232 data.pointingHIDType = mHWData->mPointingHIDType;
10233 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10234
10235 // chipset
10236 data.chipsetType = mHWData->mChipsetType;
10237
10238 // paravirt
10239 data.paravirtProvider = mHWData->mParavirtProvider;
10240 data.strParavirtDebug = mHWData->mParavirtDebug;
10241
10242 // emulated USB card reader
10243 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10244
10245 // HPET
10246 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10247
10248 // boot order
10249 data.mapBootOrder.clear();
10250 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10251 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10252
10253 // display
10254 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10255 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10256 data.cMonitors = mHWData->mMonitorCount;
10257 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10258 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10259 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10260 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10261 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10262 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10263 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10264 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10265 {
10266 if (mHWData->maVideoCaptureScreens[i])
10267 ASMBitSet(&data.u64VideoCaptureScreens, i);
10268 else
10269 ASMBitClear(&data.u64VideoCaptureScreens, i);
10270 }
10271 /* store relative video capture file if possible */
10272 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10273
10274 /* VRDEServer settings (optional) */
10275 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10276 if (FAILED(rc)) throw rc;
10277
10278 /* BIOS (required) */
10279 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10280 if (FAILED(rc)) throw rc;
10281
10282 /* USB Controller (required) */
10283 data.usbSettings.llUSBControllers.clear();
10284 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10285 {
10286 ComObjPtr<USBController> ctrl = *it;
10287 settings::USBController settingsCtrl;
10288
10289 settingsCtrl.strName = ctrl->i_getName();
10290 settingsCtrl.enmType = ctrl->i_getControllerType();
10291
10292 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10293 }
10294
10295 /* USB device filters (required) */
10296 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10297 if (FAILED(rc)) throw rc;
10298
10299 /* Network adapters (required) */
10300 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10301 data.llNetworkAdapters.clear();
10302 /* Write out only the nominal number of network adapters for this
10303 * chipset type. Since Machine::commit() hasn't been called there
10304 * may be extra NIC settings in the vector. */
10305 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10306 {
10307 settings::NetworkAdapter nic;
10308 nic.ulSlot = (uint32_t)slot;
10309 /* paranoia check... must not be NULL, but must not crash either. */
10310 if (mNetworkAdapters[slot])
10311 {
10312 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10313 if (FAILED(rc)) throw rc;
10314
10315 data.llNetworkAdapters.push_back(nic);
10316 }
10317 }
10318
10319 /* Serial ports */
10320 data.llSerialPorts.clear();
10321 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10322 {
10323 if (mSerialPorts[slot]->i_hasDefaults())
10324 continue;
10325
10326 settings::SerialPort s;
10327 s.ulSlot = slot;
10328 rc = mSerialPorts[slot]->i_saveSettings(s);
10329 if (FAILED(rc)) return rc;
10330
10331 data.llSerialPorts.push_back(s);
10332 }
10333
10334 /* Parallel ports */
10335 data.llParallelPorts.clear();
10336 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10337 {
10338 if (mParallelPorts[slot]->i_hasDefaults())
10339 continue;
10340
10341 settings::ParallelPort p;
10342 p.ulSlot = slot;
10343 rc = mParallelPorts[slot]->i_saveSettings(p);
10344 if (FAILED(rc)) return rc;
10345
10346 data.llParallelPorts.push_back(p);
10347 }
10348
10349 /* Audio adapter */
10350 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10351 if (FAILED(rc)) return rc;
10352
10353 rc = i_saveStorageControllers(data.storage);
10354 if (FAILED(rc)) return rc;
10355
10356 /* Shared folders */
10357 data.llSharedFolders.clear();
10358 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10359 it != mHWData->mSharedFolders.end();
10360 ++it)
10361 {
10362 SharedFolder *pSF = *it;
10363 AutoCaller sfCaller(pSF);
10364 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10365 settings::SharedFolder sf;
10366 sf.strName = pSF->i_getName();
10367 sf.strHostPath = pSF->i_getHostPath();
10368 sf.fWritable = !!pSF->i_isWritable();
10369 sf.fAutoMount = !!pSF->i_isAutoMounted();
10370
10371 data.llSharedFolders.push_back(sf);
10372 }
10373
10374 // clipboard
10375 data.clipboardMode = mHWData->mClipboardMode;
10376
10377 // drag'n'drop
10378 data.dndMode = mHWData->mDnDMode;
10379
10380 /* Guest */
10381 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10382
10383 // IO settings
10384 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10385 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10386
10387 /* BandwidthControl (required) */
10388 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10389 if (FAILED(rc)) throw rc;
10390
10391 /* Host PCI devices */
10392 data.pciAttachments.clear();
10393 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10394 it != mHWData->mPCIDeviceAssignments.end();
10395 ++it)
10396 {
10397 ComObjPtr<PCIDeviceAttachment> pda = *it;
10398 settings::HostPCIDeviceAttachment hpda;
10399
10400 rc = pda->i_saveSettings(hpda);
10401 if (FAILED(rc)) throw rc;
10402
10403 data.pciAttachments.push_back(hpda);
10404 }
10405
10406 // guest properties
10407 data.llGuestProperties.clear();
10408#ifdef VBOX_WITH_GUEST_PROPS
10409 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10410 it != mHWData->mGuestProperties.end();
10411 ++it)
10412 {
10413 HWData::GuestProperty property = it->second;
10414
10415 /* Remove transient guest properties at shutdown unless we
10416 * are saving state. Note that restoring snapshot intentionally
10417 * keeps them, they will be removed if appropriate once the final
10418 * machine state is set (as crashes etc. need to work). */
10419 if ( ( mData->mMachineState == MachineState_PoweredOff
10420 || mData->mMachineState == MachineState_Aborted
10421 || mData->mMachineState == MachineState_Teleported)
10422 && ( property.mFlags & guestProp::TRANSIENT
10423 || property.mFlags & guestProp::TRANSRESET))
10424 continue;
10425 settings::GuestProperty prop;
10426 prop.strName = it->first;
10427 prop.strValue = property.strValue;
10428 prop.timestamp = property.mTimestamp;
10429 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10430 guestProp::writeFlags(property.mFlags, szFlags);
10431 prop.strFlags = szFlags;
10432
10433 data.llGuestProperties.push_back(prop);
10434 }
10435
10436 /* I presume this doesn't require a backup(). */
10437 mData->mGuestPropertiesModified = FALSE;
10438#endif /* VBOX_WITH_GUEST_PROPS defined */
10439
10440 *pDbg = mHWData->mDebugging;
10441 *pAutostart = mHWData->mAutostart;
10442
10443 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10444 }
10445 catch(std::bad_alloc &)
10446 {
10447 return E_OUTOFMEMORY;
10448 }
10449
10450 AssertComRC(rc);
10451 return rc;
10452}
10453
10454/**
10455 * Saves the storage controller configuration.
10456 *
10457 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10458 */
10459HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10460{
10461 data.llStorageControllers.clear();
10462
10463 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10464 it != mStorageControllers->end();
10465 ++it)
10466 {
10467 HRESULT rc;
10468 ComObjPtr<StorageController> pCtl = *it;
10469
10470 settings::StorageController ctl;
10471 ctl.strName = pCtl->i_getName();
10472 ctl.controllerType = pCtl->i_getControllerType();
10473 ctl.storageBus = pCtl->i_getStorageBus();
10474 ctl.ulInstance = pCtl->i_getInstance();
10475 ctl.fBootable = pCtl->i_getBootable();
10476
10477 /* Save the port count. */
10478 ULONG portCount;
10479 rc = pCtl->COMGETTER(PortCount)(&portCount);
10480 ComAssertComRCRet(rc, rc);
10481 ctl.ulPortCount = portCount;
10482
10483 /* Save fUseHostIOCache */
10484 BOOL fUseHostIOCache;
10485 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10486 ComAssertComRCRet(rc, rc);
10487 ctl.fUseHostIOCache = !!fUseHostIOCache;
10488
10489 /* save the devices now. */
10490 rc = i_saveStorageDevices(pCtl, ctl);
10491 ComAssertComRCRet(rc, rc);
10492
10493 data.llStorageControllers.push_back(ctl);
10494 }
10495
10496 return S_OK;
10497}
10498
10499/**
10500 * Saves the hard disk configuration.
10501 */
10502HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10503 settings::StorageController &data)
10504{
10505 MediaData::AttachmentList atts;
10506
10507 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10508 if (FAILED(rc)) return rc;
10509
10510 data.llAttachedDevices.clear();
10511 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10512 it != atts.end();
10513 ++it)
10514 {
10515 settings::AttachedDevice dev;
10516 IMediumAttachment *iA = *it;
10517 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10518 Medium *pMedium = pAttach->i_getMedium();
10519
10520 dev.deviceType = pAttach->i_getType();
10521 dev.lPort = pAttach->i_getPort();
10522 dev.lDevice = pAttach->i_getDevice();
10523 dev.fPassThrough = pAttach->i_getPassthrough();
10524 dev.fHotPluggable = pAttach->i_getHotPluggable();
10525 if (pMedium)
10526 {
10527 if (pMedium->i_isHostDrive())
10528 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10529 else
10530 dev.uuid = pMedium->i_getId();
10531 dev.fTempEject = pAttach->i_getTempEject();
10532 dev.fNonRotational = pAttach->i_getNonRotational();
10533 dev.fDiscard = pAttach->i_getDiscard();
10534 }
10535
10536 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10537
10538 data.llAttachedDevices.push_back(dev);
10539 }
10540
10541 return S_OK;
10542}
10543
10544/**
10545 * Saves machine state settings as defined by aFlags
10546 * (SaveSTS_* values).
10547 *
10548 * @param aFlags Combination of SaveSTS_* flags.
10549 *
10550 * @note Locks objects for writing.
10551 */
10552HRESULT Machine::i_saveStateSettings(int aFlags)
10553{
10554 if (aFlags == 0)
10555 return S_OK;
10556
10557 AutoCaller autoCaller(this);
10558 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10559
10560 /* This object's write lock is also necessary to serialize file access
10561 * (prevent concurrent reads and writes) */
10562 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10563
10564 HRESULT rc = S_OK;
10565
10566 Assert(mData->pMachineConfigFile);
10567
10568 try
10569 {
10570 if (aFlags & SaveSTS_CurStateModified)
10571 mData->pMachineConfigFile->fCurrentStateModified = true;
10572
10573 if (aFlags & SaveSTS_StateFilePath)
10574 {
10575 if (!mSSData->strStateFilePath.isEmpty())
10576 /* try to make the file name relative to the settings file dir */
10577 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10578 else
10579 mData->pMachineConfigFile->strStateFile.setNull();
10580 }
10581
10582 if (aFlags & SaveSTS_StateTimeStamp)
10583 {
10584 Assert( mData->mMachineState != MachineState_Aborted
10585 || mSSData->strStateFilePath.isEmpty());
10586
10587 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10588
10589 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10590//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10591 }
10592
10593 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10594 }
10595 catch (...)
10596 {
10597 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10598 }
10599
10600 return rc;
10601}
10602
10603/**
10604 * Ensures that the given medium is added to a media registry. If this machine
10605 * was created with 4.0 or later, then the machine registry is used. Otherwise
10606 * the global VirtualBox media registry is used.
10607 *
10608 * Caller must NOT hold machine lock, media tree or any medium locks!
10609 *
10610 * @param pMedium
10611 */
10612void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10613{
10614 /* Paranoia checks: do not hold machine or media tree locks. */
10615 AssertReturnVoid(!isWriteLockOnCurrentThread());
10616 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10617
10618 ComObjPtr<Medium> pBase;
10619 {
10620 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10621 pBase = pMedium->i_getBase();
10622 }
10623
10624 /* Paranoia checks: do not hold medium locks. */
10625 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10626 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10627
10628 // decide which medium registry to use now that the medium is attached:
10629 Guid uuid;
10630 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10631 // machine XML is VirtualBox 4.0 or higher:
10632 uuid = i_getId(); // machine UUID
10633 else
10634 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10635
10636 if (pMedium->i_addRegistry(uuid))
10637 mParent->i_markRegistryModified(uuid);
10638
10639 /* For more complex hard disk structures it can happen that the base
10640 * medium isn't yet associated with any medium registry. Do that now. */
10641 if (pMedium != pBase)
10642 {
10643 /* Tree lock needed by Medium::addRegistry when recursing. */
10644 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10645 if (pBase->i_addRegistryRecursive(uuid))
10646 {
10647 treeLock.release();
10648 mParent->i_markRegistryModified(uuid);
10649 }
10650 }
10651}
10652
10653/**
10654 * Creates differencing hard disks for all normal hard disks attached to this
10655 * machine and a new set of attachments to refer to created disks.
10656 *
10657 * Used when taking a snapshot or when deleting the current state. Gets called
10658 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10659 *
10660 * This method assumes that mMediaData contains the original hard disk attachments
10661 * it needs to create diffs for. On success, these attachments will be replaced
10662 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10663 * called to delete created diffs which will also rollback mMediaData and restore
10664 * whatever was backed up before calling this method.
10665 *
10666 * Attachments with non-normal hard disks are left as is.
10667 *
10668 * If @a aOnline is @c false then the original hard disks that require implicit
10669 * diffs will be locked for reading. Otherwise it is assumed that they are
10670 * already locked for writing (when the VM was started). Note that in the latter
10671 * case it is responsibility of the caller to lock the newly created diffs for
10672 * writing if this method succeeds.
10673 *
10674 * @param aProgress Progress object to run (must contain at least as
10675 * many operations left as the number of hard disks
10676 * attached).
10677 * @param aOnline Whether the VM was online prior to this operation.
10678 *
10679 * @note The progress object is not marked as completed, neither on success nor
10680 * on failure. This is a responsibility of the caller.
10681 *
10682 * @note Locks this object and the media tree for writing.
10683 */
10684HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10685 ULONG aWeight,
10686 bool aOnline)
10687{
10688 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10689
10690 AutoCaller autoCaller(this);
10691 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10692
10693 AutoMultiWriteLock2 alock(this->lockHandle(),
10694 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10695
10696 /* must be in a protective state because we release the lock below */
10697 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10698 || mData->mMachineState == MachineState_OnlineSnapshotting
10699 || mData->mMachineState == MachineState_LiveSnapshotting
10700 || mData->mMachineState == MachineState_RestoringSnapshot
10701 || mData->mMachineState == MachineState_DeletingSnapshot
10702 , E_FAIL);
10703
10704 HRESULT rc = S_OK;
10705
10706 // use appropriate locked media map (online or offline)
10707 MediumLockListMap lockedMediaOffline;
10708 MediumLockListMap *lockedMediaMap;
10709 if (aOnline)
10710 lockedMediaMap = &mData->mSession.mLockedMedia;
10711 else
10712 lockedMediaMap = &lockedMediaOffline;
10713
10714 try
10715 {
10716 if (!aOnline)
10717 {
10718 /* lock all attached hard disks early to detect "in use"
10719 * situations before creating actual diffs */
10720 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10721 it != mMediaData->mAttachments.end();
10722 ++it)
10723 {
10724 MediumAttachment* pAtt = *it;
10725 if (pAtt->i_getType() == DeviceType_HardDisk)
10726 {
10727 Medium* pMedium = pAtt->i_getMedium();
10728 Assert(pMedium);
10729
10730 MediumLockList *pMediumLockList(new MediumLockList());
10731 alock.release();
10732 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10733 NULL /* pToLockWrite */,
10734 false /* fMediumLockWriteAll */,
10735 NULL,
10736 *pMediumLockList);
10737 alock.acquire();
10738 if (FAILED(rc))
10739 {
10740 delete pMediumLockList;
10741 throw rc;
10742 }
10743 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10744 if (FAILED(rc))
10745 {
10746 throw setError(rc,
10747 tr("Collecting locking information for all attached media failed"));
10748 }
10749 }
10750 }
10751
10752 /* Now lock all media. If this fails, nothing is locked. */
10753 alock.release();
10754 rc = lockedMediaMap->Lock();
10755 alock.acquire();
10756 if (FAILED(rc))
10757 {
10758 throw setError(rc,
10759 tr("Locking of attached media failed"));
10760 }
10761 }
10762
10763 /* remember the current list (note that we don't use backup() since
10764 * mMediaData may be already backed up) */
10765 MediaData::AttachmentList atts = mMediaData->mAttachments;
10766
10767 /* start from scratch */
10768 mMediaData->mAttachments.clear();
10769
10770 /* go through remembered attachments and create diffs for normal hard
10771 * disks and attach them */
10772 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10773 it != atts.end();
10774 ++it)
10775 {
10776 MediumAttachment* pAtt = *it;
10777
10778 DeviceType_T devType = pAtt->i_getType();
10779 Medium* pMedium = pAtt->i_getMedium();
10780
10781 if ( devType != DeviceType_HardDisk
10782 || pMedium == NULL
10783 || pMedium->i_getType() != MediumType_Normal)
10784 {
10785 /* copy the attachment as is */
10786
10787 /** @todo the progress object created in SessionMachine::TakeSnaphot
10788 * only expects operations for hard disks. Later other
10789 * device types need to show up in the progress as well. */
10790 if (devType == DeviceType_HardDisk)
10791 {
10792 if (pMedium == NULL)
10793 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10794 aWeight); // weight
10795 else
10796 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10797 pMedium->i_getBase()->i_getName().c_str()).raw(),
10798 aWeight); // weight
10799 }
10800
10801 mMediaData->mAttachments.push_back(pAtt);
10802 continue;
10803 }
10804
10805 /* need a diff */
10806 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10807 pMedium->i_getBase()->i_getName().c_str()).raw(),
10808 aWeight); // weight
10809
10810 Utf8Str strFullSnapshotFolder;
10811 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10812
10813 ComObjPtr<Medium> diff;
10814 diff.createObject();
10815 // store the diff in the same registry as the parent
10816 // (this cannot fail here because we can't create implicit diffs for
10817 // unregistered images)
10818 Guid uuidRegistryParent;
10819 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10820 Assert(fInRegistry); NOREF(fInRegistry);
10821 rc = diff->init(mParent,
10822 pMedium->i_getPreferredDiffFormat(),
10823 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10824 uuidRegistryParent,
10825 DeviceType_HardDisk);
10826 if (FAILED(rc)) throw rc;
10827
10828 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10829 * the push_back? Looks like we're going to release medium with the
10830 * wrong kind of lock (general issue with if we fail anywhere at all)
10831 * and an orphaned VDI in the snapshots folder. */
10832
10833 /* update the appropriate lock list */
10834 MediumLockList *pMediumLockList;
10835 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10836 AssertComRCThrowRC(rc);
10837 if (aOnline)
10838 {
10839 alock.release();
10840 /* The currently attached medium will be read-only, change
10841 * the lock type to read. */
10842 rc = pMediumLockList->Update(pMedium, false);
10843 alock.acquire();
10844 AssertComRCThrowRC(rc);
10845 }
10846
10847 /* release the locks before the potentially lengthy operation */
10848 alock.release();
10849 rc = pMedium->i_createDiffStorage(diff,
10850 pMedium->i_getPreferredDiffVariant(),
10851 pMediumLockList,
10852 NULL /* aProgress */,
10853 true /* aWait */);
10854 alock.acquire();
10855 if (FAILED(rc)) throw rc;
10856
10857 /* actual lock list update is done in Machine::i_commitMedia */
10858
10859 rc = diff->i_addBackReference(mData->mUuid);
10860 AssertComRCThrowRC(rc);
10861
10862 /* add a new attachment */
10863 ComObjPtr<MediumAttachment> attachment;
10864 attachment.createObject();
10865 rc = attachment->init(this,
10866 diff,
10867 pAtt->i_getControllerName(),
10868 pAtt->i_getPort(),
10869 pAtt->i_getDevice(),
10870 DeviceType_HardDisk,
10871 true /* aImplicit */,
10872 false /* aPassthrough */,
10873 false /* aTempEject */,
10874 pAtt->i_getNonRotational(),
10875 pAtt->i_getDiscard(),
10876 pAtt->i_getHotPluggable(),
10877 pAtt->i_getBandwidthGroup());
10878 if (FAILED(rc)) throw rc;
10879
10880 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10881 AssertComRCThrowRC(rc);
10882 mMediaData->mAttachments.push_back(attachment);
10883 }
10884 }
10885 catch (HRESULT aRC) { rc = aRC; }
10886
10887 /* unlock all hard disks we locked when there is no VM */
10888 if (!aOnline)
10889 {
10890 ErrorInfoKeeper eik;
10891
10892 HRESULT rc1 = lockedMediaMap->Clear();
10893 AssertComRC(rc1);
10894 }
10895
10896 return rc;
10897}
10898
10899/**
10900 * Deletes implicit differencing hard disks created either by
10901 * #i_createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10902 *
10903 * Note that to delete hard disks created by #AttachDevice() this method is
10904 * called from #fixupMedia() when the changes are rolled back.
10905 *
10906 * @note Locks this object and the media tree for writing.
10907 */
10908HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10909{
10910 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10911
10912 AutoCaller autoCaller(this);
10913 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10914
10915 AutoMultiWriteLock2 alock(this->lockHandle(),
10916 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10917
10918 /* We absolutely must have backed up state. */
10919 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10920
10921 /* Check if there are any implicitly created diff images. */
10922 bool fImplicitDiffs = false;
10923 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10924 it != mMediaData->mAttachments.end();
10925 ++it)
10926 {
10927 const ComObjPtr<MediumAttachment> &pAtt = *it;
10928 if (pAtt->i_isImplicit())
10929 {
10930 fImplicitDiffs = true;
10931 break;
10932 }
10933 }
10934 /* If there is nothing to do, leave early. This saves lots of image locking
10935 * effort. It also avoids a MachineStateChanged event without real reason.
10936 * This is important e.g. when loading a VM config, because there should be
10937 * no events. Otherwise API clients can become thoroughly confused for
10938 * inaccessible VMs (the code for loading VM configs uses this method for
10939 * cleanup if the config makes no sense), as they take such events as an
10940 * indication that the VM is alive, and they would force the VM config to
10941 * be reread, leading to an endless loop. */
10942 if (!fImplicitDiffs)
10943 return S_OK;
10944
10945 HRESULT rc = S_OK;
10946 MachineState_T oldState = mData->mMachineState;
10947
10948 /* will release the lock before the potentially lengthy operation,
10949 * so protect with the special state (unless already protected) */
10950 if ( oldState != MachineState_Snapshotting
10951 && oldState != MachineState_OnlineSnapshotting
10952 && oldState != MachineState_LiveSnapshotting
10953 && oldState != MachineState_RestoringSnapshot
10954 && oldState != MachineState_DeletingSnapshot
10955 && oldState != MachineState_DeletingSnapshotOnline
10956 && oldState != MachineState_DeletingSnapshotPaused
10957 )
10958 i_setMachineState(MachineState_SettingUp);
10959
10960 // use appropriate locked media map (online or offline)
10961 MediumLockListMap lockedMediaOffline;
10962 MediumLockListMap *lockedMediaMap;
10963 if (aOnline)
10964 lockedMediaMap = &mData->mSession.mLockedMedia;
10965 else
10966 lockedMediaMap = &lockedMediaOffline;
10967
10968 try
10969 {
10970 if (!aOnline)
10971 {
10972 /* lock all attached hard disks early to detect "in use"
10973 * situations before deleting actual diffs */
10974 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10975 it != mMediaData->mAttachments.end();
10976 ++it)
10977 {
10978 MediumAttachment* pAtt = *it;
10979 if (pAtt->i_getType() == DeviceType_HardDisk)
10980 {
10981 Medium* pMedium = pAtt->i_getMedium();
10982 Assert(pMedium);
10983
10984 MediumLockList *pMediumLockList(new MediumLockList());
10985 alock.release();
10986 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10987 NULL /* pToLockWrite */,
10988 false /* fMediumLockWriteAll */,
10989 NULL,
10990 *pMediumLockList);
10991 alock.acquire();
10992
10993 if (FAILED(rc))
10994 {
10995 delete pMediumLockList;
10996 throw rc;
10997 }
10998
10999 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11000 if (FAILED(rc))
11001 throw rc;
11002 }
11003 }
11004
11005 if (FAILED(rc))
11006 throw rc;
11007 } // end of offline
11008
11009 /* Lock lists are now up to date and include implicitly created media */
11010
11011 /* Go through remembered attachments and delete all implicitly created
11012 * diffs and fix up the attachment information */
11013 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11014 MediaData::AttachmentList implicitAtts;
11015 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11016 it != mMediaData->mAttachments.end();
11017 ++it)
11018 {
11019 ComObjPtr<MediumAttachment> pAtt = *it;
11020 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11021 if (pMedium.isNull())
11022 continue;
11023
11024 // Implicit attachments go on the list for deletion and back references are removed.
11025 if (pAtt->i_isImplicit())
11026 {
11027 /* Deassociate and mark for deletion */
11028 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11029 rc = pMedium->i_removeBackReference(mData->mUuid);
11030 if (FAILED(rc))
11031 throw rc;
11032 implicitAtts.push_back(pAtt);
11033 continue;
11034 }
11035
11036 /* Was this medium attached before? */
11037 if (!i_findAttachment(oldAtts, pMedium))
11038 {
11039 /* no: de-associate */
11040 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11041 rc = pMedium->i_removeBackReference(mData->mUuid);
11042 if (FAILED(rc))
11043 throw rc;
11044 continue;
11045 }
11046 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11047 }
11048
11049 /* If there are implicit attachments to delete, throw away the lock
11050 * map contents (which will unlock all media) since the medium
11051 * attachments will be rolled back. Below we need to completely
11052 * recreate the lock map anyway since it is infinitely complex to
11053 * do this incrementally (would need reconstructing each attachment
11054 * change, which would be extremely hairy). */
11055 if (implicitAtts.size() != 0)
11056 {
11057 ErrorInfoKeeper eik;
11058
11059 HRESULT rc1 = lockedMediaMap->Clear();
11060 AssertComRC(rc1);
11061 }
11062
11063 /* rollback hard disk changes */
11064 mMediaData.rollback();
11065
11066 MultiResult mrc(S_OK);
11067
11068 // Delete unused implicit diffs.
11069 if (implicitAtts.size() != 0)
11070 {
11071 alock.release();
11072
11073 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11074 {
11075 // Remove medium associated with this attachment.
11076 ComObjPtr<MediumAttachment> pAtt = *it;
11077 Assert(pAtt);
11078 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11079 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11080 Assert(pMedium);
11081
11082 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11083 // continue on delete failure, just collect error messages
11084 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11085 pMedium->i_getLocationFull().c_str() ));
11086 mrc = rc;
11087 }
11088 // Clear the list of deleted implicit attachments now, while not
11089 // holding the lock, as it will ultimately trigger Medium::uninit()
11090 // calls which assume that the media tree lock isn't held.
11091 implicitAtts.clear();
11092
11093 alock.acquire();
11094
11095 /* if there is a VM recreate media lock map as mentioned above,
11096 * otherwise it is a waste of time and we leave things unlocked */
11097 if (aOnline)
11098 {
11099 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11100 /* must never be NULL, but better safe than sorry */
11101 if (!pMachine.isNull())
11102 {
11103 alock.release();
11104 rc = mData->mSession.mMachine->i_lockMedia();
11105 alock.acquire();
11106 if (FAILED(rc))
11107 throw rc;
11108 }
11109 }
11110 }
11111 }
11112 catch (HRESULT aRC) {rc = aRC;}
11113
11114 if (mData->mMachineState == MachineState_SettingUp)
11115 i_setMachineState(oldState);
11116
11117 /* unlock all hard disks we locked when there is no VM */
11118 if (!aOnline)
11119 {
11120 ErrorInfoKeeper eik;
11121
11122 HRESULT rc1 = lockedMediaMap->Clear();
11123 AssertComRC(rc1);
11124 }
11125
11126 return rc;
11127}
11128
11129
11130/**
11131 * Looks through the given list of media attachments for one with the given parameters
11132 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11133 * can be searched as well if needed.
11134 *
11135 * @param list
11136 * @param aControllerName
11137 * @param aControllerPort
11138 * @param aDevice
11139 * @return
11140 */
11141MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11142 const Utf8Str &aControllerName,
11143 LONG aControllerPort,
11144 LONG aDevice)
11145{
11146 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11147 {
11148 MediumAttachment *pAttach = *it;
11149 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11150 return pAttach;
11151 }
11152
11153 return NULL;
11154}
11155
11156/**
11157 * Looks through the given list of media attachments for one with the given parameters
11158 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11159 * can be searched as well if needed.
11160 *
11161 * @param list
11162 * @param aControllerName
11163 * @param aControllerPort
11164 * @param aDevice
11165 * @return
11166 */
11167MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11168 ComObjPtr<Medium> pMedium)
11169{
11170 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11171 {
11172 MediumAttachment *pAttach = *it;
11173 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11174 if (pMediumThis == pMedium)
11175 return pAttach;
11176 }
11177
11178 return NULL;
11179}
11180
11181/**
11182 * Looks through the given list of media attachments for one with the given parameters
11183 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11184 * can be searched as well if needed.
11185 *
11186 * @param list
11187 * @param aControllerName
11188 * @param aControllerPort
11189 * @param aDevice
11190 * @return
11191 */
11192MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11193 Guid &id)
11194{
11195 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11196 {
11197 MediumAttachment *pAttach = *it;
11198 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11199 if (pMediumThis->i_getId() == id)
11200 return pAttach;
11201 }
11202
11203 return NULL;
11204}
11205
11206/**
11207 * Main implementation for Machine::DetachDevice. This also gets called
11208 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11209 *
11210 * @param pAttach Medium attachment to detach.
11211 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11212 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11213 * SnapshotMachine, and this must be its snapshot.
11214 * @return
11215 */
11216HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11217 AutoWriteLock &writeLock,
11218 Snapshot *pSnapshot)
11219{
11220 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11221 DeviceType_T mediumType = pAttach->i_getType();
11222
11223 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11224
11225 if (pAttach->i_isImplicit())
11226 {
11227 /* attempt to implicitly delete the implicitly created diff */
11228
11229 /// @todo move the implicit flag from MediumAttachment to Medium
11230 /// and forbid any hard disk operation when it is implicit. Or maybe
11231 /// a special media state for it to make it even more simple.
11232
11233 Assert(mMediaData.isBackedUp());
11234
11235 /* will release the lock before the potentially lengthy operation, so
11236 * protect with the special state */
11237 MachineState_T oldState = mData->mMachineState;
11238 i_setMachineState(MachineState_SettingUp);
11239
11240 writeLock.release();
11241
11242 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11243 true /*aWait*/);
11244
11245 writeLock.acquire();
11246
11247 i_setMachineState(oldState);
11248
11249 if (FAILED(rc)) return rc;
11250 }
11251
11252 i_setModified(IsModified_Storage);
11253 mMediaData.backup();
11254 mMediaData->mAttachments.remove(pAttach);
11255
11256 if (!oldmedium.isNull())
11257 {
11258 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11259 if (pSnapshot)
11260 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11261 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11262 else if (mediumType != DeviceType_HardDisk)
11263 oldmedium->i_removeBackReference(mData->mUuid);
11264 }
11265
11266 return S_OK;
11267}
11268
11269/**
11270 * Goes thru all media of the given list and
11271 *
11272 * 1) calls i_detachDevice() on each of them for this machine and
11273 * 2) adds all Medium objects found in the process to the given list,
11274 * depending on cleanupMode.
11275 *
11276 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11277 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11278 * media to the list.
11279 *
11280 * This gets called from Machine::Unregister, both for the actual Machine and
11281 * the SnapshotMachine objects that might be found in the snapshots.
11282 *
11283 * Requires caller and locking. The machine lock must be passed in because it
11284 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11285 *
11286 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11287 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11288 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11289 * Full, then all media get added;
11290 * otherwise no media get added.
11291 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11292 * @return
11293 */
11294HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11295 Snapshot *pSnapshot,
11296 CleanupMode_T cleanupMode,
11297 MediaList &llMedia)
11298{
11299 Assert(isWriteLockOnCurrentThread());
11300
11301 HRESULT rc;
11302
11303 // make a temporary list because i_detachDevice invalidates iterators into
11304 // mMediaData->mAttachments
11305 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11306
11307 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11308 {
11309 ComObjPtr<MediumAttachment> &pAttach = *it;
11310 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11311
11312 if (!pMedium.isNull())
11313 {
11314 AutoCaller mac(pMedium);
11315 if (FAILED(mac.rc())) return mac.rc();
11316 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11317 DeviceType_T devType = pMedium->i_getDeviceType();
11318 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11319 && devType == DeviceType_HardDisk)
11320 || (cleanupMode == CleanupMode_Full)
11321 )
11322 {
11323 llMedia.push_back(pMedium);
11324 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11325 /* Not allowed to keep this lock as below we need the parent
11326 * medium lock, and the lock order is parent to child. */
11327 lock.release();
11328 /*
11329 * Search for medias which are not attached to any machine, but
11330 * in the chain to an attached disk. Mediums are only consided
11331 * if they are:
11332 * - have only one child
11333 * - no references to any machines
11334 * - are of normal medium type
11335 */
11336 while (!pParent.isNull())
11337 {
11338 AutoCaller mac1(pParent);
11339 if (FAILED(mac1.rc())) return mac1.rc();
11340 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11341 if (pParent->i_getChildren().size() == 1)
11342 {
11343 if ( pParent->i_getMachineBackRefCount() == 0
11344 && pParent->i_getType() == MediumType_Normal
11345 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11346 llMedia.push_back(pParent);
11347 }
11348 else
11349 break;
11350 pParent = pParent->i_getParent();
11351 }
11352 }
11353 }
11354
11355 // real machine: then we need to use the proper method
11356 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11357
11358 if (FAILED(rc))
11359 return rc;
11360 }
11361
11362 return S_OK;
11363}
11364
11365/**
11366 * Perform deferred hard disk detachments.
11367 *
11368 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11369 * backed up).
11370 *
11371 * If @a aOnline is @c true then this method will also unlock the old hard disks
11372 * for which the new implicit diffs were created and will lock these new diffs for
11373 * writing.
11374 *
11375 * @param aOnline Whether the VM was online prior to this operation.
11376 *
11377 * @note Locks this object for writing!
11378 */
11379void Machine::i_commitMedia(bool aOnline /*= false*/)
11380{
11381 AutoCaller autoCaller(this);
11382 AssertComRCReturnVoid(autoCaller.rc());
11383
11384 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11385
11386 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11387
11388 HRESULT rc = S_OK;
11389
11390 /* no attach/detach operations -- nothing to do */
11391 if (!mMediaData.isBackedUp())
11392 return;
11393
11394 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11395 bool fMediaNeedsLocking = false;
11396
11397 /* enumerate new attachments */
11398 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11399 it != mMediaData->mAttachments.end();
11400 ++it)
11401 {
11402 MediumAttachment *pAttach = *it;
11403
11404 pAttach->i_commit();
11405
11406 Medium* pMedium = pAttach->i_getMedium();
11407 bool fImplicit = pAttach->i_isImplicit();
11408
11409 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11410 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11411 fImplicit));
11412
11413 /** @todo convert all this Machine-based voodoo to MediumAttachment
11414 * based commit logic. */
11415 if (fImplicit)
11416 {
11417 /* convert implicit attachment to normal */
11418 pAttach->i_setImplicit(false);
11419
11420 if ( aOnline
11421 && pMedium
11422 && pAttach->i_getType() == DeviceType_HardDisk
11423 )
11424 {
11425 /* update the appropriate lock list */
11426 MediumLockList *pMediumLockList;
11427 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11428 AssertComRC(rc);
11429 if (pMediumLockList)
11430 {
11431 /* unlock if there's a need to change the locking */
11432 if (!fMediaNeedsLocking)
11433 {
11434 rc = mData->mSession.mLockedMedia.Unlock();
11435 AssertComRC(rc);
11436 fMediaNeedsLocking = true;
11437 }
11438 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11439 AssertComRC(rc);
11440 rc = pMediumLockList->Append(pMedium, true);
11441 AssertComRC(rc);
11442 }
11443 }
11444
11445 continue;
11446 }
11447
11448 if (pMedium)
11449 {
11450 /* was this medium attached before? */
11451 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11452 {
11453 MediumAttachment *pOldAttach = *oldIt;
11454 if (pOldAttach->i_getMedium() == pMedium)
11455 {
11456 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11457
11458 /* yes: remove from old to avoid de-association */
11459 oldAtts.erase(oldIt);
11460 break;
11461 }
11462 }
11463 }
11464 }
11465
11466 /* enumerate remaining old attachments and de-associate from the
11467 * current machine state */
11468 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11469 {
11470 MediumAttachment *pAttach = *it;
11471 Medium* pMedium = pAttach->i_getMedium();
11472
11473 /* Detach only hard disks, since DVD/floppy media is detached
11474 * instantly in MountMedium. */
11475 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11476 {
11477 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11478
11479 /* now de-associate from the current machine state */
11480 rc = pMedium->i_removeBackReference(mData->mUuid);
11481 AssertComRC(rc);
11482
11483 if (aOnline)
11484 {
11485 /* unlock since medium is not used anymore */
11486 MediumLockList *pMediumLockList;
11487 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11488 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11489 {
11490 /* this happens for online snapshots, there the attachment
11491 * is changing, but only to a diff image created under
11492 * the old one, so there is no separate lock list */
11493 Assert(!pMediumLockList);
11494 }
11495 else
11496 {
11497 AssertComRC(rc);
11498 if (pMediumLockList)
11499 {
11500 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11501 AssertComRC(rc);
11502 }
11503 }
11504 }
11505 }
11506 }
11507
11508 /* take media locks again so that the locking state is consistent */
11509 if (fMediaNeedsLocking)
11510 {
11511 Assert(aOnline);
11512 rc = mData->mSession.mLockedMedia.Lock();
11513 AssertComRC(rc);
11514 }
11515
11516 /* commit the hard disk changes */
11517 mMediaData.commit();
11518
11519 if (i_isSessionMachine())
11520 {
11521 /*
11522 * Update the parent machine to point to the new owner.
11523 * This is necessary because the stored parent will point to the
11524 * session machine otherwise and cause crashes or errors later
11525 * when the session machine gets invalid.
11526 */
11527 /** @todo Change the MediumAttachment class to behave like any other
11528 * class in this regard by creating peer MediumAttachment
11529 * objects for session machines and share the data with the peer
11530 * machine.
11531 */
11532 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11533 it != mMediaData->mAttachments.end();
11534 ++it)
11535 (*it)->i_updateParentMachine(mPeer);
11536
11537 /* attach new data to the primary machine and reshare it */
11538 mPeer->mMediaData.attach(mMediaData);
11539 }
11540
11541 return;
11542}
11543
11544/**
11545 * Perform deferred deletion of implicitly created diffs.
11546 *
11547 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11548 * backed up).
11549 *
11550 * @note Locks this object for writing!
11551 */
11552void Machine::i_rollbackMedia()
11553{
11554 AutoCaller autoCaller(this);
11555 AssertComRCReturnVoid(autoCaller.rc());
11556
11557 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11558 LogFlowThisFunc(("Entering rollbackMedia\n"));
11559
11560 HRESULT rc = S_OK;
11561
11562 /* no attach/detach operations -- nothing to do */
11563 if (!mMediaData.isBackedUp())
11564 return;
11565
11566 /* enumerate new attachments */
11567 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11568 it != mMediaData->mAttachments.end();
11569 ++it)
11570 {
11571 MediumAttachment *pAttach = *it;
11572 /* Fix up the backrefs for DVD/floppy media. */
11573 if (pAttach->i_getType() != DeviceType_HardDisk)
11574 {
11575 Medium* pMedium = pAttach->i_getMedium();
11576 if (pMedium)
11577 {
11578 rc = pMedium->i_removeBackReference(mData->mUuid);
11579 AssertComRC(rc);
11580 }
11581 }
11582
11583 (*it)->i_rollback();
11584
11585 pAttach = *it;
11586 /* Fix up the backrefs for DVD/floppy media. */
11587 if (pAttach->i_getType() != DeviceType_HardDisk)
11588 {
11589 Medium* pMedium = pAttach->i_getMedium();
11590 if (pMedium)
11591 {
11592 rc = pMedium->i_addBackReference(mData->mUuid);
11593 AssertComRC(rc);
11594 }
11595 }
11596 }
11597
11598 /** @todo convert all this Machine-based voodoo to MediumAttachment
11599 * based rollback logic. */
11600 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11601
11602 return;
11603}
11604
11605/**
11606 * Returns true if the settings file is located in the directory named exactly
11607 * as the machine; this means, among other things, that the machine directory
11608 * should be auto-renamed.
11609 *
11610 * @param aSettingsDir if not NULL, the full machine settings file directory
11611 * name will be assigned there.
11612 *
11613 * @note Doesn't lock anything.
11614 * @note Not thread safe (must be called from this object's lock).
11615 */
11616bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11617{
11618 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11619 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11620 if (aSettingsDir)
11621 *aSettingsDir = strMachineDirName;
11622 strMachineDirName.stripPath(); // vmname
11623 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11624 strConfigFileOnly.stripPath() // vmname.vbox
11625 .stripSuffix(); // vmname
11626 /** @todo hack, make somehow use of ComposeMachineFilename */
11627 if (mUserData->s.fDirectoryIncludesUUID)
11628 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11629
11630 AssertReturn(!strMachineDirName.isEmpty(), false);
11631 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11632
11633 return strMachineDirName == strConfigFileOnly;
11634}
11635
11636/**
11637 * Discards all changes to machine settings.
11638 *
11639 * @param aNotify Whether to notify the direct session about changes or not.
11640 *
11641 * @note Locks objects for writing!
11642 */
11643void Machine::i_rollback(bool aNotify)
11644{
11645 AutoCaller autoCaller(this);
11646 AssertComRCReturn(autoCaller.rc(), (void)0);
11647
11648 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11649
11650 if (!mStorageControllers.isNull())
11651 {
11652 if (mStorageControllers.isBackedUp())
11653 {
11654 /* unitialize all new devices (absent in the backed up list). */
11655 StorageControllerList::const_iterator it = mStorageControllers->begin();
11656 StorageControllerList *backedList = mStorageControllers.backedUpData();
11657 while (it != mStorageControllers->end())
11658 {
11659 if ( std::find(backedList->begin(), backedList->end(), *it)
11660 == backedList->end()
11661 )
11662 {
11663 (*it)->uninit();
11664 }
11665 ++it;
11666 }
11667
11668 /* restore the list */
11669 mStorageControllers.rollback();
11670 }
11671
11672 /* rollback any changes to devices after restoring the list */
11673 if (mData->flModifications & IsModified_Storage)
11674 {
11675 StorageControllerList::const_iterator it = mStorageControllers->begin();
11676 while (it != mStorageControllers->end())
11677 {
11678 (*it)->i_rollback();
11679 ++it;
11680 }
11681 }
11682 }
11683
11684 if (!mUSBControllers.isNull())
11685 {
11686 if (mUSBControllers.isBackedUp())
11687 {
11688 /* unitialize all new devices (absent in the backed up list). */
11689 USBControllerList::const_iterator it = mUSBControllers->begin();
11690 USBControllerList *backedList = mUSBControllers.backedUpData();
11691 while (it != mUSBControllers->end())
11692 {
11693 if ( std::find(backedList->begin(), backedList->end(), *it)
11694 == backedList->end()
11695 )
11696 {
11697 (*it)->uninit();
11698 }
11699 ++it;
11700 }
11701
11702 /* restore the list */
11703 mUSBControllers.rollback();
11704 }
11705
11706 /* rollback any changes to devices after restoring the list */
11707 if (mData->flModifications & IsModified_USB)
11708 {
11709 USBControllerList::const_iterator it = mUSBControllers->begin();
11710 while (it != mUSBControllers->end())
11711 {
11712 (*it)->i_rollback();
11713 ++it;
11714 }
11715 }
11716 }
11717
11718 mUserData.rollback();
11719
11720 mHWData.rollback();
11721
11722 if (mData->flModifications & IsModified_Storage)
11723 i_rollbackMedia();
11724
11725 if (mBIOSSettings)
11726 mBIOSSettings->i_rollback();
11727
11728 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11729 mVRDEServer->i_rollback();
11730
11731 if (mAudioAdapter)
11732 mAudioAdapter->i_rollback();
11733
11734 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11735 mUSBDeviceFilters->i_rollback();
11736
11737 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11738 mBandwidthControl->i_rollback();
11739
11740 if (!mHWData.isNull())
11741 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11742 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11743 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11744 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11745
11746 if (mData->flModifications & IsModified_NetworkAdapters)
11747 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11748 if ( mNetworkAdapters[slot]
11749 && mNetworkAdapters[slot]->i_isModified())
11750 {
11751 mNetworkAdapters[slot]->i_rollback();
11752 networkAdapters[slot] = mNetworkAdapters[slot];
11753 }
11754
11755 if (mData->flModifications & IsModified_SerialPorts)
11756 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11757 if ( mSerialPorts[slot]
11758 && mSerialPorts[slot]->i_isModified())
11759 {
11760 mSerialPorts[slot]->i_rollback();
11761 serialPorts[slot] = mSerialPorts[slot];
11762 }
11763
11764 if (mData->flModifications & IsModified_ParallelPorts)
11765 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11766 if ( mParallelPorts[slot]
11767 && mParallelPorts[slot]->i_isModified())
11768 {
11769 mParallelPorts[slot]->i_rollback();
11770 parallelPorts[slot] = mParallelPorts[slot];
11771 }
11772
11773 if (aNotify)
11774 {
11775 /* inform the direct session about changes */
11776
11777 ComObjPtr<Machine> that = this;
11778 uint32_t flModifications = mData->flModifications;
11779 alock.release();
11780
11781 if (flModifications & IsModified_SharedFolders)
11782 that->i_onSharedFolderChange();
11783
11784 if (flModifications & IsModified_VRDEServer)
11785 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11786 if (flModifications & IsModified_USB)
11787 that->i_onUSBControllerChange();
11788
11789 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11790 if (networkAdapters[slot])
11791 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11792 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11793 if (serialPorts[slot])
11794 that->i_onSerialPortChange(serialPorts[slot]);
11795 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11796 if (parallelPorts[slot])
11797 that->i_onParallelPortChange(parallelPorts[slot]);
11798
11799 if (flModifications & IsModified_Storage)
11800 that->i_onStorageControllerChange();
11801
11802#if 0
11803 if (flModifications & IsModified_BandwidthControl)
11804 that->onBandwidthControlChange();
11805#endif
11806 }
11807}
11808
11809/**
11810 * Commits all the changes to machine settings.
11811 *
11812 * Note that this operation is supposed to never fail.
11813 *
11814 * @note Locks this object and children for writing.
11815 */
11816void Machine::i_commit()
11817{
11818 AutoCaller autoCaller(this);
11819 AssertComRCReturnVoid(autoCaller.rc());
11820
11821 AutoCaller peerCaller(mPeer);
11822 AssertComRCReturnVoid(peerCaller.rc());
11823
11824 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11825
11826 /*
11827 * use safe commit to ensure Snapshot machines (that share mUserData)
11828 * will still refer to a valid memory location
11829 */
11830 mUserData.commitCopy();
11831
11832 mHWData.commit();
11833
11834 if (mMediaData.isBackedUp())
11835 i_commitMedia(Global::IsOnline(mData->mMachineState));
11836
11837 mBIOSSettings->i_commit();
11838 mVRDEServer->i_commit();
11839 mAudioAdapter->i_commit();
11840 mUSBDeviceFilters->i_commit();
11841 mBandwidthControl->i_commit();
11842
11843 /* Since mNetworkAdapters is a list which might have been changed (resized)
11844 * without using the Backupable<> template we need to handle the copying
11845 * of the list entries manually, including the creation of peers for the
11846 * new objects. */
11847 bool commitNetworkAdapters = false;
11848 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11849 if (mPeer)
11850 {
11851 /* commit everything, even the ones which will go away */
11852 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11853 mNetworkAdapters[slot]->i_commit();
11854 /* copy over the new entries, creating a peer and uninit the original */
11855 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11856 for (size_t slot = 0; slot < newSize; slot++)
11857 {
11858 /* look if this adapter has a peer device */
11859 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11860 if (!peer)
11861 {
11862 /* no peer means the adapter is a newly created one;
11863 * create a peer owning data this data share it with */
11864 peer.createObject();
11865 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11866 }
11867 mPeer->mNetworkAdapters[slot] = peer;
11868 }
11869 /* uninit any no longer needed network adapters */
11870 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11871 mNetworkAdapters[slot]->uninit();
11872 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11873 {
11874 if (mPeer->mNetworkAdapters[slot])
11875 mPeer->mNetworkAdapters[slot]->uninit();
11876 }
11877 /* Keep the original network adapter count until this point, so that
11878 * discarding a chipset type change will not lose settings. */
11879 mNetworkAdapters.resize(newSize);
11880 mPeer->mNetworkAdapters.resize(newSize);
11881 }
11882 else
11883 {
11884 /* we have no peer (our parent is the newly created machine);
11885 * just commit changes to the network adapters */
11886 commitNetworkAdapters = true;
11887 }
11888 if (commitNetworkAdapters)
11889 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11890 mNetworkAdapters[slot]->i_commit();
11891
11892 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11893 mSerialPorts[slot]->i_commit();
11894 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11895 mParallelPorts[slot]->i_commit();
11896
11897 bool commitStorageControllers = false;
11898
11899 if (mStorageControllers.isBackedUp())
11900 {
11901 mStorageControllers.commit();
11902
11903 if (mPeer)
11904 {
11905 /* Commit all changes to new controllers (this will reshare data with
11906 * peers for those who have peers) */
11907 StorageControllerList *newList = new StorageControllerList();
11908 StorageControllerList::const_iterator it = mStorageControllers->begin();
11909 while (it != mStorageControllers->end())
11910 {
11911 (*it)->i_commit();
11912
11913 /* look if this controller has a peer device */
11914 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11915 if (!peer)
11916 {
11917 /* no peer means the device is a newly created one;
11918 * create a peer owning data this device share it with */
11919 peer.createObject();
11920 peer->init(mPeer, *it, true /* aReshare */);
11921 }
11922 else
11923 {
11924 /* remove peer from the old list */
11925 mPeer->mStorageControllers->remove(peer);
11926 }
11927 /* and add it to the new list */
11928 newList->push_back(peer);
11929
11930 ++it;
11931 }
11932
11933 /* uninit old peer's controllers that are left */
11934 it = mPeer->mStorageControllers->begin();
11935 while (it != mPeer->mStorageControllers->end())
11936 {
11937 (*it)->uninit();
11938 ++it;
11939 }
11940
11941 /* attach new list of controllers to our peer */
11942 mPeer->mStorageControllers.attach(newList);
11943 }
11944 else
11945 {
11946 /* we have no peer (our parent is the newly created machine);
11947 * just commit changes to devices */
11948 commitStorageControllers = true;
11949 }
11950 }
11951 else
11952 {
11953 /* the list of controllers itself is not changed,
11954 * just commit changes to controllers themselves */
11955 commitStorageControllers = true;
11956 }
11957
11958 if (commitStorageControllers)
11959 {
11960 StorageControllerList::const_iterator it = mStorageControllers->begin();
11961 while (it != mStorageControllers->end())
11962 {
11963 (*it)->i_commit();
11964 ++it;
11965 }
11966 }
11967
11968 bool commitUSBControllers = false;
11969
11970 if (mUSBControllers.isBackedUp())
11971 {
11972 mUSBControllers.commit();
11973
11974 if (mPeer)
11975 {
11976 /* Commit all changes to new controllers (this will reshare data with
11977 * peers for those who have peers) */
11978 USBControllerList *newList = new USBControllerList();
11979 USBControllerList::const_iterator it = mUSBControllers->begin();
11980 while (it != mUSBControllers->end())
11981 {
11982 (*it)->i_commit();
11983
11984 /* look if this controller has a peer device */
11985 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11986 if (!peer)
11987 {
11988 /* no peer means the device is a newly created one;
11989 * create a peer owning data this device share it with */
11990 peer.createObject();
11991 peer->init(mPeer, *it, true /* aReshare */);
11992 }
11993 else
11994 {
11995 /* remove peer from the old list */
11996 mPeer->mUSBControllers->remove(peer);
11997 }
11998 /* and add it to the new list */
11999 newList->push_back(peer);
12000
12001 ++it;
12002 }
12003
12004 /* uninit old peer's controllers that are left */
12005 it = mPeer->mUSBControllers->begin();
12006 while (it != mPeer->mUSBControllers->end())
12007 {
12008 (*it)->uninit();
12009 ++it;
12010 }
12011
12012 /* attach new list of controllers to our peer */
12013 mPeer->mUSBControllers.attach(newList);
12014 }
12015 else
12016 {
12017 /* we have no peer (our parent is the newly created machine);
12018 * just commit changes to devices */
12019 commitUSBControllers = true;
12020 }
12021 }
12022 else
12023 {
12024 /* the list of controllers itself is not changed,
12025 * just commit changes to controllers themselves */
12026 commitUSBControllers = true;
12027 }
12028
12029 if (commitUSBControllers)
12030 {
12031 USBControllerList::const_iterator it = mUSBControllers->begin();
12032 while (it != mUSBControllers->end())
12033 {
12034 (*it)->i_commit();
12035 ++it;
12036 }
12037 }
12038
12039 if (i_isSessionMachine())
12040 {
12041 /* attach new data to the primary machine and reshare it */
12042 mPeer->mUserData.attach(mUserData);
12043 mPeer->mHWData.attach(mHWData);
12044 /* mMediaData is reshared by fixupMedia */
12045 // mPeer->mMediaData.attach(mMediaData);
12046 Assert(mPeer->mMediaData.data() == mMediaData.data());
12047 }
12048}
12049
12050/**
12051 * Copies all the hardware data from the given machine.
12052 *
12053 * Currently, only called when the VM is being restored from a snapshot. In
12054 * particular, this implies that the VM is not running during this method's
12055 * call.
12056 *
12057 * @note This method must be called from under this object's lock.
12058 *
12059 * @note This method doesn't call #commit(), so all data remains backed up and
12060 * unsaved.
12061 */
12062void Machine::i_copyFrom(Machine *aThat)
12063{
12064 AssertReturnVoid(!i_isSnapshotMachine());
12065 AssertReturnVoid(aThat->i_isSnapshotMachine());
12066
12067 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12068
12069 mHWData.assignCopy(aThat->mHWData);
12070
12071 // create copies of all shared folders (mHWData after attaching a copy
12072 // contains just references to original objects)
12073 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12074 it != mHWData->mSharedFolders.end();
12075 ++it)
12076 {
12077 ComObjPtr<SharedFolder> folder;
12078 folder.createObject();
12079 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12080 AssertComRC(rc);
12081 *it = folder;
12082 }
12083
12084 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12085 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12086 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12087 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12088 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12089
12090 /* create private copies of all controllers */
12091 mStorageControllers.backup();
12092 mStorageControllers->clear();
12093 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12094 it != aThat->mStorageControllers->end();
12095 ++it)
12096 {
12097 ComObjPtr<StorageController> ctrl;
12098 ctrl.createObject();
12099 ctrl->initCopy(this, *it);
12100 mStorageControllers->push_back(ctrl);
12101 }
12102
12103 /* create private copies of all USB controllers */
12104 mUSBControllers.backup();
12105 mUSBControllers->clear();
12106 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12107 it != aThat->mUSBControllers->end();
12108 ++it)
12109 {
12110 ComObjPtr<USBController> ctrl;
12111 ctrl.createObject();
12112 ctrl->initCopy(this, *it);
12113 mUSBControllers->push_back(ctrl);
12114 }
12115
12116 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12117 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12118 {
12119 if (mNetworkAdapters[slot].isNotNull())
12120 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12121 else
12122 {
12123 unconst(mNetworkAdapters[slot]).createObject();
12124 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12125 }
12126 }
12127 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12128 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12129 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12130 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12131}
12132
12133/**
12134 * Returns whether the given storage controller is hotplug capable.
12135 *
12136 * @returns true if the controller supports hotplugging
12137 * false otherwise.
12138 * @param enmCtrlType The controller type to check for.
12139 */
12140bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12141{
12142 ComPtr<ISystemProperties> systemProperties;
12143 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12144 if (FAILED(rc))
12145 return false;
12146
12147 BOOL aHotplugCapable = FALSE;
12148 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12149
12150 return RT_BOOL(aHotplugCapable);
12151}
12152
12153#ifdef VBOX_WITH_RESOURCE_USAGE_API
12154
12155void Machine::i_getDiskList(MediaList &list)
12156{
12157 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12158 it != mMediaData->mAttachments.end();
12159 ++it)
12160 {
12161 MediumAttachment* pAttach = *it;
12162 /* just in case */
12163 AssertContinue(pAttach);
12164
12165 AutoCaller localAutoCallerA(pAttach);
12166 if (FAILED(localAutoCallerA.rc())) continue;
12167
12168 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12169
12170 if (pAttach->i_getType() == DeviceType_HardDisk)
12171 list.push_back(pAttach->i_getMedium());
12172 }
12173}
12174
12175void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12176{
12177 AssertReturnVoid(isWriteLockOnCurrentThread());
12178 AssertPtrReturnVoid(aCollector);
12179
12180 pm::CollectorHAL *hal = aCollector->getHAL();
12181 /* Create sub metrics */
12182 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12183 "Percentage of processor time spent in user mode by the VM process.");
12184 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12185 "Percentage of processor time spent in kernel mode by the VM process.");
12186 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12187 "Size of resident portion of VM process in memory.");
12188 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12189 "Actual size of all VM disks combined.");
12190 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12191 "Network receive rate.");
12192 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12193 "Network transmit rate.");
12194 /* Create and register base metrics */
12195 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12196 cpuLoadUser, cpuLoadKernel);
12197 aCollector->registerBaseMetric(cpuLoad);
12198 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12199 ramUsageUsed);
12200 aCollector->registerBaseMetric(ramUsage);
12201 MediaList disks;
12202 i_getDiskList(disks);
12203 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12204 diskUsageUsed);
12205 aCollector->registerBaseMetric(diskUsage);
12206
12207 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12208 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12209 new pm::AggregateAvg()));
12210 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12211 new pm::AggregateMin()));
12212 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12213 new pm::AggregateMax()));
12214 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12215 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12216 new pm::AggregateAvg()));
12217 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12218 new pm::AggregateMin()));
12219 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12220 new pm::AggregateMax()));
12221
12222 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12223 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12224 new pm::AggregateAvg()));
12225 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12226 new pm::AggregateMin()));
12227 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12228 new pm::AggregateMax()));
12229
12230 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12231 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12232 new pm::AggregateAvg()));
12233 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12234 new pm::AggregateMin()));
12235 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12236 new pm::AggregateMax()));
12237
12238
12239 /* Guest metrics collector */
12240 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12241 aCollector->registerGuest(mCollectorGuest);
12242 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12243
12244 /* Create sub metrics */
12245 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12246 "Percentage of processor time spent in user mode as seen by the guest.");
12247 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12248 "Percentage of processor time spent in kernel mode as seen by the guest.");
12249 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12250 "Percentage of processor time spent idling as seen by the guest.");
12251
12252 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12253 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12254 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12255 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12256 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12257 pm::SubMetric *guestMemCache = new pm::SubMetric(
12258 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12259
12260 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12261 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12262
12263 /* Create and register base metrics */
12264 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12265 machineNetRx, machineNetTx);
12266 aCollector->registerBaseMetric(machineNetRate);
12267
12268 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12269 guestLoadUser, guestLoadKernel, guestLoadIdle);
12270 aCollector->registerBaseMetric(guestCpuLoad);
12271
12272 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12273 guestMemTotal, guestMemFree,
12274 guestMemBalloon, guestMemShared,
12275 guestMemCache, guestPagedTotal);
12276 aCollector->registerBaseMetric(guestCpuMem);
12277
12278 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12279 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12280 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12281 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12282
12283 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12284 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12285 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12286 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12287
12288 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12289 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12290 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12291 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12292
12293 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12294 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12295 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12296 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12297
12298 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12299 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12300 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12301 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12302
12303 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12304 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12305 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12306 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12307
12308 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12309 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12310 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12311 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12312
12313 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12314 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12315 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12316 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12317
12318 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12319 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12320 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12321 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12322
12323 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12324 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12325 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12326 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12327
12328 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12329 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12330 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12331 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12332}
12333
12334void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12335{
12336 AssertReturnVoid(isWriteLockOnCurrentThread());
12337
12338 if (aCollector)
12339 {
12340 aCollector->unregisterMetricsFor(aMachine);
12341 aCollector->unregisterBaseMetricsFor(aMachine);
12342 }
12343}
12344
12345#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12346
12347
12348////////////////////////////////////////////////////////////////////////////////
12349
12350DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12351
12352HRESULT SessionMachine::FinalConstruct()
12353{
12354 LogFlowThisFunc(("\n"));
12355
12356 mClientToken = NULL;
12357
12358 return BaseFinalConstruct();
12359}
12360
12361void SessionMachine::FinalRelease()
12362{
12363 LogFlowThisFunc(("\n"));
12364
12365 Assert(!mClientToken);
12366 /* paranoia, should not hang around any more */
12367 if (mClientToken)
12368 {
12369 delete mClientToken;
12370 mClientToken = NULL;
12371 }
12372
12373 uninit(Uninit::Unexpected);
12374
12375 BaseFinalRelease();
12376}
12377
12378/**
12379 * @note Must be called only by Machine::LockMachine() from its own write lock.
12380 */
12381HRESULT SessionMachine::init(Machine *aMachine)
12382{
12383 LogFlowThisFuncEnter();
12384 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12385
12386 AssertReturn(aMachine, E_INVALIDARG);
12387
12388 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12389
12390 /* Enclose the state transition NotReady->InInit->Ready */
12391 AutoInitSpan autoInitSpan(this);
12392 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12393
12394 HRESULT rc = S_OK;
12395
12396 RT_ZERO(mAuthLibCtx);
12397
12398 /* create the machine client token */
12399 try
12400 {
12401 mClientToken = new ClientToken(aMachine, this);
12402 if (!mClientToken->isReady())
12403 {
12404 delete mClientToken;
12405 mClientToken = NULL;
12406 rc = E_FAIL;
12407 }
12408 }
12409 catch (std::bad_alloc &)
12410 {
12411 rc = E_OUTOFMEMORY;
12412 }
12413 if (FAILED(rc))
12414 return rc;
12415
12416 /* memorize the peer Machine */
12417 unconst(mPeer) = aMachine;
12418 /* share the parent pointer */
12419 unconst(mParent) = aMachine->mParent;
12420
12421 /* take the pointers to data to share */
12422 mData.share(aMachine->mData);
12423 mSSData.share(aMachine->mSSData);
12424
12425 mUserData.share(aMachine->mUserData);
12426 mHWData.share(aMachine->mHWData);
12427 mMediaData.share(aMachine->mMediaData);
12428
12429 mStorageControllers.allocate();
12430 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12431 it != aMachine->mStorageControllers->end();
12432 ++it)
12433 {
12434 ComObjPtr<StorageController> ctl;
12435 ctl.createObject();
12436 ctl->init(this, *it);
12437 mStorageControllers->push_back(ctl);
12438 }
12439
12440 mUSBControllers.allocate();
12441 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12442 it != aMachine->mUSBControllers->end();
12443 ++it)
12444 {
12445 ComObjPtr<USBController> ctl;
12446 ctl.createObject();
12447 ctl->init(this, *it);
12448 mUSBControllers->push_back(ctl);
12449 }
12450
12451 unconst(mBIOSSettings).createObject();
12452 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12453 /* create another VRDEServer object that will be mutable */
12454 unconst(mVRDEServer).createObject();
12455 mVRDEServer->init(this, aMachine->mVRDEServer);
12456 /* create another audio adapter object that will be mutable */
12457 unconst(mAudioAdapter).createObject();
12458 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12459 /* create a list of serial ports that will be mutable */
12460 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12461 {
12462 unconst(mSerialPorts[slot]).createObject();
12463 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12464 }
12465 /* create a list of parallel ports that will be mutable */
12466 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12467 {
12468 unconst(mParallelPorts[slot]).createObject();
12469 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12470 }
12471
12472 /* create another USB device filters object that will be mutable */
12473 unconst(mUSBDeviceFilters).createObject();
12474 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12475
12476 /* create a list of network adapters that will be mutable */
12477 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12478 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12479 {
12480 unconst(mNetworkAdapters[slot]).createObject();
12481 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12482 }
12483
12484 /* create another bandwidth control object that will be mutable */
12485 unconst(mBandwidthControl).createObject();
12486 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12487
12488 /* default is to delete saved state on Saved -> PoweredOff transition */
12489 mRemoveSavedState = true;
12490
12491 /* Confirm a successful initialization when it's the case */
12492 autoInitSpan.setSucceeded();
12493
12494 miNATNetworksStarted = 0;
12495
12496 LogFlowThisFuncLeave();
12497 return rc;
12498}
12499
12500/**
12501 * Uninitializes this session object. If the reason is other than
12502 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12503 * or the client watcher code.
12504 *
12505 * @param aReason uninitialization reason
12506 *
12507 * @note Locks mParent + this object for writing.
12508 */
12509void SessionMachine::uninit(Uninit::Reason aReason)
12510{
12511 LogFlowThisFuncEnter();
12512 LogFlowThisFunc(("reason=%d\n", aReason));
12513
12514 /*
12515 * Strongly reference ourselves to prevent this object deletion after
12516 * mData->mSession.mMachine.setNull() below (which can release the last
12517 * reference and call the destructor). Important: this must be done before
12518 * accessing any members (and before AutoUninitSpan that does it as well).
12519 * This self reference will be released as the very last step on return.
12520 */
12521 ComObjPtr<SessionMachine> selfRef = this;
12522
12523 /* Enclose the state transition Ready->InUninit->NotReady */
12524 AutoUninitSpan autoUninitSpan(this);
12525 if (autoUninitSpan.uninitDone())
12526 {
12527 LogFlowThisFunc(("Already uninitialized\n"));
12528 LogFlowThisFuncLeave();
12529 return;
12530 }
12531
12532 if (autoUninitSpan.initFailed())
12533 {
12534 /* We've been called by init() because it's failed. It's not really
12535 * necessary (nor it's safe) to perform the regular uninit sequence
12536 * below, the following is enough.
12537 */
12538 LogFlowThisFunc(("Initialization failed.\n"));
12539 /* destroy the machine client token */
12540 if (mClientToken)
12541 {
12542 delete mClientToken;
12543 mClientToken = NULL;
12544 }
12545 uninitDataAndChildObjects();
12546 mData.free();
12547 unconst(mParent) = NULL;
12548 unconst(mPeer) = NULL;
12549 LogFlowThisFuncLeave();
12550 return;
12551 }
12552
12553 MachineState_T lastState;
12554 {
12555 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12556 lastState = mData->mMachineState;
12557 }
12558 NOREF(lastState);
12559
12560#ifdef VBOX_WITH_USB
12561 // release all captured USB devices, but do this before requesting the locks below
12562 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12563 {
12564 /* Console::captureUSBDevices() is called in the VM process only after
12565 * setting the machine state to Starting or Restoring.
12566 * Console::detachAllUSBDevices() will be called upon successful
12567 * termination. So, we need to release USB devices only if there was
12568 * an abnormal termination of a running VM.
12569 *
12570 * This is identical to SessionMachine::DetachAllUSBDevices except
12571 * for the aAbnormal argument. */
12572 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12573 AssertComRC(rc);
12574 NOREF(rc);
12575
12576 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12577 if (service)
12578 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12579 }
12580#endif /* VBOX_WITH_USB */
12581
12582 // we need to lock this object in uninit() because the lock is shared
12583 // with mPeer (as well as data we modify below). mParent lock is needed
12584 // by several calls to it, and USB needs host lock.
12585 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12586
12587#ifdef VBOX_WITH_RESOURCE_USAGE_API
12588 /*
12589 * It is safe to call Machine::i_unregisterMetrics() here because
12590 * PerformanceCollector::samplerCallback no longer accesses guest methods
12591 * holding the lock.
12592 */
12593 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12594 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12595 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12596 if (mCollectorGuest)
12597 {
12598 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12599 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12600 mCollectorGuest = NULL;
12601 }
12602#endif
12603
12604 if (aReason == Uninit::Abnormal)
12605 {
12606 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12607
12608 /* reset the state to Aborted */
12609 if (mData->mMachineState != MachineState_Aborted)
12610 i_setMachineState(MachineState_Aborted);
12611 }
12612
12613 // any machine settings modified?
12614 if (mData->flModifications)
12615 {
12616 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12617 i_rollback(false /* aNotify */);
12618 }
12619
12620 mData->mSession.mPID = NIL_RTPROCESS;
12621
12622 if (aReason == Uninit::Unexpected)
12623 {
12624 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12625 * client watcher thread to update the set of machines that have open
12626 * sessions. */
12627 mParent->i_updateClientWatcher();
12628 }
12629
12630 /* uninitialize all remote controls */
12631 if (mData->mSession.mRemoteControls.size())
12632 {
12633 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12634 mData->mSession.mRemoteControls.size()));
12635
12636 Data::Session::RemoteControlList::iterator it =
12637 mData->mSession.mRemoteControls.begin();
12638 while (it != mData->mSession.mRemoteControls.end())
12639 {
12640 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12641 HRESULT rc = (*it)->Uninitialize();
12642 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12643 if (FAILED(rc))
12644 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12645 ++it;
12646 }
12647 mData->mSession.mRemoteControls.clear();
12648 }
12649
12650 /* Remove all references to the NAT network service. The service will stop
12651 * if all references (also from other VMs) are removed. */
12652 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12653 {
12654 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12655 {
12656 BOOL enabled;
12657 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12658 if ( FAILED(hrc)
12659 || !enabled)
12660 continue;
12661
12662 NetworkAttachmentType_T type;
12663 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12664 if ( SUCCEEDED(hrc)
12665 && type == NetworkAttachmentType_NATNetwork)
12666 {
12667 Bstr name;
12668 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12669 if (SUCCEEDED(hrc))
12670 {
12671 multilock.release();
12672 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12673 mUserData->s.strName.c_str(), name.raw()));
12674 mParent->i_natNetworkRefDec(name.raw());
12675 multilock.acquire();
12676 }
12677 }
12678 }
12679 }
12680
12681 /*
12682 * An expected uninitialization can come only from #i_checkForDeath().
12683 * Otherwise it means that something's gone really wrong (for example,
12684 * the Session implementation has released the VirtualBox reference
12685 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12686 * etc). However, it's also possible, that the client releases the IPC
12687 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12688 * but the VirtualBox release event comes first to the server process.
12689 * This case is practically possible, so we should not assert on an
12690 * unexpected uninit, just log a warning.
12691 */
12692
12693 if ((aReason == Uninit::Unexpected))
12694 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12695
12696 if (aReason != Uninit::Normal)
12697 {
12698 mData->mSession.mDirectControl.setNull();
12699 }
12700 else
12701 {
12702 /* this must be null here (see #OnSessionEnd()) */
12703 Assert(mData->mSession.mDirectControl.isNull());
12704 Assert(mData->mSession.mState == SessionState_Unlocking);
12705 Assert(!mData->mSession.mProgress.isNull());
12706 }
12707 if (mData->mSession.mProgress)
12708 {
12709 if (aReason == Uninit::Normal)
12710 mData->mSession.mProgress->i_notifyComplete(S_OK);
12711 else
12712 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12713 COM_IIDOF(ISession),
12714 getComponentName(),
12715 tr("The VM session was aborted"));
12716 mData->mSession.mProgress.setNull();
12717 }
12718
12719 if (mConsoleTaskData.mProgress)
12720 {
12721 Assert(aReason == Uninit::Abnormal);
12722 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12723 COM_IIDOF(ISession),
12724 getComponentName(),
12725 tr("The VM session was aborted"));
12726 mConsoleTaskData.mProgress.setNull();
12727 }
12728
12729 /* remove the association between the peer machine and this session machine */
12730 Assert( (SessionMachine*)mData->mSession.mMachine == this
12731 || aReason == Uninit::Unexpected);
12732
12733 /* reset the rest of session data */
12734 mData->mSession.mLockType = LockType_Null;
12735 mData->mSession.mMachine.setNull();
12736 mData->mSession.mState = SessionState_Unlocked;
12737 mData->mSession.mName.setNull();
12738
12739 /* destroy the machine client token before leaving the exclusive lock */
12740 if (mClientToken)
12741 {
12742 delete mClientToken;
12743 mClientToken = NULL;
12744 }
12745
12746 /* fire an event */
12747 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12748
12749 uninitDataAndChildObjects();
12750
12751 /* free the essential data structure last */
12752 mData.free();
12753
12754 /* release the exclusive lock before setting the below two to NULL */
12755 multilock.release();
12756
12757 unconst(mParent) = NULL;
12758 unconst(mPeer) = NULL;
12759
12760 AuthLibUnload(&mAuthLibCtx);
12761
12762 LogFlowThisFuncLeave();
12763}
12764
12765// util::Lockable interface
12766////////////////////////////////////////////////////////////////////////////////
12767
12768/**
12769 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12770 * with the primary Machine instance (mPeer).
12771 */
12772RWLockHandle *SessionMachine::lockHandle() const
12773{
12774 AssertReturn(mPeer != NULL, NULL);
12775 return mPeer->lockHandle();
12776}
12777
12778// IInternalMachineControl methods
12779////////////////////////////////////////////////////////////////////////////////
12780
12781/**
12782 * Passes collected guest statistics to performance collector object
12783 */
12784HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12785 ULONG aCpuKernel, ULONG aCpuIdle,
12786 ULONG aMemTotal, ULONG aMemFree,
12787 ULONG aMemBalloon, ULONG aMemShared,
12788 ULONG aMemCache, ULONG aPageTotal,
12789 ULONG aAllocVMM, ULONG aFreeVMM,
12790 ULONG aBalloonedVMM, ULONG aSharedVMM,
12791 ULONG aVmNetRx, ULONG aVmNetTx)
12792{
12793#ifdef VBOX_WITH_RESOURCE_USAGE_API
12794 if (mCollectorGuest)
12795 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12796 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12797 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12798 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12799
12800 return S_OK;
12801#else
12802 NOREF(aValidStats);
12803 NOREF(aCpuUser);
12804 NOREF(aCpuKernel);
12805 NOREF(aCpuIdle);
12806 NOREF(aMemTotal);
12807 NOREF(aMemFree);
12808 NOREF(aMemBalloon);
12809 NOREF(aMemShared);
12810 NOREF(aMemCache);
12811 NOREF(aPageTotal);
12812 NOREF(aAllocVMM);
12813 NOREF(aFreeVMM);
12814 NOREF(aBalloonedVMM);
12815 NOREF(aSharedVMM);
12816 NOREF(aVmNetRx);
12817 NOREF(aVmNetTx);
12818 return E_NOTIMPL;
12819#endif
12820}
12821
12822////////////////////////////////////////////////////////////////////////////////
12823//
12824// SessionMachine task records
12825//
12826////////////////////////////////////////////////////////////////////////////////
12827
12828/**
12829 * Task record for saving the machine state.
12830 */
12831struct SessionMachine::SaveStateTask
12832 : public Machine::Task
12833{
12834 SaveStateTask(SessionMachine *m,
12835 Progress *p,
12836 const Utf8Str &t,
12837 Reason_T enmReason,
12838 const Utf8Str &strStateFilePath)
12839 : Task(m, p, t),
12840 m_enmReason(enmReason),
12841 m_strStateFilePath(strStateFilePath)
12842 {}
12843
12844 void handler()
12845 {
12846 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12847 }
12848
12849 Reason_T m_enmReason;
12850 Utf8Str m_strStateFilePath;
12851};
12852
12853/**
12854 * Task thread implementation for SessionMachine::SaveState(), called from
12855 * SessionMachine::taskHandler().
12856 *
12857 * @note Locks this object for writing.
12858 *
12859 * @param task
12860 * @return
12861 */
12862void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12863{
12864 LogFlowThisFuncEnter();
12865
12866 AutoCaller autoCaller(this);
12867 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12868 if (FAILED(autoCaller.rc()))
12869 {
12870 /* we might have been uninitialized because the session was accidentally
12871 * closed by the client, so don't assert */
12872 HRESULT rc = setError(E_FAIL,
12873 tr("The session has been accidentally closed"));
12874 task.m_pProgress->i_notifyComplete(rc);
12875 LogFlowThisFuncLeave();
12876 return;
12877 }
12878
12879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12880
12881 HRESULT rc = S_OK;
12882
12883 try
12884 {
12885 ComPtr<IInternalSessionControl> directControl;
12886 if (mData->mSession.mLockType == LockType_VM)
12887 directControl = mData->mSession.mDirectControl;
12888 if (directControl.isNull())
12889 throw setError(VBOX_E_INVALID_VM_STATE,
12890 tr("Trying to save state without a running VM"));
12891 alock.release();
12892 BOOL fSuspendedBySave;
12893 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12894 Assert(!fSuspendedBySave);
12895 alock.acquire();
12896
12897 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12898 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12899 throw E_FAIL);
12900
12901 if (SUCCEEDED(rc))
12902 {
12903 mSSData->strStateFilePath = task.m_strStateFilePath;
12904
12905 /* save all VM settings */
12906 rc = i_saveSettings(NULL);
12907 // no need to check whether VirtualBox.xml needs saving also since
12908 // we can't have a name change pending at this point
12909 }
12910 else
12911 {
12912 // On failure, set the state to the state we had at the beginning.
12913 i_setMachineState(task.m_machineStateBackup);
12914 i_updateMachineStateOnClient();
12915
12916 // Delete the saved state file (might have been already created).
12917 // No need to check whether this is shared with a snapshot here
12918 // because we certainly created a fresh saved state file here.
12919 RTFileDelete(task.m_strStateFilePath.c_str());
12920 }
12921 }
12922 catch (HRESULT aRC) { rc = aRC; }
12923
12924 task.m_pProgress->i_notifyComplete(rc);
12925
12926 LogFlowThisFuncLeave();
12927}
12928
12929/**
12930 * @note Locks this object for writing.
12931 */
12932HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12933{
12934 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12935}
12936
12937HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12938{
12939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12940
12941 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12942 if (FAILED(rc)) return rc;
12943
12944 if ( mData->mMachineState != MachineState_Running
12945 && mData->mMachineState != MachineState_Paused
12946 )
12947 return setError(VBOX_E_INVALID_VM_STATE,
12948 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12949 Global::stringifyMachineState(mData->mMachineState));
12950
12951 ComObjPtr<Progress> pProgress;
12952 pProgress.createObject();
12953 rc = pProgress->init(i_getVirtualBox(),
12954 static_cast<IMachine *>(this) /* aInitiator */,
12955 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12956 FALSE /* aCancelable */);
12957 if (FAILED(rc))
12958 return rc;
12959
12960 Utf8Str strStateFilePath;
12961 i_composeSavedStateFilename(strStateFilePath);
12962
12963 /* create and start the task on a separate thread (note that it will not
12964 * start working until we release alock) */
12965 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12966 rc = pTask->createThread();
12967 if (FAILED(rc))
12968 return rc;
12969
12970 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12971 i_setMachineState(MachineState_Saving);
12972 i_updateMachineStateOnClient();
12973
12974 pProgress.queryInterfaceTo(aProgress.asOutParam());
12975
12976 return S_OK;
12977}
12978
12979/**
12980 * @note Locks this object for writing.
12981 */
12982HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12983{
12984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12985
12986 HRESULT rc = i_checkStateDependency(MutableStateDep);
12987 if (FAILED(rc)) return rc;
12988
12989 if ( mData->mMachineState != MachineState_PoweredOff
12990 && mData->mMachineState != MachineState_Teleported
12991 && mData->mMachineState != MachineState_Aborted
12992 )
12993 return setError(VBOX_E_INVALID_VM_STATE,
12994 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12995 Global::stringifyMachineState(mData->mMachineState));
12996
12997 com::Utf8Str stateFilePathFull;
12998 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12999 if (RT_FAILURE(vrc))
13000 return setError(VBOX_E_FILE_ERROR,
13001 tr("Invalid saved state file path '%s' (%Rrc)"),
13002 aSavedStateFile.c_str(),
13003 vrc);
13004
13005 mSSData->strStateFilePath = stateFilePathFull;
13006
13007 /* The below i_setMachineState() will detect the state transition and will
13008 * update the settings file */
13009
13010 return i_setMachineState(MachineState_Saved);
13011}
13012
13013/**
13014 * @note Locks this object for writing.
13015 */
13016HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13017{
13018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13019
13020 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13021 if (FAILED(rc)) return rc;
13022
13023 if (mData->mMachineState != MachineState_Saved)
13024 return setError(VBOX_E_INVALID_VM_STATE,
13025 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13026 Global::stringifyMachineState(mData->mMachineState));
13027
13028 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13029
13030 /*
13031 * Saved -> PoweredOff transition will be detected in the SessionMachine
13032 * and properly handled.
13033 */
13034 rc = i_setMachineState(MachineState_PoweredOff);
13035 return rc;
13036}
13037
13038
13039/**
13040 * @note Locks the same as #i_setMachineState() does.
13041 */
13042HRESULT SessionMachine::updateState(MachineState_T aState)
13043{
13044 return i_setMachineState(aState);
13045}
13046
13047/**
13048 * @note Locks this object for writing.
13049 */
13050HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13051{
13052 IProgress* pProgress(aProgress);
13053
13054 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13055
13056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13057
13058 if (mData->mSession.mState != SessionState_Locked)
13059 return VBOX_E_INVALID_OBJECT_STATE;
13060
13061 if (!mData->mSession.mProgress.isNull())
13062 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13063
13064 /* If we didn't reference the NAT network service yet, add a reference to
13065 * force a start */
13066 if (miNATNetworksStarted < 1)
13067 {
13068 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13069 {
13070 BOOL enabled;
13071 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13072 if ( FAILED(hrc)
13073 || !enabled)
13074 continue;
13075
13076 NetworkAttachmentType_T type;
13077 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13078 if ( SUCCEEDED(hrc)
13079 && type == NetworkAttachmentType_NATNetwork)
13080 {
13081 Bstr name;
13082 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13083 if (SUCCEEDED(hrc))
13084 {
13085 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13086 mUserData->s.strName.c_str(), name.raw()));
13087 mPeer->lockHandle()->unlockWrite();
13088 mParent->i_natNetworkRefInc(name.raw());
13089#ifdef RT_LOCK_STRICT
13090 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13091#else
13092 mPeer->lockHandle()->lockWrite();
13093#endif
13094 }
13095 }
13096 }
13097 miNATNetworksStarted++;
13098 }
13099
13100 LogFlowThisFunc(("returns S_OK.\n"));
13101 return S_OK;
13102}
13103
13104/**
13105 * @note Locks this object for writing.
13106 */
13107HRESULT SessionMachine::endPowerUp(LONG aResult)
13108{
13109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13110
13111 if (mData->mSession.mState != SessionState_Locked)
13112 return VBOX_E_INVALID_OBJECT_STATE;
13113
13114 /* Finalize the LaunchVMProcess progress object. */
13115 if (mData->mSession.mProgress)
13116 {
13117 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13118 mData->mSession.mProgress.setNull();
13119 }
13120
13121 if (SUCCEEDED((HRESULT)aResult))
13122 {
13123#ifdef VBOX_WITH_RESOURCE_USAGE_API
13124 /* The VM has been powered up successfully, so it makes sense
13125 * now to offer the performance metrics for a running machine
13126 * object. Doing it earlier wouldn't be safe. */
13127 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13128 mData->mSession.mPID);
13129#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13130 }
13131
13132 return S_OK;
13133}
13134
13135/**
13136 * @note Locks this object for writing.
13137 */
13138HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13139{
13140 LogFlowThisFuncEnter();
13141
13142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13143
13144 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13145 E_FAIL);
13146
13147 /* create a progress object to track operation completion */
13148 ComObjPtr<Progress> pProgress;
13149 pProgress.createObject();
13150 pProgress->init(i_getVirtualBox(),
13151 static_cast<IMachine *>(this) /* aInitiator */,
13152 Bstr(tr("Stopping the virtual machine")).raw(),
13153 FALSE /* aCancelable */);
13154
13155 /* fill in the console task data */
13156 mConsoleTaskData.mLastState = mData->mMachineState;
13157 mConsoleTaskData.mProgress = pProgress;
13158
13159 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13160 i_setMachineState(MachineState_Stopping);
13161
13162 pProgress.queryInterfaceTo(aProgress.asOutParam());
13163
13164 return S_OK;
13165}
13166
13167/**
13168 * @note Locks this object for writing.
13169 */
13170HRESULT SessionMachine::endPoweringDown(LONG aResult,
13171 const com::Utf8Str &aErrMsg)
13172{
13173 LogFlowThisFuncEnter();
13174
13175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13176
13177 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13178 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13179 && mConsoleTaskData.mLastState != MachineState_Null,
13180 E_FAIL);
13181
13182 /*
13183 * On failure, set the state to the state we had when BeginPoweringDown()
13184 * was called (this is expected by Console::PowerDown() and the associated
13185 * task). On success the VM process already changed the state to
13186 * MachineState_PoweredOff, so no need to do anything.
13187 */
13188 if (FAILED(aResult))
13189 i_setMachineState(mConsoleTaskData.mLastState);
13190
13191 /* notify the progress object about operation completion */
13192 Assert(mConsoleTaskData.mProgress);
13193 if (SUCCEEDED(aResult))
13194 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13195 else
13196 {
13197 if (aErrMsg.length())
13198 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13199 COM_IIDOF(ISession),
13200 getComponentName(),
13201 aErrMsg.c_str());
13202 else
13203 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13204 }
13205
13206 /* clear out the temporary saved state data */
13207 mConsoleTaskData.mLastState = MachineState_Null;
13208 mConsoleTaskData.mProgress.setNull();
13209
13210 LogFlowThisFuncLeave();
13211 return S_OK;
13212}
13213
13214
13215/**
13216 * Goes through the USB filters of the given machine to see if the given
13217 * device matches any filter or not.
13218 *
13219 * @note Locks the same as USBController::hasMatchingFilter() does.
13220 */
13221HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13222 BOOL *aMatched,
13223 ULONG *aMaskedInterfaces)
13224{
13225 LogFlowThisFunc(("\n"));
13226
13227#ifdef VBOX_WITH_USB
13228 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13229#else
13230 NOREF(aDevice);
13231 NOREF(aMaskedInterfaces);
13232 *aMatched = FALSE;
13233#endif
13234
13235 return S_OK;
13236}
13237
13238/**
13239 * @note Locks the same as Host::captureUSBDevice() does.
13240 */
13241HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13242{
13243 LogFlowThisFunc(("\n"));
13244
13245#ifdef VBOX_WITH_USB
13246 /* if captureDeviceForVM() fails, it must have set extended error info */
13247 clearError();
13248 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13249 if (FAILED(rc)) return rc;
13250
13251 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13252 AssertReturn(service, E_FAIL);
13253 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13254#else
13255 NOREF(aId);
13256 return E_NOTIMPL;
13257#endif
13258}
13259
13260/**
13261 * @note Locks the same as Host::detachUSBDevice() does.
13262 */
13263HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13264 BOOL aDone)
13265{
13266 LogFlowThisFunc(("\n"));
13267
13268#ifdef VBOX_WITH_USB
13269 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13270 AssertReturn(service, E_FAIL);
13271 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13272#else
13273 NOREF(aId);
13274 NOREF(aDone);
13275 return E_NOTIMPL;
13276#endif
13277}
13278
13279/**
13280 * Inserts all machine filters to the USB proxy service and then calls
13281 * Host::autoCaptureUSBDevices().
13282 *
13283 * Called by Console from the VM process upon VM startup.
13284 *
13285 * @note Locks what called methods lock.
13286 */
13287HRESULT SessionMachine::autoCaptureUSBDevices()
13288{
13289 LogFlowThisFunc(("\n"));
13290
13291#ifdef VBOX_WITH_USB
13292 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13293 AssertComRC(rc);
13294 NOREF(rc);
13295
13296 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13297 AssertReturn(service, E_FAIL);
13298 return service->autoCaptureDevicesForVM(this);
13299#else
13300 return S_OK;
13301#endif
13302}
13303
13304/**
13305 * Removes all machine filters from the USB proxy service and then calls
13306 * Host::detachAllUSBDevices().
13307 *
13308 * Called by Console from the VM process upon normal VM termination or by
13309 * SessionMachine::uninit() upon abnormal VM termination (from under the
13310 * Machine/SessionMachine lock).
13311 *
13312 * @note Locks what called methods lock.
13313 */
13314HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13315{
13316 LogFlowThisFunc(("\n"));
13317
13318#ifdef VBOX_WITH_USB
13319 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13320 AssertComRC(rc);
13321 NOREF(rc);
13322
13323 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13324 AssertReturn(service, E_FAIL);
13325 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13326#else
13327 NOREF(aDone);
13328 return S_OK;
13329#endif
13330}
13331
13332/**
13333 * @note Locks this object for writing.
13334 */
13335HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13336 ComPtr<IProgress> &aProgress)
13337{
13338 LogFlowThisFuncEnter();
13339
13340 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13341 /*
13342 * We don't assert below because it might happen that a non-direct session
13343 * informs us it is closed right after we've been uninitialized -- it's ok.
13344 */
13345
13346 /* get IInternalSessionControl interface */
13347 ComPtr<IInternalSessionControl> control(aSession);
13348
13349 ComAssertRet(!control.isNull(), E_INVALIDARG);
13350
13351 /* Creating a Progress object requires the VirtualBox lock, and
13352 * thus locking it here is required by the lock order rules. */
13353 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13354
13355 if (control == mData->mSession.mDirectControl)
13356 {
13357 /* The direct session is being normally closed by the client process
13358 * ----------------------------------------------------------------- */
13359
13360 /* go to the closing state (essential for all open*Session() calls and
13361 * for #i_checkForDeath()) */
13362 Assert(mData->mSession.mState == SessionState_Locked);
13363 mData->mSession.mState = SessionState_Unlocking;
13364
13365 /* set direct control to NULL to release the remote instance */
13366 mData->mSession.mDirectControl.setNull();
13367 LogFlowThisFunc(("Direct control is set to NULL\n"));
13368
13369 if (mData->mSession.mProgress)
13370 {
13371 /* finalize the progress, someone might wait if a frontend
13372 * closes the session before powering on the VM. */
13373 mData->mSession.mProgress->notifyComplete(E_FAIL,
13374 COM_IIDOF(ISession),
13375 getComponentName(),
13376 tr("The VM session was closed before any attempt to power it on"));
13377 mData->mSession.mProgress.setNull();
13378 }
13379
13380 /* Create the progress object the client will use to wait until
13381 * #i_checkForDeath() is called to uninitialize this session object after
13382 * it releases the IPC semaphore.
13383 * Note! Because we're "reusing" mProgress here, this must be a proxy
13384 * object just like for LaunchVMProcess. */
13385 Assert(mData->mSession.mProgress.isNull());
13386 ComObjPtr<ProgressProxy> progress;
13387 progress.createObject();
13388 ComPtr<IUnknown> pPeer(mPeer);
13389 progress->init(mParent, pPeer,
13390 Bstr(tr("Closing session")).raw(),
13391 FALSE /* aCancelable */);
13392 progress.queryInterfaceTo(aProgress.asOutParam());
13393 mData->mSession.mProgress = progress;
13394 }
13395 else
13396 {
13397 /* the remote session is being normally closed */
13398 Data::Session::RemoteControlList::iterator it =
13399 mData->mSession.mRemoteControls.begin();
13400 while (it != mData->mSession.mRemoteControls.end())
13401 {
13402 if (control == *it)
13403 break;
13404 ++it;
13405 }
13406 BOOL found = it != mData->mSession.mRemoteControls.end();
13407 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13408 E_INVALIDARG);
13409 // This MUST be erase(it), not remove(*it) as the latter triggers a
13410 // very nasty use after free due to the place where the value "lives".
13411 mData->mSession.mRemoteControls.erase(it);
13412 }
13413
13414 /* signal the client watcher thread, because the client is going away */
13415 mParent->i_updateClientWatcher();
13416
13417 LogFlowThisFuncLeave();
13418 return S_OK;
13419}
13420
13421HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13422 std::vector<com::Utf8Str> &aValues,
13423 std::vector<LONG64> &aTimestamps,
13424 std::vector<com::Utf8Str> &aFlags)
13425{
13426 LogFlowThisFunc(("\n"));
13427
13428#ifdef VBOX_WITH_GUEST_PROPS
13429 using namespace guestProp;
13430
13431 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13432
13433 size_t cEntries = mHWData->mGuestProperties.size();
13434 aNames.resize(cEntries);
13435 aValues.resize(cEntries);
13436 aTimestamps.resize(cEntries);
13437 aFlags.resize(cEntries);
13438
13439 size_t i = 0;
13440 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13441 it != mHWData->mGuestProperties.end();
13442 ++it, ++i)
13443 {
13444 char szFlags[MAX_FLAGS_LEN + 1];
13445 aNames[i] = it->first;
13446 aValues[i] = it->second.strValue;
13447 aTimestamps[i] = it->second.mTimestamp;
13448
13449 /* If it is NULL, keep it NULL. */
13450 if (it->second.mFlags)
13451 {
13452 writeFlags(it->second.mFlags, szFlags);
13453 aFlags[i] = szFlags;
13454 }
13455 else
13456 aFlags[i] = "";
13457 }
13458 return S_OK;
13459#else
13460 ReturnComNotImplemented();
13461#endif
13462}
13463
13464HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13465 const com::Utf8Str &aValue,
13466 LONG64 aTimestamp,
13467 const com::Utf8Str &aFlags)
13468{
13469 LogFlowThisFunc(("\n"));
13470
13471#ifdef VBOX_WITH_GUEST_PROPS
13472 using namespace guestProp;
13473
13474 try
13475 {
13476 /*
13477 * Convert input up front.
13478 */
13479 uint32_t fFlags = NILFLAG;
13480 if (aFlags.length())
13481 {
13482 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13483 AssertRCReturn(vrc, E_INVALIDARG);
13484 }
13485
13486 /*
13487 * Now grab the object lock, validate the state and do the update.
13488 */
13489
13490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13491
13492 if (!Global::IsOnline(mData->mMachineState))
13493 {
13494 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13495 VBOX_E_INVALID_VM_STATE);
13496 }
13497
13498 i_setModified(IsModified_MachineData);
13499 mHWData.backup();
13500
13501 bool fDelete = !aValue.length();
13502 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13503 if (it != mHWData->mGuestProperties.end())
13504 {
13505 if (!fDelete)
13506 {
13507 it->second.strValue = aValue;
13508 it->second.mTimestamp = aTimestamp;
13509 it->second.mFlags = fFlags;
13510 }
13511 else
13512 mHWData->mGuestProperties.erase(it);
13513
13514 mData->mGuestPropertiesModified = TRUE;
13515 }
13516 else if (!fDelete)
13517 {
13518 HWData::GuestProperty prop;
13519 prop.strValue = aValue;
13520 prop.mTimestamp = aTimestamp;
13521 prop.mFlags = fFlags;
13522
13523 mHWData->mGuestProperties[aName] = prop;
13524 mData->mGuestPropertiesModified = TRUE;
13525 }
13526
13527 alock.release();
13528
13529 mParent->i_onGuestPropertyChange(mData->mUuid,
13530 Bstr(aName).raw(),
13531 Bstr(aValue).raw(),
13532 Bstr(aFlags).raw());
13533 }
13534 catch (...)
13535 {
13536 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13537 }
13538 return S_OK;
13539#else
13540 ReturnComNotImplemented();
13541#endif
13542}
13543
13544
13545HRESULT SessionMachine::lockMedia()
13546{
13547 AutoMultiWriteLock2 alock(this->lockHandle(),
13548 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13549
13550 AssertReturn( mData->mMachineState == MachineState_Starting
13551 || mData->mMachineState == MachineState_Restoring
13552 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13553
13554 clearError();
13555 alock.release();
13556 return i_lockMedia();
13557}
13558
13559HRESULT SessionMachine::unlockMedia()
13560{
13561 HRESULT hrc = i_unlockMedia();
13562 return hrc;
13563}
13564
13565HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13566 ComPtr<IMediumAttachment> &aNewAttachment)
13567{
13568 // request the host lock first, since might be calling Host methods for getting host drives;
13569 // next, protect the media tree all the while we're in here, as well as our member variables
13570 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13571 this->lockHandle(),
13572 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13573
13574 IMediumAttachment *iAttach = aAttachment;
13575 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13576
13577 Bstr ctrlName;
13578 LONG lPort;
13579 LONG lDevice;
13580 bool fTempEject;
13581 {
13582 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13583
13584 /* Need to query the details first, as the IMediumAttachment reference
13585 * might be to the original settings, which we are going to change. */
13586 ctrlName = pAttach->i_getControllerName();
13587 lPort = pAttach->i_getPort();
13588 lDevice = pAttach->i_getDevice();
13589 fTempEject = pAttach->i_getTempEject();
13590 }
13591
13592 if (!fTempEject)
13593 {
13594 /* Remember previously mounted medium. The medium before taking the
13595 * backup is not necessarily the same thing. */
13596 ComObjPtr<Medium> oldmedium;
13597 oldmedium = pAttach->i_getMedium();
13598
13599 i_setModified(IsModified_Storage);
13600 mMediaData.backup();
13601
13602 // The backup operation makes the pAttach reference point to the
13603 // old settings. Re-get the correct reference.
13604 pAttach = i_findAttachment(mMediaData->mAttachments,
13605 ctrlName.raw(),
13606 lPort,
13607 lDevice);
13608
13609 {
13610 AutoCaller autoAttachCaller(this);
13611 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13612
13613 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13614 if (!oldmedium.isNull())
13615 oldmedium->i_removeBackReference(mData->mUuid);
13616
13617 pAttach->i_updateMedium(NULL);
13618 pAttach->i_updateEjected();
13619 }
13620
13621 i_setModified(IsModified_Storage);
13622 }
13623 else
13624 {
13625 {
13626 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13627 pAttach->i_updateEjected();
13628 }
13629 }
13630
13631 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13632
13633 return S_OK;
13634}
13635
13636HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13637 com::Utf8Str &aResult)
13638{
13639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13640
13641 HRESULT hr = S_OK;
13642
13643 if (!mAuthLibCtx.hAuthLibrary)
13644 {
13645 /* Load the external authentication library. */
13646 Bstr authLibrary;
13647 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13648
13649 Utf8Str filename = authLibrary;
13650
13651 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13652 if (RT_FAILURE(rc))
13653 {
13654 hr = setError(E_FAIL,
13655 tr("Could not load the external authentication library '%s' (%Rrc)"),
13656 filename.c_str(), rc);
13657 }
13658 }
13659
13660 /* The auth library might need the machine lock. */
13661 alock.release();
13662
13663 if (FAILED(hr))
13664 return hr;
13665
13666 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13667 {
13668 enum VRDEAuthParams
13669 {
13670 parmUuid = 1,
13671 parmGuestJudgement,
13672 parmUser,
13673 parmPassword,
13674 parmDomain,
13675 parmClientId
13676 };
13677
13678 AuthResult result = AuthResultAccessDenied;
13679
13680 Guid uuid(aAuthParams[parmUuid]);
13681 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13682 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13683
13684 result = AuthLibAuthenticate(&mAuthLibCtx,
13685 uuid.raw(), guestJudgement,
13686 aAuthParams[parmUser].c_str(),
13687 aAuthParams[parmPassword].c_str(),
13688 aAuthParams[parmDomain].c_str(),
13689 u32ClientId);
13690
13691 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13692 size_t cbPassword = aAuthParams[parmPassword].length();
13693 if (cbPassword)
13694 {
13695 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13696 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13697 }
13698
13699 if (result == AuthResultAccessGranted)
13700 aResult = "granted";
13701 else
13702 aResult = "denied";
13703
13704 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13705 aAuthParams[parmUser].c_str(), aResult.c_str()));
13706 }
13707 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13708 {
13709 enum VRDEAuthDisconnectParams
13710 {
13711 parmUuid = 1,
13712 parmClientId
13713 };
13714
13715 Guid uuid(aAuthParams[parmUuid]);
13716 uint32_t u32ClientId = 0;
13717 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13718 }
13719 else
13720 {
13721 hr = E_INVALIDARG;
13722 }
13723
13724 return hr;
13725}
13726
13727// public methods only for internal purposes
13728/////////////////////////////////////////////////////////////////////////////
13729
13730#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13731/**
13732 * Called from the client watcher thread to check for expected or unexpected
13733 * death of the client process that has a direct session to this machine.
13734 *
13735 * On Win32 and on OS/2, this method is called only when we've got the
13736 * mutex (i.e. the client has either died or terminated normally) so it always
13737 * returns @c true (the client is terminated, the session machine is
13738 * uninitialized).
13739 *
13740 * On other platforms, the method returns @c true if the client process has
13741 * terminated normally or abnormally and the session machine was uninitialized,
13742 * and @c false if the client process is still alive.
13743 *
13744 * @note Locks this object for writing.
13745 */
13746bool SessionMachine::i_checkForDeath()
13747{
13748 Uninit::Reason reason;
13749 bool terminated = false;
13750
13751 /* Enclose autoCaller with a block because calling uninit() from under it
13752 * will deadlock. */
13753 {
13754 AutoCaller autoCaller(this);
13755 if (!autoCaller.isOk())
13756 {
13757 /* return true if not ready, to cause the client watcher to exclude
13758 * the corresponding session from watching */
13759 LogFlowThisFunc(("Already uninitialized!\n"));
13760 return true;
13761 }
13762
13763 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13764
13765 /* Determine the reason of death: if the session state is Closing here,
13766 * everything is fine. Otherwise it means that the client did not call
13767 * OnSessionEnd() before it released the IPC semaphore. This may happen
13768 * either because the client process has abnormally terminated, or
13769 * because it simply forgot to call ISession::Close() before exiting. We
13770 * threat the latter also as an abnormal termination (see
13771 * Session::uninit() for details). */
13772 reason = mData->mSession.mState == SessionState_Unlocking ?
13773 Uninit::Normal :
13774 Uninit::Abnormal;
13775
13776 if (mClientToken)
13777 terminated = mClientToken->release();
13778 } /* AutoCaller block */
13779
13780 if (terminated)
13781 uninit(reason);
13782
13783 return terminated;
13784}
13785
13786void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13787{
13788 LogFlowThisFunc(("\n"));
13789
13790 strTokenId.setNull();
13791
13792 AutoCaller autoCaller(this);
13793 AssertComRCReturnVoid(autoCaller.rc());
13794
13795 Assert(mClientToken);
13796 if (mClientToken)
13797 mClientToken->getId(strTokenId);
13798}
13799#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13800IToken *SessionMachine::i_getToken()
13801{
13802 LogFlowThisFunc(("\n"));
13803
13804 AutoCaller autoCaller(this);
13805 AssertComRCReturn(autoCaller.rc(), NULL);
13806
13807 Assert(mClientToken);
13808 if (mClientToken)
13809 return mClientToken->getToken();
13810 else
13811 return NULL;
13812}
13813#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13814
13815Machine::ClientToken *SessionMachine::i_getClientToken()
13816{
13817 LogFlowThisFunc(("\n"));
13818
13819 AutoCaller autoCaller(this);
13820 AssertComRCReturn(autoCaller.rc(), NULL);
13821
13822 return mClientToken;
13823}
13824
13825
13826/**
13827 * @note Locks this object for reading.
13828 */
13829HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13830{
13831 LogFlowThisFunc(("\n"));
13832
13833 AutoCaller autoCaller(this);
13834 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13835
13836 ComPtr<IInternalSessionControl> directControl;
13837 {
13838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13839 if (mData->mSession.mLockType == LockType_VM)
13840 directControl = mData->mSession.mDirectControl;
13841 }
13842
13843 /* ignore notifications sent after #OnSessionEnd() is called */
13844 if (!directControl)
13845 return S_OK;
13846
13847 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13848}
13849
13850/**
13851 * @note Locks this object for reading.
13852 */
13853HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13854 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13855 IN_BSTR aGuestIp, LONG aGuestPort)
13856{
13857 LogFlowThisFunc(("\n"));
13858
13859 AutoCaller autoCaller(this);
13860 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13861
13862 ComPtr<IInternalSessionControl> directControl;
13863 {
13864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13865 if (mData->mSession.mLockType == LockType_VM)
13866 directControl = mData->mSession.mDirectControl;
13867 }
13868
13869 /* ignore notifications sent after #OnSessionEnd() is called */
13870 if (!directControl)
13871 return S_OK;
13872 /*
13873 * instead acting like callback we ask IVirtualBox deliver corresponding event
13874 */
13875
13876 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13877 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13878 return S_OK;
13879}
13880
13881/**
13882 * @note Locks this object for reading.
13883 */
13884HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13885{
13886 LogFlowThisFunc(("\n"));
13887
13888 AutoCaller autoCaller(this);
13889 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13890
13891 ComPtr<IInternalSessionControl> directControl;
13892 {
13893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13894 if (mData->mSession.mLockType == LockType_VM)
13895 directControl = mData->mSession.mDirectControl;
13896 }
13897
13898 /* ignore notifications sent after #OnSessionEnd() is called */
13899 if (!directControl)
13900 return S_OK;
13901
13902 return directControl->OnSerialPortChange(serialPort);
13903}
13904
13905/**
13906 * @note Locks this object for reading.
13907 */
13908HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13909{
13910 LogFlowThisFunc(("\n"));
13911
13912 AutoCaller autoCaller(this);
13913 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13914
13915 ComPtr<IInternalSessionControl> directControl;
13916 {
13917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13918 if (mData->mSession.mLockType == LockType_VM)
13919 directControl = mData->mSession.mDirectControl;
13920 }
13921
13922 /* ignore notifications sent after #OnSessionEnd() is called */
13923 if (!directControl)
13924 return S_OK;
13925
13926 return directControl->OnParallelPortChange(parallelPort);
13927}
13928
13929/**
13930 * @note Locks this object for reading.
13931 */
13932HRESULT SessionMachine::i_onStorageControllerChange()
13933{
13934 LogFlowThisFunc(("\n"));
13935
13936 AutoCaller autoCaller(this);
13937 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13938
13939 ComPtr<IInternalSessionControl> directControl;
13940 {
13941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13942 if (mData->mSession.mLockType == LockType_VM)
13943 directControl = mData->mSession.mDirectControl;
13944 }
13945
13946 /* ignore notifications sent after #OnSessionEnd() is called */
13947 if (!directControl)
13948 return S_OK;
13949
13950 return directControl->OnStorageControllerChange();
13951}
13952
13953/**
13954 * @note Locks this object for reading.
13955 */
13956HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13957{
13958 LogFlowThisFunc(("\n"));
13959
13960 AutoCaller autoCaller(this);
13961 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13962
13963 ComPtr<IInternalSessionControl> directControl;
13964 {
13965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13966 if (mData->mSession.mLockType == LockType_VM)
13967 directControl = mData->mSession.mDirectControl;
13968 }
13969
13970 /* ignore notifications sent after #OnSessionEnd() is called */
13971 if (!directControl)
13972 return S_OK;
13973
13974 return directControl->OnMediumChange(aAttachment, aForce);
13975}
13976
13977/**
13978 * @note Locks this object for reading.
13979 */
13980HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13981{
13982 LogFlowThisFunc(("\n"));
13983
13984 AutoCaller autoCaller(this);
13985 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13986
13987 ComPtr<IInternalSessionControl> directControl;
13988 {
13989 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13990 if (mData->mSession.mLockType == LockType_VM)
13991 directControl = mData->mSession.mDirectControl;
13992 }
13993
13994 /* ignore notifications sent after #OnSessionEnd() is called */
13995 if (!directControl)
13996 return S_OK;
13997
13998 return directControl->OnCPUChange(aCPU, aRemove);
13999}
14000
14001HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14002{
14003 LogFlowThisFunc(("\n"));
14004
14005 AutoCaller autoCaller(this);
14006 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14007
14008 ComPtr<IInternalSessionControl> directControl;
14009 {
14010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14011 if (mData->mSession.mLockType == LockType_VM)
14012 directControl = mData->mSession.mDirectControl;
14013 }
14014
14015 /* ignore notifications sent after #OnSessionEnd() is called */
14016 if (!directControl)
14017 return S_OK;
14018
14019 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14020}
14021
14022/**
14023 * @note Locks this object for reading.
14024 */
14025HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14026{
14027 LogFlowThisFunc(("\n"));
14028
14029 AutoCaller autoCaller(this);
14030 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14031
14032 ComPtr<IInternalSessionControl> directControl;
14033 {
14034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14035 if (mData->mSession.mLockType == LockType_VM)
14036 directControl = mData->mSession.mDirectControl;
14037 }
14038
14039 /* ignore notifications sent after #OnSessionEnd() is called */
14040 if (!directControl)
14041 return S_OK;
14042
14043 return directControl->OnVRDEServerChange(aRestart);
14044}
14045
14046/**
14047 * @note Locks this object for reading.
14048 */
14049HRESULT SessionMachine::i_onVideoCaptureChange()
14050{
14051 LogFlowThisFunc(("\n"));
14052
14053 AutoCaller autoCaller(this);
14054 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14055
14056 ComPtr<IInternalSessionControl> directControl;
14057 {
14058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14059 if (mData->mSession.mLockType == LockType_VM)
14060 directControl = mData->mSession.mDirectControl;
14061 }
14062
14063 /* ignore notifications sent after #OnSessionEnd() is called */
14064 if (!directControl)
14065 return S_OK;
14066
14067 return directControl->OnVideoCaptureChange();
14068}
14069
14070/**
14071 * @note Locks this object for reading.
14072 */
14073HRESULT SessionMachine::i_onUSBControllerChange()
14074{
14075 LogFlowThisFunc(("\n"));
14076
14077 AutoCaller autoCaller(this);
14078 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14079
14080 ComPtr<IInternalSessionControl> directControl;
14081 {
14082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14083 if (mData->mSession.mLockType == LockType_VM)
14084 directControl = mData->mSession.mDirectControl;
14085 }
14086
14087 /* ignore notifications sent after #OnSessionEnd() is called */
14088 if (!directControl)
14089 return S_OK;
14090
14091 return directControl->OnUSBControllerChange();
14092}
14093
14094/**
14095 * @note Locks this object for reading.
14096 */
14097HRESULT SessionMachine::i_onSharedFolderChange()
14098{
14099 LogFlowThisFunc(("\n"));
14100
14101 AutoCaller autoCaller(this);
14102 AssertComRCReturnRC(autoCaller.rc());
14103
14104 ComPtr<IInternalSessionControl> directControl;
14105 {
14106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14107 if (mData->mSession.mLockType == LockType_VM)
14108 directControl = mData->mSession.mDirectControl;
14109 }
14110
14111 /* ignore notifications sent after #OnSessionEnd() is called */
14112 if (!directControl)
14113 return S_OK;
14114
14115 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14116}
14117
14118/**
14119 * @note Locks this object for reading.
14120 */
14121HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14122{
14123 LogFlowThisFunc(("\n"));
14124
14125 AutoCaller autoCaller(this);
14126 AssertComRCReturnRC(autoCaller.rc());
14127
14128 ComPtr<IInternalSessionControl> directControl;
14129 {
14130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14131 if (mData->mSession.mLockType == LockType_VM)
14132 directControl = mData->mSession.mDirectControl;
14133 }
14134
14135 /* ignore notifications sent after #OnSessionEnd() is called */
14136 if (!directControl)
14137 return S_OK;
14138
14139 return directControl->OnClipboardModeChange(aClipboardMode);
14140}
14141
14142/**
14143 * @note Locks this object for reading.
14144 */
14145HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14146{
14147 LogFlowThisFunc(("\n"));
14148
14149 AutoCaller autoCaller(this);
14150 AssertComRCReturnRC(autoCaller.rc());
14151
14152 ComPtr<IInternalSessionControl> directControl;
14153 {
14154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14155 if (mData->mSession.mLockType == LockType_VM)
14156 directControl = mData->mSession.mDirectControl;
14157 }
14158
14159 /* ignore notifications sent after #OnSessionEnd() is called */
14160 if (!directControl)
14161 return S_OK;
14162
14163 return directControl->OnDnDModeChange(aDnDMode);
14164}
14165
14166/**
14167 * @note Locks this object for reading.
14168 */
14169HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14170{
14171 LogFlowThisFunc(("\n"));
14172
14173 AutoCaller autoCaller(this);
14174 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14175
14176 ComPtr<IInternalSessionControl> directControl;
14177 {
14178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14179 if (mData->mSession.mLockType == LockType_VM)
14180 directControl = mData->mSession.mDirectControl;
14181 }
14182
14183 /* ignore notifications sent after #OnSessionEnd() is called */
14184 if (!directControl)
14185 return S_OK;
14186
14187 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14188}
14189
14190/**
14191 * @note Locks this object for reading.
14192 */
14193HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14194{
14195 LogFlowThisFunc(("\n"));
14196
14197 AutoCaller autoCaller(this);
14198 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14199
14200 ComPtr<IInternalSessionControl> directControl;
14201 {
14202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14203 if (mData->mSession.mLockType == LockType_VM)
14204 directControl = mData->mSession.mDirectControl;
14205 }
14206
14207 /* ignore notifications sent after #OnSessionEnd() is called */
14208 if (!directControl)
14209 return S_OK;
14210
14211 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14212}
14213
14214/**
14215 * Returns @c true if this machine's USB controller reports it has a matching
14216 * filter for the given USB device and @c false otherwise.
14217 *
14218 * @note locks this object for reading.
14219 */
14220bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14221{
14222 AutoCaller autoCaller(this);
14223 /* silently return if not ready -- this method may be called after the
14224 * direct machine session has been called */
14225 if (!autoCaller.isOk())
14226 return false;
14227
14228#ifdef VBOX_WITH_USB
14229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14230
14231 switch (mData->mMachineState)
14232 {
14233 case MachineState_Starting:
14234 case MachineState_Restoring:
14235 case MachineState_TeleportingIn:
14236 case MachineState_Paused:
14237 case MachineState_Running:
14238 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14239 * elsewhere... */
14240 alock.release();
14241 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14242 default: break;
14243 }
14244#else
14245 NOREF(aDevice);
14246 NOREF(aMaskedIfs);
14247#endif
14248 return false;
14249}
14250
14251/**
14252 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14253 */
14254HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14255 IVirtualBoxErrorInfo *aError,
14256 ULONG aMaskedIfs,
14257 const com::Utf8Str &aCaptureFilename)
14258{
14259 LogFlowThisFunc(("\n"));
14260
14261 AutoCaller autoCaller(this);
14262
14263 /* This notification may happen after the machine object has been
14264 * uninitialized (the session was closed), so don't assert. */
14265 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14266
14267 ComPtr<IInternalSessionControl> directControl;
14268 {
14269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14270 if (mData->mSession.mLockType == LockType_VM)
14271 directControl = mData->mSession.mDirectControl;
14272 }
14273
14274 /* fail on notifications sent after #OnSessionEnd() is called, it is
14275 * expected by the caller */
14276 if (!directControl)
14277 return E_FAIL;
14278
14279 /* No locks should be held at this point. */
14280 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14281 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14282
14283 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14284}
14285
14286/**
14287 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14288 */
14289HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14290 IVirtualBoxErrorInfo *aError)
14291{
14292 LogFlowThisFunc(("\n"));
14293
14294 AutoCaller autoCaller(this);
14295
14296 /* This notification may happen after the machine object has been
14297 * uninitialized (the session was closed), so don't assert. */
14298 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14299
14300 ComPtr<IInternalSessionControl> directControl;
14301 {
14302 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14303 if (mData->mSession.mLockType == LockType_VM)
14304 directControl = mData->mSession.mDirectControl;
14305 }
14306
14307 /* fail on notifications sent after #OnSessionEnd() is called, it is
14308 * expected by the caller */
14309 if (!directControl)
14310 return E_FAIL;
14311
14312 /* No locks should be held at this point. */
14313 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14314 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14315
14316 return directControl->OnUSBDeviceDetach(aId, aError);
14317}
14318
14319// protected methods
14320/////////////////////////////////////////////////////////////////////////////
14321
14322/**
14323 * Deletes the given file if it is no longer in use by either the current machine state
14324 * (if the machine is "saved") or any of the machine's snapshots.
14325 *
14326 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14327 * but is different for each SnapshotMachine. When calling this, the order of calling this
14328 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14329 * is therefore critical. I know, it's all rather messy.
14330 *
14331 * @param strStateFile
14332 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14333 * the test for whether the saved state file is in use.
14334 */
14335void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14336 Snapshot *pSnapshotToIgnore)
14337{
14338 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14339 if ( (strStateFile.isNotEmpty())
14340 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14341 )
14342 // ... and it must also not be shared with other snapshots
14343 if ( !mData->mFirstSnapshot
14344 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14345 // this checks the SnapshotMachine's state file paths
14346 )
14347 RTFileDelete(strStateFile.c_str());
14348}
14349
14350/**
14351 * Locks the attached media.
14352 *
14353 * All attached hard disks are locked for writing and DVD/floppy are locked for
14354 * reading. Parents of attached hard disks (if any) are locked for reading.
14355 *
14356 * This method also performs accessibility check of all media it locks: if some
14357 * media is inaccessible, the method will return a failure and a bunch of
14358 * extended error info objects per each inaccessible medium.
14359 *
14360 * Note that this method is atomic: if it returns a success, all media are
14361 * locked as described above; on failure no media is locked at all (all
14362 * succeeded individual locks will be undone).
14363 *
14364 * The caller is responsible for doing the necessary state sanity checks.
14365 *
14366 * The locks made by this method must be undone by calling #unlockMedia() when
14367 * no more needed.
14368 */
14369HRESULT SessionMachine::i_lockMedia()
14370{
14371 AutoCaller autoCaller(this);
14372 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14373
14374 AutoMultiWriteLock2 alock(this->lockHandle(),
14375 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14376
14377 /* bail out if trying to lock things with already set up locking */
14378 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14379
14380 MultiResult mrc(S_OK);
14381
14382 /* Collect locking information for all medium objects attached to the VM. */
14383 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14384 it != mMediaData->mAttachments.end();
14385 ++it)
14386 {
14387 MediumAttachment* pAtt = *it;
14388 DeviceType_T devType = pAtt->i_getType();
14389 Medium *pMedium = pAtt->i_getMedium();
14390
14391 MediumLockList *pMediumLockList(new MediumLockList());
14392 // There can be attachments without a medium (floppy/dvd), and thus
14393 // it's impossible to create a medium lock list. It still makes sense
14394 // to have the empty medium lock list in the map in case a medium is
14395 // attached later.
14396 if (pMedium != NULL)
14397 {
14398 MediumType_T mediumType = pMedium->i_getType();
14399 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14400 || mediumType == MediumType_Shareable;
14401 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14402
14403 alock.release();
14404 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14405 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14406 false /* fMediumLockWriteAll */,
14407 NULL,
14408 *pMediumLockList);
14409 alock.acquire();
14410 if (FAILED(mrc))
14411 {
14412 delete pMediumLockList;
14413 mData->mSession.mLockedMedia.Clear();
14414 break;
14415 }
14416 }
14417
14418 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14419 if (FAILED(rc))
14420 {
14421 mData->mSession.mLockedMedia.Clear();
14422 mrc = setError(rc,
14423 tr("Collecting locking information for all attached media failed"));
14424 break;
14425 }
14426 }
14427
14428 if (SUCCEEDED(mrc))
14429 {
14430 /* Now lock all media. If this fails, nothing is locked. */
14431 alock.release();
14432 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14433 alock.acquire();
14434 if (FAILED(rc))
14435 {
14436 mrc = setError(rc,
14437 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14438 }
14439 }
14440
14441 return mrc;
14442}
14443
14444/**
14445 * Undoes the locks made by by #lockMedia().
14446 */
14447HRESULT SessionMachine::i_unlockMedia()
14448{
14449 AutoCaller autoCaller(this);
14450 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14451
14452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14453
14454 /* we may be holding important error info on the current thread;
14455 * preserve it */
14456 ErrorInfoKeeper eik;
14457
14458 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14459 AssertComRC(rc);
14460 return rc;
14461}
14462
14463/**
14464 * Helper to change the machine state (reimplementation).
14465 *
14466 * @note Locks this object for writing.
14467 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14468 * it can cause crashes in random places due to unexpectedly committing
14469 * the current settings. The caller is responsible for that. The call
14470 * to saveStateSettings is fine, because this method does not commit.
14471 */
14472HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14473{
14474 LogFlowThisFuncEnter();
14475 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14476
14477 AutoCaller autoCaller(this);
14478 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14479
14480 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14481
14482 MachineState_T oldMachineState = mData->mMachineState;
14483
14484 AssertMsgReturn(oldMachineState != aMachineState,
14485 ("oldMachineState=%s, aMachineState=%s\n",
14486 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14487 E_FAIL);
14488
14489 HRESULT rc = S_OK;
14490
14491 int stsFlags = 0;
14492 bool deleteSavedState = false;
14493
14494 /* detect some state transitions */
14495
14496 if ( ( oldMachineState == MachineState_Saved
14497 && aMachineState == MachineState_Restoring)
14498 || ( ( oldMachineState == MachineState_PoweredOff
14499 || oldMachineState == MachineState_Teleported
14500 || oldMachineState == MachineState_Aborted
14501 )
14502 && ( aMachineState == MachineState_TeleportingIn
14503 || aMachineState == MachineState_Starting
14504 )
14505 )
14506 )
14507 {
14508 /* The EMT thread is about to start */
14509
14510 /* Nothing to do here for now... */
14511
14512 /// @todo NEWMEDIA don't let mDVDDrive and other children
14513 /// change anything when in the Starting/Restoring state
14514 }
14515 else if ( ( oldMachineState == MachineState_Running
14516 || oldMachineState == MachineState_Paused
14517 || oldMachineState == MachineState_Teleporting
14518 || oldMachineState == MachineState_OnlineSnapshotting
14519 || oldMachineState == MachineState_LiveSnapshotting
14520 || oldMachineState == MachineState_Stuck
14521 || oldMachineState == MachineState_Starting
14522 || oldMachineState == MachineState_Stopping
14523 || oldMachineState == MachineState_Saving
14524 || oldMachineState == MachineState_Restoring
14525 || oldMachineState == MachineState_TeleportingPausedVM
14526 || oldMachineState == MachineState_TeleportingIn
14527 )
14528 && ( aMachineState == MachineState_PoweredOff
14529 || aMachineState == MachineState_Saved
14530 || aMachineState == MachineState_Teleported
14531 || aMachineState == MachineState_Aborted
14532 )
14533 )
14534 {
14535 /* The EMT thread has just stopped, unlock attached media. Note that as
14536 * opposed to locking that is done from Console, we do unlocking here
14537 * because the VM process may have aborted before having a chance to
14538 * properly unlock all media it locked. */
14539
14540 unlockMedia();
14541 }
14542
14543 if (oldMachineState == MachineState_Restoring)
14544 {
14545 if (aMachineState != MachineState_Saved)
14546 {
14547 /*
14548 * delete the saved state file once the machine has finished
14549 * restoring from it (note that Console sets the state from
14550 * Restoring to Saved if the VM couldn't restore successfully,
14551 * to give the user an ability to fix an error and retry --
14552 * we keep the saved state file in this case)
14553 */
14554 deleteSavedState = true;
14555 }
14556 }
14557 else if ( oldMachineState == MachineState_Saved
14558 && ( aMachineState == MachineState_PoweredOff
14559 || aMachineState == MachineState_Aborted
14560 || aMachineState == MachineState_Teleported
14561 )
14562 )
14563 {
14564 /*
14565 * delete the saved state after SessionMachine::ForgetSavedState() is called
14566 * or if the VM process (owning a direct VM session) crashed while the
14567 * VM was Saved
14568 */
14569
14570 /// @todo (dmik)
14571 // Not sure that deleting the saved state file just because of the
14572 // client death before it attempted to restore the VM is a good
14573 // thing. But when it crashes we need to go to the Aborted state
14574 // which cannot have the saved state file associated... The only
14575 // way to fix this is to make the Aborted condition not a VM state
14576 // but a bool flag: i.e., when a crash occurs, set it to true and
14577 // change the state to PoweredOff or Saved depending on the
14578 // saved state presence.
14579
14580 deleteSavedState = true;
14581 mData->mCurrentStateModified = TRUE;
14582 stsFlags |= SaveSTS_CurStateModified;
14583 }
14584
14585 if ( aMachineState == MachineState_Starting
14586 || aMachineState == MachineState_Restoring
14587 || aMachineState == MachineState_TeleportingIn
14588 )
14589 {
14590 /* set the current state modified flag to indicate that the current
14591 * state is no more identical to the state in the
14592 * current snapshot */
14593 if (!mData->mCurrentSnapshot.isNull())
14594 {
14595 mData->mCurrentStateModified = TRUE;
14596 stsFlags |= SaveSTS_CurStateModified;
14597 }
14598 }
14599
14600 if (deleteSavedState)
14601 {
14602 if (mRemoveSavedState)
14603 {
14604 Assert(!mSSData->strStateFilePath.isEmpty());
14605
14606 // it is safe to delete the saved state file if ...
14607 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14608 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14609 // ... none of the snapshots share the saved state file
14610 )
14611 RTFileDelete(mSSData->strStateFilePath.c_str());
14612 }
14613
14614 mSSData->strStateFilePath.setNull();
14615 stsFlags |= SaveSTS_StateFilePath;
14616 }
14617
14618 /* redirect to the underlying peer machine */
14619 mPeer->i_setMachineState(aMachineState);
14620
14621 if ( oldMachineState != MachineState_RestoringSnapshot
14622 && ( aMachineState == MachineState_PoweredOff
14623 || aMachineState == MachineState_Teleported
14624 || aMachineState == MachineState_Aborted
14625 || aMachineState == MachineState_Saved))
14626 {
14627 /* the machine has stopped execution
14628 * (or the saved state file was adopted) */
14629 stsFlags |= SaveSTS_StateTimeStamp;
14630 }
14631
14632 if ( ( oldMachineState == MachineState_PoweredOff
14633 || oldMachineState == MachineState_Aborted
14634 || oldMachineState == MachineState_Teleported
14635 )
14636 && aMachineState == MachineState_Saved)
14637 {
14638 /* the saved state file was adopted */
14639 Assert(!mSSData->strStateFilePath.isEmpty());
14640 stsFlags |= SaveSTS_StateFilePath;
14641 }
14642
14643#ifdef VBOX_WITH_GUEST_PROPS
14644 if ( aMachineState == MachineState_PoweredOff
14645 || aMachineState == MachineState_Aborted
14646 || aMachineState == MachineState_Teleported)
14647 {
14648 /* Make sure any transient guest properties get removed from the
14649 * property store on shutdown. */
14650 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14651
14652 /* remove it from the settings representation */
14653 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14654 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14655 it != llGuestProperties.end();
14656 /*nothing*/)
14657 {
14658 const settings::GuestProperty &prop = *it;
14659 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14660 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14661 {
14662 it = llGuestProperties.erase(it);
14663 fNeedsSaving = true;
14664 }
14665 else
14666 {
14667 ++it;
14668 }
14669 }
14670
14671 /* Additionally remove it from the HWData representation. Required to
14672 * keep everything in sync, as this is what the API keeps using. */
14673 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14674 for (HWData::GuestPropertyMap::iterator it = llHWGuestProperties.begin();
14675 it != llHWGuestProperties.end();
14676 /*nothing*/)
14677 {
14678 uint32_t fFlags = it->second.mFlags;
14679 if ( fFlags & guestProp::TRANSIENT
14680 || fFlags & guestProp::TRANSRESET)
14681 {
14682 /* iterator where we need to continue after the erase call
14683 * (C++03 is a fact still, and it doesn't return the iterator
14684 * which would allow continuing) */
14685 HWData::GuestPropertyMap::iterator it2 = it;
14686 ++it2;
14687 llHWGuestProperties.erase(it);
14688 it = it2;
14689 fNeedsSaving = true;
14690 }
14691 else
14692 {
14693 ++it;
14694 }
14695 }
14696
14697 if (fNeedsSaving)
14698 {
14699 mData->mCurrentStateModified = TRUE;
14700 stsFlags |= SaveSTS_CurStateModified;
14701 }
14702 }
14703#endif /* VBOX_WITH_GUEST_PROPS */
14704
14705 rc = i_saveStateSettings(stsFlags);
14706
14707 if ( ( oldMachineState != MachineState_PoweredOff
14708 && oldMachineState != MachineState_Aborted
14709 && oldMachineState != MachineState_Teleported
14710 )
14711 && ( aMachineState == MachineState_PoweredOff
14712 || aMachineState == MachineState_Aborted
14713 || aMachineState == MachineState_Teleported
14714 )
14715 )
14716 {
14717 /* we've been shut down for any reason */
14718 /* no special action so far */
14719 }
14720
14721 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14722 LogFlowThisFuncLeave();
14723 return rc;
14724}
14725
14726/**
14727 * Sends the current machine state value to the VM process.
14728 *
14729 * @note Locks this object for reading, then calls a client process.
14730 */
14731HRESULT SessionMachine::i_updateMachineStateOnClient()
14732{
14733 AutoCaller autoCaller(this);
14734 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14735
14736 ComPtr<IInternalSessionControl> directControl;
14737 {
14738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14739 AssertReturn(!!mData, E_FAIL);
14740 if (mData->mSession.mLockType == LockType_VM)
14741 directControl = mData->mSession.mDirectControl;
14742
14743 /* directControl may be already set to NULL here in #OnSessionEnd()
14744 * called too early by the direct session process while there is still
14745 * some operation (like deleting the snapshot) in progress. The client
14746 * process in this case is waiting inside Session::close() for the
14747 * "end session" process object to complete, while #uninit() called by
14748 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14749 * operation to complete. For now, we accept this inconsistent behavior
14750 * and simply do nothing here. */
14751
14752 if (mData->mSession.mState == SessionState_Unlocking)
14753 return S_OK;
14754 }
14755
14756 /* ignore notifications sent after #OnSessionEnd() is called */
14757 if (!directControl)
14758 return S_OK;
14759
14760 return directControl->UpdateMachineState(mData->mMachineState);
14761}
14762
14763
14764/**
14765 * Static Machine method that can get passed to RTThreadCreate to
14766 * have a thread started for a Task. See Machine::Task.
14767 */
14768/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14769{
14770 AssertReturn(pvUser, VERR_INVALID_POINTER);
14771
14772 Task *pTask = static_cast<Task *>(pvUser);
14773 pTask->handler();
14774 /** @todo r=klaus it would be safer to update the progress object here,
14775 * as it avoids possible races due to scoping issues/tricks in the handler */
14776 // it's our responsibility to delete the task
14777 delete pTask;
14778
14779 return 0;
14780}
14781
14782/*static*/
14783HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14784{
14785 va_list args;
14786 va_start(args, pcszMsg);
14787 HRESULT rc = setErrorInternal(aResultCode,
14788 getStaticClassIID(),
14789 getStaticComponentName(),
14790 Utf8Str(pcszMsg, args),
14791 false /* aWarning */,
14792 true /* aLogIt */);
14793 va_end(args);
14794 return rc;
14795}
14796
14797
14798HRESULT Machine::updateState(MachineState_T aState)
14799{
14800 NOREF(aState);
14801 ReturnComNotImplemented();
14802}
14803
14804HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14805{
14806 NOREF(aProgress);
14807 ReturnComNotImplemented();
14808}
14809
14810HRESULT Machine::endPowerUp(LONG aResult)
14811{
14812 NOREF(aResult);
14813 ReturnComNotImplemented();
14814}
14815
14816HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14817{
14818 NOREF(aProgress);
14819 ReturnComNotImplemented();
14820}
14821
14822HRESULT Machine::endPoweringDown(LONG aResult,
14823 const com::Utf8Str &aErrMsg)
14824{
14825 NOREF(aResult);
14826 NOREF(aErrMsg);
14827 ReturnComNotImplemented();
14828}
14829
14830HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14831 BOOL *aMatched,
14832 ULONG *aMaskedInterfaces)
14833{
14834 NOREF(aDevice);
14835 NOREF(aMatched);
14836 NOREF(aMaskedInterfaces);
14837 ReturnComNotImplemented();
14838
14839}
14840
14841HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14842{
14843 NOREF(aId); NOREF(aCaptureFilename);
14844 ReturnComNotImplemented();
14845}
14846
14847HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14848 BOOL aDone)
14849{
14850 NOREF(aId);
14851 NOREF(aDone);
14852 ReturnComNotImplemented();
14853}
14854
14855HRESULT Machine::autoCaptureUSBDevices()
14856{
14857 ReturnComNotImplemented();
14858}
14859
14860HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14861{
14862 NOREF(aDone);
14863 ReturnComNotImplemented();
14864}
14865
14866HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14867 ComPtr<IProgress> &aProgress)
14868{
14869 NOREF(aSession);
14870 NOREF(aProgress);
14871 ReturnComNotImplemented();
14872}
14873
14874HRESULT Machine::finishOnlineMergeMedium()
14875{
14876 ReturnComNotImplemented();
14877}
14878
14879HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14880 std::vector<com::Utf8Str> &aValues,
14881 std::vector<LONG64> &aTimestamps,
14882 std::vector<com::Utf8Str> &aFlags)
14883{
14884 NOREF(aNames);
14885 NOREF(aValues);
14886 NOREF(aTimestamps);
14887 NOREF(aFlags);
14888 ReturnComNotImplemented();
14889}
14890
14891HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14892 const com::Utf8Str &aValue,
14893 LONG64 aTimestamp,
14894 const com::Utf8Str &aFlags)
14895{
14896 NOREF(aName);
14897 NOREF(aValue);
14898 NOREF(aTimestamp);
14899 NOREF(aFlags);
14900 ReturnComNotImplemented();
14901}
14902
14903HRESULT Machine::lockMedia()
14904{
14905 ReturnComNotImplemented();
14906}
14907
14908HRESULT Machine::unlockMedia()
14909{
14910 ReturnComNotImplemented();
14911}
14912
14913HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14914 ComPtr<IMediumAttachment> &aNewAttachment)
14915{
14916 NOREF(aAttachment);
14917 NOREF(aNewAttachment);
14918 ReturnComNotImplemented();
14919}
14920
14921HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14922 ULONG aCpuUser,
14923 ULONG aCpuKernel,
14924 ULONG aCpuIdle,
14925 ULONG aMemTotal,
14926 ULONG aMemFree,
14927 ULONG aMemBalloon,
14928 ULONG aMemShared,
14929 ULONG aMemCache,
14930 ULONG aPagedTotal,
14931 ULONG aMemAllocTotal,
14932 ULONG aMemFreeTotal,
14933 ULONG aMemBalloonTotal,
14934 ULONG aMemSharedTotal,
14935 ULONG aVmNetRx,
14936 ULONG aVmNetTx)
14937{
14938 NOREF(aValidStats);
14939 NOREF(aCpuUser);
14940 NOREF(aCpuKernel);
14941 NOREF(aCpuIdle);
14942 NOREF(aMemTotal);
14943 NOREF(aMemFree);
14944 NOREF(aMemBalloon);
14945 NOREF(aMemShared);
14946 NOREF(aMemCache);
14947 NOREF(aPagedTotal);
14948 NOREF(aMemAllocTotal);
14949 NOREF(aMemFreeTotal);
14950 NOREF(aMemBalloonTotal);
14951 NOREF(aMemSharedTotal);
14952 NOREF(aVmNetRx);
14953 NOREF(aVmNetTx);
14954 ReturnComNotImplemented();
14955}
14956
14957HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14958 com::Utf8Str &aResult)
14959{
14960 NOREF(aAuthParams);
14961 NOREF(aResult);
14962 ReturnComNotImplemented();
14963}
14964
14965HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
14966{
14967 NOREF(aFlags);
14968 ReturnComNotImplemented();
14969}
14970
14971/* This isn't handled entirely by the wrapper generator yet. */
14972#ifdef VBOX_WITH_XPCOM
14973NS_DECL_CLASSINFO(SessionMachine)
14974NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
14975
14976NS_DECL_CLASSINFO(SnapshotMachine)
14977NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
14978#endif
Note: See TracBrowser for help on using the repository browser.

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