VirtualBox

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

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

Main: big settings cleanup and writing optimization. Moved constructors/equality/default checks into the .cpp file, and write only settings which aren't at the default value. Greatly reduces the effort needed to write everything out, especially when a lot of snapshots have to be dealt with. Move the storage controllers to the hardware settings, where they always belonged. No change to the XML file (yet). Lots of settings related cleanups in the API code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 514.5 KB
Line 
1/* $Id: MachineImpl.cpp 61009 2016-05-17 17:18:29Z 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 mHPETEnabled = false;
196 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
197 mCpuIdPortabilityLevel = 0;
198 mCpuProfile = "host";
199
200 /* default boot order: floppy - DVD - HDD */
201 mBootOrder[0] = DeviceType_Floppy;
202 mBootOrder[1] = DeviceType_DVD;
203 mBootOrder[2] = DeviceType_HardDisk;
204 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
205 mBootOrder[i] = DeviceType_Null;
206
207 mClipboardMode = ClipboardMode_Disabled;
208 mDnDMode = DnDMode_Disabled;
209
210 mFirmwareType = FirmwareType_BIOS;
211 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
212 mPointingHIDType = PointingHIDType_PS2Mouse;
213 mChipsetType = ChipsetType_PIIX3;
214 mParavirtProvider = ParavirtProvider_Default;
215 mEmulatedUSBCardReaderEnabled = FALSE;
216
217 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
218 mCPUAttached[i] = false;
219
220 mIOCacheEnabled = true;
221 mIOCacheSize = 5; /* 5MB */
222}
223
224Machine::HWData::~HWData()
225{
226}
227
228/////////////////////////////////////////////////////////////////////////////
229// Machine::HDData structure
230/////////////////////////////////////////////////////////////////////////////
231
232Machine::MediaData::MediaData()
233{
234}
235
236Machine::MediaData::~MediaData()
237{
238}
239
240/////////////////////////////////////////////////////////////////////////////
241// Machine class
242/////////////////////////////////////////////////////////////////////////////
243
244// constructor / destructor
245/////////////////////////////////////////////////////////////////////////////
246
247Machine::Machine() :
248#ifdef VBOX_WITH_RESOURCE_USAGE_API
249 mCollectorGuest(NULL),
250#endif
251 mPeer(NULL),
252 mParent(NULL),
253 mSerialPorts(),
254 mParallelPorts(),
255 uRegistryNeedsSaving(0)
256{}
257
258Machine::~Machine()
259{}
260
261HRESULT Machine::FinalConstruct()
262{
263 LogFlowThisFunc(("\n"));
264 return BaseFinalConstruct();
265}
266
267void Machine::FinalRelease()
268{
269 LogFlowThisFunc(("\n"));
270 uninit();
271 BaseFinalRelease();
272}
273
274/**
275 * Initializes a new machine instance; this init() variant creates a new, empty machine.
276 * This gets called from VirtualBox::CreateMachine().
277 *
278 * @param aParent Associated parent object
279 * @param strConfigFile Local file system path to the VM settings file (can
280 * be relative to the VirtualBox config directory).
281 * @param strName name for the machine
282 * @param llGroups list of groups for the machine
283 * @param aOsType OS Type of this machine or NULL.
284 * @param aId UUID for the new machine.
285 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
286 *
287 * @return Success indicator. if not S_OK, the machine object is invalid
288 */
289HRESULT Machine::init(VirtualBox *aParent,
290 const Utf8Str &strConfigFile,
291 const Utf8Str &strName,
292 const StringsList &llGroups,
293 GuestOSType *aOsType,
294 const Guid &aId,
295 bool fForceOverwrite,
296 bool fDirectoryIncludesUUID)
297{
298 LogFlowThisFuncEnter();
299 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
300
301 /* Enclose the state transition NotReady->InInit->Ready */
302 AutoInitSpan autoInitSpan(this);
303 AssertReturn(autoInitSpan.isOk(), E_FAIL);
304
305 HRESULT rc = initImpl(aParent, strConfigFile);
306 if (FAILED(rc)) return rc;
307
308 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
309 if (FAILED(rc)) return rc;
310
311 if (SUCCEEDED(rc))
312 {
313 // create an empty machine config
314 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
315
316 rc = initDataAndChildObjects();
317 }
318
319 if (SUCCEEDED(rc))
320 {
321 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
322 mData->mAccessible = TRUE;
323
324 unconst(mData->mUuid) = aId;
325
326 mUserData->s.strName = strName;
327
328 mUserData->s.llGroups = llGroups;
329
330 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
331 // the "name sync" flag determines whether the machine directory gets renamed along
332 // with the machine file; say so if the settings file name is the same as the
333 // settings file parent directory (machine directory)
334 mUserData->s.fNameSync = i_isInOwnDir();
335
336 // initialize the default snapshots folder
337 rc = COMSETTER(SnapshotFolder)(NULL);
338 AssertComRC(rc);
339
340 if (aOsType)
341 {
342 /* Store OS type */
343 mUserData->s.strOsType = aOsType->i_id();
344
345 /* Apply BIOS defaults */
346 mBIOSSettings->i_applyDefaults(aOsType);
347
348 /* Apply network adapters defaults */
349 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
350 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
351
352 /* Apply serial port defaults */
353 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
354 mSerialPorts[slot]->i_applyDefaults(aOsType);
355
356 /* Let the OS type select 64-bit ness. */
357 mHWData->mLongMode = aOsType->i_is64Bit()
358 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
359 }
360
361 /* Apply parallel port defaults */
362 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
363 mParallelPorts[slot]->i_applyDefaults();
364
365 /* At this point the changing of the current state modification
366 * flag is allowed. */
367 i_allowStateModification();
368
369 /* commit all changes made during the initialization */
370 i_commit();
371 }
372
373 /* Confirm a successful initialization when it's the case */
374 if (SUCCEEDED(rc))
375 {
376 if (mData->mAccessible)
377 autoInitSpan.setSucceeded();
378 else
379 autoInitSpan.setLimited();
380 }
381
382 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
383 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
384 mData->mRegistered,
385 mData->mAccessible,
386 rc));
387
388 LogFlowThisFuncLeave();
389
390 return rc;
391}
392
393/**
394 * Initializes a new instance with data from machine XML (formerly Init_Registered).
395 * Gets called in two modes:
396 *
397 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
398 * UUID is specified and we mark the machine as "registered";
399 *
400 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
401 * and the machine remains unregistered until RegisterMachine() is called.
402 *
403 * @param aParent Associated parent object
404 * @param aConfigFile Local file system path to the VM settings file (can
405 * be relative to the VirtualBox config directory).
406 * @param aId UUID of the machine or NULL (see above).
407 *
408 * @return Success indicator. if not S_OK, the machine object is invalid
409 */
410HRESULT Machine::initFromSettings(VirtualBox *aParent,
411 const Utf8Str &strConfigFile,
412 const Guid *aId)
413{
414 LogFlowThisFuncEnter();
415 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
416
417 /* Enclose the state transition NotReady->InInit->Ready */
418 AutoInitSpan autoInitSpan(this);
419 AssertReturn(autoInitSpan.isOk(), E_FAIL);
420
421 HRESULT rc = initImpl(aParent, strConfigFile);
422 if (FAILED(rc)) return rc;
423
424 if (aId)
425 {
426 // loading a registered VM:
427 unconst(mData->mUuid) = *aId;
428 mData->mRegistered = TRUE;
429 // now load the settings from XML:
430 rc = i_registeredInit();
431 // this calls initDataAndChildObjects() and loadSettings()
432 }
433 else
434 {
435 // opening an unregistered VM (VirtualBox::OpenMachine()):
436 rc = initDataAndChildObjects();
437
438 if (SUCCEEDED(rc))
439 {
440 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
441 mData->mAccessible = TRUE;
442
443 try
444 {
445 // load and parse machine XML; this will throw on XML or logic errors
446 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
447
448 // reject VM UUID duplicates, they can happen if someone
449 // tries to register an already known VM config again
450 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
451 true /* fPermitInaccessible */,
452 false /* aDoSetError */,
453 NULL) != VBOX_E_OBJECT_NOT_FOUND)
454 {
455 throw setError(E_FAIL,
456 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
457 mData->m_strConfigFile.c_str());
458 }
459
460 // use UUID from machine config
461 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
462
463 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
464 NULL /* puuidRegistry */);
465 if (FAILED(rc)) throw rc;
466
467 /* At this point the changing of the current state modification
468 * flag is allowed. */
469 i_allowStateModification();
470
471 i_commit();
472 }
473 catch (HRESULT err)
474 {
475 /* we assume that error info is set by the thrower */
476 rc = err;
477 }
478 catch (...)
479 {
480 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
481 }
482 }
483 }
484
485 /* Confirm a successful initialization when it's the case */
486 if (SUCCEEDED(rc))
487 {
488 if (mData->mAccessible)
489 autoInitSpan.setSucceeded();
490 else
491 {
492 autoInitSpan.setLimited();
493
494 // uninit media from this machine's media registry, or else
495 // reloading the settings will fail
496 mParent->i_unregisterMachineMedia(i_getId());
497 }
498 }
499
500 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
501 "rc=%08X\n",
502 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
503 mData->mRegistered, mData->mAccessible, rc));
504
505 LogFlowThisFuncLeave();
506
507 return rc;
508}
509
510/**
511 * Initializes a new instance from a machine config that is already in memory
512 * (import OVF case). Since we are importing, the UUID in the machine
513 * config is ignored and we always generate a fresh one.
514 *
515 * @param strName Name for the new machine; this overrides what is specified in config and is used
516 * for the settings file as well.
517 * @param config Machine configuration loaded and parsed from XML.
518 *
519 * @return Success indicator. if not S_OK, the machine object is invalid
520 */
521HRESULT Machine::init(VirtualBox *aParent,
522 const Utf8Str &strName,
523 const settings::MachineConfigFile &config)
524{
525 LogFlowThisFuncEnter();
526
527 /* Enclose the state transition NotReady->InInit->Ready */
528 AutoInitSpan autoInitSpan(this);
529 AssertReturn(autoInitSpan.isOk(), E_FAIL);
530
531 Utf8Str strConfigFile;
532 aParent->i_getDefaultMachineFolder(strConfigFile);
533 strConfigFile.append(RTPATH_DELIMITER);
534 strConfigFile.append(strName);
535 strConfigFile.append(RTPATH_DELIMITER);
536 strConfigFile.append(strName);
537 strConfigFile.append(".vbox");
538
539 HRESULT rc = initImpl(aParent, strConfigFile);
540 if (FAILED(rc)) return rc;
541
542 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
543 if (FAILED(rc)) return rc;
544
545 rc = initDataAndChildObjects();
546
547 if (SUCCEEDED(rc))
548 {
549 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
550 mData->mAccessible = TRUE;
551
552 // create empty machine config for instance data
553 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
554
555 // generate fresh UUID, ignore machine config
556 unconst(mData->mUuid).create();
557
558 rc = i_loadMachineDataFromSettings(config,
559 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
560
561 // override VM name as well, it may be different
562 mUserData->s.strName = strName;
563
564 if (SUCCEEDED(rc))
565 {
566 /* At this point the changing of the current state modification
567 * flag is allowed. */
568 i_allowStateModification();
569
570 /* commit all changes made during the initialization */
571 i_commit();
572 }
573 }
574
575 /* Confirm a successful initialization when it's the case */
576 if (SUCCEEDED(rc))
577 {
578 if (mData->mAccessible)
579 autoInitSpan.setSucceeded();
580 else
581 {
582 /* Ignore all errors from unregistering, they would destroy
583- * the more interesting error information we already have,
584- * pinpointing the issue with the VM config. */
585 ErrorInfoKeeper eik;
586
587 autoInitSpan.setLimited();
588
589 // uninit media from this machine's media registry, or else
590 // reloading the settings will fail
591 mParent->i_unregisterMachineMedia(i_getId());
592 }
593 }
594
595 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
596 "rc=%08X\n",
597 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
598 mData->mRegistered, mData->mAccessible, rc));
599
600 LogFlowThisFuncLeave();
601
602 return rc;
603}
604
605/**
606 * Shared code between the various init() implementations.
607 * @param aParent
608 * @return
609 */
610HRESULT Machine::initImpl(VirtualBox *aParent,
611 const Utf8Str &strConfigFile)
612{
613 LogFlowThisFuncEnter();
614
615 AssertReturn(aParent, E_INVALIDARG);
616 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
617
618 HRESULT rc = S_OK;
619
620 /* share the parent weakly */
621 unconst(mParent) = aParent;
622
623 /* allocate the essential machine data structure (the rest will be
624 * allocated later by initDataAndChildObjects() */
625 mData.allocate();
626
627 /* memorize the config file name (as provided) */
628 mData->m_strConfigFile = strConfigFile;
629
630 /* get the full file name */
631 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
632 if (RT_FAILURE(vrc1))
633 return setError(VBOX_E_FILE_ERROR,
634 tr("Invalid machine settings file name '%s' (%Rrc)"),
635 strConfigFile.c_str(),
636 vrc1);
637
638 LogFlowThisFuncLeave();
639
640 return rc;
641}
642
643/**
644 * Tries to create a machine settings file in the path stored in the machine
645 * instance data. Used when a new machine is created to fail gracefully if
646 * the settings file could not be written (e.g. because machine dir is read-only).
647 * @return
648 */
649HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
650{
651 HRESULT rc = S_OK;
652
653 // when we create a new machine, we must be able to create the settings file
654 RTFILE f = NIL_RTFILE;
655 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
656 if ( RT_SUCCESS(vrc)
657 || vrc == VERR_SHARING_VIOLATION
658 )
659 {
660 if (RT_SUCCESS(vrc))
661 RTFileClose(f);
662 if (!fForceOverwrite)
663 rc = setError(VBOX_E_FILE_ERROR,
664 tr("Machine settings file '%s' already exists"),
665 mData->m_strConfigFileFull.c_str());
666 else
667 {
668 /* try to delete the config file, as otherwise the creation
669 * of a new settings file will fail. */
670 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
671 if (RT_FAILURE(vrc2))
672 rc = setError(VBOX_E_FILE_ERROR,
673 tr("Could not delete the existing settings file '%s' (%Rrc)"),
674 mData->m_strConfigFileFull.c_str(), vrc2);
675 }
676 }
677 else if ( vrc != VERR_FILE_NOT_FOUND
678 && vrc != VERR_PATH_NOT_FOUND
679 )
680 rc = setError(VBOX_E_FILE_ERROR,
681 tr("Invalid machine settings file name '%s' (%Rrc)"),
682 mData->m_strConfigFileFull.c_str(),
683 vrc);
684 return rc;
685}
686
687/**
688 * Initializes the registered machine by loading the settings file.
689 * This method is separated from #init() in order to make it possible to
690 * retry the operation after VirtualBox startup instead of refusing to
691 * startup the whole VirtualBox server in case if the settings file of some
692 * registered VM is invalid or inaccessible.
693 *
694 * @note Must be always called from this object's write lock
695 * (unless called from #init() that doesn't need any locking).
696 * @note Locks the mUSBController method for writing.
697 * @note Subclasses must not call this method.
698 */
699HRESULT Machine::i_registeredInit()
700{
701 AssertReturn(!i_isSessionMachine(), E_FAIL);
702 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
703 AssertReturn(mData->mUuid.isValid(), E_FAIL);
704 AssertReturn(!mData->mAccessible, E_FAIL);
705
706 HRESULT rc = initDataAndChildObjects();
707
708 if (SUCCEEDED(rc))
709 {
710 /* Temporarily reset the registered flag in order to let setters
711 * potentially called from loadSettings() succeed (isMutable() used in
712 * all setters will return FALSE for a Machine instance if mRegistered
713 * is TRUE). */
714 mData->mRegistered = FALSE;
715
716 try
717 {
718 // load and parse machine XML; this will throw on XML or logic errors
719 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
720
721 if (mData->mUuid != mData->pMachineConfigFile->uuid)
722 throw setError(E_FAIL,
723 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
724 mData->pMachineConfigFile->uuid.raw(),
725 mData->m_strConfigFileFull.c_str(),
726 mData->mUuid.toString().c_str(),
727 mParent->i_settingsFilePath().c_str());
728
729 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
730 NULL /* const Guid *puuidRegistry */);
731 if (FAILED(rc)) throw rc;
732 }
733 catch (HRESULT err)
734 {
735 /* we assume that error info is set by the thrower */
736 rc = err;
737 }
738 catch (...)
739 {
740 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
741 }
742
743 /* Restore the registered flag (even on failure) */
744 mData->mRegistered = TRUE;
745 }
746
747 if (SUCCEEDED(rc))
748 {
749 /* Set mAccessible to TRUE only if we successfully locked and loaded
750 * the settings file */
751 mData->mAccessible = TRUE;
752
753 /* commit all changes made during loading the settings file */
754 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
755 /// @todo r=klaus for some reason the settings loading logic backs up
756 // the settings, and therefore a commit is needed. Should probably be changed.
757 }
758 else
759 {
760 /* If the machine is registered, then, instead of returning a
761 * failure, we mark it as inaccessible and set the result to
762 * success to give it a try later */
763
764 /* fetch the current error info */
765 mData->mAccessError = com::ErrorInfo();
766 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
767
768 /* rollback all changes */
769 i_rollback(false /* aNotify */);
770
771 // uninit media from this machine's media registry, or else
772 // reloading the settings will fail
773 mParent->i_unregisterMachineMedia(i_getId());
774
775 /* uninitialize the common part to make sure all data is reset to
776 * default (null) values */
777 uninitDataAndChildObjects();
778
779 rc = S_OK;
780 }
781
782 return rc;
783}
784
785/**
786 * Uninitializes the instance.
787 * Called either from FinalRelease() or by the parent when it gets destroyed.
788 *
789 * @note The caller of this method must make sure that this object
790 * a) doesn't have active callers on the current thread and b) is not locked
791 * by the current thread; otherwise uninit() will hang either a) due to
792 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
793 * a dead-lock caused by this thread waiting for all callers on the other
794 * threads are done but preventing them from doing so by holding a lock.
795 */
796void Machine::uninit()
797{
798 LogFlowThisFuncEnter();
799
800 Assert(!isWriteLockOnCurrentThread());
801
802 Assert(!uRegistryNeedsSaving);
803 if (uRegistryNeedsSaving)
804 {
805 AutoCaller autoCaller(this);
806 if (SUCCEEDED(autoCaller.rc()))
807 {
808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
809 i_saveSettings(NULL, Machine::SaveS_Force);
810 }
811 }
812
813 /* Enclose the state transition Ready->InUninit->NotReady */
814 AutoUninitSpan autoUninitSpan(this);
815 if (autoUninitSpan.uninitDone())
816 return;
817
818 Assert(!i_isSnapshotMachine());
819 Assert(!i_isSessionMachine());
820 Assert(!!mData);
821
822 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
823 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
824
825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
826
827 if (!mData->mSession.mMachine.isNull())
828 {
829 /* Theoretically, this can only happen if the VirtualBox server has been
830 * terminated while there were clients running that owned open direct
831 * sessions. Since in this case we are definitely called by
832 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
833 * won't happen on the client watcher thread (because it has a
834 * VirtualBox caller for the duration of the
835 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
836 * cannot happen until the VirtualBox caller is released). This is
837 * important, because SessionMachine::uninit() cannot correctly operate
838 * after we return from this method (it expects the Machine instance is
839 * still valid). We'll call it ourselves below.
840 */
841 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
842 (SessionMachine*)mData->mSession.mMachine));
843
844 if (Global::IsOnlineOrTransient(mData->mMachineState))
845 {
846 Log1WarningThisFunc(("Setting state to Aborted!\n"));
847 /* set machine state using SessionMachine reimplementation */
848 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
849 }
850
851 /*
852 * Uninitialize SessionMachine using public uninit() to indicate
853 * an unexpected uninitialization.
854 */
855 mData->mSession.mMachine->uninit();
856 /* SessionMachine::uninit() must set mSession.mMachine to null */
857 Assert(mData->mSession.mMachine.isNull());
858 }
859
860 // uninit media from this machine's media registry, if they're still there
861 Guid uuidMachine(i_getId());
862
863 /* the lock is no more necessary (SessionMachine is uninitialized) */
864 alock.release();
865
866 /* XXX This will fail with
867 * "cannot be closed because it is still attached to 1 virtual machines"
868 * because at this point we did not call uninitDataAndChildObjects() yet
869 * and therefore also removeBackReference() for all these mediums was not called! */
870
871 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
872 mParent->i_unregisterMachineMedia(uuidMachine);
873
874 // has machine been modified?
875 if (mData->flModifications)
876 {
877 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
878 i_rollback(false /* aNotify */);
879 }
880
881 if (mData->mAccessible)
882 uninitDataAndChildObjects();
883
884 /* free the essential data structure last */
885 mData.free();
886
887 LogFlowThisFuncLeave();
888}
889
890// Wrapped IMachine properties
891/////////////////////////////////////////////////////////////////////////////
892HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
893{
894 /* mParent is constant during life time, no need to lock */
895 ComObjPtr<VirtualBox> pVirtualBox(mParent);
896 aParent = pVirtualBox;
897
898 return S_OK;
899}
900
901
902HRESULT Machine::getAccessible(BOOL *aAccessible)
903{
904 /* In some cases (medium registry related), it is necessary to be able to
905 * go through the list of all machines. Happens when an inaccessible VM
906 * has a sensible medium registry. */
907 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
909
910 HRESULT rc = S_OK;
911
912 if (!mData->mAccessible)
913 {
914 /* try to initialize the VM once more if not accessible */
915
916 AutoReinitSpan autoReinitSpan(this);
917 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
918
919#ifdef DEBUG
920 LogFlowThisFunc(("Dumping media backreferences\n"));
921 mParent->i_dumpAllBackRefs();
922#endif
923
924 if (mData->pMachineConfigFile)
925 {
926 // reset the XML file to force loadSettings() (called from registeredInit())
927 // to parse it again; the file might have changed
928 delete mData->pMachineConfigFile;
929 mData->pMachineConfigFile = NULL;
930 }
931
932 rc = i_registeredInit();
933
934 if (SUCCEEDED(rc) && mData->mAccessible)
935 {
936 autoReinitSpan.setSucceeded();
937
938 /* make sure interesting parties will notice the accessibility
939 * state change */
940 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
941 mParent->i_onMachineDataChange(mData->mUuid);
942 }
943 }
944
945 if (SUCCEEDED(rc))
946 *aAccessible = mData->mAccessible;
947
948 LogFlowThisFuncLeave();
949
950 return rc;
951}
952
953HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
954{
955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
956
957 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
958 {
959 /* return shortly */
960 aAccessError = NULL;
961 return S_OK;
962 }
963
964 HRESULT rc = S_OK;
965
966 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
967 rc = errorInfo.createObject();
968 if (SUCCEEDED(rc))
969 {
970 errorInfo->init(mData->mAccessError.getResultCode(),
971 mData->mAccessError.getInterfaceID().ref(),
972 Utf8Str(mData->mAccessError.getComponent()).c_str(),
973 Utf8Str(mData->mAccessError.getText()));
974 aAccessError = errorInfo;
975 }
976
977 return rc;
978}
979
980HRESULT Machine::getName(com::Utf8Str &aName)
981{
982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
983
984 aName = mUserData->s.strName;
985
986 return S_OK;
987}
988
989HRESULT Machine::setName(const com::Utf8Str &aName)
990{
991 // prohibit setting a UUID only as the machine name, or else it can
992 // never be found by findMachine()
993 Guid test(aName);
994
995 if (test.isValid())
996 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
997
998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
999
1000 HRESULT rc = i_checkStateDependency(MutableStateDep);
1001 if (FAILED(rc)) return rc;
1002
1003 i_setModified(IsModified_MachineData);
1004 mUserData.backup();
1005 mUserData->s.strName = aName;
1006
1007 return S_OK;
1008}
1009
1010HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1011{
1012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1013
1014 aDescription = mUserData->s.strDescription;
1015
1016 return S_OK;
1017}
1018
1019HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1020{
1021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1022
1023 // this can be done in principle in any state as it doesn't affect the VM
1024 // significantly, but play safe by not messing around while complex
1025 // activities are going on
1026 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1027 if (FAILED(rc)) return rc;
1028
1029 i_setModified(IsModified_MachineData);
1030 mUserData.backup();
1031 mUserData->s.strDescription = aDescription;
1032
1033 return S_OK;
1034}
1035
1036HRESULT Machine::getId(com::Guid &aId)
1037{
1038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1039
1040 aId = mData->mUuid;
1041
1042 return S_OK;
1043}
1044
1045HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1046{
1047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1048 aGroups.resize(mUserData->s.llGroups.size());
1049 size_t i = 0;
1050 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1051 it != mUserData->s.llGroups.end(); ++it, ++i)
1052 aGroups[i] = (*it);
1053
1054 return S_OK;
1055}
1056
1057HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1058{
1059 StringsList llGroups;
1060 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1061 if (FAILED(rc))
1062 return rc;
1063
1064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1065
1066 rc = i_checkStateDependency(MutableOrSavedStateDep);
1067 if (FAILED(rc)) return rc;
1068
1069 i_setModified(IsModified_MachineData);
1070 mUserData.backup();
1071 mUserData->s.llGroups = llGroups;
1072
1073 return S_OK;
1074}
1075
1076HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1077{
1078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1079
1080 aOSTypeId = mUserData->s.strOsType;
1081
1082 return S_OK;
1083}
1084
1085HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1086{
1087 /* look up the object by Id to check it is valid */
1088 ComPtr<IGuestOSType> guestOSType;
1089 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1090 if (FAILED(rc)) return rc;
1091
1092 /* when setting, always use the "etalon" value for consistency -- lookup
1093 * by ID is case-insensitive and the input value may have different case */
1094 Bstr osTypeId;
1095 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1096 if (FAILED(rc)) return rc;
1097
1098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1099
1100 rc = i_checkStateDependency(MutableStateDep);
1101 if (FAILED(rc)) return rc;
1102
1103 i_setModified(IsModified_MachineData);
1104 mUserData.backup();
1105 mUserData->s.strOsType = osTypeId;
1106
1107 return S_OK;
1108}
1109
1110HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1111{
1112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1113
1114 *aFirmwareType = mHWData->mFirmwareType;
1115
1116 return S_OK;
1117}
1118
1119HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1120{
1121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1122
1123 HRESULT rc = i_checkStateDependency(MutableStateDep);
1124 if (FAILED(rc)) return rc;
1125
1126 i_setModified(IsModified_MachineData);
1127 mHWData.backup();
1128 mHWData->mFirmwareType = aFirmwareType;
1129
1130 return S_OK;
1131}
1132
1133HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1134{
1135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1136
1137 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1138
1139 return S_OK;
1140}
1141
1142HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1143{
1144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1145
1146 HRESULT rc = i_checkStateDependency(MutableStateDep);
1147 if (FAILED(rc)) return rc;
1148
1149 i_setModified(IsModified_MachineData);
1150 mHWData.backup();
1151 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1152
1153 return S_OK;
1154}
1155
1156HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1157{
1158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1159
1160 *aPointingHIDType = mHWData->mPointingHIDType;
1161
1162 return S_OK;
1163}
1164
1165HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1166{
1167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1168
1169 HRESULT rc = i_checkStateDependency(MutableStateDep);
1170 if (FAILED(rc)) return rc;
1171
1172 i_setModified(IsModified_MachineData);
1173 mHWData.backup();
1174 mHWData->mPointingHIDType = aPointingHIDType;
1175
1176 return S_OK;
1177}
1178
1179HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1180{
1181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1182
1183 *aChipsetType = mHWData->mChipsetType;
1184
1185 return S_OK;
1186}
1187
1188HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1189{
1190 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1191
1192 HRESULT rc = i_checkStateDependency(MutableStateDep);
1193 if (FAILED(rc)) return rc;
1194
1195 if (aChipsetType != mHWData->mChipsetType)
1196 {
1197 i_setModified(IsModified_MachineData);
1198 mHWData.backup();
1199 mHWData->mChipsetType = aChipsetType;
1200
1201 // Resize network adapter array, to be finalized on commit/rollback.
1202 // We must not throw away entries yet, otherwise settings are lost
1203 // without a way to roll back.
1204 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1205 size_t oldCount = mNetworkAdapters.size();
1206 if (newCount > oldCount)
1207 {
1208 mNetworkAdapters.resize(newCount);
1209 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1210 {
1211 unconst(mNetworkAdapters[slot]).createObject();
1212 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1213 }
1214 }
1215 }
1216
1217 return S_OK;
1218}
1219
1220HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1221{
1222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1223
1224 aParavirtDebug = mHWData->mParavirtDebug;
1225 return S_OK;
1226}
1227
1228HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1229{
1230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1231
1232 HRESULT rc = i_checkStateDependency(MutableStateDep);
1233 if (FAILED(rc)) return rc;
1234
1235 /** @todo Parse/validate options? */
1236 if (aParavirtDebug != mHWData->mParavirtDebug)
1237 {
1238 i_setModified(IsModified_MachineData);
1239 mHWData.backup();
1240 mHWData->mParavirtDebug = aParavirtDebug;
1241 }
1242
1243 return S_OK;
1244}
1245
1246HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1247{
1248 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1249
1250 *aParavirtProvider = mHWData->mParavirtProvider;
1251
1252 return S_OK;
1253}
1254
1255HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1256{
1257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1258
1259 HRESULT rc = i_checkStateDependency(MutableStateDep);
1260 if (FAILED(rc)) return rc;
1261
1262 if (aParavirtProvider != mHWData->mParavirtProvider)
1263 {
1264 i_setModified(IsModified_MachineData);
1265 mHWData.backup();
1266 mHWData->mParavirtProvider = aParavirtProvider;
1267 }
1268
1269 return S_OK;
1270}
1271
1272HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1273{
1274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1275
1276 *aParavirtProvider = mHWData->mParavirtProvider;
1277 switch (mHWData->mParavirtProvider)
1278 {
1279 case ParavirtProvider_None:
1280 case ParavirtProvider_HyperV:
1281 case ParavirtProvider_KVM:
1282 case ParavirtProvider_Minimal:
1283 break;
1284
1285 /* Resolve dynamic provider types to the effective types. */
1286 default:
1287 {
1288 ComPtr<IGuestOSType> ptrGuestOSType;
1289 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1290 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1291
1292 Bstr guestTypeFamilyId;
1293 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1294 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1295 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1296
1297 switch (mHWData->mParavirtProvider)
1298 {
1299 case ParavirtProvider_Legacy:
1300 {
1301 if (fOsXGuest)
1302 *aParavirtProvider = ParavirtProvider_Minimal;
1303 else
1304 *aParavirtProvider = ParavirtProvider_None;
1305 break;
1306 }
1307
1308 case ParavirtProvider_Default:
1309 {
1310 if (fOsXGuest)
1311 *aParavirtProvider = ParavirtProvider_Minimal;
1312 else if ( mUserData->s.strOsType == "Windows10"
1313 || mUserData->s.strOsType == "Windows10_64"
1314 || mUserData->s.strOsType == "Windows81"
1315 || mUserData->s.strOsType == "Windows81_64"
1316 || mUserData->s.strOsType == "Windows8"
1317 || mUserData->s.strOsType == "Windows8_64"
1318 || mUserData->s.strOsType == "Windows7"
1319 || mUserData->s.strOsType == "Windows7_64"
1320 || mUserData->s.strOsType == "WindowsVista"
1321 || mUserData->s.strOsType == "WindowsVista_64"
1322 || mUserData->s.strOsType == "Windows2012"
1323 || mUserData->s.strOsType == "Windows2012_64"
1324 || mUserData->s.strOsType == "Windows2008"
1325 || mUserData->s.strOsType == "Windows2008_64")
1326 {
1327 *aParavirtProvider = ParavirtProvider_HyperV;
1328 }
1329 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1330 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1331 || mUserData->s.strOsType == "Linux"
1332 || mUserData->s.strOsType == "Linux_64"
1333 || mUserData->s.strOsType == "ArchLinux"
1334 || mUserData->s.strOsType == "ArchLinux_64"
1335 || mUserData->s.strOsType == "Debian"
1336 || mUserData->s.strOsType == "Debian_64"
1337 || mUserData->s.strOsType == "Fedora"
1338 || mUserData->s.strOsType == "Fedora_64"
1339 || mUserData->s.strOsType == "Gentoo"
1340 || mUserData->s.strOsType == "Gentoo_64"
1341 || mUserData->s.strOsType == "Mandriva"
1342 || mUserData->s.strOsType == "Mandriva_64"
1343 || mUserData->s.strOsType == "OpenSUSE"
1344 || mUserData->s.strOsType == "OpenSUSE_64"
1345 || mUserData->s.strOsType == "Oracle"
1346 || mUserData->s.strOsType == "Oracle_64"
1347 || mUserData->s.strOsType == "RedHat"
1348 || mUserData->s.strOsType == "RedHat_64"
1349 || mUserData->s.strOsType == "Turbolinux"
1350 || mUserData->s.strOsType == "Turbolinux_64"
1351 || mUserData->s.strOsType == "Ubuntu"
1352 || mUserData->s.strOsType == "Ubuntu_64"
1353 || mUserData->s.strOsType == "Xandros"
1354 || mUserData->s.strOsType == "Xandros_64")
1355 {
1356 *aParavirtProvider = ParavirtProvider_KVM;
1357 }
1358 else
1359 *aParavirtProvider = ParavirtProvider_None;
1360 break;
1361 }
1362 }
1363 break;
1364 }
1365 }
1366
1367 Assert( *aParavirtProvider == ParavirtProvider_None
1368 || *aParavirtProvider == ParavirtProvider_Minimal
1369 || *aParavirtProvider == ParavirtProvider_HyperV
1370 || *aParavirtProvider == ParavirtProvider_KVM);
1371 return S_OK;
1372}
1373
1374HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1375{
1376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1377
1378 aHardwareVersion = mHWData->mHWVersion;
1379
1380 return S_OK;
1381}
1382
1383HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1384{
1385 /* check known version */
1386 Utf8Str hwVersion = aHardwareVersion;
1387 if ( hwVersion.compare("1") != 0
1388 && hwVersion.compare("2") != 0)
1389 return setError(E_INVALIDARG,
1390 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1391
1392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1393
1394 HRESULT rc = i_checkStateDependency(MutableStateDep);
1395 if (FAILED(rc)) return rc;
1396
1397 i_setModified(IsModified_MachineData);
1398 mHWData.backup();
1399 mHWData->mHWVersion = aHardwareVersion;
1400
1401 return S_OK;
1402}
1403
1404HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1405{
1406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1407
1408 if (!mHWData->mHardwareUUID.isZero())
1409 aHardwareUUID = mHWData->mHardwareUUID;
1410 else
1411 aHardwareUUID = mData->mUuid;
1412
1413 return S_OK;
1414}
1415
1416HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1417{
1418 if (!aHardwareUUID.isValid())
1419 return E_INVALIDARG;
1420
1421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1422
1423 HRESULT rc = i_checkStateDependency(MutableStateDep);
1424 if (FAILED(rc)) return rc;
1425
1426 i_setModified(IsModified_MachineData);
1427 mHWData.backup();
1428 if (aHardwareUUID == mData->mUuid)
1429 mHWData->mHardwareUUID.clear();
1430 else
1431 mHWData->mHardwareUUID = aHardwareUUID;
1432
1433 return S_OK;
1434}
1435
1436HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1437{
1438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1439
1440 *aMemorySize = mHWData->mMemorySize;
1441
1442 return S_OK;
1443}
1444
1445HRESULT Machine::setMemorySize(ULONG aMemorySize)
1446{
1447 /* check RAM limits */
1448 if ( aMemorySize < MM_RAM_MIN_IN_MB
1449 || aMemorySize > MM_RAM_MAX_IN_MB
1450 )
1451 return setError(E_INVALIDARG,
1452 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1453 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1454
1455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1456
1457 HRESULT rc = i_checkStateDependency(MutableStateDep);
1458 if (FAILED(rc)) return rc;
1459
1460 i_setModified(IsModified_MachineData);
1461 mHWData.backup();
1462 mHWData->mMemorySize = aMemorySize;
1463
1464 return S_OK;
1465}
1466
1467HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1468{
1469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1470
1471 *aCPUCount = mHWData->mCPUCount;
1472
1473 return S_OK;
1474}
1475
1476HRESULT Machine::setCPUCount(ULONG aCPUCount)
1477{
1478 /* check CPU limits */
1479 if ( aCPUCount < SchemaDefs::MinCPUCount
1480 || aCPUCount > SchemaDefs::MaxCPUCount
1481 )
1482 return setError(E_INVALIDARG,
1483 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1484 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1485
1486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1487
1488 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1489 if (mHWData->mCPUHotPlugEnabled)
1490 {
1491 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1492 {
1493 if (mHWData->mCPUAttached[idx])
1494 return setError(E_INVALIDARG,
1495 tr("There is still a CPU attached to socket %lu."
1496 "Detach the CPU before removing the socket"),
1497 aCPUCount, idx+1);
1498 }
1499 }
1500
1501 HRESULT rc = i_checkStateDependency(MutableStateDep);
1502 if (FAILED(rc)) return rc;
1503
1504 i_setModified(IsModified_MachineData);
1505 mHWData.backup();
1506 mHWData->mCPUCount = aCPUCount;
1507
1508 return S_OK;
1509}
1510
1511HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1512{
1513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1514
1515 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1516
1517 return S_OK;
1518}
1519
1520HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1521{
1522 HRESULT rc = S_OK;
1523
1524 /* check throttle limits */
1525 if ( aCPUExecutionCap < 1
1526 || aCPUExecutionCap > 100
1527 )
1528 return setError(E_INVALIDARG,
1529 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1530 aCPUExecutionCap, 1, 100);
1531
1532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1533
1534 alock.release();
1535 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1536 alock.acquire();
1537 if (FAILED(rc)) return rc;
1538
1539 i_setModified(IsModified_MachineData);
1540 mHWData.backup();
1541 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1542
1543 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1544 if (Global::IsOnline(mData->mMachineState))
1545 i_saveSettings(NULL);
1546
1547 return S_OK;
1548}
1549
1550HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1551{
1552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1553
1554 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1555
1556 return S_OK;
1557}
1558
1559HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1560{
1561 HRESULT rc = S_OK;
1562
1563 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1564
1565 rc = i_checkStateDependency(MutableStateDep);
1566 if (FAILED(rc)) return rc;
1567
1568 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1569 {
1570 if (aCPUHotPlugEnabled)
1571 {
1572 i_setModified(IsModified_MachineData);
1573 mHWData.backup();
1574
1575 /* Add the amount of CPUs currently attached */
1576 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1577 mHWData->mCPUAttached[i] = true;
1578 }
1579 else
1580 {
1581 /*
1582 * We can disable hotplug only if the amount of maximum CPUs is equal
1583 * to the amount of attached CPUs
1584 */
1585 unsigned cCpusAttached = 0;
1586 unsigned iHighestId = 0;
1587
1588 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1589 {
1590 if (mHWData->mCPUAttached[i])
1591 {
1592 cCpusAttached++;
1593 iHighestId = i;
1594 }
1595 }
1596
1597 if ( (cCpusAttached != mHWData->mCPUCount)
1598 || (iHighestId >= mHWData->mCPUCount))
1599 return setError(E_INVALIDARG,
1600 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1601
1602 i_setModified(IsModified_MachineData);
1603 mHWData.backup();
1604 }
1605 }
1606
1607 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1608
1609 return rc;
1610}
1611
1612HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1613{
1614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1615
1616 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1617
1618 return S_OK;
1619}
1620
1621HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1622{
1623 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1624
1625 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1626 if (SUCCEEDED(hrc))
1627 {
1628 i_setModified(IsModified_MachineData);
1629 mHWData.backup();
1630 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1631 }
1632 return hrc;
1633}
1634
1635HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1636{
1637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1638 aCPUProfile = mHWData->mCpuProfile;
1639 return S_OK;
1640}
1641
1642HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1643{
1644 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1645 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1646 if (SUCCEEDED(hrc))
1647 {
1648 i_setModified(IsModified_MachineData);
1649 mHWData.backup();
1650 /* Empty equals 'host'. */
1651 if (aCPUProfile.isNotEmpty())
1652 mHWData->mCpuProfile = aCPUProfile;
1653 else
1654 mHWData->mCpuProfile = "host";
1655 }
1656 return hrc;
1657}
1658
1659HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1660{
1661#ifdef VBOX_WITH_USB_CARDREADER
1662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1663
1664 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1665
1666 return S_OK;
1667#else
1668 NOREF(aEmulatedUSBCardReaderEnabled);
1669 return E_NOTIMPL;
1670#endif
1671}
1672
1673HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1674{
1675#ifdef VBOX_WITH_USB_CARDREADER
1676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1677
1678 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1679 if (FAILED(rc)) return rc;
1680
1681 i_setModified(IsModified_MachineData);
1682 mHWData.backup();
1683 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1684
1685 return S_OK;
1686#else
1687 NOREF(aEmulatedUSBCardReaderEnabled);
1688 return E_NOTIMPL;
1689#endif
1690}
1691
1692HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1693{
1694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1695
1696 *aHPETEnabled = mHWData->mHPETEnabled;
1697
1698 return S_OK;
1699}
1700
1701HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1702{
1703 HRESULT rc = S_OK;
1704
1705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1706
1707 rc = i_checkStateDependency(MutableStateDep);
1708 if (FAILED(rc)) return rc;
1709
1710 i_setModified(IsModified_MachineData);
1711 mHWData.backup();
1712
1713 mHWData->mHPETEnabled = aHPETEnabled;
1714
1715 return rc;
1716}
1717
1718HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1719{
1720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1721
1722 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1723 return S_OK;
1724}
1725
1726HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1727{
1728 HRESULT rc = S_OK;
1729
1730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1731
1732 i_setModified(IsModified_MachineData);
1733 mHWData.backup();
1734 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1735
1736 alock.release();
1737 rc = i_onVideoCaptureChange();
1738 alock.acquire();
1739 if (FAILED(rc))
1740 {
1741 /*
1742 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1743 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1744 * determine if it should start or stop capturing. Therefore we need to manually
1745 * undo change.
1746 */
1747 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1748 return rc;
1749 }
1750
1751 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1752 if (Global::IsOnline(mData->mMachineState))
1753 i_saveSettings(NULL);
1754
1755 return rc;
1756}
1757
1758HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1759{
1760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1761 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1762 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1763 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1764 return S_OK;
1765}
1766
1767HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1768{
1769 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1770 bool fChanged = false;
1771
1772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1773
1774 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1775 {
1776 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1777 {
1778 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1779 fChanged = true;
1780 }
1781 }
1782 if (fChanged)
1783 {
1784 alock.release();
1785 HRESULT rc = i_onVideoCaptureChange();
1786 alock.acquire();
1787 if (FAILED(rc)) return rc;
1788 i_setModified(IsModified_MachineData);
1789
1790 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1791 if (Global::IsOnline(mData->mMachineState))
1792 i_saveSettings(NULL);
1793 }
1794
1795 return S_OK;
1796}
1797
1798HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1799{
1800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1801 if (mHWData->mVideoCaptureFile.isEmpty())
1802 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1803 else
1804 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1805 return S_OK;
1806}
1807
1808HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1809{
1810 Utf8Str strFile(aVideoCaptureFile);
1811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1812
1813 if ( Global::IsOnline(mData->mMachineState)
1814 && mHWData->mVideoCaptureEnabled)
1815 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1816
1817 if (!RTPathStartsWithRoot(strFile.c_str()))
1818 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1819
1820 if (!strFile.isEmpty())
1821 {
1822 Utf8Str defaultFile;
1823 i_getDefaultVideoCaptureFile(defaultFile);
1824 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1825 strFile.setNull();
1826 }
1827
1828 i_setModified(IsModified_MachineData);
1829 mHWData.backup();
1830 mHWData->mVideoCaptureFile = strFile;
1831
1832 return S_OK;
1833}
1834
1835HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1836{
1837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1838 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1839 return S_OK;
1840}
1841
1842HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1843{
1844 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1845
1846 if ( Global::IsOnline(mData->mMachineState)
1847 && mHWData->mVideoCaptureEnabled)
1848 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1849
1850 i_setModified(IsModified_MachineData);
1851 mHWData.backup();
1852 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1853
1854 return S_OK;
1855}
1856
1857HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1858{
1859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1860 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1861 return S_OK;
1862}
1863
1864HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1865{
1866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1867
1868 if ( Global::IsOnline(mData->mMachineState)
1869 && mHWData->mVideoCaptureEnabled)
1870 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1871
1872 i_setModified(IsModified_MachineData);
1873 mHWData.backup();
1874 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1875
1876 return S_OK;
1877}
1878
1879HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1880{
1881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1882 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1883 return S_OK;
1884}
1885
1886HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1887{
1888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1889
1890 if ( Global::IsOnline(mData->mMachineState)
1891 && mHWData->mVideoCaptureEnabled)
1892 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1893
1894 i_setModified(IsModified_MachineData);
1895 mHWData.backup();
1896 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1897
1898 return S_OK;
1899}
1900
1901HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1902{
1903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1904 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1905 return S_OK;
1906}
1907
1908HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1909{
1910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1911
1912 if ( Global::IsOnline(mData->mMachineState)
1913 && mHWData->mVideoCaptureEnabled)
1914 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1915
1916 i_setModified(IsModified_MachineData);
1917 mHWData.backup();
1918 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1919
1920 return S_OK;
1921}
1922
1923HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1924{
1925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1926 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1927 return S_OK;
1928}
1929
1930HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1931{
1932 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1933
1934 if ( Global::IsOnline(mData->mMachineState)
1935 && mHWData->mVideoCaptureEnabled)
1936 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1937
1938 i_setModified(IsModified_MachineData);
1939 mHWData.backup();
1940 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1941
1942 return S_OK;
1943}
1944
1945HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1946{
1947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1948 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1949 return S_OK;
1950}
1951
1952HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1953{
1954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1955
1956 if ( Global::IsOnline(mData->mMachineState)
1957 && mHWData->mVideoCaptureEnabled)
1958 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1959
1960 i_setModified(IsModified_MachineData);
1961 mHWData.backup();
1962 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1963
1964 return S_OK;
1965}
1966
1967HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1968{
1969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1970
1971 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1972 return S_OK;
1973}
1974
1975HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1976{
1977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1978
1979 if ( Global::IsOnline(mData->mMachineState)
1980 && mHWData->mVideoCaptureEnabled)
1981 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1982
1983 i_setModified(IsModified_MachineData);
1984 mHWData.backup();
1985 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1986
1987 return S_OK;
1988}
1989
1990HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1991{
1992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1993
1994 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1995
1996 return S_OK;
1997}
1998
1999HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2000{
2001 switch (aGraphicsControllerType)
2002 {
2003 case GraphicsControllerType_Null:
2004 case GraphicsControllerType_VBoxVGA:
2005#ifdef VBOX_WITH_VMSVGA
2006 case GraphicsControllerType_VMSVGA:
2007#endif
2008 break;
2009 default:
2010 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2011 }
2012
2013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2014
2015 HRESULT rc = i_checkStateDependency(MutableStateDep);
2016 if (FAILED(rc)) return rc;
2017
2018 i_setModified(IsModified_MachineData);
2019 mHWData.backup();
2020 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2021
2022 return S_OK;
2023}
2024
2025HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2026{
2027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2028
2029 *aVRAMSize = mHWData->mVRAMSize;
2030
2031 return S_OK;
2032}
2033
2034HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2035{
2036 /* check VRAM limits */
2037 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2038 return setError(E_INVALIDARG,
2039 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2040 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2041
2042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2043
2044 HRESULT rc = i_checkStateDependency(MutableStateDep);
2045 if (FAILED(rc)) return rc;
2046
2047 i_setModified(IsModified_MachineData);
2048 mHWData.backup();
2049 mHWData->mVRAMSize = aVRAMSize;
2050
2051 return S_OK;
2052}
2053
2054/** @todo this method should not be public */
2055HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2056{
2057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2060
2061 return S_OK;
2062}
2063
2064/**
2065 * Set the memory balloon size.
2066 *
2067 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2068 * we have to make sure that we never call IGuest from here.
2069 */
2070HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2071{
2072 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2073#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2074 /* check limits */
2075 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2076 return setError(E_INVALIDARG,
2077 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2078 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2079
2080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2081
2082 i_setModified(IsModified_MachineData);
2083 mHWData.backup();
2084 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2085
2086 return S_OK;
2087#else
2088 NOREF(aMemoryBalloonSize);
2089 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2090#endif
2091}
2092
2093HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2094{
2095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2096
2097 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2098 return S_OK;
2099}
2100
2101HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2102{
2103#ifdef VBOX_WITH_PAGE_SHARING
2104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2105
2106 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2107 i_setModified(IsModified_MachineData);
2108 mHWData.backup();
2109 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2110 return S_OK;
2111#else
2112 NOREF(aPageFusionEnabled);
2113 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2114#endif
2115}
2116
2117HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2118{
2119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2120
2121 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2122
2123 return S_OK;
2124}
2125
2126HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2127{
2128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2129
2130 HRESULT rc = i_checkStateDependency(MutableStateDep);
2131 if (FAILED(rc)) return rc;
2132
2133 /** @todo check validity! */
2134
2135 i_setModified(IsModified_MachineData);
2136 mHWData.backup();
2137 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2138
2139 return S_OK;
2140}
2141
2142
2143HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2144{
2145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2146
2147 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2148
2149 return S_OK;
2150}
2151
2152HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2153{
2154 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2155
2156 HRESULT rc = i_checkStateDependency(MutableStateDep);
2157 if (FAILED(rc)) return rc;
2158
2159 /** @todo check validity! */
2160 i_setModified(IsModified_MachineData);
2161 mHWData.backup();
2162 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2163
2164 return S_OK;
2165}
2166
2167HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2168{
2169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2170
2171 *aMonitorCount = mHWData->mMonitorCount;
2172
2173 return S_OK;
2174}
2175
2176HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2177{
2178 /* make sure monitor count is a sensible number */
2179 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2180 return setError(E_INVALIDARG,
2181 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2182 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2183
2184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2185
2186 HRESULT rc = i_checkStateDependency(MutableStateDep);
2187 if (FAILED(rc)) return rc;
2188
2189 i_setModified(IsModified_MachineData);
2190 mHWData.backup();
2191 mHWData->mMonitorCount = aMonitorCount;
2192
2193 return S_OK;
2194}
2195
2196HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2197{
2198 /* mBIOSSettings is constant during life time, no need to lock */
2199 aBIOSSettings = mBIOSSettings;
2200
2201 return S_OK;
2202}
2203
2204HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2205{
2206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2207
2208 switch (aProperty)
2209 {
2210 case CPUPropertyType_PAE:
2211 *aValue = mHWData->mPAEEnabled;
2212 break;
2213
2214 case CPUPropertyType_LongMode:
2215 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2216 *aValue = TRUE;
2217 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2218 *aValue = FALSE;
2219#if HC_ARCH_BITS == 64
2220 else
2221 *aValue = TRUE;
2222#else
2223 else
2224 {
2225 *aValue = FALSE;
2226
2227 ComPtr<IGuestOSType> ptrGuestOSType;
2228 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2229 if (SUCCEEDED(hrc2))
2230 {
2231 BOOL fIs64Bit = FALSE;
2232 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2233 if (SUCCEEDED(hrc2) && fIs64Bit)
2234 {
2235 ComObjPtr<Host> ptrHost = mParent->i_host();
2236 alock.release();
2237
2238 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2239 if (FAILED(hrc2))
2240 *aValue = FALSE;
2241 }
2242 }
2243 }
2244#endif
2245 break;
2246
2247 case CPUPropertyType_TripleFaultReset:
2248 *aValue = mHWData->mTripleFaultReset;
2249 break;
2250
2251 default:
2252 return E_INVALIDARG;
2253 }
2254 return S_OK;
2255}
2256
2257HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2258{
2259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2260
2261 HRESULT rc = i_checkStateDependency(MutableStateDep);
2262 if (FAILED(rc)) return rc;
2263
2264 switch (aProperty)
2265 {
2266 case CPUPropertyType_PAE:
2267 i_setModified(IsModified_MachineData);
2268 mHWData.backup();
2269 mHWData->mPAEEnabled = !!aValue;
2270 break;
2271
2272 case CPUPropertyType_LongMode:
2273 i_setModified(IsModified_MachineData);
2274 mHWData.backup();
2275 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2276 break;
2277
2278 case CPUPropertyType_TripleFaultReset:
2279 i_setModified(IsModified_MachineData);
2280 mHWData.backup();
2281 mHWData->mTripleFaultReset = !!aValue;
2282 break;
2283
2284 default:
2285 return E_INVALIDARG;
2286 }
2287 return S_OK;
2288}
2289
2290HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2291{
2292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2293
2294 switch(aId)
2295 {
2296 case 0x0:
2297 case 0x1:
2298 case 0x2:
2299 case 0x3:
2300 case 0x4:
2301 case 0x5:
2302 case 0x6:
2303 case 0x7:
2304 case 0x8:
2305 case 0x9:
2306 case 0xA:
2307 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2308 return E_INVALIDARG;
2309
2310 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2311 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2312 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2313 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2314 break;
2315
2316 case 0x80000000:
2317 case 0x80000001:
2318 case 0x80000002:
2319 case 0x80000003:
2320 case 0x80000004:
2321 case 0x80000005:
2322 case 0x80000006:
2323 case 0x80000007:
2324 case 0x80000008:
2325 case 0x80000009:
2326 case 0x8000000A:
2327 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2328 return E_INVALIDARG;
2329
2330 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2331 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2332 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2333 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2334 break;
2335
2336 default:
2337 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2338 }
2339 return S_OK;
2340}
2341
2342
2343HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2344{
2345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2346
2347 HRESULT rc = i_checkStateDependency(MutableStateDep);
2348 if (FAILED(rc)) return rc;
2349
2350 switch(aId)
2351 {
2352 case 0x0:
2353 case 0x1:
2354 case 0x2:
2355 case 0x3:
2356 case 0x4:
2357 case 0x5:
2358 case 0x6:
2359 case 0x7:
2360 case 0x8:
2361 case 0x9:
2362 case 0xA:
2363 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2364 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2365 i_setModified(IsModified_MachineData);
2366 mHWData.backup();
2367 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2368 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2369 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2370 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2371 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2372 break;
2373
2374 case 0x80000000:
2375 case 0x80000001:
2376 case 0x80000002:
2377 case 0x80000003:
2378 case 0x80000004:
2379 case 0x80000005:
2380 case 0x80000006:
2381 case 0x80000007:
2382 case 0x80000008:
2383 case 0x80000009:
2384 case 0x8000000A:
2385 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2386 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2387 i_setModified(IsModified_MachineData);
2388 mHWData.backup();
2389 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2390 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2391 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2392 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2393 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2394 break;
2395
2396 default:
2397 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2398 }
2399 return S_OK;
2400}
2401
2402HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2403{
2404 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2405
2406 HRESULT rc = i_checkStateDependency(MutableStateDep);
2407 if (FAILED(rc)) return rc;
2408
2409 switch(aId)
2410 {
2411 case 0x0:
2412 case 0x1:
2413 case 0x2:
2414 case 0x3:
2415 case 0x4:
2416 case 0x5:
2417 case 0x6:
2418 case 0x7:
2419 case 0x8:
2420 case 0x9:
2421 case 0xA:
2422 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2423 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2424 i_setModified(IsModified_MachineData);
2425 mHWData.backup();
2426 /* Invalidate leaf. */
2427 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2428 break;
2429
2430 case 0x80000000:
2431 case 0x80000001:
2432 case 0x80000002:
2433 case 0x80000003:
2434 case 0x80000004:
2435 case 0x80000005:
2436 case 0x80000006:
2437 case 0x80000007:
2438 case 0x80000008:
2439 case 0x80000009:
2440 case 0x8000000A:
2441 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2442 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2443 i_setModified(IsModified_MachineData);
2444 mHWData.backup();
2445 /* Invalidate leaf. */
2446 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2447 break;
2448
2449 default:
2450 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2451 }
2452 return S_OK;
2453}
2454
2455HRESULT Machine::removeAllCPUIDLeaves()
2456{
2457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2458
2459 HRESULT rc = i_checkStateDependency(MutableStateDep);
2460 if (FAILED(rc)) return rc;
2461
2462 i_setModified(IsModified_MachineData);
2463 mHWData.backup();
2464
2465 /* Invalidate all standard leafs. */
2466 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2467 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2468
2469 /* Invalidate all extended leafs. */
2470 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2471 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2472
2473 return S_OK;
2474}
2475HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2476{
2477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2478
2479 switch(aProperty)
2480 {
2481 case HWVirtExPropertyType_Enabled:
2482 *aValue = mHWData->mHWVirtExEnabled;
2483 break;
2484
2485 case HWVirtExPropertyType_VPID:
2486 *aValue = mHWData->mHWVirtExVPIDEnabled;
2487 break;
2488
2489 case HWVirtExPropertyType_NestedPaging:
2490 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2491 break;
2492
2493 case HWVirtExPropertyType_UnrestrictedExecution:
2494 *aValue = mHWData->mHWVirtExUXEnabled;
2495 break;
2496
2497 case HWVirtExPropertyType_LargePages:
2498 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2499#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2500 *aValue = FALSE;
2501#endif
2502 break;
2503
2504 case HWVirtExPropertyType_Force:
2505 *aValue = mHWData->mHWVirtExForceEnabled;
2506 break;
2507
2508 default:
2509 return E_INVALIDARG;
2510 }
2511 return S_OK;
2512}
2513
2514HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2515{
2516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2517
2518 HRESULT rc = i_checkStateDependency(MutableStateDep);
2519 if (FAILED(rc)) return rc;
2520
2521 switch(aProperty)
2522 {
2523 case HWVirtExPropertyType_Enabled:
2524 i_setModified(IsModified_MachineData);
2525 mHWData.backup();
2526 mHWData->mHWVirtExEnabled = !!aValue;
2527 break;
2528
2529 case HWVirtExPropertyType_VPID:
2530 i_setModified(IsModified_MachineData);
2531 mHWData.backup();
2532 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2533 break;
2534
2535 case HWVirtExPropertyType_NestedPaging:
2536 i_setModified(IsModified_MachineData);
2537 mHWData.backup();
2538 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2539 break;
2540
2541 case HWVirtExPropertyType_UnrestrictedExecution:
2542 i_setModified(IsModified_MachineData);
2543 mHWData.backup();
2544 mHWData->mHWVirtExUXEnabled = !!aValue;
2545 break;
2546
2547 case HWVirtExPropertyType_LargePages:
2548 i_setModified(IsModified_MachineData);
2549 mHWData.backup();
2550 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2551 break;
2552
2553 case HWVirtExPropertyType_Force:
2554 i_setModified(IsModified_MachineData);
2555 mHWData.backup();
2556 mHWData->mHWVirtExForceEnabled = !!aValue;
2557 break;
2558
2559 default:
2560 return E_INVALIDARG;
2561 }
2562
2563 return S_OK;
2564}
2565
2566HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2567{
2568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2569
2570 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2571
2572 return S_OK;
2573}
2574
2575HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2576{
2577 /* @todo (r=dmik):
2578 * 1. Allow to change the name of the snapshot folder containing snapshots
2579 * 2. Rename the folder on disk instead of just changing the property
2580 * value (to be smart and not to leave garbage). Note that it cannot be
2581 * done here because the change may be rolled back. Thus, the right
2582 * place is #saveSettings().
2583 */
2584
2585 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2586
2587 HRESULT rc = i_checkStateDependency(MutableStateDep);
2588 if (FAILED(rc)) return rc;
2589
2590 if (!mData->mCurrentSnapshot.isNull())
2591 return setError(E_FAIL,
2592 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2593
2594 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2595
2596 if (strSnapshotFolder.isEmpty())
2597 strSnapshotFolder = "Snapshots";
2598 int vrc = i_calculateFullPath(strSnapshotFolder,
2599 strSnapshotFolder);
2600 if (RT_FAILURE(vrc))
2601 return setError(E_FAIL,
2602 tr("Invalid snapshot folder '%s' (%Rrc)"),
2603 strSnapshotFolder.c_str(), vrc);
2604
2605 i_setModified(IsModified_MachineData);
2606 mUserData.backup();
2607
2608 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2609
2610 return S_OK;
2611}
2612
2613HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2614{
2615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2616
2617 aMediumAttachments.resize(mMediaData->mAttachments.size());
2618 size_t i = 0;
2619 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2620 it != mMediaData->mAttachments.end(); ++it, ++i)
2621 aMediumAttachments[i] = *it;
2622
2623 return S_OK;
2624}
2625
2626HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2627{
2628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2629
2630 Assert(!!mVRDEServer);
2631
2632 aVRDEServer = mVRDEServer;
2633
2634 return S_OK;
2635}
2636
2637HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2638{
2639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2640
2641 aAudioAdapter = mAudioAdapter;
2642
2643 return S_OK;
2644}
2645
2646HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2647{
2648#ifdef VBOX_WITH_VUSB
2649 clearError();
2650 MultiResult rc(S_OK);
2651
2652# ifdef VBOX_WITH_USB
2653 rc = mParent->i_host()->i_checkUSBProxyService();
2654 if (FAILED(rc)) return rc;
2655# endif
2656
2657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2658
2659 USBControllerList data = *mUSBControllers.data();
2660 aUSBControllers.resize(data.size());
2661 size_t i = 0;
2662 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2663 aUSBControllers[i] = *it;
2664
2665 return S_OK;
2666#else
2667 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2668 * extended error info to indicate that USB is simply not available
2669 * (w/o treating it as a failure), for example, as in OSE */
2670 NOREF(aUSBControllers);
2671 ReturnComNotImplemented();
2672#endif /* VBOX_WITH_VUSB */
2673}
2674
2675HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2676{
2677#ifdef VBOX_WITH_VUSB
2678 clearError();
2679 MultiResult rc(S_OK);
2680
2681# ifdef VBOX_WITH_USB
2682 rc = mParent->i_host()->i_checkUSBProxyService();
2683 if (FAILED(rc)) return rc;
2684# endif
2685
2686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2687
2688 aUSBDeviceFilters = mUSBDeviceFilters;
2689 return rc;
2690#else
2691 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2692 * extended error info to indicate that USB is simply not available
2693 * (w/o treating it as a failure), for example, as in OSE */
2694 NOREF(aUSBDeviceFilters);
2695 ReturnComNotImplemented();
2696#endif /* VBOX_WITH_VUSB */
2697}
2698
2699HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2700{
2701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2702
2703 aSettingsFilePath = mData->m_strConfigFileFull;
2704
2705 return S_OK;
2706}
2707
2708HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2709{
2710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2711
2712 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2713 if (FAILED(rc)) return rc;
2714
2715 if (!mData->pMachineConfigFile->fileExists())
2716 // this is a new machine, and no config file exists yet:
2717 *aSettingsModified = TRUE;
2718 else
2719 *aSettingsModified = (mData->flModifications != 0);
2720
2721 return S_OK;
2722}
2723
2724HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2725{
2726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2727
2728 *aSessionState = mData->mSession.mState;
2729
2730 return S_OK;
2731}
2732
2733HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2734{
2735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2736
2737 aSessionName = mData->mSession.mName;
2738
2739 return S_OK;
2740}
2741
2742HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2743{
2744 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2745
2746 *aSessionPID = mData->mSession.mPID;
2747
2748 return S_OK;
2749}
2750
2751HRESULT Machine::getState(MachineState_T *aState)
2752{
2753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2754
2755 *aState = mData->mMachineState;
2756 Assert(mData->mMachineState != MachineState_Null);
2757
2758 return S_OK;
2759}
2760
2761HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2762{
2763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2764
2765 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2766
2767 return S_OK;
2768}
2769
2770HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2771{
2772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2773
2774 aStateFilePath = mSSData->strStateFilePath;
2775
2776 return S_OK;
2777}
2778
2779HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2780{
2781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2782
2783 i_getLogFolder(aLogFolder);
2784
2785 return S_OK;
2786}
2787
2788HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2789{
2790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2791
2792 aCurrentSnapshot = mData->mCurrentSnapshot;
2793
2794 return S_OK;
2795}
2796
2797HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2798{
2799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2800
2801 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2802 ? 0
2803 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2809{
2810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 /* Note: for machines with no snapshots, we always return FALSE
2813 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2814 * reasons :) */
2815
2816 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2817 ? FALSE
2818 : mData->mCurrentStateModified;
2819
2820 return S_OK;
2821}
2822
2823HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2824{
2825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2826
2827 aSharedFolders.resize(mHWData->mSharedFolders.size());
2828 size_t i = 0;
2829 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2830 it != mHWData->mSharedFolders.end(); ++i, ++it)
2831 aSharedFolders[i] = *it;
2832
2833 return S_OK;
2834}
2835
2836HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2837{
2838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2839
2840 *aClipboardMode = mHWData->mClipboardMode;
2841
2842 return S_OK;
2843}
2844
2845HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2846{
2847 HRESULT rc = S_OK;
2848
2849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2850
2851 alock.release();
2852 rc = i_onClipboardModeChange(aClipboardMode);
2853 alock.acquire();
2854 if (FAILED(rc)) return rc;
2855
2856 i_setModified(IsModified_MachineData);
2857 mHWData.backup();
2858 mHWData->mClipboardMode = aClipboardMode;
2859
2860 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2861 if (Global::IsOnline(mData->mMachineState))
2862 i_saveSettings(NULL);
2863
2864 return S_OK;
2865}
2866
2867HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2868{
2869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2870
2871 *aDnDMode = mHWData->mDnDMode;
2872
2873 return S_OK;
2874}
2875
2876HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2877{
2878 HRESULT rc = S_OK;
2879
2880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2881
2882 alock.release();
2883 rc = i_onDnDModeChange(aDnDMode);
2884
2885 alock.acquire();
2886 if (FAILED(rc)) return rc;
2887
2888 i_setModified(IsModified_MachineData);
2889 mHWData.backup();
2890 mHWData->mDnDMode = aDnDMode;
2891
2892 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2893 if (Global::IsOnline(mData->mMachineState))
2894 i_saveSettings(NULL);
2895
2896 return S_OK;
2897}
2898
2899HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2900{
2901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2902 StorageControllerList data = *mStorageControllers.data();
2903 size_t i = 0;
2904 aStorageControllers.resize(data.size());
2905 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2906 aStorageControllers[i] = *it;
2907 return S_OK;
2908}
2909
2910HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2911{
2912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2913
2914 *aEnabled = mUserData->s.fTeleporterEnabled;
2915
2916 return S_OK;
2917}
2918
2919HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2920{
2921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2922
2923 /* Only allow it to be set to true when PoweredOff or Aborted.
2924 (Clearing it is always permitted.) */
2925 if ( aTeleporterEnabled
2926 && mData->mRegistered
2927 && ( !i_isSessionMachine()
2928 || ( mData->mMachineState != MachineState_PoweredOff
2929 && mData->mMachineState != MachineState_Teleported
2930 && mData->mMachineState != MachineState_Aborted
2931 )
2932 )
2933 )
2934 return setError(VBOX_E_INVALID_VM_STATE,
2935 tr("The machine is not powered off (state is %s)"),
2936 Global::stringifyMachineState(mData->mMachineState));
2937
2938 i_setModified(IsModified_MachineData);
2939 mUserData.backup();
2940 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2941
2942 return S_OK;
2943}
2944
2945HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2946{
2947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2948
2949 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2950
2951 return S_OK;
2952}
2953
2954HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2955{
2956 if (aTeleporterPort >= _64K)
2957 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2958
2959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2960
2961 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2962 if (FAILED(rc)) return rc;
2963
2964 i_setModified(IsModified_MachineData);
2965 mUserData.backup();
2966 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2967
2968 return S_OK;
2969}
2970
2971HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2972{
2973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2976
2977 return S_OK;
2978}
2979
2980HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2981{
2982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2983
2984 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2985 if (FAILED(rc)) return rc;
2986
2987 i_setModified(IsModified_MachineData);
2988 mUserData.backup();
2989 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2990
2991 return S_OK;
2992}
2993
2994HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2995{
2996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2997 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2998
2999 return S_OK;
3000}
3001
3002HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3003{
3004 /*
3005 * Hash the password first.
3006 */
3007 com::Utf8Str aT = aTeleporterPassword;
3008
3009 if (!aT.isEmpty())
3010 {
3011 if (VBoxIsPasswordHashed(&aT))
3012 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3013 VBoxHashPassword(&aT);
3014 }
3015
3016 /*
3017 * Do the update.
3018 */
3019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3020 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3021 if (SUCCEEDED(hrc))
3022 {
3023 i_setModified(IsModified_MachineData);
3024 mUserData.backup();
3025 mUserData->s.strTeleporterPassword = aT;
3026 }
3027
3028 return hrc;
3029}
3030
3031HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3032{
3033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3034
3035 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3036 return S_OK;
3037}
3038
3039HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3040{
3041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3042
3043 /* @todo deal with running state change. */
3044 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3045 if (FAILED(rc)) return rc;
3046
3047 i_setModified(IsModified_MachineData);
3048 mUserData.backup();
3049 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3050 return S_OK;
3051}
3052
3053HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3054{
3055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3056
3057 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3058 return S_OK;
3059}
3060
3061HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3062{
3063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3064
3065 /* @todo deal with running state change. */
3066 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3067 if (FAILED(rc)) return rc;
3068
3069 i_setModified(IsModified_MachineData);
3070 mUserData.backup();
3071 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3072 return S_OK;
3073}
3074
3075HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3076{
3077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3078
3079 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3080 return S_OK;
3081}
3082
3083HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3084{
3085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3086
3087 /* @todo deal with running state change. */
3088 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3089 if (FAILED(rc)) return rc;
3090
3091 i_setModified(IsModified_MachineData);
3092 mUserData.backup();
3093 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3094 return S_OK;
3095}
3096
3097HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3098{
3099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3100
3101 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3102
3103 return S_OK;
3104}
3105
3106HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3107{
3108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3109
3110 /* @todo deal with running state change. */
3111 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3112 if (FAILED(rc)) return rc;
3113
3114 i_setModified(IsModified_MachineData);
3115 mUserData.backup();
3116 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3117
3118 return S_OK;
3119}
3120
3121HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3122{
3123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3124
3125 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3126 return S_OK;
3127}
3128
3129HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3130{
3131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3132
3133 /* @todo deal with running state change. */
3134 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3135 if (FAILED(rc)) return rc;
3136
3137 i_setModified(IsModified_MachineData);
3138 mUserData.backup();
3139 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3140 return S_OK;
3141}
3142
3143HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3144{
3145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3146
3147 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3148
3149 return S_OK;
3150}
3151
3152HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3153{
3154 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3155
3156 /* Only allow it to be set to true when PoweredOff or Aborted.
3157 (Clearing it is always permitted.) */
3158 if ( aRTCUseUTC
3159 && mData->mRegistered
3160 && ( !i_isSessionMachine()
3161 || ( mData->mMachineState != MachineState_PoweredOff
3162 && mData->mMachineState != MachineState_Teleported
3163 && mData->mMachineState != MachineState_Aborted
3164 )
3165 )
3166 )
3167 return setError(VBOX_E_INVALID_VM_STATE,
3168 tr("The machine is not powered off (state is %s)"),
3169 Global::stringifyMachineState(mData->mMachineState));
3170
3171 i_setModified(IsModified_MachineData);
3172 mUserData.backup();
3173 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3174
3175 return S_OK;
3176}
3177
3178HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3179{
3180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3183
3184 return S_OK;
3185}
3186
3187HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3188{
3189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3190
3191 HRESULT rc = i_checkStateDependency(MutableStateDep);
3192 if (FAILED(rc)) return rc;
3193
3194 i_setModified(IsModified_MachineData);
3195 mHWData.backup();
3196 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3197
3198 return S_OK;
3199}
3200
3201HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3202{
3203 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3204
3205 *aIOCacheSize = mHWData->mIOCacheSize;
3206
3207 return S_OK;
3208}
3209
3210HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3211{
3212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3213
3214 HRESULT rc = i_checkStateDependency(MutableStateDep);
3215 if (FAILED(rc)) return rc;
3216
3217 i_setModified(IsModified_MachineData);
3218 mHWData.backup();
3219 mHWData->mIOCacheSize = aIOCacheSize;
3220
3221 return S_OK;
3222}
3223
3224
3225/**
3226 * @note Locks objects!
3227 */
3228HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3229 LockType_T aLockType)
3230{
3231 /* check the session state */
3232 SessionState_T state;
3233 HRESULT rc = aSession->COMGETTER(State)(&state);
3234 if (FAILED(rc)) return rc;
3235
3236 if (state != SessionState_Unlocked)
3237 return setError(VBOX_E_INVALID_OBJECT_STATE,
3238 tr("The given session is busy"));
3239
3240 // get the client's IInternalSessionControl interface
3241 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3242 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3243 E_INVALIDARG);
3244
3245 // session name (only used in some code paths)
3246 Utf8Str strSessionName;
3247
3248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3249
3250 if (!mData->mRegistered)
3251 return setError(E_UNEXPECTED,
3252 tr("The machine '%s' is not registered"),
3253 mUserData->s.strName.c_str());
3254
3255 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3256
3257 SessionState_T oldState = mData->mSession.mState;
3258 /* Hack: in case the session is closing and there is a progress object
3259 * which allows waiting for the session to be closed, take the opportunity
3260 * and do a limited wait (max. 1 second). This helps a lot when the system
3261 * is busy and thus session closing can take a little while. */
3262 if ( mData->mSession.mState == SessionState_Unlocking
3263 && mData->mSession.mProgress)
3264 {
3265 alock.release();
3266 mData->mSession.mProgress->WaitForCompletion(1000);
3267 alock.acquire();
3268 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3269 }
3270
3271 // try again now
3272 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3273 // (i.e. session machine exists)
3274 && (aLockType == LockType_Shared) // caller wants a shared link to the
3275 // existing session that holds the write lock:
3276 )
3277 {
3278 // OK, share the session... we are now dealing with three processes:
3279 // 1) VBoxSVC (where this code runs);
3280 // 2) process C: the caller's client process (who wants a shared session);
3281 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3282
3283 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3284 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3285 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3286 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3287 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3288
3289 /*
3290 * Release the lock before calling the client process. It's safe here
3291 * since the only thing to do after we get the lock again is to add
3292 * the remote control to the list (which doesn't directly influence
3293 * anything).
3294 */
3295 alock.release();
3296
3297 // get the console of the session holding the write lock (this is a remote call)
3298 ComPtr<IConsole> pConsoleW;
3299 if (mData->mSession.mLockType == LockType_VM)
3300 {
3301 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3302 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3303 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3304 if (FAILED(rc))
3305 // the failure may occur w/o any error info (from RPC), so provide one
3306 return setError(VBOX_E_VM_ERROR,
3307 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3308 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3309 }
3310
3311 // share the session machine and W's console with the caller's session
3312 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3313 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3314 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3315
3316 if (FAILED(rc))
3317 // the failure may occur w/o any error info (from RPC), so provide one
3318 return setError(VBOX_E_VM_ERROR,
3319 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3320 alock.acquire();
3321
3322 // need to revalidate the state after acquiring the lock again
3323 if (mData->mSession.mState != SessionState_Locked)
3324 {
3325 pSessionControl->Uninitialize();
3326 return setError(VBOX_E_INVALID_SESSION_STATE,
3327 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3328 mUserData->s.strName.c_str());
3329 }
3330
3331 // add the caller's session to the list
3332 mData->mSession.mRemoteControls.push_back(pSessionControl);
3333 }
3334 else if ( mData->mSession.mState == SessionState_Locked
3335 || mData->mSession.mState == SessionState_Unlocking
3336 )
3337 {
3338 // sharing not permitted, or machine still unlocking:
3339 return setError(VBOX_E_INVALID_OBJECT_STATE,
3340 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3341 mUserData->s.strName.c_str());
3342 }
3343 else
3344 {
3345 // machine is not locked: then write-lock the machine (create the session machine)
3346
3347 // must not be busy
3348 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3349
3350 // get the caller's session PID
3351 RTPROCESS pid = NIL_RTPROCESS;
3352 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3353 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3354 Assert(pid != NIL_RTPROCESS);
3355
3356 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3357
3358 if (fLaunchingVMProcess)
3359 {
3360 if (mData->mSession.mPID == NIL_RTPROCESS)
3361 {
3362 // two or more clients racing for a lock, the one which set the
3363 // session state to Spawning will win, the others will get an
3364 // error as we can't decide here if waiting a little would help
3365 // (only for shared locks this would avoid an error)
3366 return setError(VBOX_E_INVALID_OBJECT_STATE,
3367 tr("The machine '%s' already has a lock request pending"),
3368 mUserData->s.strName.c_str());
3369 }
3370
3371 // this machine is awaiting for a spawning session to be opened:
3372 // then the calling process must be the one that got started by
3373 // LaunchVMProcess()
3374
3375 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3376 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3377
3378#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3379 /* Hardened windows builds spawns three processes when a VM is
3380 launched, the 3rd one is the one that will end up here. */
3381 RTPROCESS ppid;
3382 int rc = RTProcQueryParent(pid, &ppid);
3383 if (RT_SUCCESS(rc))
3384 rc = RTProcQueryParent(ppid, &ppid);
3385 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3386 || rc == VERR_ACCESS_DENIED)
3387 {
3388 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3389 mData->mSession.mPID = pid;
3390 }
3391#endif
3392
3393 if (mData->mSession.mPID != pid)
3394 return setError(E_ACCESSDENIED,
3395 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3396 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3397 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3398 }
3399
3400 // create the mutable SessionMachine from the current machine
3401 ComObjPtr<SessionMachine> sessionMachine;
3402 sessionMachine.createObject();
3403 rc = sessionMachine->init(this);
3404 AssertComRC(rc);
3405
3406 /* NOTE: doing return from this function after this point but
3407 * before the end is forbidden since it may call SessionMachine::uninit()
3408 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3409 * lock while still holding the Machine lock in alock so that a deadlock
3410 * is possible due to the wrong lock order. */
3411
3412 if (SUCCEEDED(rc))
3413 {
3414 /*
3415 * Set the session state to Spawning to protect against subsequent
3416 * attempts to open a session and to unregister the machine after
3417 * we release the lock.
3418 */
3419 SessionState_T origState = mData->mSession.mState;
3420 mData->mSession.mState = SessionState_Spawning;
3421
3422#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3423 /* Get the client token ID to be passed to the client process */
3424 Utf8Str strTokenId;
3425 sessionMachine->i_getTokenId(strTokenId);
3426 Assert(!strTokenId.isEmpty());
3427#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3428 /* Get the client token to be passed to the client process */
3429 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3430 /* The token is now "owned" by pToken, fix refcount */
3431 if (!pToken.isNull())
3432 pToken->Release();
3433#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3434
3435 /*
3436 * Release the lock before calling the client process -- it will call
3437 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3438 * because the state is Spawning, so that LaunchVMProcess() and
3439 * LockMachine() calls will fail. This method, called before we
3440 * acquire the lock again, will fail because of the wrong PID.
3441 *
3442 * Note that mData->mSession.mRemoteControls accessed outside
3443 * the lock may not be modified when state is Spawning, so it's safe.
3444 */
3445 alock.release();
3446
3447 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3448#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3449 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3450#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3451 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3452 /* Now the token is owned by the client process. */
3453 pToken.setNull();
3454#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3455 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3456
3457 /* The failure may occur w/o any error info (from RPC), so provide one */
3458 if (FAILED(rc))
3459 setError(VBOX_E_VM_ERROR,
3460 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3461
3462 // get session name, either to remember or to compare against
3463 // the already known session name.
3464 {
3465 Bstr bstrSessionName;
3466 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3467 if (SUCCEEDED(rc2))
3468 strSessionName = bstrSessionName;
3469 }
3470
3471 if ( SUCCEEDED(rc)
3472 && fLaunchingVMProcess
3473 )
3474 {
3475 /* complete the remote session initialization */
3476
3477 /* get the console from the direct session */
3478 ComPtr<IConsole> console;
3479 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3480 ComAssertComRC(rc);
3481
3482 if (SUCCEEDED(rc) && !console)
3483 {
3484 ComAssert(!!console);
3485 rc = E_FAIL;
3486 }
3487
3488 /* assign machine & console to the remote session */
3489 if (SUCCEEDED(rc))
3490 {
3491 /*
3492 * after LaunchVMProcess(), the first and the only
3493 * entry in remoteControls is that remote session
3494 */
3495 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3496 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3497 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3498
3499 /* The failure may occur w/o any error info (from RPC), so provide one */
3500 if (FAILED(rc))
3501 setError(VBOX_E_VM_ERROR,
3502 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3503 }
3504
3505 if (FAILED(rc))
3506 pSessionControl->Uninitialize();
3507 }
3508
3509 /* acquire the lock again */
3510 alock.acquire();
3511
3512 /* Restore the session state */
3513 mData->mSession.mState = origState;
3514 }
3515
3516 // finalize spawning anyway (this is why we don't return on errors above)
3517 if (fLaunchingVMProcess)
3518 {
3519 Assert(mData->mSession.mName == strSessionName);
3520 /* Note that the progress object is finalized later */
3521 /** @todo Consider checking mData->mSession.mProgress for cancellation
3522 * around here. */
3523
3524 /* We don't reset mSession.mPID here because it is necessary for
3525 * SessionMachine::uninit() to reap the child process later. */
3526
3527 if (FAILED(rc))
3528 {
3529 /* Close the remote session, remove the remote control from the list
3530 * and reset session state to Closed (@note keep the code in sync
3531 * with the relevant part in checkForSpawnFailure()). */
3532
3533 Assert(mData->mSession.mRemoteControls.size() == 1);
3534 if (mData->mSession.mRemoteControls.size() == 1)
3535 {
3536 ErrorInfoKeeper eik;
3537 mData->mSession.mRemoteControls.front()->Uninitialize();
3538 }
3539
3540 mData->mSession.mRemoteControls.clear();
3541 mData->mSession.mState = SessionState_Unlocked;
3542 }
3543 }
3544 else
3545 {
3546 /* memorize PID of the directly opened session */
3547 if (SUCCEEDED(rc))
3548 mData->mSession.mPID = pid;
3549 }
3550
3551 if (SUCCEEDED(rc))
3552 {
3553 mData->mSession.mLockType = aLockType;
3554 /* memorize the direct session control and cache IUnknown for it */
3555 mData->mSession.mDirectControl = pSessionControl;
3556 mData->mSession.mState = SessionState_Locked;
3557 if (!fLaunchingVMProcess)
3558 mData->mSession.mName = strSessionName;
3559 /* associate the SessionMachine with this Machine */
3560 mData->mSession.mMachine = sessionMachine;
3561
3562 /* request an IUnknown pointer early from the remote party for later
3563 * identity checks (it will be internally cached within mDirectControl
3564 * at least on XPCOM) */
3565 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3566 NOREF(unk);
3567 }
3568
3569 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3570 * would break the lock order */
3571 alock.release();
3572
3573 /* uninitialize the created session machine on failure */
3574 if (FAILED(rc))
3575 sessionMachine->uninit();
3576 }
3577
3578 if (SUCCEEDED(rc))
3579 {
3580 /*
3581 * tell the client watcher thread to update the set of
3582 * machines that have open sessions
3583 */
3584 mParent->i_updateClientWatcher();
3585
3586 if (oldState != SessionState_Locked)
3587 /* fire an event */
3588 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3589 }
3590
3591 return rc;
3592}
3593
3594/**
3595 * @note Locks objects!
3596 */
3597HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3598 const com::Utf8Str &aName,
3599 const com::Utf8Str &aEnvironment,
3600 ComPtr<IProgress> &aProgress)
3601{
3602 Utf8Str strFrontend(aName);
3603 /* "emergencystop" doesn't need the session, so skip the checks/interface
3604 * retrieval. This code doesn't quite fit in here, but introducing a
3605 * special API method would be even more effort, and would require explicit
3606 * support by every API client. It's better to hide the feature a bit. */
3607 if (strFrontend != "emergencystop")
3608 CheckComArgNotNull(aSession);
3609
3610 HRESULT rc = S_OK;
3611 if (strFrontend.isEmpty())
3612 {
3613 Bstr bstrFrontend;
3614 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3615 if (FAILED(rc))
3616 return rc;
3617 strFrontend = bstrFrontend;
3618 if (strFrontend.isEmpty())
3619 {
3620 ComPtr<ISystemProperties> systemProperties;
3621 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3622 if (FAILED(rc))
3623 return rc;
3624 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3625 if (FAILED(rc))
3626 return rc;
3627 strFrontend = bstrFrontend;
3628 }
3629 /* paranoia - emergencystop is not a valid default */
3630 if (strFrontend == "emergencystop")
3631 strFrontend = Utf8Str::Empty;
3632 }
3633 /* default frontend: Qt GUI */
3634 if (strFrontend.isEmpty())
3635 strFrontend = "GUI/Qt";
3636
3637 if (strFrontend != "emergencystop")
3638 {
3639 /* check the session state */
3640 SessionState_T state;
3641 rc = aSession->COMGETTER(State)(&state);
3642 if (FAILED(rc))
3643 return rc;
3644
3645 if (state != SessionState_Unlocked)
3646 return setError(VBOX_E_INVALID_OBJECT_STATE,
3647 tr("The given session is busy"));
3648
3649 /* get the IInternalSessionControl interface */
3650 ComPtr<IInternalSessionControl> control(aSession);
3651 ComAssertMsgRet(!control.isNull(),
3652 ("No IInternalSessionControl interface"),
3653 E_INVALIDARG);
3654
3655 /* get the teleporter enable state for the progress object init. */
3656 BOOL fTeleporterEnabled;
3657 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3658 if (FAILED(rc))
3659 return rc;
3660
3661 /* create a progress object */
3662 ComObjPtr<ProgressProxy> progress;
3663 progress.createObject();
3664 rc = progress->init(mParent,
3665 static_cast<IMachine*>(this),
3666 Bstr(tr("Starting VM")).raw(),
3667 TRUE /* aCancelable */,
3668 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3669 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3670 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3671 2 /* uFirstOperationWeight */,
3672 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3673
3674 if (SUCCEEDED(rc))
3675 {
3676 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3677 if (SUCCEEDED(rc))
3678 {
3679 aProgress = progress;
3680
3681 /* signal the client watcher thread */
3682 mParent->i_updateClientWatcher();
3683
3684 /* fire an event */
3685 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3686 }
3687 }
3688 }
3689 else
3690 {
3691 /* no progress object - either instant success or failure */
3692 aProgress = NULL;
3693
3694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3695
3696 if (mData->mSession.mState != SessionState_Locked)
3697 return setError(VBOX_E_INVALID_OBJECT_STATE,
3698 tr("The machine '%s' is not locked by a session"),
3699 mUserData->s.strName.c_str());
3700
3701 /* must have a VM process associated - do not kill normal API clients
3702 * with an open session */
3703 if (!Global::IsOnline(mData->mMachineState))
3704 return setError(VBOX_E_INVALID_OBJECT_STATE,
3705 tr("The machine '%s' does not have a VM process"),
3706 mUserData->s.strName.c_str());
3707
3708 /* forcibly terminate the VM process */
3709 if (mData->mSession.mPID != NIL_RTPROCESS)
3710 RTProcTerminate(mData->mSession.mPID);
3711
3712 /* signal the client watcher thread, as most likely the client has
3713 * been terminated */
3714 mParent->i_updateClientWatcher();
3715 }
3716
3717 return rc;
3718}
3719
3720HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3721{
3722 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3723 return setError(E_INVALIDARG,
3724 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3725 aPosition, SchemaDefs::MaxBootPosition);
3726
3727 if (aDevice == DeviceType_USB)
3728 return setError(E_NOTIMPL,
3729 tr("Booting from USB device is currently not supported"));
3730
3731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3732
3733 HRESULT rc = i_checkStateDependency(MutableStateDep);
3734 if (FAILED(rc)) return rc;
3735
3736 i_setModified(IsModified_MachineData);
3737 mHWData.backup();
3738 mHWData->mBootOrder[aPosition - 1] = aDevice;
3739
3740 return S_OK;
3741}
3742
3743HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3744{
3745 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3746 return setError(E_INVALIDARG,
3747 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3748 aPosition, SchemaDefs::MaxBootPosition);
3749
3750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3751
3752 *aDevice = mHWData->mBootOrder[aPosition - 1];
3753
3754 return S_OK;
3755}
3756
3757HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3758 LONG aControllerPort,
3759 LONG aDevice,
3760 DeviceType_T aType,
3761 const ComPtr<IMedium> &aMedium)
3762{
3763 IMedium *aM = aMedium;
3764 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3765 aName.c_str(), aControllerPort, aDevice, aType, aM));
3766
3767 // request the host lock first, since might be calling Host methods for getting host drives;
3768 // next, protect the media tree all the while we're in here, as well as our member variables
3769 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3770 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3771
3772 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3773 if (FAILED(rc)) return rc;
3774
3775 /// @todo NEWMEDIA implicit machine registration
3776 if (!mData->mRegistered)
3777 return setError(VBOX_E_INVALID_OBJECT_STATE,
3778 tr("Cannot attach storage devices to an unregistered machine"));
3779
3780 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3781
3782 /* Check for an existing controller. */
3783 ComObjPtr<StorageController> ctl;
3784 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3785 if (FAILED(rc)) return rc;
3786
3787 StorageControllerType_T ctrlType;
3788 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3789 if (FAILED(rc))
3790 return setError(E_FAIL,
3791 tr("Could not get type of controller '%s'"),
3792 aName.c_str());
3793
3794 bool fSilent = false;
3795 Utf8Str strReconfig;
3796
3797 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3798 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3799 if ( mData->mMachineState == MachineState_Paused
3800 && strReconfig == "1")
3801 fSilent = true;
3802
3803 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3804 bool fHotplug = false;
3805 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3806 fHotplug = true;
3807
3808 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3809 return setError(VBOX_E_INVALID_VM_STATE,
3810 tr("Controller '%s' does not support hotplugging"),
3811 aName.c_str());
3812
3813 // check that the port and device are not out of range
3814 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3815 if (FAILED(rc)) return rc;
3816
3817 /* check if the device slot is already busy */
3818 MediumAttachment *pAttachTemp;
3819 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3820 Bstr(aName).raw(),
3821 aControllerPort,
3822 aDevice)))
3823 {
3824 Medium *pMedium = pAttachTemp->i_getMedium();
3825 if (pMedium)
3826 {
3827 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3828 return setError(VBOX_E_OBJECT_IN_USE,
3829 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3830 pMedium->i_getLocationFull().c_str(),
3831 aControllerPort,
3832 aDevice,
3833 aName.c_str());
3834 }
3835 else
3836 return setError(VBOX_E_OBJECT_IN_USE,
3837 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3838 aControllerPort, aDevice, aName.c_str());
3839 }
3840
3841 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3842 if (aMedium && medium.isNull())
3843 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3844
3845 AutoCaller mediumCaller(medium);
3846 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3847
3848 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3849
3850 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3851 && !medium.isNull()
3852 )
3853 return setError(VBOX_E_OBJECT_IN_USE,
3854 tr("Medium '%s' is already attached to this virtual machine"),
3855 medium->i_getLocationFull().c_str());
3856
3857 if (!medium.isNull())
3858 {
3859 MediumType_T mtype = medium->i_getType();
3860 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3861 // For DVDs it's not written to the config file, so needs no global config
3862 // version bump. For floppies it's a new attribute "type", which is ignored
3863 // by older VirtualBox version, so needs no global config version bump either.
3864 // For hard disks this type is not accepted.
3865 if (mtype == MediumType_MultiAttach)
3866 {
3867 // This type is new with VirtualBox 4.0 and therefore requires settings
3868 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3869 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3870 // two reasons: The medium type is a property of the media registry tree, which
3871 // can reside in the global config file (for pre-4.0 media); we would therefore
3872 // possibly need to bump the global config version. We don't want to do that though
3873 // because that might make downgrading to pre-4.0 impossible.
3874 // As a result, we can only use these two new types if the medium is NOT in the
3875 // global registry:
3876 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3877 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3878 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3879 )
3880 return setError(VBOX_E_INVALID_OBJECT_STATE,
3881 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3882 "to machines that were created with VirtualBox 4.0 or later"),
3883 medium->i_getLocationFull().c_str());
3884 }
3885 }
3886
3887 bool fIndirect = false;
3888 if (!medium.isNull())
3889 fIndirect = medium->i_isReadOnly();
3890 bool associate = true;
3891
3892 do
3893 {
3894 if ( aType == DeviceType_HardDisk
3895 && mMediaData.isBackedUp())
3896 {
3897 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3898
3899 /* check if the medium was attached to the VM before we started
3900 * changing attachments in which case the attachment just needs to
3901 * be restored */
3902 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3903 {
3904 AssertReturn(!fIndirect, E_FAIL);
3905
3906 /* see if it's the same bus/channel/device */
3907 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3908 {
3909 /* the simplest case: restore the whole attachment
3910 * and return, nothing else to do */
3911 mMediaData->mAttachments.push_back(pAttachTemp);
3912
3913 /* Reattach the medium to the VM. */
3914 if (fHotplug || fSilent)
3915 {
3916 mediumLock.release();
3917 treeLock.release();
3918 alock.release();
3919
3920 MediumLockList *pMediumLockList(new MediumLockList());
3921
3922 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3923 medium /* pToLockWrite */,
3924 false /* fMediumLockWriteAll */,
3925 NULL,
3926 *pMediumLockList);
3927 alock.acquire();
3928 if (FAILED(rc))
3929 delete pMediumLockList;
3930 else
3931 {
3932 mData->mSession.mLockedMedia.Unlock();
3933 alock.release();
3934 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3935 mData->mSession.mLockedMedia.Lock();
3936 alock.acquire();
3937 }
3938 alock.release();
3939
3940 if (SUCCEEDED(rc))
3941 {
3942 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3943 /* Remove lock list in case of error. */
3944 if (FAILED(rc))
3945 {
3946 mData->mSession.mLockedMedia.Unlock();
3947 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3948 mData->mSession.mLockedMedia.Lock();
3949 }
3950 }
3951 }
3952
3953 return S_OK;
3954 }
3955
3956 /* bus/channel/device differ; we need a new attachment object,
3957 * but don't try to associate it again */
3958 associate = false;
3959 break;
3960 }
3961 }
3962
3963 /* go further only if the attachment is to be indirect */
3964 if (!fIndirect)
3965 break;
3966
3967 /* perform the so called smart attachment logic for indirect
3968 * attachments. Note that smart attachment is only applicable to base
3969 * hard disks. */
3970
3971 if (medium->i_getParent().isNull())
3972 {
3973 /* first, investigate the backup copy of the current hard disk
3974 * attachments to make it possible to re-attach existing diffs to
3975 * another device slot w/o losing their contents */
3976 if (mMediaData.isBackedUp())
3977 {
3978 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3979
3980 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3981 uint32_t foundLevel = 0;
3982
3983 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3984 {
3985 uint32_t level = 0;
3986 MediumAttachment *pAttach = *it;
3987 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3988 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3989 if (pMedium.isNull())
3990 continue;
3991
3992 if (pMedium->i_getBase(&level) == medium)
3993 {
3994 /* skip the hard disk if its currently attached (we
3995 * cannot attach the same hard disk twice) */
3996 if (i_findAttachment(mMediaData->mAttachments,
3997 pMedium))
3998 continue;
3999
4000 /* matched device, channel and bus (i.e. attached to the
4001 * same place) will win and immediately stop the search;
4002 * otherwise the attachment that has the youngest
4003 * descendant of medium will be used
4004 */
4005 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
4006 {
4007 /* the simplest case: restore the whole attachment
4008 * and return, nothing else to do */
4009 mMediaData->mAttachments.push_back(*it);
4010
4011 /* Reattach the medium to the VM. */
4012 if (fHotplug || fSilent)
4013 {
4014 mediumLock.release();
4015 treeLock.release();
4016 alock.release();
4017
4018 MediumLockList *pMediumLockList(new MediumLockList());
4019
4020 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4021 medium /* pToLockWrite */,
4022 false /* fMediumLockWriteAll */,
4023 NULL,
4024 *pMediumLockList);
4025 alock.acquire();
4026 if (FAILED(rc))
4027 delete pMediumLockList;
4028 else
4029 {
4030 mData->mSession.mLockedMedia.Unlock();
4031 alock.release();
4032 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4033 mData->mSession.mLockedMedia.Lock();
4034 alock.acquire();
4035 }
4036 alock.release();
4037
4038 if (SUCCEEDED(rc))
4039 {
4040 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4041 /* Remove lock list in case of error. */
4042 if (FAILED(rc))
4043 {
4044 mData->mSession.mLockedMedia.Unlock();
4045 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4046 mData->mSession.mLockedMedia.Lock();
4047 }
4048 }
4049 }
4050
4051 return S_OK;
4052 }
4053 else if ( foundIt == oldAtts.end()
4054 || level > foundLevel /* prefer younger */
4055 )
4056 {
4057 foundIt = it;
4058 foundLevel = level;
4059 }
4060 }
4061 }
4062
4063 if (foundIt != oldAtts.end())
4064 {
4065 /* use the previously attached hard disk */
4066 medium = (*foundIt)->i_getMedium();
4067 mediumCaller.attach(medium);
4068 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4069 mediumLock.attach(medium);
4070 /* not implicit, doesn't require association with this VM */
4071 fIndirect = false;
4072 associate = false;
4073 /* go right to the MediumAttachment creation */
4074 break;
4075 }
4076 }
4077
4078 /* must give up the medium lock and medium tree lock as below we
4079 * go over snapshots, which needs a lock with higher lock order. */
4080 mediumLock.release();
4081 treeLock.release();
4082
4083 /* then, search through snapshots for the best diff in the given
4084 * hard disk's chain to base the new diff on */
4085
4086 ComObjPtr<Medium> base;
4087 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4088 while (snap)
4089 {
4090 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4091
4092 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4093
4094 MediumAttachment *pAttachFound = NULL;
4095 uint32_t foundLevel = 0;
4096
4097 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4098 {
4099 MediumAttachment *pAttach = *it;
4100 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4101 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4102 if (pMedium.isNull())
4103 continue;
4104
4105 uint32_t level = 0;
4106 if (pMedium->i_getBase(&level) == medium)
4107 {
4108 /* matched device, channel and bus (i.e. attached to the
4109 * same place) will win and immediately stop the search;
4110 * otherwise the attachment that has the youngest
4111 * descendant of medium will be used
4112 */
4113 if ( pAttach->i_getDevice() == aDevice
4114 && pAttach->i_getPort() == aControllerPort
4115 && pAttach->i_getControllerName() == aName
4116 )
4117 {
4118 pAttachFound = pAttach;
4119 break;
4120 }
4121 else if ( !pAttachFound
4122 || level > foundLevel /* prefer younger */
4123 )
4124 {
4125 pAttachFound = pAttach;
4126 foundLevel = level;
4127 }
4128 }
4129 }
4130
4131 if (pAttachFound)
4132 {
4133 base = pAttachFound->i_getMedium();
4134 break;
4135 }
4136
4137 snap = snap->i_getParent();
4138 }
4139
4140 /* re-lock medium tree and the medium, as we need it below */
4141 treeLock.acquire();
4142 mediumLock.acquire();
4143
4144 /* found a suitable diff, use it as a base */
4145 if (!base.isNull())
4146 {
4147 medium = base;
4148 mediumCaller.attach(medium);
4149 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4150 mediumLock.attach(medium);
4151 }
4152 }
4153
4154 Utf8Str strFullSnapshotFolder;
4155 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4156
4157 ComObjPtr<Medium> diff;
4158 diff.createObject();
4159 // store this diff in the same registry as the parent
4160 Guid uuidRegistryParent;
4161 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4162 {
4163 // parent image has no registry: this can happen if we're attaching a new immutable
4164 // image that has not yet been attached (medium then points to the base and we're
4165 // creating the diff image for the immutable, and the parent is not yet registered);
4166 // put the parent in the machine registry then
4167 mediumLock.release();
4168 treeLock.release();
4169 alock.release();
4170 i_addMediumToRegistry(medium);
4171 alock.acquire();
4172 treeLock.acquire();
4173 mediumLock.acquire();
4174 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4175 }
4176 rc = diff->init(mParent,
4177 medium->i_getPreferredDiffFormat(),
4178 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4179 uuidRegistryParent,
4180 DeviceType_HardDisk);
4181 if (FAILED(rc)) return rc;
4182
4183 /* Apply the normal locking logic to the entire chain. */
4184 MediumLockList *pMediumLockList(new MediumLockList());
4185 mediumLock.release();
4186 treeLock.release();
4187 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4188 diff /* pToLockWrite */,
4189 false /* fMediumLockWriteAll */,
4190 medium,
4191 *pMediumLockList);
4192 treeLock.acquire();
4193 mediumLock.acquire();
4194 if (SUCCEEDED(rc))
4195 {
4196 mediumLock.release();
4197 treeLock.release();
4198 rc = pMediumLockList->Lock();
4199 treeLock.acquire();
4200 mediumLock.acquire();
4201 if (FAILED(rc))
4202 setError(rc,
4203 tr("Could not lock medium when creating diff '%s'"),
4204 diff->i_getLocationFull().c_str());
4205 else
4206 {
4207 /* will release the lock before the potentially lengthy
4208 * operation, so protect with the special state */
4209 MachineState_T oldState = mData->mMachineState;
4210 i_setMachineState(MachineState_SettingUp);
4211
4212 mediumLock.release();
4213 treeLock.release();
4214 alock.release();
4215
4216 rc = medium->i_createDiffStorage(diff,
4217 medium->i_getPreferredDiffVariant(),
4218 pMediumLockList,
4219 NULL /* aProgress */,
4220 true /* aWait */);
4221
4222 alock.acquire();
4223 treeLock.acquire();
4224 mediumLock.acquire();
4225
4226 i_setMachineState(oldState);
4227 }
4228 }
4229
4230 /* Unlock the media and free the associated memory. */
4231 delete pMediumLockList;
4232
4233 if (FAILED(rc)) return rc;
4234
4235 /* use the created diff for the actual attachment */
4236 medium = diff;
4237 mediumCaller.attach(medium);
4238 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4239 mediumLock.attach(medium);
4240 }
4241 while (0);
4242
4243 ComObjPtr<MediumAttachment> attachment;
4244 attachment.createObject();
4245 rc = attachment->init(this,
4246 medium,
4247 aName,
4248 aControllerPort,
4249 aDevice,
4250 aType,
4251 fIndirect,
4252 false /* fPassthrough */,
4253 false /* fTempEject */,
4254 false /* fNonRotational */,
4255 false /* fDiscard */,
4256 fHotplug /* fHotPluggable */,
4257 Utf8Str::Empty);
4258 if (FAILED(rc)) return rc;
4259
4260 if (associate && !medium.isNull())
4261 {
4262 // as the last step, associate the medium to the VM
4263 rc = medium->i_addBackReference(mData->mUuid);
4264 // here we can fail because of Deleting, or being in process of creating a Diff
4265 if (FAILED(rc)) return rc;
4266
4267 mediumLock.release();
4268 treeLock.release();
4269 alock.release();
4270 i_addMediumToRegistry(medium);
4271 alock.acquire();
4272 treeLock.acquire();
4273 mediumLock.acquire();
4274 }
4275
4276 /* success: finally remember the attachment */
4277 i_setModified(IsModified_Storage);
4278 mMediaData.backup();
4279 mMediaData->mAttachments.push_back(attachment);
4280
4281 mediumLock.release();
4282 treeLock.release();
4283 alock.release();
4284
4285 if (fHotplug || fSilent)
4286 {
4287 if (!medium.isNull())
4288 {
4289 MediumLockList *pMediumLockList(new MediumLockList());
4290
4291 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4292 medium /* pToLockWrite */,
4293 false /* fMediumLockWriteAll */,
4294 NULL,
4295 *pMediumLockList);
4296 alock.acquire();
4297 if (FAILED(rc))
4298 delete pMediumLockList;
4299 else
4300 {
4301 mData->mSession.mLockedMedia.Unlock();
4302 alock.release();
4303 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4304 mData->mSession.mLockedMedia.Lock();
4305 alock.acquire();
4306 }
4307 alock.release();
4308 }
4309
4310 if (SUCCEEDED(rc))
4311 {
4312 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4313 /* Remove lock list in case of error. */
4314 if (FAILED(rc))
4315 {
4316 mData->mSession.mLockedMedia.Unlock();
4317 mData->mSession.mLockedMedia.Remove(attachment);
4318 mData->mSession.mLockedMedia.Lock();
4319 }
4320 }
4321 }
4322
4323 /* Save modified registries, but skip this machine as it's the caller's
4324 * job to save its settings like all other settings changes. */
4325 mParent->i_unmarkRegistryModified(i_getId());
4326 mParent->i_saveModifiedRegistries();
4327
4328 return rc;
4329}
4330
4331HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4332 LONG aDevice)
4333{
4334 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4335 aName.c_str(), aControllerPort, aDevice));
4336
4337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4338
4339 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4340 if (FAILED(rc)) return rc;
4341
4342 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4343
4344 /* Check for an existing controller. */
4345 ComObjPtr<StorageController> ctl;
4346 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4347 if (FAILED(rc)) return rc;
4348
4349 StorageControllerType_T ctrlType;
4350 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4351 if (FAILED(rc))
4352 return setError(E_FAIL,
4353 tr("Could not get type of controller '%s'"),
4354 aName.c_str());
4355
4356 bool fSilent = false;
4357 Utf8Str strReconfig;
4358
4359 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4360 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4361 if ( mData->mMachineState == MachineState_Paused
4362 && strReconfig == "1")
4363 fSilent = true;
4364
4365 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4366 bool fHotplug = false;
4367 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4368 fHotplug = true;
4369
4370 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4371 return setError(VBOX_E_INVALID_VM_STATE,
4372 tr("Controller '%s' does not support hotplugging"),
4373 aName.c_str());
4374
4375 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4376 Bstr(aName).raw(),
4377 aControllerPort,
4378 aDevice);
4379 if (!pAttach)
4380 return setError(VBOX_E_OBJECT_NOT_FOUND,
4381 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4382 aDevice, aControllerPort, aName.c_str());
4383
4384 if (fHotplug && !pAttach->i_getHotPluggable())
4385 return setError(VBOX_E_NOT_SUPPORTED,
4386 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4387 aDevice, aControllerPort, aName.c_str());
4388
4389 /*
4390 * The VM has to detach the device before we delete any implicit diffs.
4391 * If this fails we can roll back without loosing data.
4392 */
4393 if (fHotplug || fSilent)
4394 {
4395 alock.release();
4396 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4397 alock.acquire();
4398 }
4399 if (FAILED(rc)) return rc;
4400
4401 /* If we are here everything went well and we can delete the implicit now. */
4402 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4403
4404 alock.release();
4405
4406 /* Save modified registries, but skip this machine as it's the caller's
4407 * job to save its settings like all other settings changes. */
4408 mParent->i_unmarkRegistryModified(i_getId());
4409 mParent->i_saveModifiedRegistries();
4410
4411 return rc;
4412}
4413
4414HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4415 LONG aDevice, BOOL aPassthrough)
4416{
4417 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4418 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4419
4420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4421
4422 HRESULT rc = i_checkStateDependency(MutableStateDep);
4423 if (FAILED(rc)) return rc;
4424
4425 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4426
4427 if (Global::IsOnlineOrTransient(mData->mMachineState))
4428 return setError(VBOX_E_INVALID_VM_STATE,
4429 tr("Invalid machine state: %s"),
4430 Global::stringifyMachineState(mData->mMachineState));
4431
4432 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4433 Bstr(aName).raw(),
4434 aControllerPort,
4435 aDevice);
4436 if (!pAttach)
4437 return setError(VBOX_E_OBJECT_NOT_FOUND,
4438 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4439 aDevice, aControllerPort, aName.c_str());
4440
4441
4442 i_setModified(IsModified_Storage);
4443 mMediaData.backup();
4444
4445 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4446
4447 if (pAttach->i_getType() != DeviceType_DVD)
4448 return setError(E_INVALIDARG,
4449 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4450 aDevice, aControllerPort, aName.c_str());
4451 pAttach->i_updatePassthrough(!!aPassthrough);
4452
4453 return S_OK;
4454}
4455
4456HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4457 LONG aDevice, BOOL aTemporaryEject)
4458{
4459
4460 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4461 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4462
4463 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4464
4465 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4466 if (FAILED(rc)) return rc;
4467
4468 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4469 Bstr(aName).raw(),
4470 aControllerPort,
4471 aDevice);
4472 if (!pAttach)
4473 return setError(VBOX_E_OBJECT_NOT_FOUND,
4474 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4475 aDevice, aControllerPort, aName.c_str());
4476
4477
4478 i_setModified(IsModified_Storage);
4479 mMediaData.backup();
4480
4481 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4482
4483 if (pAttach->i_getType() != DeviceType_DVD)
4484 return setError(E_INVALIDARG,
4485 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4486 aDevice, aControllerPort, aName.c_str());
4487 pAttach->i_updateTempEject(!!aTemporaryEject);
4488
4489 return S_OK;
4490}
4491
4492HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4493 LONG aDevice, BOOL aNonRotational)
4494{
4495
4496 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4497 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4498
4499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4500
4501 HRESULT rc = i_checkStateDependency(MutableStateDep);
4502 if (FAILED(rc)) return rc;
4503
4504 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4505
4506 if (Global::IsOnlineOrTransient(mData->mMachineState))
4507 return setError(VBOX_E_INVALID_VM_STATE,
4508 tr("Invalid machine state: %s"),
4509 Global::stringifyMachineState(mData->mMachineState));
4510
4511 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4512 Bstr(aName).raw(),
4513 aControllerPort,
4514 aDevice);
4515 if (!pAttach)
4516 return setError(VBOX_E_OBJECT_NOT_FOUND,
4517 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4518 aDevice, aControllerPort, aName.c_str());
4519
4520
4521 i_setModified(IsModified_Storage);
4522 mMediaData.backup();
4523
4524 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4525
4526 if (pAttach->i_getType() != DeviceType_HardDisk)
4527 return setError(E_INVALIDARG,
4528 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"),
4529 aDevice, aControllerPort, aName.c_str());
4530 pAttach->i_updateNonRotational(!!aNonRotational);
4531
4532 return S_OK;
4533}
4534
4535HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4536 LONG aDevice, BOOL aDiscard)
4537{
4538
4539 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4540 aName.c_str(), aControllerPort, aDevice, aDiscard));
4541
4542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4543
4544 HRESULT rc = i_checkStateDependency(MutableStateDep);
4545 if (FAILED(rc)) return rc;
4546
4547 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4548
4549 if (Global::IsOnlineOrTransient(mData->mMachineState))
4550 return setError(VBOX_E_INVALID_VM_STATE,
4551 tr("Invalid machine state: %s"),
4552 Global::stringifyMachineState(mData->mMachineState));
4553
4554 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4555 Bstr(aName).raw(),
4556 aControllerPort,
4557 aDevice);
4558 if (!pAttach)
4559 return setError(VBOX_E_OBJECT_NOT_FOUND,
4560 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4561 aDevice, aControllerPort, aName.c_str());
4562
4563
4564 i_setModified(IsModified_Storage);
4565 mMediaData.backup();
4566
4567 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4568
4569 if (pAttach->i_getType() != DeviceType_HardDisk)
4570 return setError(E_INVALIDARG,
4571 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"),
4572 aDevice, aControllerPort, aName.c_str());
4573 pAttach->i_updateDiscard(!!aDiscard);
4574
4575 return S_OK;
4576}
4577
4578HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4579 LONG aDevice, BOOL aHotPluggable)
4580{
4581 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4582 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4583
4584 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4585
4586 HRESULT rc = i_checkStateDependency(MutableStateDep);
4587 if (FAILED(rc)) return rc;
4588
4589 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4590
4591 if (Global::IsOnlineOrTransient(mData->mMachineState))
4592 return setError(VBOX_E_INVALID_VM_STATE,
4593 tr("Invalid machine state: %s"),
4594 Global::stringifyMachineState(mData->mMachineState));
4595
4596 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4597 Bstr(aName).raw(),
4598 aControllerPort,
4599 aDevice);
4600 if (!pAttach)
4601 return setError(VBOX_E_OBJECT_NOT_FOUND,
4602 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4603 aDevice, aControllerPort, aName.c_str());
4604
4605 /* Check for an existing controller. */
4606 ComObjPtr<StorageController> ctl;
4607 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4608 if (FAILED(rc)) return rc;
4609
4610 StorageControllerType_T ctrlType;
4611 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4612 if (FAILED(rc))
4613 return setError(E_FAIL,
4614 tr("Could not get type of controller '%s'"),
4615 aName.c_str());
4616
4617 if (!i_isControllerHotplugCapable(ctrlType))
4618 return setError(VBOX_E_NOT_SUPPORTED,
4619 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4620 aName.c_str());
4621
4622 i_setModified(IsModified_Storage);
4623 mMediaData.backup();
4624
4625 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4626
4627 if (pAttach->i_getType() == DeviceType_Floppy)
4628 return setError(E_INVALIDARG,
4629 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"),
4630 aDevice, aControllerPort, aName.c_str());
4631 pAttach->i_updateHotPluggable(!!aHotPluggable);
4632
4633 return S_OK;
4634}
4635
4636HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4637 LONG aDevice)
4638{
4639 int rc = S_OK;
4640 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4641 aName.c_str(), aControllerPort, aDevice));
4642
4643 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4644
4645 return rc;
4646}
4647
4648HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4649 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4650{
4651 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4652 aName.c_str(), aControllerPort, aDevice));
4653
4654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4655
4656 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4657 if (FAILED(rc)) return rc;
4658
4659 if (Global::IsOnlineOrTransient(mData->mMachineState))
4660 return setError(VBOX_E_INVALID_VM_STATE,
4661 tr("Invalid machine state: %s"),
4662 Global::stringifyMachineState(mData->mMachineState));
4663
4664 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4665 Bstr(aName).raw(),
4666 aControllerPort,
4667 aDevice);
4668 if (!pAttach)
4669 return setError(VBOX_E_OBJECT_NOT_FOUND,
4670 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4671 aDevice, aControllerPort, aName.c_str());
4672
4673
4674 i_setModified(IsModified_Storage);
4675 mMediaData.backup();
4676
4677 IBandwidthGroup *iB = aBandwidthGroup;
4678 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4679 if (aBandwidthGroup && group.isNull())
4680 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4681
4682 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4683
4684 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4685 if (strBandwidthGroupOld.isNotEmpty())
4686 {
4687 /* Get the bandwidth group object and release it - this must not fail. */
4688 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4689 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4690 Assert(SUCCEEDED(rc));
4691
4692 pBandwidthGroupOld->i_release();
4693 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4694 }
4695
4696 if (!group.isNull())
4697 {
4698 group->i_reference();
4699 pAttach->i_updateBandwidthGroup(group->i_getName());
4700 }
4701
4702 return S_OK;
4703}
4704
4705HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4706 LONG aControllerPort,
4707 LONG aDevice,
4708 DeviceType_T aType)
4709{
4710 HRESULT rc = S_OK;
4711
4712 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4713 aName.c_str(), aControllerPort, aDevice, aType));
4714
4715 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4716
4717 return rc;
4718}
4719
4720
4721HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4722 LONG aControllerPort,
4723 LONG aDevice,
4724 BOOL aForce)
4725{
4726 int rc = S_OK;
4727 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4728 aName.c_str(), aControllerPort, aForce));
4729
4730 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4731
4732 return rc;
4733}
4734
4735HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4736 LONG aControllerPort,
4737 LONG aDevice,
4738 const ComPtr<IMedium> &aMedium,
4739 BOOL aForce)
4740{
4741 int rc = S_OK;
4742 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4743 aName.c_str(), aControllerPort, aDevice, aForce));
4744
4745 // request the host lock first, since might be calling Host methods for getting host drives;
4746 // next, protect the media tree all the while we're in here, as well as our member variables
4747 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4748 this->lockHandle(),
4749 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4750
4751 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4752 Bstr(aName).raw(),
4753 aControllerPort,
4754 aDevice);
4755 if (pAttach.isNull())
4756 return setError(VBOX_E_OBJECT_NOT_FOUND,
4757 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4758 aDevice, aControllerPort, aName.c_str());
4759
4760 /* Remember previously mounted medium. The medium before taking the
4761 * backup is not necessarily the same thing. */
4762 ComObjPtr<Medium> oldmedium;
4763 oldmedium = pAttach->i_getMedium();
4764
4765 IMedium *iM = aMedium;
4766 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4767 if (aMedium && pMedium.isNull())
4768 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4769
4770 AutoCaller mediumCaller(pMedium);
4771 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4772
4773 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4774 if (pMedium)
4775 {
4776 DeviceType_T mediumType = pAttach->i_getType();
4777 switch (mediumType)
4778 {
4779 case DeviceType_DVD:
4780 case DeviceType_Floppy:
4781 break;
4782
4783 default:
4784 return setError(VBOX_E_INVALID_OBJECT_STATE,
4785 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4786 aControllerPort,
4787 aDevice,
4788 aName.c_str());
4789 }
4790 }
4791
4792 i_setModified(IsModified_Storage);
4793 mMediaData.backup();
4794
4795 {
4796 // The backup operation makes the pAttach reference point to the
4797 // old settings. Re-get the correct reference.
4798 pAttach = i_findAttachment(mMediaData->mAttachments,
4799 Bstr(aName).raw(),
4800 aControllerPort,
4801 aDevice);
4802 if (!oldmedium.isNull())
4803 oldmedium->i_removeBackReference(mData->mUuid);
4804 if (!pMedium.isNull())
4805 {
4806 pMedium->i_addBackReference(mData->mUuid);
4807
4808 mediumLock.release();
4809 multiLock.release();
4810 i_addMediumToRegistry(pMedium);
4811 multiLock.acquire();
4812 mediumLock.acquire();
4813 }
4814
4815 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4816 pAttach->i_updateMedium(pMedium);
4817 }
4818
4819 i_setModified(IsModified_Storage);
4820
4821 mediumLock.release();
4822 multiLock.release();
4823 rc = i_onMediumChange(pAttach, aForce);
4824 multiLock.acquire();
4825 mediumLock.acquire();
4826
4827 /* On error roll back this change only. */
4828 if (FAILED(rc))
4829 {
4830 if (!pMedium.isNull())
4831 pMedium->i_removeBackReference(mData->mUuid);
4832 pAttach = i_findAttachment(mMediaData->mAttachments,
4833 Bstr(aName).raw(),
4834 aControllerPort,
4835 aDevice);
4836 /* If the attachment is gone in the meantime, bail out. */
4837 if (pAttach.isNull())
4838 return rc;
4839 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4840 if (!oldmedium.isNull())
4841 oldmedium->i_addBackReference(mData->mUuid);
4842 pAttach->i_updateMedium(oldmedium);
4843 }
4844
4845 mediumLock.release();
4846 multiLock.release();
4847
4848 /* Save modified registries, but skip this machine as it's the caller's
4849 * job to save its settings like all other settings changes. */
4850 mParent->i_unmarkRegistryModified(i_getId());
4851 mParent->i_saveModifiedRegistries();
4852
4853 return rc;
4854}
4855HRESULT Machine::getMedium(const com::Utf8Str &aName,
4856 LONG aControllerPort,
4857 LONG aDevice,
4858 ComPtr<IMedium> &aMedium)
4859{
4860 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4861 aName.c_str(), aControllerPort, aDevice));
4862
4863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4864
4865 aMedium = NULL;
4866
4867 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4868 Bstr(aName).raw(),
4869 aControllerPort,
4870 aDevice);
4871 if (pAttach.isNull())
4872 return setError(VBOX_E_OBJECT_NOT_FOUND,
4873 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4874 aDevice, aControllerPort, aName.c_str());
4875
4876 aMedium = pAttach->i_getMedium();
4877
4878 return S_OK;
4879}
4880
4881HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4882{
4883
4884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4885
4886 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4887
4888 return S_OK;
4889}
4890
4891HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4892{
4893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4894
4895 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4896
4897 return S_OK;
4898}
4899
4900HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4901{
4902 /* Do not assert if slot is out of range, just return the advertised
4903 status. testdriver/vbox.py triggers this in logVmInfo. */
4904 if (aSlot >= mNetworkAdapters.size())
4905 return setError(E_INVALIDARG,
4906 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4907 aSlot, mNetworkAdapters.size());
4908
4909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4910
4911 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4912
4913 return S_OK;
4914}
4915
4916HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4917{
4918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4919
4920 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4921 size_t i = 0;
4922 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4923 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4924 ++it, ++i)
4925 aKeys[i] = it->first;
4926
4927 return S_OK;
4928}
4929
4930 /**
4931 * @note Locks this object for reading.
4932 */
4933HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4934 com::Utf8Str &aValue)
4935{
4936 /* start with nothing found */
4937 aValue = "";
4938
4939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4940
4941 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4942 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4943 // found:
4944 aValue = it->second; // source is a Utf8Str
4945
4946 /* return the result to caller (may be empty) */
4947 return S_OK;
4948}
4949
4950 /**
4951 * @note Locks mParent for writing + this object for writing.
4952 */
4953HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4954{
4955 Utf8Str strOldValue; // empty
4956
4957 // locking note: we only hold the read lock briefly to look up the old value,
4958 // then release it and call the onExtraCanChange callbacks. There is a small
4959 // chance of a race insofar as the callback might be called twice if two callers
4960 // change the same key at the same time, but that's a much better solution
4961 // than the deadlock we had here before. The actual changing of the extradata
4962 // is then performed under the write lock and race-free.
4963
4964 // look up the old value first; if nothing has changed then we need not do anything
4965 {
4966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4967
4968 // For snapshots don't even think about allowing changes, extradata
4969 // is global for a machine, so there is nothing snapshot specific.
4970 if (i_isSnapshotMachine())
4971 return setError(VBOX_E_INVALID_VM_STATE,
4972 tr("Cannot set extradata for a snapshot"));
4973
4974 // check if the right IMachine instance is used
4975 if (mData->mRegistered && !i_isSessionMachine())
4976 return setError(VBOX_E_INVALID_VM_STATE,
4977 tr("Cannot set extradata for an immutable machine"));
4978
4979 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4980 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4981 strOldValue = it->second;
4982 }
4983
4984 bool fChanged;
4985 if ((fChanged = (strOldValue != aValue)))
4986 {
4987 // ask for permission from all listeners outside the locks;
4988 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4989 // lock to copy the list of callbacks to invoke
4990 Bstr error;
4991 Bstr bstrValue(aValue);
4992
4993 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4994 {
4995 const char *sep = error.isEmpty() ? "" : ": ";
4996 CBSTR err = error.raw();
4997 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
4998 return setError(E_ACCESSDENIED,
4999 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5000 aKey.c_str(),
5001 aValue.c_str(),
5002 sep,
5003 err);
5004 }
5005
5006 // data is changing and change not vetoed: then write it out under the lock
5007 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5008
5009 if (aValue.isEmpty())
5010 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5011 else
5012 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5013 // creates a new key if needed
5014
5015 bool fNeedsGlobalSaveSettings = false;
5016 // This saving of settings is tricky: there is no "old state" for the
5017 // extradata items at all (unlike all other settings), so the old/new
5018 // settings comparison would give a wrong result!
5019 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5020
5021 if (fNeedsGlobalSaveSettings)
5022 {
5023 // save the global settings; for that we should hold only the VirtualBox lock
5024 alock.release();
5025 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5026 mParent->i_saveSettings();
5027 }
5028 }
5029
5030 // fire notification outside the lock
5031 if (fChanged)
5032 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5033
5034 return S_OK;
5035}
5036
5037HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5038{
5039 aProgress = NULL;
5040 NOREF(aSettingsFilePath);
5041 ReturnComNotImplemented();
5042}
5043
5044HRESULT Machine::saveSettings()
5045{
5046 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5047
5048 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5049 if (FAILED(rc)) return rc;
5050
5051 /* the settings file path may never be null */
5052 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5053
5054 /* save all VM data excluding snapshots */
5055 bool fNeedsGlobalSaveSettings = false;
5056 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5057 mlock.release();
5058
5059 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5060 {
5061 // save the global settings; for that we should hold only the VirtualBox lock
5062 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5063 rc = mParent->i_saveSettings();
5064 }
5065
5066 return rc;
5067}
5068
5069
5070HRESULT Machine::discardSettings()
5071{
5072 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5073
5074 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5075 if (FAILED(rc)) return rc;
5076
5077 /*
5078 * during this rollback, the session will be notified if data has
5079 * been actually changed
5080 */
5081 i_rollback(true /* aNotify */);
5082
5083 return S_OK;
5084}
5085
5086/** @note Locks objects! */
5087HRESULT Machine::unregister(AutoCaller &autoCaller,
5088 CleanupMode_T aCleanupMode,
5089 std::vector<ComPtr<IMedium> > &aMedia)
5090{
5091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5092
5093 Guid id(i_getId());
5094
5095 if (mData->mSession.mState != SessionState_Unlocked)
5096 return setError(VBOX_E_INVALID_OBJECT_STATE,
5097 tr("Cannot unregister the machine '%s' while it is locked"),
5098 mUserData->s.strName.c_str());
5099
5100 // wait for state dependents to drop to zero
5101 i_ensureNoStateDependencies();
5102
5103 if (!mData->mAccessible)
5104 {
5105 // inaccessible maschines can only be unregistered; uninitialize ourselves
5106 // here because currently there may be no unregistered that are inaccessible
5107 // (this state combination is not supported). Note releasing the caller and
5108 // leaving the lock before calling uninit()
5109 alock.release();
5110 autoCaller.release();
5111
5112 uninit();
5113
5114 mParent->i_unregisterMachine(this, id);
5115 // calls VirtualBox::i_saveSettings()
5116
5117 return S_OK;
5118 }
5119
5120 HRESULT rc = S_OK;
5121
5122 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5123 // discard saved state
5124 if (mData->mMachineState == MachineState_Saved)
5125 {
5126 // add the saved state file to the list of files the caller should delete
5127 Assert(!mSSData->strStateFilePath.isEmpty());
5128 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5129
5130 mSSData->strStateFilePath.setNull();
5131
5132 // unconditionally set the machine state to powered off, we now
5133 // know no session has locked the machine
5134 mData->mMachineState = MachineState_PoweredOff;
5135 }
5136
5137 size_t cSnapshots = 0;
5138 if (mData->mFirstSnapshot)
5139 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5140 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5141 // fail now before we start detaching media
5142 return setError(VBOX_E_INVALID_OBJECT_STATE,
5143 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5144 mUserData->s.strName.c_str(), cSnapshots);
5145
5146 // This list collects the medium objects from all medium attachments
5147 // which we will detach from the machine and its snapshots, in a specific
5148 // order which allows for closing all media without getting "media in use"
5149 // errors, simply by going through the list from the front to the back:
5150 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5151 // and must be closed before the parent media from the snapshots, or closing the parents
5152 // will fail because they still have children);
5153 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5154 // the root ("first") snapshot of the machine.
5155 MediaList llMedia;
5156
5157 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5158 && mMediaData->mAttachments.size()
5159 )
5160 {
5161 // we have media attachments: detach them all and add the Medium objects to our list
5162 if (aCleanupMode != CleanupMode_UnregisterOnly)
5163 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5164 else
5165 return setError(VBOX_E_INVALID_OBJECT_STATE,
5166 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5167 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5168 }
5169
5170 if (cSnapshots)
5171 {
5172 // add the media from the medium attachments of the snapshots to llMedia
5173 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5174 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5175 // into the children first
5176
5177 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5178 MachineState_T oldState = mData->mMachineState;
5179 mData->mMachineState = MachineState_DeletingSnapshot;
5180
5181 // make a copy of the first snapshot so the refcount does not drop to 0
5182 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5183 // because of the AutoCaller voodoo)
5184 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5185
5186 // GO!
5187 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5188
5189 mData->mMachineState = oldState;
5190 }
5191
5192 if (FAILED(rc))
5193 {
5194 i_rollbackMedia();
5195 return rc;
5196 }
5197
5198 // commit all the media changes made above
5199 i_commitMedia();
5200
5201 mData->mRegistered = false;
5202
5203 // machine lock no longer needed
5204 alock.release();
5205
5206 // return media to caller
5207 size_t i = 0;
5208 aMedia.resize(llMedia.size());
5209 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5210 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5211
5212 mParent->i_unregisterMachine(this, id);
5213 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5214
5215 return S_OK;
5216}
5217
5218/**
5219 * Task record for deleting a machine config.
5220 */
5221struct Machine::DeleteConfigTask
5222 : public Machine::Task
5223{
5224 DeleteConfigTask(Machine *m,
5225 Progress *p,
5226 const Utf8Str &t,
5227 const RTCList<ComPtr<IMedium> > &llMediums,
5228 const StringsList &llFilesToDelete)
5229 : Task(m, p, t),
5230 m_llMediums(llMediums),
5231 m_llFilesToDelete(llFilesToDelete)
5232 {}
5233
5234 void handler()
5235 {
5236 m_pMachine->i_deleteConfigHandler(*this);
5237 }
5238
5239 RTCList<ComPtr<IMedium> > m_llMediums;
5240 StringsList m_llFilesToDelete;
5241};
5242
5243/**
5244 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5245 * SessionMachine::taskHandler().
5246 *
5247 * @note Locks this object for writing.
5248 *
5249 * @param task
5250 * @return
5251 */
5252void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5253{
5254 LogFlowThisFuncEnter();
5255
5256 AutoCaller autoCaller(this);
5257 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5258 if (FAILED(autoCaller.rc()))
5259 {
5260 /* we might have been uninitialized because the session was accidentally
5261 * closed by the client, so don't assert */
5262 HRESULT rc = setError(E_FAIL,
5263 tr("The session has been accidentally closed"));
5264 task.m_pProgress->i_notifyComplete(rc);
5265 LogFlowThisFuncLeave();
5266 return;
5267 }
5268
5269 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5270
5271 HRESULT rc = S_OK;
5272
5273 try
5274 {
5275 ULONG uLogHistoryCount = 3;
5276 ComPtr<ISystemProperties> systemProperties;
5277 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5278 if (FAILED(rc)) throw rc;
5279
5280 if (!systemProperties.isNull())
5281 {
5282 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5283 if (FAILED(rc)) throw rc;
5284 }
5285
5286 MachineState_T oldState = mData->mMachineState;
5287 i_setMachineState(MachineState_SettingUp);
5288 alock.release();
5289 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5290 {
5291 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5292 {
5293 AutoCaller mac(pMedium);
5294 if (FAILED(mac.rc())) throw mac.rc();
5295 Utf8Str strLocation = pMedium->i_getLocationFull();
5296 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5297 if (FAILED(rc)) throw rc;
5298 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5299 }
5300 if (pMedium->i_isMediumFormatFile())
5301 {
5302 ComPtr<IProgress> pProgress2;
5303 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5304 if (FAILED(rc)) throw rc;
5305 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5306 if (FAILED(rc)) throw rc;
5307 /* Check the result of the asynchronous process. */
5308 LONG iRc;
5309 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5310 if (FAILED(rc)) throw rc;
5311 /* If the thread of the progress object has an error, then
5312 * retrieve the error info from there, or it'll be lost. */
5313 if (FAILED(iRc))
5314 throw setError(ProgressErrorInfo(pProgress2));
5315 }
5316
5317 /* Close the medium, deliberately without checking the return
5318 * code, and without leaving any trace in the error info, as
5319 * a failure here is a very minor issue, which shouldn't happen
5320 * as above we even managed to delete the medium. */
5321 {
5322 ErrorInfoKeeper eik;
5323 pMedium->Close();
5324 }
5325 }
5326 i_setMachineState(oldState);
5327 alock.acquire();
5328
5329 // delete the files pushed on the task list by Machine::Delete()
5330 // (this includes saved states of the machine and snapshots and
5331 // medium storage files from the IMedium list passed in, and the
5332 // machine XML file)
5333 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5334 while (it != task.m_llFilesToDelete.end())
5335 {
5336 const Utf8Str &strFile = *it;
5337 LogFunc(("Deleting file %s\n", strFile.c_str()));
5338 int vrc = RTFileDelete(strFile.c_str());
5339 if (RT_FAILURE(vrc))
5340 throw setError(VBOX_E_IPRT_ERROR,
5341 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5342
5343 ++it;
5344 if (it == task.m_llFilesToDelete.end())
5345 {
5346 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5347 if (FAILED(rc)) throw rc;
5348 break;
5349 }
5350
5351 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5352 if (FAILED(rc)) throw rc;
5353 }
5354
5355 /* delete the settings only when the file actually exists */
5356 if (mData->pMachineConfigFile->fileExists())
5357 {
5358 /* Delete any backup or uncommitted XML files. Ignore failures.
5359 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5360 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5361 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5362 RTFileDelete(otherXml.c_str());
5363 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5364 RTFileDelete(otherXml.c_str());
5365
5366 /* delete the Logs folder, nothing important should be left
5367 * there (we don't check for errors because the user might have
5368 * some private files there that we don't want to delete) */
5369 Utf8Str logFolder;
5370 getLogFolder(logFolder);
5371 Assert(logFolder.length());
5372 if (RTDirExists(logFolder.c_str()))
5373 {
5374 /* Delete all VBox.log[.N] files from the Logs folder
5375 * (this must be in sync with the rotation logic in
5376 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5377 * files that may have been created by the GUI. */
5378 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5379 logFolder.c_str(), RTPATH_DELIMITER);
5380 RTFileDelete(log.c_str());
5381 log = Utf8StrFmt("%s%cVBox.png",
5382 logFolder.c_str(), RTPATH_DELIMITER);
5383 RTFileDelete(log.c_str());
5384 for (int i = uLogHistoryCount; i > 0; i--)
5385 {
5386 log = Utf8StrFmt("%s%cVBox.log.%d",
5387 logFolder.c_str(), RTPATH_DELIMITER, i);
5388 RTFileDelete(log.c_str());
5389 log = Utf8StrFmt("%s%cVBox.png.%d",
5390 logFolder.c_str(), RTPATH_DELIMITER, i);
5391 RTFileDelete(log.c_str());
5392 }
5393#if defined(RT_OS_WINDOWS)
5394 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5395 RTFileDelete(log.c_str());
5396 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5397 RTFileDelete(log.c_str());
5398#endif
5399
5400 RTDirRemove(logFolder.c_str());
5401 }
5402
5403 /* delete the Snapshots folder, nothing important should be left
5404 * there (we don't check for errors because the user might have
5405 * some private files there that we don't want to delete) */
5406 Utf8Str strFullSnapshotFolder;
5407 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5408 Assert(!strFullSnapshotFolder.isEmpty());
5409 if (RTDirExists(strFullSnapshotFolder.c_str()))
5410 RTDirRemove(strFullSnapshotFolder.c_str());
5411
5412 // delete the directory that contains the settings file, but only
5413 // if it matches the VM name
5414 Utf8Str settingsDir;
5415 if (i_isInOwnDir(&settingsDir))
5416 RTDirRemove(settingsDir.c_str());
5417 }
5418
5419 alock.release();
5420
5421 mParent->i_saveModifiedRegistries();
5422 }
5423 catch (HRESULT aRC) { rc = aRC; }
5424
5425 task.m_pProgress->i_notifyComplete(rc);
5426
5427 LogFlowThisFuncLeave();
5428}
5429
5430HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5431{
5432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5433
5434 HRESULT rc = i_checkStateDependency(MutableStateDep);
5435 if (FAILED(rc)) return rc;
5436
5437 if (mData->mRegistered)
5438 return setError(VBOX_E_INVALID_VM_STATE,
5439 tr("Cannot delete settings of a registered machine"));
5440
5441 // collect files to delete
5442 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5443 if (mData->pMachineConfigFile->fileExists())
5444 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5445
5446 RTCList<ComPtr<IMedium> > llMediums;
5447 for (size_t i = 0; i < aMedia.size(); ++i)
5448 {
5449 IMedium *pIMedium(aMedia[i]);
5450 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5451 if (pMedium.isNull())
5452 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5453 SafeArray<BSTR> ids;
5454 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5455 if (FAILED(rc)) return rc;
5456 /* At this point the medium should not have any back references
5457 * anymore. If it has it is attached to another VM and *must* not
5458 * deleted. */
5459 if (ids.size() < 1)
5460 llMediums.append(pMedium);
5461 }
5462
5463 ComObjPtr<Progress> pProgress;
5464 pProgress.createObject();
5465 rc = pProgress->init(i_getVirtualBox(),
5466 static_cast<IMachine*>(this) /* aInitiator */,
5467 Bstr(tr("Deleting files")).raw(),
5468 true /* fCancellable */,
5469 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5470 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5471 if (FAILED(rc))
5472 return rc;
5473
5474 /* create and start the task on a separate thread (note that it will not
5475 * start working until we release alock) */
5476 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5477 rc = pTask->createThread();
5478 if (FAILED(rc))
5479 return rc;
5480
5481 pProgress.queryInterfaceTo(aProgress.asOutParam());
5482
5483 LogFlowFuncLeave();
5484
5485 return S_OK;
5486}
5487
5488HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5489{
5490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5491
5492 ComObjPtr<Snapshot> pSnapshot;
5493 HRESULT rc;
5494
5495 if (aNameOrId.isEmpty())
5496 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5497 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5498 else
5499 {
5500 Guid uuid(aNameOrId);
5501 if (uuid.isValid())
5502 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5503 else
5504 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5505 }
5506 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5507
5508 return rc;
5509}
5510
5511HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5512{
5513 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5514
5515 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5516 if (FAILED(rc)) return rc;
5517
5518 ComObjPtr<SharedFolder> sharedFolder;
5519 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5520 if (SUCCEEDED(rc))
5521 return setError(VBOX_E_OBJECT_IN_USE,
5522 tr("Shared folder named '%s' already exists"),
5523 aName.c_str());
5524
5525 sharedFolder.createObject();
5526 rc = sharedFolder->init(i_getMachine(),
5527 aName,
5528 aHostPath,
5529 !!aWritable,
5530 !!aAutomount,
5531 true /* fFailOnError */);
5532 if (FAILED(rc)) return rc;
5533
5534 i_setModified(IsModified_SharedFolders);
5535 mHWData.backup();
5536 mHWData->mSharedFolders.push_back(sharedFolder);
5537
5538 /* inform the direct session if any */
5539 alock.release();
5540 i_onSharedFolderChange();
5541
5542 return S_OK;
5543}
5544
5545HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5546{
5547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5548
5549 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5550 if (FAILED(rc)) return rc;
5551
5552 ComObjPtr<SharedFolder> sharedFolder;
5553 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5554 if (FAILED(rc)) return rc;
5555
5556 i_setModified(IsModified_SharedFolders);
5557 mHWData.backup();
5558 mHWData->mSharedFolders.remove(sharedFolder);
5559
5560 /* inform the direct session if any */
5561 alock.release();
5562 i_onSharedFolderChange();
5563
5564 return S_OK;
5565}
5566
5567HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5568{
5569 /* start with No */
5570 *aCanShow = FALSE;
5571
5572 ComPtr<IInternalSessionControl> directControl;
5573 {
5574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5575
5576 if (mData->mSession.mState != SessionState_Locked)
5577 return setError(VBOX_E_INVALID_VM_STATE,
5578 tr("Machine is not locked for session (session state: %s)"),
5579 Global::stringifySessionState(mData->mSession.mState));
5580
5581 if (mData->mSession.mLockType == LockType_VM)
5582 directControl = mData->mSession.mDirectControl;
5583 }
5584
5585 /* ignore calls made after #OnSessionEnd() is called */
5586 if (!directControl)
5587 return S_OK;
5588
5589 LONG64 dummy;
5590 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5591}
5592
5593HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5594{
5595 ComPtr<IInternalSessionControl> directControl;
5596 {
5597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5598
5599 if (mData->mSession.mState != SessionState_Locked)
5600 return setError(E_FAIL,
5601 tr("Machine is not locked for session (session state: %s)"),
5602 Global::stringifySessionState(mData->mSession.mState));
5603
5604 if (mData->mSession.mLockType == LockType_VM)
5605 directControl = mData->mSession.mDirectControl;
5606 }
5607
5608 /* ignore calls made after #OnSessionEnd() is called */
5609 if (!directControl)
5610 return S_OK;
5611
5612 BOOL dummy;
5613 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5614}
5615
5616#ifdef VBOX_WITH_GUEST_PROPS
5617/**
5618 * Look up a guest property in VBoxSVC's internal structures.
5619 */
5620HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5621 com::Utf8Str &aValue,
5622 LONG64 *aTimestamp,
5623 com::Utf8Str &aFlags) const
5624{
5625 using namespace guestProp;
5626
5627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5628 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5629
5630 if (it != mHWData->mGuestProperties.end())
5631 {
5632 char szFlags[MAX_FLAGS_LEN + 1];
5633 aValue = it->second.strValue;
5634 *aTimestamp = it->second.mTimestamp;
5635 writeFlags(it->second.mFlags, szFlags);
5636 aFlags = Utf8Str(szFlags);
5637 }
5638
5639 return S_OK;
5640}
5641
5642/**
5643 * Query the VM that a guest property belongs to for the property.
5644 * @returns E_ACCESSDENIED if the VM process is not available or not
5645 * currently handling queries and the lookup should then be done in
5646 * VBoxSVC.
5647 */
5648HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5649 com::Utf8Str &aValue,
5650 LONG64 *aTimestamp,
5651 com::Utf8Str &aFlags) const
5652{
5653 HRESULT rc = S_OK;
5654 BSTR bValue = NULL;
5655 BSTR bFlags = NULL;
5656
5657 ComPtr<IInternalSessionControl> directControl;
5658 {
5659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5660 if (mData->mSession.mLockType == LockType_VM)
5661 directControl = mData->mSession.mDirectControl;
5662 }
5663
5664 /* ignore calls made after #OnSessionEnd() is called */
5665 if (!directControl)
5666 rc = E_ACCESSDENIED;
5667 else
5668 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5669 0 /* accessMode */,
5670 &bValue, aTimestamp, &bFlags);
5671
5672 aValue = bValue;
5673 aFlags = bFlags;
5674
5675 return rc;
5676}
5677#endif // VBOX_WITH_GUEST_PROPS
5678
5679HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5680 com::Utf8Str &aValue,
5681 LONG64 *aTimestamp,
5682 com::Utf8Str &aFlags)
5683{
5684#ifndef VBOX_WITH_GUEST_PROPS
5685 ReturnComNotImplemented();
5686#else // VBOX_WITH_GUEST_PROPS
5687
5688 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5689
5690 if (rc == E_ACCESSDENIED)
5691 /* The VM is not running or the service is not (yet) accessible */
5692 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5693 return rc;
5694#endif // VBOX_WITH_GUEST_PROPS
5695}
5696
5697HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5698{
5699 LONG64 dummyTimestamp;
5700 com::Utf8Str dummyFlags;
5701 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5702 return rc;
5703
5704}
5705HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5706{
5707 com::Utf8Str dummyFlags;
5708 com::Utf8Str dummyValue;
5709 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5710 return rc;
5711}
5712
5713#ifdef VBOX_WITH_GUEST_PROPS
5714/**
5715 * Set a guest property in VBoxSVC's internal structures.
5716 */
5717HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5718 const com::Utf8Str &aFlags, bool fDelete)
5719{
5720 using namespace guestProp;
5721
5722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5723 HRESULT rc = S_OK;
5724
5725 rc = i_checkStateDependency(MutableOrSavedStateDep);
5726 if (FAILED(rc)) return rc;
5727
5728 try
5729 {
5730 uint32_t fFlags = NILFLAG;
5731 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5732 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5733
5734 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5735 if (it == mHWData->mGuestProperties.end())
5736 {
5737 if (!fDelete)
5738 {
5739 i_setModified(IsModified_MachineData);
5740 mHWData.backupEx();
5741
5742 RTTIMESPEC time;
5743 HWData::GuestProperty prop;
5744 prop.strValue = Bstr(aValue).raw();
5745 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5746 prop.mFlags = fFlags;
5747 mHWData->mGuestProperties[aName] = prop;
5748 }
5749 }
5750 else
5751 {
5752 if (it->second.mFlags & (RDONLYHOST))
5753 {
5754 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5755 }
5756 else
5757 {
5758 i_setModified(IsModified_MachineData);
5759 mHWData.backupEx();
5760
5761 /* The backupEx() operation invalidates our iterator,
5762 * so get a new one. */
5763 it = mHWData->mGuestProperties.find(aName);
5764 Assert(it != mHWData->mGuestProperties.end());
5765
5766 if (!fDelete)
5767 {
5768 RTTIMESPEC time;
5769 it->second.strValue = aValue;
5770 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5771 it->second.mFlags = fFlags;
5772 }
5773 else
5774 mHWData->mGuestProperties.erase(it);
5775 }
5776 }
5777
5778 if (SUCCEEDED(rc))
5779 {
5780 alock.release();
5781
5782 mParent->i_onGuestPropertyChange(mData->mUuid,
5783 Bstr(aName).raw(),
5784 Bstr(aValue).raw(),
5785 Bstr(aFlags).raw());
5786 }
5787 }
5788 catch (std::bad_alloc &)
5789 {
5790 rc = E_OUTOFMEMORY;
5791 }
5792
5793 return rc;
5794}
5795
5796/**
5797 * Set a property on the VM that that property belongs to.
5798 * @returns E_ACCESSDENIED if the VM process is not available or not
5799 * currently handling queries and the setting should then be done in
5800 * VBoxSVC.
5801 */
5802HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5803 const com::Utf8Str &aFlags, bool fDelete)
5804{
5805 HRESULT rc;
5806
5807 try
5808 {
5809 ComPtr<IInternalSessionControl> directControl;
5810 {
5811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5812 if (mData->mSession.mLockType == LockType_VM)
5813 directControl = mData->mSession.mDirectControl;
5814 }
5815
5816 BSTR dummy = NULL; /* will not be changed (setter) */
5817 LONG64 dummy64;
5818 if (!directControl)
5819 rc = E_ACCESSDENIED;
5820 else
5821 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5822 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5823 fDelete? 2: 1 /* accessMode */,
5824 &dummy, &dummy64, &dummy);
5825 }
5826 catch (std::bad_alloc &)
5827 {
5828 rc = E_OUTOFMEMORY;
5829 }
5830
5831 return rc;
5832}
5833#endif // VBOX_WITH_GUEST_PROPS
5834
5835HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5836 const com::Utf8Str &aFlags)
5837{
5838#ifndef VBOX_WITH_GUEST_PROPS
5839 ReturnComNotImplemented();
5840#else // VBOX_WITH_GUEST_PROPS
5841 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5842 if (rc == E_ACCESSDENIED)
5843 /* The VM is not running or the service is not (yet) accessible */
5844 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5845 return rc;
5846#endif // VBOX_WITH_GUEST_PROPS
5847}
5848
5849HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5850{
5851 return setGuestProperty(aProperty, aValue, "");
5852}
5853
5854HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5855{
5856#ifndef VBOX_WITH_GUEST_PROPS
5857 ReturnComNotImplemented();
5858#else // VBOX_WITH_GUEST_PROPS
5859 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5860 if (rc == E_ACCESSDENIED)
5861 /* The VM is not running or the service is not (yet) accessible */
5862 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5863 return rc;
5864#endif // VBOX_WITH_GUEST_PROPS
5865}
5866
5867#ifdef VBOX_WITH_GUEST_PROPS
5868/**
5869 * Enumerate the guest properties in VBoxSVC's internal structures.
5870 */
5871HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5872 std::vector<com::Utf8Str> &aNames,
5873 std::vector<com::Utf8Str> &aValues,
5874 std::vector<LONG64> &aTimestamps,
5875 std::vector<com::Utf8Str> &aFlags)
5876{
5877 using namespace guestProp;
5878
5879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5880 Utf8Str strPatterns(aPatterns);
5881
5882 HWData::GuestPropertyMap propMap;
5883
5884 /*
5885 * Look for matching patterns and build up a list.
5886 */
5887 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5888 while (it != mHWData->mGuestProperties.end())
5889 {
5890 if ( strPatterns.isEmpty()
5891 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5892 RTSTR_MAX,
5893 it->first.c_str(),
5894 RTSTR_MAX,
5895 NULL)
5896 )
5897 propMap.insert(*it);
5898 ++it;
5899 }
5900
5901 alock.release();
5902
5903 /*
5904 * And build up the arrays for returning the property information.
5905 */
5906 size_t cEntries = propMap.size();
5907
5908 aNames.resize(cEntries);
5909 aValues.resize(cEntries);
5910 aTimestamps.resize(cEntries);
5911 aFlags.resize(cEntries);
5912
5913 char szFlags[MAX_FLAGS_LEN + 1];
5914 size_t i= 0;
5915 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5916 {
5917 aNames[i] = it->first;
5918 aValues[i] = it->second.strValue;
5919 aTimestamps[i] = it->second.mTimestamp;
5920 writeFlags(it->second.mFlags, szFlags);
5921 aFlags[i] = Utf8Str(szFlags);
5922 }
5923
5924 return S_OK;
5925}
5926
5927/**
5928 * Enumerate the properties managed by a VM.
5929 * @returns E_ACCESSDENIED if the VM process is not available or not
5930 * currently handling queries and the setting should then be done in
5931 * VBoxSVC.
5932 */
5933HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5934 std::vector<com::Utf8Str> &aNames,
5935 std::vector<com::Utf8Str> &aValues,
5936 std::vector<LONG64> &aTimestamps,
5937 std::vector<com::Utf8Str> &aFlags)
5938{
5939 HRESULT rc;
5940 ComPtr<IInternalSessionControl> directControl;
5941 {
5942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5943 if (mData->mSession.mLockType == LockType_VM)
5944 directControl = mData->mSession.mDirectControl;
5945 }
5946
5947 com::SafeArray<BSTR> bNames;
5948 com::SafeArray<BSTR> bValues;
5949 com::SafeArray<LONG64> bTimestamps;
5950 com::SafeArray<BSTR> bFlags;
5951
5952 if (!directControl)
5953 rc = E_ACCESSDENIED;
5954 else
5955 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5956 ComSafeArrayAsOutParam(bNames),
5957 ComSafeArrayAsOutParam(bValues),
5958 ComSafeArrayAsOutParam(bTimestamps),
5959 ComSafeArrayAsOutParam(bFlags));
5960 size_t i;
5961 aNames.resize(bNames.size());
5962 for (i = 0; i < bNames.size(); ++i)
5963 aNames[i] = Utf8Str(bNames[i]);
5964 aValues.resize(bValues.size());
5965 for (i = 0; i < bValues.size(); ++i)
5966 aValues[i] = Utf8Str(bValues[i]);
5967 aTimestamps.resize(bTimestamps.size());
5968 for (i = 0; i < bTimestamps.size(); ++i)
5969 aTimestamps[i] = bTimestamps[i];
5970 aFlags.resize(bFlags.size());
5971 for (i = 0; i < bFlags.size(); ++i)
5972 aFlags[i] = Utf8Str(bFlags[i]);
5973
5974 return rc;
5975}
5976#endif // VBOX_WITH_GUEST_PROPS
5977HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5978 std::vector<com::Utf8Str> &aNames,
5979 std::vector<com::Utf8Str> &aValues,
5980 std::vector<LONG64> &aTimestamps,
5981 std::vector<com::Utf8Str> &aFlags)
5982{
5983#ifndef VBOX_WITH_GUEST_PROPS
5984 ReturnComNotImplemented();
5985#else // VBOX_WITH_GUEST_PROPS
5986
5987 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5988
5989 if (rc == E_ACCESSDENIED)
5990 /* The VM is not running or the service is not (yet) accessible */
5991 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5992 return rc;
5993#endif // VBOX_WITH_GUEST_PROPS
5994}
5995
5996HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5997 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5998{
5999 MediaData::AttachmentList atts;
6000
6001 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6002 if (FAILED(rc)) return rc;
6003
6004 size_t i = 0;
6005 aMediumAttachments.resize(atts.size());
6006 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
6007 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6008
6009 return S_OK;
6010}
6011
6012HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6013 LONG aControllerPort,
6014 LONG aDevice,
6015 ComPtr<IMediumAttachment> &aAttachment)
6016{
6017 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6018 aName.c_str(), aControllerPort, aDevice));
6019
6020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6021
6022 aAttachment = NULL;
6023
6024 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
6025 Bstr(aName).raw(),
6026 aControllerPort,
6027 aDevice);
6028 if (pAttach.isNull())
6029 return setError(VBOX_E_OBJECT_NOT_FOUND,
6030 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6031 aDevice, aControllerPort, aName.c_str());
6032
6033 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6034
6035 return S_OK;
6036}
6037
6038
6039HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6040 StorageBus_T aConnectionType,
6041 ComPtr<IStorageController> &aController)
6042{
6043 if ( (aConnectionType <= StorageBus_Null)
6044 || (aConnectionType > StorageBus_USB))
6045 return setError(E_INVALIDARG,
6046 tr("Invalid connection type: %d"),
6047 aConnectionType);
6048
6049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6050
6051 HRESULT rc = i_checkStateDependency(MutableStateDep);
6052 if (FAILED(rc)) return rc;
6053
6054 /* try to find one with the name first. */
6055 ComObjPtr<StorageController> ctrl;
6056
6057 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6058 if (SUCCEEDED(rc))
6059 return setError(VBOX_E_OBJECT_IN_USE,
6060 tr("Storage controller named '%s' already exists"),
6061 aName.c_str());
6062
6063 ctrl.createObject();
6064
6065 /* get a new instance number for the storage controller */
6066 ULONG ulInstance = 0;
6067 bool fBootable = true;
6068 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6069 it != mStorageControllers->end();
6070 ++it)
6071 {
6072 if ((*it)->i_getStorageBus() == aConnectionType)
6073 {
6074 ULONG ulCurInst = (*it)->i_getInstance();
6075
6076 if (ulCurInst >= ulInstance)
6077 ulInstance = ulCurInst + 1;
6078
6079 /* Only one controller of each type can be marked as bootable. */
6080 if ((*it)->i_getBootable())
6081 fBootable = false;
6082 }
6083 }
6084
6085 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6086 if (FAILED(rc)) return rc;
6087
6088 i_setModified(IsModified_Storage);
6089 mStorageControllers.backup();
6090 mStorageControllers->push_back(ctrl);
6091
6092 ctrl.queryInterfaceTo(aController.asOutParam());
6093
6094 /* inform the direct session if any */
6095 alock.release();
6096 i_onStorageControllerChange();
6097
6098 return S_OK;
6099}
6100
6101HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6102 ComPtr<IStorageController> &aStorageController)
6103{
6104 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6105
6106 ComObjPtr<StorageController> ctrl;
6107
6108 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6109 if (SUCCEEDED(rc))
6110 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6111
6112 return rc;
6113}
6114
6115HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6116 ULONG aInstance,
6117 ComPtr<IStorageController> &aStorageController)
6118{
6119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6120
6121 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6122 it != mStorageControllers->end();
6123 ++it)
6124 {
6125 if ( (*it)->i_getStorageBus() == aConnectionType
6126 && (*it)->i_getInstance() == aInstance)
6127 {
6128 (*it).queryInterfaceTo(aStorageController.asOutParam());
6129 return S_OK;
6130 }
6131 }
6132
6133 return setError(VBOX_E_OBJECT_NOT_FOUND,
6134 tr("Could not find a storage controller with instance number '%lu'"),
6135 aInstance);
6136}
6137
6138HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6139{
6140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6141
6142 HRESULT rc = i_checkStateDependency(MutableStateDep);
6143 if (FAILED(rc)) return rc;
6144
6145 ComObjPtr<StorageController> ctrl;
6146
6147 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6148 if (SUCCEEDED(rc))
6149 {
6150 /* Ensure that only one controller of each type is marked as bootable. */
6151 if (aBootable == TRUE)
6152 {
6153 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6154 it != mStorageControllers->end();
6155 ++it)
6156 {
6157 ComObjPtr<StorageController> aCtrl = (*it);
6158
6159 if ( (aCtrl->i_getName() != aName)
6160 && aCtrl->i_getBootable() == TRUE
6161 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6162 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6163 {
6164 aCtrl->i_setBootable(FALSE);
6165 break;
6166 }
6167 }
6168 }
6169
6170 if (SUCCEEDED(rc))
6171 {
6172 ctrl->i_setBootable(aBootable);
6173 i_setModified(IsModified_Storage);
6174 }
6175 }
6176
6177 if (SUCCEEDED(rc))
6178 {
6179 /* inform the direct session if any */
6180 alock.release();
6181 i_onStorageControllerChange();
6182 }
6183
6184 return rc;
6185}
6186
6187HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6188{
6189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6190
6191 HRESULT rc = i_checkStateDependency(MutableStateDep);
6192 if (FAILED(rc)) return rc;
6193
6194 ComObjPtr<StorageController> ctrl;
6195 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6196 if (FAILED(rc)) return rc;
6197
6198 {
6199 /* find all attached devices to the appropriate storage controller and detach them all */
6200 // make a temporary list because detachDevice invalidates iterators into
6201 // mMediaData->mAttachments
6202 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6203
6204 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6205 it != llAttachments2.end();
6206 ++it)
6207 {
6208 MediumAttachment *pAttachTemp = *it;
6209
6210 AutoCaller localAutoCaller(pAttachTemp);
6211 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6212
6213 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6214
6215 if (pAttachTemp->i_getControllerName() == aName)
6216 {
6217 rc = i_detachDevice(pAttachTemp, alock, NULL);
6218 if (FAILED(rc)) return rc;
6219 }
6220 }
6221 }
6222
6223 /* We can remove it now. */
6224 i_setModified(IsModified_Storage);
6225 mStorageControllers.backup();
6226
6227 ctrl->i_unshare();
6228
6229 mStorageControllers->remove(ctrl);
6230
6231 /* inform the direct session if any */
6232 alock.release();
6233 i_onStorageControllerChange();
6234
6235 return S_OK;
6236}
6237
6238HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6239 ComPtr<IUSBController> &aController)
6240{
6241 if ( (aType <= USBControllerType_Null)
6242 || (aType >= USBControllerType_Last))
6243 return setError(E_INVALIDARG,
6244 tr("Invalid USB controller type: %d"),
6245 aType);
6246
6247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6248
6249 HRESULT rc = i_checkStateDependency(MutableStateDep);
6250 if (FAILED(rc)) return rc;
6251
6252 /* try to find one with the same type first. */
6253 ComObjPtr<USBController> ctrl;
6254
6255 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6256 if (SUCCEEDED(rc))
6257 return setError(VBOX_E_OBJECT_IN_USE,
6258 tr("USB controller named '%s' already exists"),
6259 aName.c_str());
6260
6261 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6262 ULONG maxInstances;
6263 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6264 if (FAILED(rc))
6265 return rc;
6266
6267 ULONG cInstances = i_getUSBControllerCountByType(aType);
6268 if (cInstances >= maxInstances)
6269 return setError(E_INVALIDARG,
6270 tr("Too many USB controllers of this type"));
6271
6272 ctrl.createObject();
6273
6274 rc = ctrl->init(this, aName, aType);
6275 if (FAILED(rc)) return rc;
6276
6277 i_setModified(IsModified_USB);
6278 mUSBControllers.backup();
6279 mUSBControllers->push_back(ctrl);
6280
6281 ctrl.queryInterfaceTo(aController.asOutParam());
6282
6283 /* inform the direct session if any */
6284 alock.release();
6285 i_onUSBControllerChange();
6286
6287 return S_OK;
6288}
6289
6290HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6291{
6292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6293
6294 ComObjPtr<USBController> ctrl;
6295
6296 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6297 if (SUCCEEDED(rc))
6298 ctrl.queryInterfaceTo(aController.asOutParam());
6299
6300 return rc;
6301}
6302
6303HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6304 ULONG *aControllers)
6305{
6306 if ( (aType <= USBControllerType_Null)
6307 || (aType >= USBControllerType_Last))
6308 return setError(E_INVALIDARG,
6309 tr("Invalid USB controller type: %d"),
6310 aType);
6311
6312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6313
6314 ComObjPtr<USBController> ctrl;
6315
6316 *aControllers = i_getUSBControllerCountByType(aType);
6317
6318 return S_OK;
6319}
6320
6321HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6322{
6323
6324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6325
6326 HRESULT rc = i_checkStateDependency(MutableStateDep);
6327 if (FAILED(rc)) return rc;
6328
6329 ComObjPtr<USBController> ctrl;
6330 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6331 if (FAILED(rc)) return rc;
6332
6333 i_setModified(IsModified_USB);
6334 mUSBControllers.backup();
6335
6336 ctrl->i_unshare();
6337
6338 mUSBControllers->remove(ctrl);
6339
6340 /* inform the direct session if any */
6341 alock.release();
6342 i_onUSBControllerChange();
6343
6344 return S_OK;
6345}
6346
6347HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6348 ULONG *aOriginX,
6349 ULONG *aOriginY,
6350 ULONG *aWidth,
6351 ULONG *aHeight,
6352 BOOL *aEnabled)
6353{
6354 uint32_t u32OriginX= 0;
6355 uint32_t u32OriginY= 0;
6356 uint32_t u32Width = 0;
6357 uint32_t u32Height = 0;
6358 uint16_t u16Flags = 0;
6359
6360 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6361 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6362 if (RT_FAILURE(vrc))
6363 {
6364#ifdef RT_OS_WINDOWS
6365 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6366 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6367 * So just assign fEnable to TRUE again.
6368 * The right fix would be to change GUI API wrappers to make sure that parameters
6369 * are changed only if API succeeds.
6370 */
6371 *aEnabled = TRUE;
6372#endif
6373 return setError(VBOX_E_IPRT_ERROR,
6374 tr("Saved guest size is not available (%Rrc)"),
6375 vrc);
6376 }
6377
6378 *aOriginX = u32OriginX;
6379 *aOriginY = u32OriginY;
6380 *aWidth = u32Width;
6381 *aHeight = u32Height;
6382 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6383
6384 return S_OK;
6385}
6386
6387HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6388 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6389{
6390 if (aScreenId != 0)
6391 return E_NOTIMPL;
6392
6393 if ( aBitmapFormat != BitmapFormat_BGR0
6394 && aBitmapFormat != BitmapFormat_BGRA
6395 && aBitmapFormat != BitmapFormat_RGBA
6396 && aBitmapFormat != BitmapFormat_PNG)
6397 return setError(E_NOTIMPL,
6398 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6399
6400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6401
6402 uint8_t *pu8Data = NULL;
6403 uint32_t cbData = 0;
6404 uint32_t u32Width = 0;
6405 uint32_t u32Height = 0;
6406
6407 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6408
6409 if (RT_FAILURE(vrc))
6410 return setError(VBOX_E_IPRT_ERROR,
6411 tr("Saved thumbnail data is not available (%Rrc)"),
6412 vrc);
6413
6414 HRESULT hr = S_OK;
6415
6416 *aWidth = u32Width;
6417 *aHeight = u32Height;
6418
6419 if (cbData > 0)
6420 {
6421 /* Convert pixels to the format expected by the API caller. */
6422 if (aBitmapFormat == BitmapFormat_BGR0)
6423 {
6424 /* [0] B, [1] G, [2] R, [3] 0. */
6425 aData.resize(cbData);
6426 memcpy(&aData.front(), pu8Data, cbData);
6427 }
6428 else if (aBitmapFormat == BitmapFormat_BGRA)
6429 {
6430 /* [0] B, [1] G, [2] R, [3] A. */
6431 aData.resize(cbData);
6432 for (uint32_t i = 0; i < cbData; i += 4)
6433 {
6434 aData[i] = pu8Data[i];
6435 aData[i + 1] = pu8Data[i + 1];
6436 aData[i + 2] = pu8Data[i + 2];
6437 aData[i + 3] = 0xff;
6438 }
6439 }
6440 else if (aBitmapFormat == BitmapFormat_RGBA)
6441 {
6442 /* [0] R, [1] G, [2] B, [3] A. */
6443 aData.resize(cbData);
6444 for (uint32_t i = 0; i < cbData; i += 4)
6445 {
6446 aData[i] = pu8Data[i + 2];
6447 aData[i + 1] = pu8Data[i + 1];
6448 aData[i + 2] = pu8Data[i];
6449 aData[i + 3] = 0xff;
6450 }
6451 }
6452 else if (aBitmapFormat == BitmapFormat_PNG)
6453 {
6454 uint8_t *pu8PNG = NULL;
6455 uint32_t cbPNG = 0;
6456 uint32_t cxPNG = 0;
6457 uint32_t cyPNG = 0;
6458
6459 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6460
6461 if (RT_SUCCESS(vrc))
6462 {
6463 aData.resize(cbPNG);
6464 if (cbPNG)
6465 memcpy(&aData.front(), pu8PNG, cbPNG);
6466 }
6467 else
6468 hr = setError(VBOX_E_IPRT_ERROR,
6469 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6470 vrc);
6471
6472 RTMemFree(pu8PNG);
6473 }
6474 }
6475
6476 freeSavedDisplayScreenshot(pu8Data);
6477
6478 return hr;
6479}
6480
6481HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6482 ULONG *aWidth,
6483 ULONG *aHeight,
6484 std::vector<BitmapFormat_T> &aBitmapFormats)
6485{
6486 if (aScreenId != 0)
6487 return E_NOTIMPL;
6488
6489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6490
6491 uint8_t *pu8Data = NULL;
6492 uint32_t cbData = 0;
6493 uint32_t u32Width = 0;
6494 uint32_t u32Height = 0;
6495
6496 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6497
6498 if (RT_FAILURE(vrc))
6499 return setError(VBOX_E_IPRT_ERROR,
6500 tr("Saved screenshot data is not available (%Rrc)"),
6501 vrc);
6502
6503 *aWidth = u32Width;
6504 *aHeight = u32Height;
6505 aBitmapFormats.resize(1);
6506 aBitmapFormats[0] = BitmapFormat_PNG;
6507
6508 freeSavedDisplayScreenshot(pu8Data);
6509
6510 return S_OK;
6511}
6512
6513HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6514 BitmapFormat_T aBitmapFormat,
6515 ULONG *aWidth,
6516 ULONG *aHeight,
6517 std::vector<BYTE> &aData)
6518{
6519 if (aScreenId != 0)
6520 return E_NOTIMPL;
6521
6522 if (aBitmapFormat != BitmapFormat_PNG)
6523 return E_NOTIMPL;
6524
6525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6526
6527 uint8_t *pu8Data = NULL;
6528 uint32_t cbData = 0;
6529 uint32_t u32Width = 0;
6530 uint32_t u32Height = 0;
6531
6532 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6533
6534 if (RT_FAILURE(vrc))
6535 return setError(VBOX_E_IPRT_ERROR,
6536 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6537 vrc);
6538
6539 *aWidth = u32Width;
6540 *aHeight = u32Height;
6541
6542 aData.resize(cbData);
6543 if (cbData)
6544 memcpy(&aData.front(), pu8Data, cbData);
6545
6546 freeSavedDisplayScreenshot(pu8Data);
6547
6548 return S_OK;
6549}
6550
6551HRESULT Machine::hotPlugCPU(ULONG aCpu)
6552{
6553 HRESULT rc = S_OK;
6554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6555
6556 if (!mHWData->mCPUHotPlugEnabled)
6557 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6558
6559 if (aCpu >= mHWData->mCPUCount)
6560 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6561
6562 if (mHWData->mCPUAttached[aCpu])
6563 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6564
6565 alock.release();
6566 rc = i_onCPUChange(aCpu, false);
6567 alock.acquire();
6568 if (FAILED(rc)) return rc;
6569
6570 i_setModified(IsModified_MachineData);
6571 mHWData.backup();
6572 mHWData->mCPUAttached[aCpu] = true;
6573
6574 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6575 if (Global::IsOnline(mData->mMachineState))
6576 i_saveSettings(NULL);
6577
6578 return S_OK;
6579}
6580
6581HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6582{
6583 HRESULT rc = S_OK;
6584
6585 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6586
6587 if (!mHWData->mCPUHotPlugEnabled)
6588 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6589
6590 if (aCpu >= SchemaDefs::MaxCPUCount)
6591 return setError(E_INVALIDARG,
6592 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6593 SchemaDefs::MaxCPUCount);
6594
6595 if (!mHWData->mCPUAttached[aCpu])
6596 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6597
6598 /* CPU 0 can't be detached */
6599 if (aCpu == 0)
6600 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6601
6602 alock.release();
6603 rc = i_onCPUChange(aCpu, true);
6604 alock.acquire();
6605 if (FAILED(rc)) return rc;
6606
6607 i_setModified(IsModified_MachineData);
6608 mHWData.backup();
6609 mHWData->mCPUAttached[aCpu] = false;
6610
6611 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6612 if (Global::IsOnline(mData->mMachineState))
6613 i_saveSettings(NULL);
6614
6615 return S_OK;
6616}
6617
6618HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6619{
6620 *aAttached = false;
6621
6622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6623
6624 /* If hotplug is enabled the CPU is always enabled. */
6625 if (!mHWData->mCPUHotPlugEnabled)
6626 {
6627 if (aCpu < mHWData->mCPUCount)
6628 *aAttached = true;
6629 }
6630 else
6631 {
6632 if (aCpu < SchemaDefs::MaxCPUCount)
6633 *aAttached = mHWData->mCPUAttached[aCpu];
6634 }
6635
6636 return S_OK;
6637}
6638
6639HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6640{
6641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6642
6643 Utf8Str log = i_getLogFilename(aIdx);
6644 if (!RTFileExists(log.c_str()))
6645 log.setNull();
6646 aFilename = log;
6647
6648 return S_OK;
6649}
6650
6651HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6652{
6653 if (aSize < 0)
6654 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6655
6656 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6657
6658 HRESULT rc = S_OK;
6659 Utf8Str log = i_getLogFilename(aIdx);
6660
6661 /* do not unnecessarily hold the lock while doing something which does
6662 * not need the lock and potentially takes a long time. */
6663 alock.release();
6664
6665 /* Limit the chunk size to 32K for now, as that gives better performance
6666 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6667 * One byte expands to approx. 25 bytes of breathtaking XML. */
6668 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6669 aData.resize(cbData);
6670
6671 RTFILE LogFile;
6672 int vrc = RTFileOpen(&LogFile, log.c_str(),
6673 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6674 if (RT_SUCCESS(vrc))
6675 {
6676 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6677 if (RT_SUCCESS(vrc))
6678 aData.resize(cbData);
6679 else
6680 rc = setError(VBOX_E_IPRT_ERROR,
6681 tr("Could not read log file '%s' (%Rrc)"),
6682 log.c_str(), vrc);
6683 RTFileClose(LogFile);
6684 }
6685 else
6686 rc = setError(VBOX_E_IPRT_ERROR,
6687 tr("Could not open log file '%s' (%Rrc)"),
6688 log.c_str(), vrc);
6689
6690 if (FAILED(rc))
6691 aData.resize(0);
6692
6693 return rc;
6694}
6695
6696
6697/**
6698 * Currently this method doesn't attach device to the running VM,
6699 * just makes sure it's plugged on next VM start.
6700 */
6701HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6702{
6703 // lock scope
6704 {
6705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6706
6707 HRESULT rc = i_checkStateDependency(MutableStateDep);
6708 if (FAILED(rc)) return rc;
6709
6710 ChipsetType_T aChipset = ChipsetType_PIIX3;
6711 COMGETTER(ChipsetType)(&aChipset);
6712
6713 if (aChipset != ChipsetType_ICH9)
6714 {
6715 return setError(E_INVALIDARG,
6716 tr("Host PCI attachment only supported with ICH9 chipset"));
6717 }
6718
6719 // check if device with this host PCI address already attached
6720 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6721 it != mHWData->mPCIDeviceAssignments.end();
6722 ++it)
6723 {
6724 LONG iHostAddress = -1;
6725 ComPtr<PCIDeviceAttachment> pAttach;
6726 pAttach = *it;
6727 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6728 if (iHostAddress == aHostAddress)
6729 return setError(E_INVALIDARG,
6730 tr("Device with host PCI address already attached to this VM"));
6731 }
6732
6733 ComObjPtr<PCIDeviceAttachment> pda;
6734 char name[32];
6735
6736 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6737 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6738 Bstr bname(name);
6739 pda.createObject();
6740 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6741 i_setModified(IsModified_MachineData);
6742 mHWData.backup();
6743 mHWData->mPCIDeviceAssignments.push_back(pda);
6744 }
6745
6746 return S_OK;
6747}
6748
6749/**
6750 * Currently this method doesn't detach device from the running VM,
6751 * just makes sure it's not plugged on next VM start.
6752 */
6753HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6754{
6755 ComObjPtr<PCIDeviceAttachment> pAttach;
6756 bool fRemoved = false;
6757 HRESULT rc;
6758
6759 // lock scope
6760 {
6761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6762
6763 rc = i_checkStateDependency(MutableStateDep);
6764 if (FAILED(rc)) return rc;
6765
6766 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6767 it != mHWData->mPCIDeviceAssignments.end();
6768 ++it)
6769 {
6770 LONG iHostAddress = -1;
6771 pAttach = *it;
6772 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6773 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6774 {
6775 i_setModified(IsModified_MachineData);
6776 mHWData.backup();
6777 mHWData->mPCIDeviceAssignments.remove(pAttach);
6778 fRemoved = true;
6779 break;
6780 }
6781 }
6782 }
6783
6784
6785 /* Fire event outside of the lock */
6786 if (fRemoved)
6787 {
6788 Assert(!pAttach.isNull());
6789 ComPtr<IEventSource> es;
6790 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6791 Assert(SUCCEEDED(rc));
6792 Bstr mid;
6793 rc = this->COMGETTER(Id)(mid.asOutParam());
6794 Assert(SUCCEEDED(rc));
6795 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6796 }
6797
6798 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6799 tr("No host PCI device %08x attached"),
6800 aHostAddress
6801 );
6802}
6803
6804HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6805{
6806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6807
6808 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6809
6810 size_t i = 0;
6811 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6812 it != mHWData->mPCIDeviceAssignments.end();
6813 ++i, ++it)
6814 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6815
6816 return S_OK;
6817}
6818
6819HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6820{
6821 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6822
6823 return S_OK;
6824}
6825
6826HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6827{
6828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6829
6830 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6831
6832 return S_OK;
6833}
6834
6835HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6836{
6837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6838 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6839 if (SUCCEEDED(hrc))
6840 {
6841 hrc = mHWData.backupEx();
6842 if (SUCCEEDED(hrc))
6843 {
6844 i_setModified(IsModified_MachineData);
6845 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6846 }
6847 }
6848 return hrc;
6849}
6850
6851HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6852{
6853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6854 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6855 return S_OK;
6856}
6857
6858HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6859{
6860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6861 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6862 if (SUCCEEDED(hrc))
6863 {
6864 hrc = mHWData.backupEx();
6865 if (SUCCEEDED(hrc))
6866 {
6867 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6868 if (SUCCEEDED(hrc))
6869 i_setModified(IsModified_MachineData);
6870 }
6871 }
6872 return hrc;
6873}
6874
6875HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6876{
6877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6878
6879 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6880
6881 return S_OK;
6882}
6883
6884HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6885{
6886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6887 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6888 if (SUCCEEDED(hrc))
6889 {
6890 hrc = mHWData.backupEx();
6891 if (SUCCEEDED(hrc))
6892 {
6893 i_setModified(IsModified_MachineData);
6894 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6895 }
6896 }
6897 return hrc;
6898}
6899
6900HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6901{
6902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6903
6904 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6905
6906 return S_OK;
6907}
6908
6909HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6910{
6911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6912
6913 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6914 if ( SUCCEEDED(hrc)
6915 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6916 {
6917 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6918 int vrc;
6919
6920 if (aAutostartEnabled)
6921 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6922 else
6923 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6924
6925 if (RT_SUCCESS(vrc))
6926 {
6927 hrc = mHWData.backupEx();
6928 if (SUCCEEDED(hrc))
6929 {
6930 i_setModified(IsModified_MachineData);
6931 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6932 }
6933 }
6934 else if (vrc == VERR_NOT_SUPPORTED)
6935 hrc = setError(VBOX_E_NOT_SUPPORTED,
6936 tr("The VM autostart feature is not supported on this platform"));
6937 else if (vrc == VERR_PATH_NOT_FOUND)
6938 hrc = setError(E_FAIL,
6939 tr("The path to the autostart database is not set"));
6940 else
6941 hrc = setError(E_UNEXPECTED,
6942 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6943 aAutostartEnabled ? "Adding" : "Removing",
6944 mUserData->s.strName.c_str(), vrc);
6945 }
6946 return hrc;
6947}
6948
6949HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6950{
6951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6952
6953 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6954
6955 return S_OK;
6956}
6957
6958HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6959{
6960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6961 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6962 if (SUCCEEDED(hrc))
6963 {
6964 hrc = mHWData.backupEx();
6965 if (SUCCEEDED(hrc))
6966 {
6967 i_setModified(IsModified_MachineData);
6968 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6969 }
6970 }
6971 return hrc;
6972}
6973
6974HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6975{
6976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6977
6978 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6979
6980 return S_OK;
6981}
6982
6983HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6984{
6985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6986 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6987 if ( SUCCEEDED(hrc)
6988 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6989 {
6990 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6991 int vrc;
6992
6993 if (aAutostopType != AutostopType_Disabled)
6994 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6995 else
6996 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6997
6998 if (RT_SUCCESS(vrc))
6999 {
7000 hrc = mHWData.backupEx();
7001 if (SUCCEEDED(hrc))
7002 {
7003 i_setModified(IsModified_MachineData);
7004 mHWData->mAutostart.enmAutostopType = aAutostopType;
7005 }
7006 }
7007 else if (vrc == VERR_NOT_SUPPORTED)
7008 hrc = setError(VBOX_E_NOT_SUPPORTED,
7009 tr("The VM autostop feature is not supported on this platform"));
7010 else if (vrc == VERR_PATH_NOT_FOUND)
7011 hrc = setError(E_FAIL,
7012 tr("The path to the autostart database is not set"));
7013 else
7014 hrc = setError(E_UNEXPECTED,
7015 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7016 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7017 mUserData->s.strName.c_str(), vrc);
7018 }
7019 return hrc;
7020}
7021
7022HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7023{
7024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7025
7026 aDefaultFrontend = mHWData->mDefaultFrontend;
7027
7028 return S_OK;
7029}
7030
7031HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7032{
7033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7034 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7035 if (SUCCEEDED(hrc))
7036 {
7037 hrc = mHWData.backupEx();
7038 if (SUCCEEDED(hrc))
7039 {
7040 i_setModified(IsModified_MachineData);
7041 mHWData->mDefaultFrontend = aDefaultFrontend;
7042 }
7043 }
7044 return hrc;
7045}
7046
7047HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7048{
7049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7050 size_t cbIcon = mUserData->s.ovIcon.size();
7051 aIcon.resize(cbIcon);
7052 if (cbIcon)
7053 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7054 return S_OK;
7055}
7056
7057HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7058{
7059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7060 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7061 if (SUCCEEDED(hrc))
7062 {
7063 i_setModified(IsModified_MachineData);
7064 mUserData.backup();
7065 size_t cbIcon = aIcon.size();
7066 mUserData->s.ovIcon.resize(cbIcon);
7067 if (cbIcon)
7068 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7069 }
7070 return hrc;
7071}
7072
7073HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7074{
7075#ifdef VBOX_WITH_USB
7076 *aUSBProxyAvailable = true;
7077#else
7078 *aUSBProxyAvailable = false;
7079#endif
7080 return S_OK;
7081}
7082
7083HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7084{
7085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7086
7087 aVMProcessPriority = mUserData->s.strVMPriority;
7088
7089 return S_OK;
7090}
7091
7092HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7093{
7094 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7095 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7096 if (SUCCEEDED(hrc))
7097 {
7098 /** @todo r=klaus: currently this is marked as not implemented, as
7099 * the code for setting the priority of the process is not there
7100 * (neither when starting the VM nor at runtime). */
7101 ReturnComNotImplemented();
7102 hrc = mUserData.backupEx();
7103 if (SUCCEEDED(hrc))
7104 {
7105 i_setModified(IsModified_MachineData);
7106 mUserData->s.strVMPriority = aVMProcessPriority;
7107 }
7108 }
7109 return hrc;
7110}
7111
7112HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7113 ComPtr<IProgress> &aProgress)
7114{
7115 ComObjPtr<Progress> pP;
7116 Progress *ppP = pP;
7117 IProgress *iP = static_cast<IProgress *>(ppP);
7118 IProgress **pProgress = &iP;
7119
7120 IMachine *pTarget = aTarget;
7121
7122 /* Convert the options. */
7123 RTCList<CloneOptions_T> optList;
7124 if (aOptions.size())
7125 for (size_t i = 0; i < aOptions.size(); ++i)
7126 optList.append(aOptions[i]);
7127
7128 if (optList.contains(CloneOptions_Link))
7129 {
7130 if (!i_isSnapshotMachine())
7131 return setError(E_INVALIDARG,
7132 tr("Linked clone can only be created from a snapshot"));
7133 if (aMode != CloneMode_MachineState)
7134 return setError(E_INVALIDARG,
7135 tr("Linked clone can only be created for a single machine state"));
7136 }
7137 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7138
7139 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7140
7141 HRESULT rc = pWorker->start(pProgress);
7142
7143 pP = static_cast<Progress *>(*pProgress);
7144 pP.queryInterfaceTo(aProgress.asOutParam());
7145
7146 return rc;
7147
7148}
7149
7150HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7151{
7152 NOREF(aProgress);
7153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7154
7155 // This check should always fail.
7156 HRESULT rc = i_checkStateDependency(MutableStateDep);
7157 if (FAILED(rc)) return rc;
7158
7159 AssertFailedReturn(E_NOTIMPL);
7160}
7161
7162HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7163{
7164 NOREF(aSavedStateFile);
7165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7166
7167 // This check should always fail.
7168 HRESULT rc = i_checkStateDependency(MutableStateDep);
7169 if (FAILED(rc)) return rc;
7170
7171 AssertFailedReturn(E_NOTIMPL);
7172}
7173
7174HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7175{
7176 NOREF(aFRemoveFile);
7177 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7178
7179 // This check should always fail.
7180 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7181 if (FAILED(rc)) return rc;
7182
7183 AssertFailedReturn(E_NOTIMPL);
7184}
7185
7186// public methods for internal purposes
7187/////////////////////////////////////////////////////////////////////////////
7188
7189/**
7190 * Adds the given IsModified_* flag to the dirty flags of the machine.
7191 * This must be called either during i_loadSettings or under the machine write lock.
7192 * @param fl
7193 */
7194void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7195{
7196 mData->flModifications |= fl;
7197 if (fAllowStateModification && i_isStateModificationAllowed())
7198 mData->mCurrentStateModified = true;
7199}
7200
7201/**
7202 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7203 * care of the write locking.
7204 *
7205 * @param fModifications The flag to add.
7206 */
7207void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7208{
7209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7210 i_setModified(fModification, fAllowStateModification);
7211}
7212
7213/**
7214 * Saves the registry entry of this machine to the given configuration node.
7215 *
7216 * @param aEntryNode Node to save the registry entry to.
7217 *
7218 * @note locks this object for reading.
7219 */
7220HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7221{
7222 AutoLimitedCaller autoCaller(this);
7223 AssertComRCReturnRC(autoCaller.rc());
7224
7225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7226
7227 data.uuid = mData->mUuid;
7228 data.strSettingsFile = mData->m_strConfigFile;
7229
7230 return S_OK;
7231}
7232
7233/**
7234 * Calculates the absolute path of the given path taking the directory of the
7235 * machine settings file as the current directory.
7236 *
7237 * @param aPath Path to calculate the absolute path for.
7238 * @param aResult Where to put the result (used only on success, can be the
7239 * same Utf8Str instance as passed in @a aPath).
7240 * @return IPRT result.
7241 *
7242 * @note Locks this object for reading.
7243 */
7244int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7245{
7246 AutoCaller autoCaller(this);
7247 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7248
7249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7250
7251 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7252
7253 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7254
7255 strSettingsDir.stripFilename();
7256 char folder[RTPATH_MAX];
7257 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7258 if (RT_SUCCESS(vrc))
7259 aResult = folder;
7260
7261 return vrc;
7262}
7263
7264/**
7265 * Copies strSource to strTarget, making it relative to the machine folder
7266 * if it is a subdirectory thereof, or simply copying it otherwise.
7267 *
7268 * @param strSource Path to evaluate and copy.
7269 * @param strTarget Buffer to receive target path.
7270 *
7271 * @note Locks this object for reading.
7272 */
7273void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7274 Utf8Str &strTarget)
7275{
7276 AutoCaller autoCaller(this);
7277 AssertComRCReturn(autoCaller.rc(), (void)0);
7278
7279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7280
7281 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7282 // use strTarget as a temporary buffer to hold the machine settings dir
7283 strTarget = mData->m_strConfigFileFull;
7284 strTarget.stripFilename();
7285 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7286 {
7287 // is relative: then append what's left
7288 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7289 // for empty paths (only possible for subdirs) use "." to avoid
7290 // triggering default settings for not present config attributes.
7291 if (strTarget.isEmpty())
7292 strTarget = ".";
7293 }
7294 else
7295 // is not relative: then overwrite
7296 strTarget = strSource;
7297}
7298
7299/**
7300 * Returns the full path to the machine's log folder in the
7301 * \a aLogFolder argument.
7302 */
7303void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7304{
7305 AutoCaller autoCaller(this);
7306 AssertComRCReturnVoid(autoCaller.rc());
7307
7308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7309
7310 char szTmp[RTPATH_MAX];
7311 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7312 if (RT_SUCCESS(vrc))
7313 {
7314 if (szTmp[0] && !mUserData.isNull())
7315 {
7316 char szTmp2[RTPATH_MAX];
7317 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7318 if (RT_SUCCESS(vrc))
7319 aLogFolder = BstrFmt("%s%c%s",
7320 szTmp2,
7321 RTPATH_DELIMITER,
7322 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7323 }
7324 else
7325 vrc = VERR_PATH_IS_RELATIVE;
7326 }
7327
7328 if (RT_FAILURE(vrc))
7329 {
7330 // fallback if VBOX_USER_LOGHOME is not set or invalid
7331 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7332 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7333 aLogFolder.append(RTPATH_DELIMITER);
7334 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7335 }
7336}
7337
7338/**
7339 * Returns the full path to the machine's log file for an given index.
7340 */
7341Utf8Str Machine::i_getLogFilename(ULONG idx)
7342{
7343 Utf8Str logFolder;
7344 getLogFolder(logFolder);
7345 Assert(logFolder.length());
7346
7347 Utf8Str log;
7348 if (idx == 0)
7349 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7350#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7351 else if (idx == 1)
7352 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7353 else
7354 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7355#else
7356 else
7357 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7358#endif
7359 return log;
7360}
7361
7362/**
7363 * Returns the full path to the machine's hardened log file.
7364 */
7365Utf8Str Machine::i_getHardeningLogFilename(void)
7366{
7367 Utf8Str strFilename;
7368 getLogFolder(strFilename);
7369 Assert(strFilename.length());
7370 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7371 return strFilename;
7372}
7373
7374
7375/**
7376 * Composes a unique saved state filename based on the current system time. The filename is
7377 * granular to the second so this will work so long as no more than one snapshot is taken on
7378 * a machine per second.
7379 *
7380 * Before version 4.1, we used this formula for saved state files:
7381 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7382 * which no longer works because saved state files can now be shared between the saved state of the
7383 * "saved" machine and an online snapshot, and the following would cause problems:
7384 * 1) save machine
7385 * 2) create online snapshot from that machine state --> reusing saved state file
7386 * 3) save machine again --> filename would be reused, breaking the online snapshot
7387 *
7388 * So instead we now use a timestamp.
7389 *
7390 * @param str
7391 */
7392
7393void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7394{
7395 AutoCaller autoCaller(this);
7396 AssertComRCReturnVoid(autoCaller.rc());
7397
7398 {
7399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7400 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7401 }
7402
7403 RTTIMESPEC ts;
7404 RTTimeNow(&ts);
7405 RTTIME time;
7406 RTTimeExplode(&time, &ts);
7407
7408 strStateFilePath += RTPATH_DELIMITER;
7409 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7410 time.i32Year, time.u8Month, time.u8MonthDay,
7411 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7412}
7413
7414/**
7415 * Returns the full path to the default video capture file.
7416 */
7417void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7418{
7419 AutoCaller autoCaller(this);
7420 AssertComRCReturnVoid(autoCaller.rc());
7421
7422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7423
7424 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7425 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7426 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7427}
7428
7429/**
7430 * Returns whether at least one USB controller is present for the VM.
7431 */
7432bool Machine::i_isUSBControllerPresent()
7433{
7434 AutoCaller autoCaller(this);
7435 AssertComRCReturn(autoCaller.rc(), false);
7436
7437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7438
7439 return (mUSBControllers->size() > 0);
7440}
7441
7442/**
7443 * @note Locks this object for writing, calls the client process
7444 * (inside the lock).
7445 */
7446HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7447 const Utf8Str &strFrontend,
7448 const Utf8Str &strEnvironment,
7449 ProgressProxy *aProgress)
7450{
7451 LogFlowThisFuncEnter();
7452
7453 AssertReturn(aControl, E_FAIL);
7454 AssertReturn(aProgress, E_FAIL);
7455 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7456
7457 AutoCaller autoCaller(this);
7458 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7459
7460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7461
7462 if (!mData->mRegistered)
7463 return setError(E_UNEXPECTED,
7464 tr("The machine '%s' is not registered"),
7465 mUserData->s.strName.c_str());
7466
7467 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7468
7469 /* The process started when launching a VM with separate UI/VM processes is always
7470 * the UI process, i.e. needs special handling as it won't claim the session. */
7471 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7472
7473 if (fSeparate)
7474 {
7475 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7476 return setError(VBOX_E_INVALID_OBJECT_STATE,
7477 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7478 mUserData->s.strName.c_str());
7479 }
7480 else
7481 {
7482 if ( mData->mSession.mState == SessionState_Locked
7483 || mData->mSession.mState == SessionState_Spawning
7484 || mData->mSession.mState == SessionState_Unlocking)
7485 return setError(VBOX_E_INVALID_OBJECT_STATE,
7486 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7487 mUserData->s.strName.c_str());
7488
7489 /* may not be busy */
7490 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7491 }
7492
7493 /* get the path to the executable */
7494 char szPath[RTPATH_MAX];
7495 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7496 size_t cchBufLeft = strlen(szPath);
7497 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7498 szPath[cchBufLeft] = 0;
7499 char *pszNamePart = szPath + cchBufLeft;
7500 cchBufLeft = sizeof(szPath) - cchBufLeft;
7501
7502 int vrc = VINF_SUCCESS;
7503 RTPROCESS pid = NIL_RTPROCESS;
7504
7505 RTENV env = RTENV_DEFAULT;
7506
7507 if (!strEnvironment.isEmpty())
7508 {
7509 char *newEnvStr = NULL;
7510
7511 do
7512 {
7513 /* clone the current environment */
7514 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7515 AssertRCBreakStmt(vrc2, vrc = vrc2);
7516
7517 newEnvStr = RTStrDup(strEnvironment.c_str());
7518 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7519
7520 /* put new variables to the environment
7521 * (ignore empty variable names here since RTEnv API
7522 * intentionally doesn't do that) */
7523 char *var = newEnvStr;
7524 for (char *p = newEnvStr; *p; ++p)
7525 {
7526 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7527 {
7528 *p = '\0';
7529 if (*var)
7530 {
7531 char *val = strchr(var, '=');
7532 if (val)
7533 {
7534 *val++ = '\0';
7535 vrc2 = RTEnvSetEx(env, var, val);
7536 }
7537 else
7538 vrc2 = RTEnvUnsetEx(env, var);
7539 if (RT_FAILURE(vrc2))
7540 break;
7541 }
7542 var = p + 1;
7543 }
7544 }
7545 if (RT_SUCCESS(vrc2) && *var)
7546 vrc2 = RTEnvPutEx(env, var);
7547
7548 AssertRCBreakStmt(vrc2, vrc = vrc2);
7549 }
7550 while (0);
7551
7552 if (newEnvStr != NULL)
7553 RTStrFree(newEnvStr);
7554 }
7555
7556 /* Hardening logging */
7557#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7558 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7559 {
7560 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7561 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7562 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7563 {
7564 Utf8Str strStartupLogDir = strHardeningLogFile;
7565 strStartupLogDir.stripFilename();
7566 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7567 file without stripping the file. */
7568 }
7569 strSupHardeningLogArg.append(strHardeningLogFile);
7570
7571 /* Remove legacy log filename to avoid confusion. */
7572 Utf8Str strOldStartupLogFile;
7573 getLogFolder(strOldStartupLogFile);
7574 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7575 RTFileDelete(strOldStartupLogFile.c_str());
7576 }
7577 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7578#else
7579 const char *pszSupHardeningLogArg = NULL;
7580#endif
7581
7582 Utf8Str strCanonicalName;
7583
7584#ifdef VBOX_WITH_QTGUI
7585 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7586 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7587 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7588 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7589 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7590 {
7591 strCanonicalName = "GUI/Qt";
7592# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7593 /* Modify the base path so that we don't need to use ".." below. */
7594 RTPathStripTrailingSlash(szPath);
7595 RTPathStripFilename(szPath);
7596 cchBufLeft = strlen(szPath);
7597 pszNamePart = szPath + cchBufLeft;
7598 cchBufLeft = sizeof(szPath) - cchBufLeft;
7599
7600# define OSX_APP_NAME "VirtualBoxVM"
7601# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7602
7603 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7604 if ( strAppOverride.contains(".")
7605 || strAppOverride.contains("/")
7606 || strAppOverride.contains("\\")
7607 || strAppOverride.contains(":"))
7608 strAppOverride.setNull();
7609 Utf8Str strAppPath;
7610 if (!strAppOverride.isEmpty())
7611 {
7612 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7613 Utf8Str strFullPath(szPath);
7614 strFullPath.append(strAppPath);
7615 /* there is a race, but people using this deserve the failure */
7616 if (!RTFileExists(strFullPath.c_str()))
7617 strAppOverride.setNull();
7618 }
7619 if (strAppOverride.isEmpty())
7620 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7621 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7622 strcpy(pszNamePart, strAppPath.c_str());
7623# else
7624 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7625 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7626 strcpy(pszNamePart, s_szVirtualBox_exe);
7627# endif
7628
7629 Utf8Str idStr = mData->mUuid.toString();
7630 const char *apszArgs[] =
7631 {
7632 szPath,
7633 "--comment", mUserData->s.strName.c_str(),
7634 "--startvm", idStr.c_str(),
7635 "--no-startvm-errormsgbox",
7636 NULL, /* For "--separate". */
7637 NULL, /* For "--sup-startup-log". */
7638 NULL
7639 };
7640 unsigned iArg = 6;
7641 if (fSeparate)
7642 apszArgs[iArg++] = "--separate";
7643 apszArgs[iArg++] = pszSupHardeningLogArg;
7644
7645 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7646 }
7647#else /* !VBOX_WITH_QTGUI */
7648 if (0)
7649 ;
7650#endif /* VBOX_WITH_QTGUI */
7651
7652 else
7653
7654#ifdef VBOX_WITH_VBOXSDL
7655 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7656 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7657 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7658 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7659 {
7660 strCanonicalName = "GUI/SDL";
7661 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7662 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7663 strcpy(pszNamePart, s_szVBoxSDL_exe);
7664
7665 Utf8Str idStr = mData->mUuid.toString();
7666 const char *apszArgs[] =
7667 {
7668 szPath,
7669 "--comment", mUserData->s.strName.c_str(),
7670 "--startvm", idStr.c_str(),
7671 NULL, /* For "--separate". */
7672 NULL, /* For "--sup-startup-log". */
7673 NULL
7674 };
7675 unsigned iArg = 5;
7676 if (fSeparate)
7677 apszArgs[iArg++] = "--separate";
7678 apszArgs[iArg++] = pszSupHardeningLogArg;
7679
7680 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7681 }
7682#else /* !VBOX_WITH_VBOXSDL */
7683 if (0)
7684 ;
7685#endif /* !VBOX_WITH_VBOXSDL */
7686
7687 else
7688
7689#ifdef VBOX_WITH_HEADLESS
7690 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7691 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7692 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7693 )
7694 {
7695 strCanonicalName = "headless";
7696 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7697 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7698 * and a VM works even if the server has not been installed.
7699 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7700 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7701 * differently in 4.0 and 3.x.
7702 */
7703 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7704 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7705 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7706
7707 Utf8Str idStr = mData->mUuid.toString();
7708 const char *apszArgs[] =
7709 {
7710 szPath,
7711 "--comment", mUserData->s.strName.c_str(),
7712 "--startvm", idStr.c_str(),
7713 "--vrde", "config",
7714 NULL, /* For "--capture". */
7715 NULL, /* For "--sup-startup-log". */
7716 NULL
7717 };
7718 unsigned iArg = 7;
7719 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7720 apszArgs[iArg++] = "--capture";
7721 apszArgs[iArg++] = pszSupHardeningLogArg;
7722
7723# ifdef RT_OS_WINDOWS
7724 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7725# else
7726 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7727# endif
7728 }
7729#else /* !VBOX_WITH_HEADLESS */
7730 if (0)
7731 ;
7732#endif /* !VBOX_WITH_HEADLESS */
7733 else
7734 {
7735 RTEnvDestroy(env);
7736 return setError(E_INVALIDARG,
7737 tr("Invalid frontend name: '%s'"),
7738 strFrontend.c_str());
7739 }
7740
7741 RTEnvDestroy(env);
7742
7743 if (RT_FAILURE(vrc))
7744 return setError(VBOX_E_IPRT_ERROR,
7745 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7746 mUserData->s.strName.c_str(), vrc);
7747
7748 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7749
7750 if (!fSeparate)
7751 {
7752 /*
7753 * Note that we don't release the lock here before calling the client,
7754 * because it doesn't need to call us back if called with a NULL argument.
7755 * Releasing the lock here is dangerous because we didn't prepare the
7756 * launch data yet, but the client we've just started may happen to be
7757 * too fast and call LockMachine() that will fail (because of PID, etc.),
7758 * so that the Machine will never get out of the Spawning session state.
7759 */
7760
7761 /* inform the session that it will be a remote one */
7762 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7763#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7764 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7765#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7766 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7767#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7768 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7769
7770 if (FAILED(rc))
7771 {
7772 /* restore the session state */
7773 mData->mSession.mState = SessionState_Unlocked;
7774 alock.release();
7775 mParent->i_addProcessToReap(pid);
7776 /* The failure may occur w/o any error info (from RPC), so provide one */
7777 return setError(VBOX_E_VM_ERROR,
7778 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7779 }
7780
7781 /* attach launch data to the machine */
7782 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7783 mData->mSession.mRemoteControls.push_back(aControl);
7784 mData->mSession.mProgress = aProgress;
7785 mData->mSession.mPID = pid;
7786 mData->mSession.mState = SessionState_Spawning;
7787 Assert(strCanonicalName.isNotEmpty());
7788 mData->mSession.mName = strCanonicalName;
7789 }
7790 else
7791 {
7792 /* For separate UI process we declare the launch as completed instantly, as the
7793 * actual headless VM start may or may not come. No point in remembering anything
7794 * yet, as what matters for us is when the headless VM gets started. */
7795 aProgress->i_notifyComplete(S_OK);
7796 }
7797
7798 alock.release();
7799 mParent->i_addProcessToReap(pid);
7800
7801 LogFlowThisFuncLeave();
7802 return S_OK;
7803}
7804
7805/**
7806 * Returns @c true if the given session machine instance has an open direct
7807 * session (and optionally also for direct sessions which are closing) and
7808 * returns the session control machine instance if so.
7809 *
7810 * Note that when the method returns @c false, the arguments remain unchanged.
7811 *
7812 * @param aMachine Session machine object.
7813 * @param aControl Direct session control object (optional).
7814 * @param aRequireVM If true then only allow VM sessions.
7815 * @param aAllowClosing If true then additionally a session which is currently
7816 * being closed will also be allowed.
7817 *
7818 * @note locks this object for reading.
7819 */
7820bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7821 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7822 bool aRequireVM /*= false*/,
7823 bool aAllowClosing /*= false*/)
7824{
7825 AutoLimitedCaller autoCaller(this);
7826 AssertComRCReturn(autoCaller.rc(), false);
7827
7828 /* just return false for inaccessible machines */
7829 if (getObjectState().getState() != ObjectState::Ready)
7830 return false;
7831
7832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7833
7834 if ( ( mData->mSession.mState == SessionState_Locked
7835 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7836 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7837 )
7838 {
7839 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7840
7841 aMachine = mData->mSession.mMachine;
7842
7843 if (aControl != NULL)
7844 *aControl = mData->mSession.mDirectControl;
7845
7846 return true;
7847 }
7848
7849 return false;
7850}
7851
7852/**
7853 * Returns @c true if the given machine has an spawning direct session.
7854 *
7855 * @note locks this object for reading.
7856 */
7857bool Machine::i_isSessionSpawning()
7858{
7859 AutoLimitedCaller autoCaller(this);
7860 AssertComRCReturn(autoCaller.rc(), false);
7861
7862 /* just return false for inaccessible machines */
7863 if (getObjectState().getState() != ObjectState::Ready)
7864 return false;
7865
7866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7867
7868 if (mData->mSession.mState == SessionState_Spawning)
7869 return true;
7870
7871 return false;
7872}
7873
7874/**
7875 * Called from the client watcher thread to check for unexpected client process
7876 * death during Session_Spawning state (e.g. before it successfully opened a
7877 * direct session).
7878 *
7879 * On Win32 and on OS/2, this method is called only when we've got the
7880 * direct client's process termination notification, so it always returns @c
7881 * true.
7882 *
7883 * On other platforms, this method returns @c true if the client process is
7884 * terminated and @c false if it's still alive.
7885 *
7886 * @note Locks this object for writing.
7887 */
7888bool Machine::i_checkForSpawnFailure()
7889{
7890 AutoCaller autoCaller(this);
7891 if (!autoCaller.isOk())
7892 {
7893 /* nothing to do */
7894 LogFlowThisFunc(("Already uninitialized!\n"));
7895 return true;
7896 }
7897
7898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7899
7900 if (mData->mSession.mState != SessionState_Spawning)
7901 {
7902 /* nothing to do */
7903 LogFlowThisFunc(("Not spawning any more!\n"));
7904 return true;
7905 }
7906
7907 HRESULT rc = S_OK;
7908
7909 /* PID not yet initialized, skip check. */
7910 if (mData->mSession.mPID == NIL_RTPROCESS)
7911 return false;
7912
7913 RTPROCSTATUS status;
7914 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7915
7916 if (vrc != VERR_PROCESS_RUNNING)
7917 {
7918 Utf8Str strExtraInfo;
7919
7920#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7921 /* If the startup logfile exists and is of non-zero length, tell the
7922 user to look there for more details to encourage them to attach it
7923 when reporting startup issues. */
7924 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7925 uint64_t cbStartupLogFile = 0;
7926 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7927 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7928 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7929#endif
7930
7931 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7932 rc = setError(E_FAIL,
7933 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7934 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7935 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7936 rc = setError(E_FAIL,
7937 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7938 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7939 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7940 rc = setError(E_FAIL,
7941 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7942 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7943 else
7944 rc = setError(E_FAIL,
7945 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7946 i_getName().c_str(), vrc, strExtraInfo.c_str());
7947 }
7948
7949 if (FAILED(rc))
7950 {
7951 /* Close the remote session, remove the remote control from the list
7952 * and reset session state to Closed (@note keep the code in sync with
7953 * the relevant part in LockMachine()). */
7954
7955 Assert(mData->mSession.mRemoteControls.size() == 1);
7956 if (mData->mSession.mRemoteControls.size() == 1)
7957 {
7958 ErrorInfoKeeper eik;
7959 mData->mSession.mRemoteControls.front()->Uninitialize();
7960 }
7961
7962 mData->mSession.mRemoteControls.clear();
7963 mData->mSession.mState = SessionState_Unlocked;
7964
7965 /* finalize the progress after setting the state */
7966 if (!mData->mSession.mProgress.isNull())
7967 {
7968 mData->mSession.mProgress->notifyComplete(rc);
7969 mData->mSession.mProgress.setNull();
7970 }
7971
7972 mData->mSession.mPID = NIL_RTPROCESS;
7973
7974 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7975 return true;
7976 }
7977
7978 return false;
7979}
7980
7981/**
7982 * Checks whether the machine can be registered. If so, commits and saves
7983 * all settings.
7984 *
7985 * @note Must be called from mParent's write lock. Locks this object and
7986 * children for writing.
7987 */
7988HRESULT Machine::i_prepareRegister()
7989{
7990 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7991
7992 AutoLimitedCaller autoCaller(this);
7993 AssertComRCReturnRC(autoCaller.rc());
7994
7995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7996
7997 /* wait for state dependents to drop to zero */
7998 i_ensureNoStateDependencies();
7999
8000 if (!mData->mAccessible)
8001 return setError(VBOX_E_INVALID_OBJECT_STATE,
8002 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8003 mUserData->s.strName.c_str(),
8004 mData->mUuid.toString().c_str());
8005
8006 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8007
8008 if (mData->mRegistered)
8009 return setError(VBOX_E_INVALID_OBJECT_STATE,
8010 tr("The machine '%s' with UUID {%s} is already registered"),
8011 mUserData->s.strName.c_str(),
8012 mData->mUuid.toString().c_str());
8013
8014 HRESULT rc = S_OK;
8015
8016 // Ensure the settings are saved. If we are going to be registered and
8017 // no config file exists yet, create it by calling i_saveSettings() too.
8018 if ( (mData->flModifications)
8019 || (!mData->pMachineConfigFile->fileExists())
8020 )
8021 {
8022 rc = i_saveSettings(NULL);
8023 // no need to check whether VirtualBox.xml needs saving too since
8024 // we can't have a machine XML file rename pending
8025 if (FAILED(rc)) return rc;
8026 }
8027
8028 /* more config checking goes here */
8029
8030 if (SUCCEEDED(rc))
8031 {
8032 /* we may have had implicit modifications we want to fix on success */
8033 i_commit();
8034
8035 mData->mRegistered = true;
8036 }
8037 else
8038 {
8039 /* we may have had implicit modifications we want to cancel on failure*/
8040 i_rollback(false /* aNotify */);
8041 }
8042
8043 return rc;
8044}
8045
8046/**
8047 * Increases the number of objects dependent on the machine state or on the
8048 * registered state. Guarantees that these two states will not change at least
8049 * until #releaseStateDependency() is called.
8050 *
8051 * Depending on the @a aDepType value, additional state checks may be made.
8052 * These checks will set extended error info on failure. See
8053 * #checkStateDependency() for more info.
8054 *
8055 * If this method returns a failure, the dependency is not added and the caller
8056 * is not allowed to rely on any particular machine state or registration state
8057 * value and may return the failed result code to the upper level.
8058 *
8059 * @param aDepType Dependency type to add.
8060 * @param aState Current machine state (NULL if not interested).
8061 * @param aRegistered Current registered state (NULL if not interested).
8062 *
8063 * @note Locks this object for writing.
8064 */
8065HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8066 MachineState_T *aState /* = NULL */,
8067 BOOL *aRegistered /* = NULL */)
8068{
8069 AutoCaller autoCaller(this);
8070 AssertComRCReturnRC(autoCaller.rc());
8071
8072 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8073
8074 HRESULT rc = i_checkStateDependency(aDepType);
8075 if (FAILED(rc)) return rc;
8076
8077 {
8078 if (mData->mMachineStateChangePending != 0)
8079 {
8080 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8081 * drop to zero so don't add more. It may make sense to wait a bit
8082 * and retry before reporting an error (since the pending state
8083 * transition should be really quick) but let's just assert for
8084 * now to see if it ever happens on practice. */
8085
8086 AssertFailed();
8087
8088 return setError(E_ACCESSDENIED,
8089 tr("Machine state change is in progress. Please retry the operation later."));
8090 }
8091
8092 ++mData->mMachineStateDeps;
8093 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8094 }
8095
8096 if (aState)
8097 *aState = mData->mMachineState;
8098 if (aRegistered)
8099 *aRegistered = mData->mRegistered;
8100
8101 return S_OK;
8102}
8103
8104/**
8105 * Decreases the number of objects dependent on the machine state.
8106 * Must always complete the #addStateDependency() call after the state
8107 * dependency is no more necessary.
8108 */
8109void Machine::i_releaseStateDependency()
8110{
8111 AutoCaller autoCaller(this);
8112 AssertComRCReturnVoid(autoCaller.rc());
8113
8114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8115
8116 /* releaseStateDependency() w/o addStateDependency()? */
8117 AssertReturnVoid(mData->mMachineStateDeps != 0);
8118 -- mData->mMachineStateDeps;
8119
8120 if (mData->mMachineStateDeps == 0)
8121 {
8122 /* inform i_ensureNoStateDependencies() that there are no more deps */
8123 if (mData->mMachineStateChangePending != 0)
8124 {
8125 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8126 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8127 }
8128 }
8129}
8130
8131Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8132{
8133 /* start with nothing found */
8134 Utf8Str strResult("");
8135
8136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8137
8138 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8139 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8140 // found:
8141 strResult = it->second; // source is a Utf8Str
8142
8143 return strResult;
8144}
8145
8146// protected methods
8147/////////////////////////////////////////////////////////////////////////////
8148
8149/**
8150 * Performs machine state checks based on the @a aDepType value. If a check
8151 * fails, this method will set extended error info, otherwise it will return
8152 * S_OK. It is supposed, that on failure, the caller will immediately return
8153 * the return value of this method to the upper level.
8154 *
8155 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8156 *
8157 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8158 * current state of this machine object allows to change settings of the
8159 * machine (i.e. the machine is not registered, or registered but not running
8160 * and not saved). It is useful to call this method from Machine setters
8161 * before performing any change.
8162 *
8163 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8164 * as for MutableStateDep except that if the machine is saved, S_OK is also
8165 * returned. This is useful in setters which allow changing machine
8166 * properties when it is in the saved state.
8167 *
8168 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8169 * if the current state of this machine object allows to change runtime
8170 * changeable settings of the machine (i.e. the machine is not registered, or
8171 * registered but either running or not running and not saved). It is useful
8172 * to call this method from Machine setters before performing any changes to
8173 * runtime changeable settings.
8174 *
8175 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8176 * the same as for MutableOrRunningStateDep except that if the machine is
8177 * saved, S_OK is also returned. This is useful in setters which allow
8178 * changing runtime and saved state changeable machine properties.
8179 *
8180 * @param aDepType Dependency type to check.
8181 *
8182 * @note Non Machine based classes should use #addStateDependency() and
8183 * #releaseStateDependency() methods or the smart AutoStateDependency
8184 * template.
8185 *
8186 * @note This method must be called from under this object's read or write
8187 * lock.
8188 */
8189HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8190{
8191 switch (aDepType)
8192 {
8193 case AnyStateDep:
8194 {
8195 break;
8196 }
8197 case MutableStateDep:
8198 {
8199 if ( mData->mRegistered
8200 && ( !i_isSessionMachine()
8201 || ( mData->mMachineState != MachineState_Aborted
8202 && mData->mMachineState != MachineState_Teleported
8203 && mData->mMachineState != MachineState_PoweredOff
8204 )
8205 )
8206 )
8207 return setError(VBOX_E_INVALID_VM_STATE,
8208 tr("The machine is not mutable (state is %s)"),
8209 Global::stringifyMachineState(mData->mMachineState));
8210 break;
8211 }
8212 case MutableOrSavedStateDep:
8213 {
8214 if ( mData->mRegistered
8215 && ( !i_isSessionMachine()
8216 || ( mData->mMachineState != MachineState_Aborted
8217 && mData->mMachineState != MachineState_Teleported
8218 && mData->mMachineState != MachineState_Saved
8219 && mData->mMachineState != MachineState_PoweredOff
8220 )
8221 )
8222 )
8223 return setError(VBOX_E_INVALID_VM_STATE,
8224 tr("The machine is not mutable or saved (state is %s)"),
8225 Global::stringifyMachineState(mData->mMachineState));
8226 break;
8227 }
8228 case MutableOrRunningStateDep:
8229 {
8230 if ( mData->mRegistered
8231 && ( !i_isSessionMachine()
8232 || ( mData->mMachineState != MachineState_Aborted
8233 && mData->mMachineState != MachineState_Teleported
8234 && mData->mMachineState != MachineState_PoweredOff
8235 && !Global::IsOnline(mData->mMachineState)
8236 )
8237 )
8238 )
8239 return setError(VBOX_E_INVALID_VM_STATE,
8240 tr("The machine is not mutable or running (state is %s)"),
8241 Global::stringifyMachineState(mData->mMachineState));
8242 break;
8243 }
8244 case MutableOrSavedOrRunningStateDep:
8245 {
8246 if ( mData->mRegistered
8247 && ( !i_isSessionMachine()
8248 || ( mData->mMachineState != MachineState_Aborted
8249 && mData->mMachineState != MachineState_Teleported
8250 && mData->mMachineState != MachineState_Saved
8251 && mData->mMachineState != MachineState_PoweredOff
8252 && !Global::IsOnline(mData->mMachineState)
8253 )
8254 )
8255 )
8256 return setError(VBOX_E_INVALID_VM_STATE,
8257 tr("The machine is not mutable, saved or running (state is %s)"),
8258 Global::stringifyMachineState(mData->mMachineState));
8259 break;
8260 }
8261 }
8262
8263 return S_OK;
8264}
8265
8266/**
8267 * Helper to initialize all associated child objects and allocate data
8268 * structures.
8269 *
8270 * This method must be called as a part of the object's initialization procedure
8271 * (usually done in the #init() method).
8272 *
8273 * @note Must be called only from #init() or from #registeredInit().
8274 */
8275HRESULT Machine::initDataAndChildObjects()
8276{
8277 AutoCaller autoCaller(this);
8278 AssertComRCReturnRC(autoCaller.rc());
8279 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8280 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8281
8282 AssertReturn(!mData->mAccessible, E_FAIL);
8283
8284 /* allocate data structures */
8285 mSSData.allocate();
8286 mUserData.allocate();
8287 mHWData.allocate();
8288 mMediaData.allocate();
8289 mStorageControllers.allocate();
8290 mUSBControllers.allocate();
8291
8292 /* initialize mOSTypeId */
8293 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8294
8295 /* create associated BIOS settings object */
8296 unconst(mBIOSSettings).createObject();
8297 mBIOSSettings->init(this);
8298
8299 /* create an associated VRDE object (default is disabled) */
8300 unconst(mVRDEServer).createObject();
8301 mVRDEServer->init(this);
8302
8303 /* create associated serial port objects */
8304 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8305 {
8306 unconst(mSerialPorts[slot]).createObject();
8307 mSerialPorts[slot]->init(this, slot);
8308 }
8309
8310 /* create associated parallel port objects */
8311 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8312 {
8313 unconst(mParallelPorts[slot]).createObject();
8314 mParallelPorts[slot]->init(this, slot);
8315 }
8316
8317 /* create the audio adapter object (always present, default is disabled) */
8318 unconst(mAudioAdapter).createObject();
8319 mAudioAdapter->init(this);
8320
8321 /* create the USB device filters object (always present) */
8322 unconst(mUSBDeviceFilters).createObject();
8323 mUSBDeviceFilters->init(this);
8324
8325 /* create associated network adapter objects */
8326 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8327 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8328 {
8329 unconst(mNetworkAdapters[slot]).createObject();
8330 mNetworkAdapters[slot]->init(this, slot);
8331 }
8332
8333 /* create the bandwidth control */
8334 unconst(mBandwidthControl).createObject();
8335 mBandwidthControl->init(this);
8336
8337 return S_OK;
8338}
8339
8340/**
8341 * Helper to uninitialize all associated child objects and to free all data
8342 * structures.
8343 *
8344 * This method must be called as a part of the object's uninitialization
8345 * procedure (usually done in the #uninit() method).
8346 *
8347 * @note Must be called only from #uninit() or from #registeredInit().
8348 */
8349void Machine::uninitDataAndChildObjects()
8350{
8351 AutoCaller autoCaller(this);
8352 AssertComRCReturnVoid(autoCaller.rc());
8353 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8354 || getObjectState().getState() == ObjectState::Limited);
8355
8356 /* tell all our other child objects we've been uninitialized */
8357 if (mBandwidthControl)
8358 {
8359 mBandwidthControl->uninit();
8360 unconst(mBandwidthControl).setNull();
8361 }
8362
8363 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8364 {
8365 if (mNetworkAdapters[slot])
8366 {
8367 mNetworkAdapters[slot]->uninit();
8368 unconst(mNetworkAdapters[slot]).setNull();
8369 }
8370 }
8371
8372 if (mUSBDeviceFilters)
8373 {
8374 mUSBDeviceFilters->uninit();
8375 unconst(mUSBDeviceFilters).setNull();
8376 }
8377
8378 if (mAudioAdapter)
8379 {
8380 mAudioAdapter->uninit();
8381 unconst(mAudioAdapter).setNull();
8382 }
8383
8384 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8385 {
8386 if (mParallelPorts[slot])
8387 {
8388 mParallelPorts[slot]->uninit();
8389 unconst(mParallelPorts[slot]).setNull();
8390 }
8391 }
8392
8393 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8394 {
8395 if (mSerialPorts[slot])
8396 {
8397 mSerialPorts[slot]->uninit();
8398 unconst(mSerialPorts[slot]).setNull();
8399 }
8400 }
8401
8402 if (mVRDEServer)
8403 {
8404 mVRDEServer->uninit();
8405 unconst(mVRDEServer).setNull();
8406 }
8407
8408 if (mBIOSSettings)
8409 {
8410 mBIOSSettings->uninit();
8411 unconst(mBIOSSettings).setNull();
8412 }
8413
8414 /* Deassociate media (only when a real Machine or a SnapshotMachine
8415 * instance is uninitialized; SessionMachine instances refer to real
8416 * Machine media). This is necessary for a clean re-initialization of
8417 * the VM after successfully re-checking the accessibility state. Note
8418 * that in case of normal Machine or SnapshotMachine uninitialization (as
8419 * a result of unregistering or deleting the snapshot), outdated media
8420 * attachments will already be uninitialized and deleted, so this
8421 * code will not affect them. */
8422 if ( !!mMediaData
8423 && (!i_isSessionMachine())
8424 )
8425 {
8426 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8427 it != mMediaData->mAttachments.end();
8428 ++it)
8429 {
8430 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8431 if (pMedium.isNull())
8432 continue;
8433 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8434 AssertComRC(rc);
8435 }
8436 }
8437
8438 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8439 {
8440 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8441 if (mData->mFirstSnapshot)
8442 {
8443 // snapshots tree is protected by machine write lock; strictly
8444 // this isn't necessary here since we're deleting the entire
8445 // machine, but otherwise we assert in Snapshot::uninit()
8446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8447 mData->mFirstSnapshot->uninit();
8448 mData->mFirstSnapshot.setNull();
8449 }
8450
8451 mData->mCurrentSnapshot.setNull();
8452 }
8453
8454 /* free data structures (the essential mData structure is not freed here
8455 * since it may be still in use) */
8456 mMediaData.free();
8457 mStorageControllers.free();
8458 mUSBControllers.free();
8459 mHWData.free();
8460 mUserData.free();
8461 mSSData.free();
8462}
8463
8464/**
8465 * Returns a pointer to the Machine object for this machine that acts like a
8466 * parent for complex machine data objects such as shared folders, etc.
8467 *
8468 * For primary Machine objects and for SnapshotMachine objects, returns this
8469 * object's pointer itself. For SessionMachine objects, returns the peer
8470 * (primary) machine pointer.
8471 */
8472Machine* Machine::i_getMachine()
8473{
8474 if (i_isSessionMachine())
8475 return (Machine*)mPeer;
8476 return this;
8477}
8478
8479/**
8480 * Makes sure that there are no machine state dependents. If necessary, waits
8481 * for the number of dependents to drop to zero.
8482 *
8483 * Make sure this method is called from under this object's write lock to
8484 * guarantee that no new dependents may be added when this method returns
8485 * control to the caller.
8486 *
8487 * @note Locks this object for writing. The lock will be released while waiting
8488 * (if necessary).
8489 *
8490 * @warning To be used only in methods that change the machine state!
8491 */
8492void Machine::i_ensureNoStateDependencies()
8493{
8494 AssertReturnVoid(isWriteLockOnCurrentThread());
8495
8496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8497
8498 /* Wait for all state dependents if necessary */
8499 if (mData->mMachineStateDeps != 0)
8500 {
8501 /* lazy semaphore creation */
8502 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8503 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8504
8505 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8506 mData->mMachineStateDeps));
8507
8508 ++mData->mMachineStateChangePending;
8509
8510 /* reset the semaphore before waiting, the last dependent will signal
8511 * it */
8512 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8513
8514 alock.release();
8515
8516 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8517
8518 alock.acquire();
8519
8520 -- mData->mMachineStateChangePending;
8521 }
8522}
8523
8524/**
8525 * Changes the machine state and informs callbacks.
8526 *
8527 * This method is not intended to fail so it either returns S_OK or asserts (and
8528 * returns a failure).
8529 *
8530 * @note Locks this object for writing.
8531 */
8532HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8533{
8534 LogFlowThisFuncEnter();
8535 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8536 Assert(aMachineState != MachineState_Null);
8537
8538 AutoCaller autoCaller(this);
8539 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8540
8541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8542
8543 /* wait for state dependents to drop to zero */
8544 i_ensureNoStateDependencies();
8545
8546 MachineState_T const enmOldState = mData->mMachineState;
8547 if (enmOldState != aMachineState)
8548 {
8549 mData->mMachineState = aMachineState;
8550 RTTimeNow(&mData->mLastStateChange);
8551
8552#ifdef VBOX_WITH_DTRACE_R3_MAIN
8553 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8554#endif
8555 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8556 }
8557
8558 LogFlowThisFuncLeave();
8559 return S_OK;
8560}
8561
8562/**
8563 * Searches for a shared folder with the given logical name
8564 * in the collection of shared folders.
8565 *
8566 * @param aName logical name of the shared folder
8567 * @param aSharedFolder where to return the found object
8568 * @param aSetError whether to set the error info if the folder is
8569 * not found
8570 * @return
8571 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8572 *
8573 * @note
8574 * must be called from under the object's lock!
8575 */
8576HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8577 ComObjPtr<SharedFolder> &aSharedFolder,
8578 bool aSetError /* = false */)
8579{
8580 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8581 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8582 it != mHWData->mSharedFolders.end();
8583 ++it)
8584 {
8585 SharedFolder *pSF = *it;
8586 AutoCaller autoCaller(pSF);
8587 if (pSF->i_getName() == aName)
8588 {
8589 aSharedFolder = pSF;
8590 rc = S_OK;
8591 break;
8592 }
8593 }
8594
8595 if (aSetError && FAILED(rc))
8596 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8597
8598 return rc;
8599}
8600
8601/**
8602 * Initializes all machine instance data from the given settings structures
8603 * from XML. The exception is the machine UUID which needs special handling
8604 * depending on the caller's use case, so the caller needs to set that herself.
8605 *
8606 * This gets called in several contexts during machine initialization:
8607 *
8608 * -- When machine XML exists on disk already and needs to be loaded into memory,
8609 * for example, from registeredInit() to load all registered machines on
8610 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8611 * attached to the machine should be part of some media registry already.
8612 *
8613 * -- During OVF import, when a machine config has been constructed from an
8614 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8615 * ensure that the media listed as attachments in the config (which have
8616 * been imported from the OVF) receive the correct registry ID.
8617 *
8618 * -- During VM cloning.
8619 *
8620 * @param config Machine settings from XML.
8621 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8622 * for each attached medium in the config.
8623 * @return
8624 */
8625HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8626 const Guid *puuidRegistry)
8627{
8628 // copy name, description, OS type, teleporter, UTC etc.
8629 mUserData->s = config.machineUserData;
8630
8631 // look up the object by Id to check it is valid
8632 ComPtr<IGuestOSType> guestOSType;
8633 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8634 guestOSType.asOutParam());
8635 if (FAILED(rc)) return rc;
8636
8637 // stateFile (optional)
8638 if (config.strStateFile.isEmpty())
8639 mSSData->strStateFilePath.setNull();
8640 else
8641 {
8642 Utf8Str stateFilePathFull(config.strStateFile);
8643 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8644 if (RT_FAILURE(vrc))
8645 return setError(E_FAIL,
8646 tr("Invalid saved state file path '%s' (%Rrc)"),
8647 config.strStateFile.c_str(),
8648 vrc);
8649 mSSData->strStateFilePath = stateFilePathFull;
8650 }
8651
8652 // snapshot folder needs special processing so set it again
8653 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8654 if (FAILED(rc)) return rc;
8655
8656 /* Copy the extra data items (Not in any case config is already the same as
8657 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8658 * make sure the extra data map is copied). */
8659 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8660
8661 /* currentStateModified (optional, default is true) */
8662 mData->mCurrentStateModified = config.fCurrentStateModified;
8663
8664 mData->mLastStateChange = config.timeLastStateChange;
8665
8666 /*
8667 * note: all mUserData members must be assigned prior this point because
8668 * we need to commit changes in order to let mUserData be shared by all
8669 * snapshot machine instances.
8670 */
8671 mUserData.commitCopy();
8672
8673 // machine registry, if present (must be loaded before snapshots)
8674 if (config.canHaveOwnMediaRegistry())
8675 {
8676 // determine machine folder
8677 Utf8Str strMachineFolder = i_getSettingsFileFull();
8678 strMachineFolder.stripFilename();
8679 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8680 config.mediaRegistry,
8681 strMachineFolder);
8682 if (FAILED(rc)) return rc;
8683 }
8684
8685 /* Snapshot node (optional) */
8686 size_t cRootSnapshots;
8687 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8688 {
8689 // there must be only one root snapshot
8690 Assert(cRootSnapshots == 1);
8691
8692 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8693
8694 rc = i_loadSnapshot(snap,
8695 config.uuidCurrentSnapshot,
8696 NULL); // no parent == first snapshot
8697 if (FAILED(rc)) return rc;
8698 }
8699
8700 // hardware data
8701 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8702 if (FAILED(rc)) return rc;
8703
8704 /*
8705 * NOTE: the assignment below must be the last thing to do,
8706 * otherwise it will be not possible to change the settings
8707 * somewhere in the code above because all setters will be
8708 * blocked by i_checkStateDependency(MutableStateDep).
8709 */
8710
8711 /* set the machine state to Aborted or Saved when appropriate */
8712 if (config.fAborted)
8713 {
8714 mSSData->strStateFilePath.setNull();
8715
8716 /* no need to use i_setMachineState() during init() */
8717 mData->mMachineState = MachineState_Aborted;
8718 }
8719 else if (!mSSData->strStateFilePath.isEmpty())
8720 {
8721 /* no need to use i_setMachineState() during init() */
8722 mData->mMachineState = MachineState_Saved;
8723 }
8724
8725 // after loading settings, we are no longer different from the XML on disk
8726 mData->flModifications = 0;
8727
8728 return S_OK;
8729}
8730
8731/**
8732 * Recursively loads all snapshots starting from the given.
8733 *
8734 * @param aNode <Snapshot> node.
8735 * @param aCurSnapshotId Current snapshot ID from the settings file.
8736 * @param aParentSnapshot Parent snapshot.
8737 */
8738HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8739 const Guid &aCurSnapshotId,
8740 Snapshot *aParentSnapshot)
8741{
8742 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8743 AssertReturn(!i_isSessionMachine(), E_FAIL);
8744
8745 HRESULT rc = S_OK;
8746
8747 Utf8Str strStateFile;
8748 if (!data.strStateFile.isEmpty())
8749 {
8750 /* optional */
8751 strStateFile = data.strStateFile;
8752 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8753 if (RT_FAILURE(vrc))
8754 return setError(E_FAIL,
8755 tr("Invalid saved state file path '%s' (%Rrc)"),
8756 strStateFile.c_str(),
8757 vrc);
8758 }
8759
8760 /* create a snapshot machine object */
8761 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8762 pSnapshotMachine.createObject();
8763 rc = pSnapshotMachine->initFromSettings(this,
8764 data.hardware,
8765 &data.debugging,
8766 &data.autostart,
8767 data.uuid.ref(),
8768 strStateFile);
8769 if (FAILED(rc)) return rc;
8770
8771 /* create a snapshot object */
8772 ComObjPtr<Snapshot> pSnapshot;
8773 pSnapshot.createObject();
8774 /* initialize the snapshot */
8775 rc = pSnapshot->init(mParent, // VirtualBox object
8776 data.uuid,
8777 data.strName,
8778 data.strDescription,
8779 data.timestamp,
8780 pSnapshotMachine,
8781 aParentSnapshot);
8782 if (FAILED(rc)) return rc;
8783
8784 /* memorize the first snapshot if necessary */
8785 if (!mData->mFirstSnapshot)
8786 mData->mFirstSnapshot = pSnapshot;
8787
8788 /* memorize the current snapshot when appropriate */
8789 if ( !mData->mCurrentSnapshot
8790 && pSnapshot->i_getId() == aCurSnapshotId
8791 )
8792 mData->mCurrentSnapshot = pSnapshot;
8793
8794 // now create the children
8795 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8796 it != data.llChildSnapshots.end();
8797 ++it)
8798 {
8799 const settings::Snapshot &childData = *it;
8800 // recurse
8801 rc = i_loadSnapshot(childData,
8802 aCurSnapshotId,
8803 pSnapshot); // parent = the one we created above
8804 if (FAILED(rc)) return rc;
8805 }
8806
8807 return rc;
8808}
8809
8810/**
8811 * Loads settings into mHWData.
8812 *
8813 * @param data Reference to the hardware settings.
8814 * @param pDbg Pointer to the debugging settings.
8815 * @param pAutostart Pointer to the autostart settings.
8816 */
8817HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8818 const Guid *puuidSnapshot,
8819 const settings::Hardware &data,
8820 const settings::Debugging *pDbg,
8821 const settings::Autostart *pAutostart)
8822{
8823 AssertReturn(!i_isSessionMachine(), E_FAIL);
8824
8825 HRESULT rc = S_OK;
8826
8827 try
8828 {
8829 /* The hardware version attribute (optional). */
8830 mHWData->mHWVersion = data.strVersion;
8831 mHWData->mHardwareUUID = data.uuid;
8832
8833 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8834 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8835 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8836 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8837 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8838 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8839 mHWData->mPAEEnabled = data.fPAE;
8840 mHWData->mLongMode = data.enmLongMode;
8841 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8842 mHWData->mCPUCount = data.cCPUs;
8843 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8844 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8845 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8846 mHWData->mCpuProfile = data.strCpuProfile;
8847
8848 // cpu
8849 if (mHWData->mCPUHotPlugEnabled)
8850 {
8851 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8852 it != data.llCpus.end();
8853 ++it)
8854 {
8855 const settings::Cpu &cpu = *it;
8856
8857 mHWData->mCPUAttached[cpu.ulId] = true;
8858 }
8859 }
8860
8861 // cpuid leafs
8862 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8863 it != data.llCpuIdLeafs.end();
8864 ++it)
8865 {
8866 const settings::CpuIdLeaf &leaf = *it;
8867
8868 switch (leaf.ulId)
8869 {
8870 case 0x0:
8871 case 0x1:
8872 case 0x2:
8873 case 0x3:
8874 case 0x4:
8875 case 0x5:
8876 case 0x6:
8877 case 0x7:
8878 case 0x8:
8879 case 0x9:
8880 case 0xA:
8881 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8882 break;
8883
8884 case 0x80000000:
8885 case 0x80000001:
8886 case 0x80000002:
8887 case 0x80000003:
8888 case 0x80000004:
8889 case 0x80000005:
8890 case 0x80000006:
8891 case 0x80000007:
8892 case 0x80000008:
8893 case 0x80000009:
8894 case 0x8000000A:
8895 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8896 break;
8897
8898 default:
8899 /* just ignore */
8900 break;
8901 }
8902 }
8903
8904 mHWData->mMemorySize = data.ulMemorySizeMB;
8905 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8906
8907 // boot order
8908 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8909 {
8910 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8911 if (it == data.mapBootOrder.end())
8912 mHWData->mBootOrder[i] = DeviceType_Null;
8913 else
8914 mHWData->mBootOrder[i] = it->second;
8915 }
8916
8917 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8918 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8919 mHWData->mMonitorCount = data.cMonitors;
8920 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8921 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8922 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8923 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8924 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8925 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8926 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8927 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8928 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8929 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8930 if (!data.strVideoCaptureFile.isEmpty())
8931 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8932 else
8933 mHWData->mVideoCaptureFile.setNull();
8934 mHWData->mFirmwareType = data.firmwareType;
8935 mHWData->mPointingHIDType = data.pointingHIDType;
8936 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8937 mHWData->mChipsetType = data.chipsetType;
8938 mHWData->mParavirtProvider = data.paravirtProvider;
8939 mHWData->mParavirtDebug = data.strParavirtDebug;
8940 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8941 mHWData->mHPETEnabled = data.fHPETEnabled;
8942
8943 /* VRDEServer */
8944 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8945 if (FAILED(rc)) return rc;
8946
8947 /* BIOS */
8948 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8949 if (FAILED(rc)) return rc;
8950
8951 // Bandwidth control (must come before network adapters)
8952 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8953 if (FAILED(rc)) return rc;
8954
8955 /* Shared folders */
8956 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8957 it != data.usbSettings.llUSBControllers.end();
8958 ++it)
8959 {
8960 const settings::USBController &settingsCtrl = *it;
8961 ComObjPtr<USBController> newCtrl;
8962
8963 newCtrl.createObject();
8964 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8965 mUSBControllers->push_back(newCtrl);
8966 }
8967
8968 /* USB device filters */
8969 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8970 if (FAILED(rc)) return rc;
8971
8972 // network adapters
8973 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8974 size_t oldCount = mNetworkAdapters.size();
8975 if (newCount > oldCount)
8976 {
8977 mNetworkAdapters.resize(newCount);
8978 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8979 {
8980 unconst(mNetworkAdapters[slot]).createObject();
8981 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8982 }
8983 }
8984 else if (newCount < oldCount)
8985 mNetworkAdapters.resize(newCount);
8986 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8987 it != data.llNetworkAdapters.end();
8988 ++it)
8989 {
8990 const settings::NetworkAdapter &nic = *it;
8991
8992 /* slot unicity is guaranteed by XML Schema */
8993 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8994 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8995 if (FAILED(rc)) return rc;
8996 }
8997
8998 // serial ports
8999 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9000 it != data.llSerialPorts.end();
9001 ++it)
9002 {
9003 const settings::SerialPort &s = *it;
9004
9005 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9006 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9007 if (FAILED(rc)) return rc;
9008 }
9009
9010 // parallel ports (optional)
9011 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9012 it != data.llParallelPorts.end();
9013 ++it)
9014 {
9015 const settings::ParallelPort &p = *it;
9016
9017 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9018 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9019 if (FAILED(rc)) return rc;
9020 }
9021
9022 /* AudioAdapter */
9023 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9024 if (FAILED(rc)) return rc;
9025
9026 /* storage controllers */
9027 rc = i_loadStorageControllers(data.storage,
9028 puuidRegistry,
9029 puuidSnapshot);
9030 if (FAILED(rc)) return rc;
9031
9032 /* Shared folders */
9033 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9034 it != data.llSharedFolders.end();
9035 ++it)
9036 {
9037 const settings::SharedFolder &sf = *it;
9038
9039 ComObjPtr<SharedFolder> sharedFolder;
9040 /* Check for double entries. Not allowed! */
9041 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9042 if (SUCCEEDED(rc))
9043 return setError(VBOX_E_OBJECT_IN_USE,
9044 tr("Shared folder named '%s' already exists"),
9045 sf.strName.c_str());
9046
9047 /* Create the new shared folder. Don't break on error. This will be
9048 * reported when the machine starts. */
9049 sharedFolder.createObject();
9050 rc = sharedFolder->init(i_getMachine(),
9051 sf.strName,
9052 sf.strHostPath,
9053 RT_BOOL(sf.fWritable),
9054 RT_BOOL(sf.fAutoMount),
9055 false /* fFailOnError */);
9056 if (FAILED(rc)) return rc;
9057 mHWData->mSharedFolders.push_back(sharedFolder);
9058 }
9059
9060 // Clipboard
9061 mHWData->mClipboardMode = data.clipboardMode;
9062
9063 // drag'n'drop
9064 mHWData->mDnDMode = data.dndMode;
9065
9066 // guest settings
9067 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9068
9069 // IO settings
9070 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9071 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9072
9073 // Host PCI devices
9074 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9075 it != data.pciAttachments.end();
9076 ++it)
9077 {
9078 const settings::HostPCIDeviceAttachment &hpda = *it;
9079 ComObjPtr<PCIDeviceAttachment> pda;
9080
9081 pda.createObject();
9082 pda->i_loadSettings(this, hpda);
9083 mHWData->mPCIDeviceAssignments.push_back(pda);
9084 }
9085
9086 /*
9087 * (The following isn't really real hardware, but it lives in HWData
9088 * for reasons of convenience.)
9089 */
9090
9091#ifdef VBOX_WITH_GUEST_PROPS
9092 /* Guest properties (optional) */
9093
9094 /* Only load transient guest properties for configs which have saved
9095 * state, because there shouldn't be any for powered off VMs. The same
9096 * logic applies for snapshots, as offline snapshots shouldn't have
9097 * any such properties. They confuse the code in various places.
9098 * Note: can't rely on the machine state, as it isn't set yet. */
9099 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9100 /* apologies for the hacky unconst() usage, but this needs hacking
9101 * actually inconsistent settings into consistency, otherwise there
9102 * will be some corner cases where the inconsistency survives
9103 * surprisingly long without getting fixed, especially for snapshots
9104 * as there are no config changes. */
9105 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9106 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
9107 it != llGuestProperties.end();
9108 /*nothing*/)
9109 {
9110 const settings::GuestProperty &prop = *it;
9111 uint32_t fFlags = guestProp::NILFLAG;
9112 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9113 if ( fSkipTransientGuestProperties
9114 && ( fFlags & guestProp::TRANSIENT
9115 || fFlags & guestProp::TRANSRESET))
9116 {
9117 it = llGuestProperties.erase(it);
9118 continue;
9119 }
9120 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9121 mHWData->mGuestProperties[prop.strName] = property;
9122 ++it;
9123 }
9124#endif /* VBOX_WITH_GUEST_PROPS defined */
9125
9126 rc = i_loadDebugging(pDbg);
9127 if (FAILED(rc))
9128 return rc;
9129
9130 mHWData->mAutostart = *pAutostart;
9131
9132 /* default frontend */
9133 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9134 }
9135 catch(std::bad_alloc &)
9136 {
9137 return E_OUTOFMEMORY;
9138 }
9139
9140 AssertComRC(rc);
9141 return rc;
9142}
9143
9144/**
9145 * Called from Machine::loadHardware() to load the debugging settings of the
9146 * machine.
9147 *
9148 * @param pDbg Pointer to the settings.
9149 */
9150HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9151{
9152 mHWData->mDebugging = *pDbg;
9153 /* no more processing currently required, this will probably change. */
9154 return S_OK;
9155}
9156
9157/**
9158 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9159 *
9160 * @param data
9161 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9162 * @param puuidSnapshot
9163 * @return
9164 */
9165HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9166 const Guid *puuidRegistry,
9167 const Guid *puuidSnapshot)
9168{
9169 AssertReturn(!i_isSessionMachine(), E_FAIL);
9170
9171 HRESULT rc = S_OK;
9172
9173 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9174 it != data.llStorageControllers.end();
9175 ++it)
9176 {
9177 const settings::StorageController &ctlData = *it;
9178
9179 ComObjPtr<StorageController> pCtl;
9180 /* Try to find one with the name first. */
9181 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9182 if (SUCCEEDED(rc))
9183 return setError(VBOX_E_OBJECT_IN_USE,
9184 tr("Storage controller named '%s' already exists"),
9185 ctlData.strName.c_str());
9186
9187 pCtl.createObject();
9188 rc = pCtl->init(this,
9189 ctlData.strName,
9190 ctlData.storageBus,
9191 ctlData.ulInstance,
9192 ctlData.fBootable);
9193 if (FAILED(rc)) return rc;
9194
9195 mStorageControllers->push_back(pCtl);
9196
9197 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9198 if (FAILED(rc)) return rc;
9199
9200 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9201 if (FAILED(rc)) return rc;
9202
9203 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9204 if (FAILED(rc)) return rc;
9205
9206 /* Load the attached devices now. */
9207 rc = i_loadStorageDevices(pCtl,
9208 ctlData,
9209 puuidRegistry,
9210 puuidSnapshot);
9211 if (FAILED(rc)) return rc;
9212 }
9213
9214 return S_OK;
9215}
9216
9217/**
9218 * Called from i_loadStorageControllers for a controller's devices.
9219 *
9220 * @param aStorageController
9221 * @param data
9222 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9223 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9224 * @return
9225 */
9226HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9227 const settings::StorageController &data,
9228 const Guid *puuidRegistry,
9229 const Guid *puuidSnapshot)
9230{
9231 HRESULT rc = S_OK;
9232
9233 /* paranoia: detect duplicate attachments */
9234 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9235 it != data.llAttachedDevices.end();
9236 ++it)
9237 {
9238 const settings::AttachedDevice &ad = *it;
9239
9240 for (settings::AttachedDevicesList::const_iterator it2 = it;
9241 it2 != data.llAttachedDevices.end();
9242 ++it2)
9243 {
9244 if (it == it2)
9245 continue;
9246
9247 const settings::AttachedDevice &ad2 = *it2;
9248
9249 if ( ad.lPort == ad2.lPort
9250 && ad.lDevice == ad2.lDevice)
9251 {
9252 return setError(E_FAIL,
9253 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9254 aStorageController->i_getName().c_str(),
9255 ad.lPort,
9256 ad.lDevice,
9257 mUserData->s.strName.c_str());
9258 }
9259 }
9260 }
9261
9262 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9263 it != data.llAttachedDevices.end();
9264 ++it)
9265 {
9266 const settings::AttachedDevice &dev = *it;
9267 ComObjPtr<Medium> medium;
9268
9269 switch (dev.deviceType)
9270 {
9271 case DeviceType_Floppy:
9272 case DeviceType_DVD:
9273 if (dev.strHostDriveSrc.isNotEmpty())
9274 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9275 false /* fRefresh */, medium);
9276 else
9277 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9278 dev.uuid,
9279 false /* fRefresh */,
9280 false /* aSetError */,
9281 medium);
9282 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9283 // This is not an error. The host drive or UUID might have vanished, so just go
9284 // ahead without this removeable medium attachment
9285 rc = S_OK;
9286 break;
9287
9288 case DeviceType_HardDisk:
9289 {
9290 /* find a hard disk by UUID */
9291 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9292 if (FAILED(rc))
9293 {
9294 if (i_isSnapshotMachine())
9295 {
9296 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9297 // so the user knows that the bad disk is in a snapshot somewhere
9298 com::ErrorInfo info;
9299 return setError(E_FAIL,
9300 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9301 puuidSnapshot->raw(),
9302 info.getText().raw());
9303 }
9304 else
9305 return rc;
9306 }
9307
9308 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9309
9310 if (medium->i_getType() == MediumType_Immutable)
9311 {
9312 if (i_isSnapshotMachine())
9313 return setError(E_FAIL,
9314 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9315 "of the virtual machine '%s' ('%s')"),
9316 medium->i_getLocationFull().c_str(),
9317 dev.uuid.raw(),
9318 puuidSnapshot->raw(),
9319 mUserData->s.strName.c_str(),
9320 mData->m_strConfigFileFull.c_str());
9321
9322 return setError(E_FAIL,
9323 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9324 medium->i_getLocationFull().c_str(),
9325 dev.uuid.raw(),
9326 mUserData->s.strName.c_str(),
9327 mData->m_strConfigFileFull.c_str());
9328 }
9329
9330 if (medium->i_getType() == MediumType_MultiAttach)
9331 {
9332 if (i_isSnapshotMachine())
9333 return setError(E_FAIL,
9334 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9335 "of the virtual machine '%s' ('%s')"),
9336 medium->i_getLocationFull().c_str(),
9337 dev.uuid.raw(),
9338 puuidSnapshot->raw(),
9339 mUserData->s.strName.c_str(),
9340 mData->m_strConfigFileFull.c_str());
9341
9342 return setError(E_FAIL,
9343 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9344 medium->i_getLocationFull().c_str(),
9345 dev.uuid.raw(),
9346 mUserData->s.strName.c_str(),
9347 mData->m_strConfigFileFull.c_str());
9348 }
9349
9350 if ( !i_isSnapshotMachine()
9351 && medium->i_getChildren().size() != 0
9352 )
9353 return setError(E_FAIL,
9354 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9355 "because it has %d differencing child hard disks"),
9356 medium->i_getLocationFull().c_str(),
9357 dev.uuid.raw(),
9358 mUserData->s.strName.c_str(),
9359 mData->m_strConfigFileFull.c_str(),
9360 medium->i_getChildren().size());
9361
9362 if (i_findAttachment(mMediaData->mAttachments,
9363 medium))
9364 return setError(E_FAIL,
9365 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9366 medium->i_getLocationFull().c_str(),
9367 dev.uuid.raw(),
9368 mUserData->s.strName.c_str(),
9369 mData->m_strConfigFileFull.c_str());
9370
9371 break;
9372 }
9373
9374 default:
9375 return setError(E_FAIL,
9376 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9377 medium->i_getLocationFull().c_str(),
9378 mUserData->s.strName.c_str(),
9379 mData->m_strConfigFileFull.c_str());
9380 }
9381
9382 if (FAILED(rc))
9383 break;
9384
9385 /* Bandwidth groups are loaded at this point. */
9386 ComObjPtr<BandwidthGroup> pBwGroup;
9387
9388 if (!dev.strBwGroup.isEmpty())
9389 {
9390 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9391 if (FAILED(rc))
9392 return setError(E_FAIL,
9393 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9394 medium->i_getLocationFull().c_str(),
9395 dev.strBwGroup.c_str(),
9396 mUserData->s.strName.c_str(),
9397 mData->m_strConfigFileFull.c_str());
9398 pBwGroup->i_reference();
9399 }
9400
9401 const Bstr controllerName = aStorageController->i_getName();
9402 ComObjPtr<MediumAttachment> pAttachment;
9403 pAttachment.createObject();
9404 rc = pAttachment->init(this,
9405 medium,
9406 controllerName,
9407 dev.lPort,
9408 dev.lDevice,
9409 dev.deviceType,
9410 false,
9411 dev.fPassThrough,
9412 dev.fTempEject,
9413 dev.fNonRotational,
9414 dev.fDiscard,
9415 dev.fHotPluggable,
9416 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9417 if (FAILED(rc)) break;
9418
9419 /* associate the medium with this machine and snapshot */
9420 if (!medium.isNull())
9421 {
9422 AutoCaller medCaller(medium);
9423 if (FAILED(medCaller.rc())) return medCaller.rc();
9424 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9425
9426 if (i_isSnapshotMachine())
9427 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9428 else
9429 rc = medium->i_addBackReference(mData->mUuid);
9430 /* If the medium->addBackReference fails it sets an appropriate
9431 * error message, so no need to do any guesswork here. */
9432
9433 if (puuidRegistry)
9434 // caller wants registry ID to be set on all attached media (OVF import case)
9435 medium->i_addRegistry(*puuidRegistry);
9436 }
9437
9438 if (FAILED(rc))
9439 break;
9440
9441 /* back up mMediaData to let registeredInit() properly rollback on failure
9442 * (= limited accessibility) */
9443 i_setModified(IsModified_Storage);
9444 mMediaData.backup();
9445 mMediaData->mAttachments.push_back(pAttachment);
9446 }
9447
9448 return rc;
9449}
9450
9451/**
9452 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9453 *
9454 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9455 * @param aSnapshot where to return the found snapshot
9456 * @param aSetError true to set extended error info on failure
9457 */
9458HRESULT Machine::i_findSnapshotById(const Guid &aId,
9459 ComObjPtr<Snapshot> &aSnapshot,
9460 bool aSetError /* = false */)
9461{
9462 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9463
9464 if (!mData->mFirstSnapshot)
9465 {
9466 if (aSetError)
9467 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9468 return E_FAIL;
9469 }
9470
9471 if (aId.isZero())
9472 aSnapshot = mData->mFirstSnapshot;
9473 else
9474 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9475
9476 if (!aSnapshot)
9477 {
9478 if (aSetError)
9479 return setError(E_FAIL,
9480 tr("Could not find a snapshot with UUID {%s}"),
9481 aId.toString().c_str());
9482 return E_FAIL;
9483 }
9484
9485 return S_OK;
9486}
9487
9488/**
9489 * Returns the snapshot with the given name or fails of no such snapshot.
9490 *
9491 * @param aName snapshot name to find
9492 * @param aSnapshot where to return the found snapshot
9493 * @param aSetError true to set extended error info on failure
9494 */
9495HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9496 ComObjPtr<Snapshot> &aSnapshot,
9497 bool aSetError /* = false */)
9498{
9499 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9500
9501 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9502
9503 if (!mData->mFirstSnapshot)
9504 {
9505 if (aSetError)
9506 return setError(VBOX_E_OBJECT_NOT_FOUND,
9507 tr("This machine does not have any snapshots"));
9508 return VBOX_E_OBJECT_NOT_FOUND;
9509 }
9510
9511 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9512
9513 if (!aSnapshot)
9514 {
9515 if (aSetError)
9516 return setError(VBOX_E_OBJECT_NOT_FOUND,
9517 tr("Could not find a snapshot named '%s'"), strName.c_str());
9518 return VBOX_E_OBJECT_NOT_FOUND;
9519 }
9520
9521 return S_OK;
9522}
9523
9524/**
9525 * Returns a storage controller object with the given name.
9526 *
9527 * @param aName storage controller name to find
9528 * @param aStorageController where to return the found storage controller
9529 * @param aSetError true to set extended error info on failure
9530 */
9531HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9532 ComObjPtr<StorageController> &aStorageController,
9533 bool aSetError /* = false */)
9534{
9535 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9536
9537 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9538 it != mStorageControllers->end();
9539 ++it)
9540 {
9541 if ((*it)->i_getName() == aName)
9542 {
9543 aStorageController = (*it);
9544 return S_OK;
9545 }
9546 }
9547
9548 if (aSetError)
9549 return setError(VBOX_E_OBJECT_NOT_FOUND,
9550 tr("Could not find a storage controller named '%s'"),
9551 aName.c_str());
9552 return VBOX_E_OBJECT_NOT_FOUND;
9553}
9554
9555/**
9556 * Returns a USB controller object with the given name.
9557 *
9558 * @param aName USB controller name to find
9559 * @param aUSBController where to return the found USB controller
9560 * @param aSetError true to set extended error info on failure
9561 */
9562HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9563 ComObjPtr<USBController> &aUSBController,
9564 bool aSetError /* = false */)
9565{
9566 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9567
9568 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9569 it != mUSBControllers->end();
9570 ++it)
9571 {
9572 if ((*it)->i_getName() == aName)
9573 {
9574 aUSBController = (*it);
9575 return S_OK;
9576 }
9577 }
9578
9579 if (aSetError)
9580 return setError(VBOX_E_OBJECT_NOT_FOUND,
9581 tr("Could not find a storage controller named '%s'"),
9582 aName.c_str());
9583 return VBOX_E_OBJECT_NOT_FOUND;
9584}
9585
9586/**
9587 * Returns the number of USB controller instance of the given type.
9588 *
9589 * @param enmType USB controller type.
9590 */
9591ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9592{
9593 ULONG cCtrls = 0;
9594
9595 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9596 it != mUSBControllers->end();
9597 ++it)
9598 {
9599 if ((*it)->i_getControllerType() == enmType)
9600 cCtrls++;
9601 }
9602
9603 return cCtrls;
9604}
9605
9606HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9607 MediaData::AttachmentList &atts)
9608{
9609 AutoCaller autoCaller(this);
9610 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9611
9612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9613
9614 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9615 it != mMediaData->mAttachments.end();
9616 ++it)
9617 {
9618 const ComObjPtr<MediumAttachment> &pAtt = *it;
9619 // should never happen, but deal with NULL pointers in the list.
9620 AssertContinue(!pAtt.isNull());
9621
9622 // getControllerName() needs caller+read lock
9623 AutoCaller autoAttCaller(pAtt);
9624 if (FAILED(autoAttCaller.rc()))
9625 {
9626 atts.clear();
9627 return autoAttCaller.rc();
9628 }
9629 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9630
9631 if (pAtt->i_getControllerName() == aName)
9632 atts.push_back(pAtt);
9633 }
9634
9635 return S_OK;
9636}
9637
9638
9639/**
9640 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9641 * file if the machine name was changed and about creating a new settings file
9642 * if this is a new machine.
9643 *
9644 * @note Must be never called directly but only from #saveSettings().
9645 */
9646HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9647{
9648 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9649
9650 HRESULT rc = S_OK;
9651
9652 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9653
9654 /// @todo need to handle primary group change, too
9655
9656 /* attempt to rename the settings file if machine name is changed */
9657 if ( mUserData->s.fNameSync
9658 && mUserData.isBackedUp()
9659 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9660 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9661 )
9662 {
9663 bool dirRenamed = false;
9664 bool fileRenamed = false;
9665
9666 Utf8Str configFile, newConfigFile;
9667 Utf8Str configFilePrev, newConfigFilePrev;
9668 Utf8Str configDir, newConfigDir;
9669
9670 do
9671 {
9672 int vrc = VINF_SUCCESS;
9673
9674 Utf8Str name = mUserData.backedUpData()->s.strName;
9675 Utf8Str newName = mUserData->s.strName;
9676 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9677 if (group == "/")
9678 group.setNull();
9679 Utf8Str newGroup = mUserData->s.llGroups.front();
9680 if (newGroup == "/")
9681 newGroup.setNull();
9682
9683 configFile = mData->m_strConfigFileFull;
9684
9685 /* first, rename the directory if it matches the group and machine name */
9686 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9687 group.c_str(), RTPATH_DELIMITER, name.c_str());
9688 /** @todo hack, make somehow use of ComposeMachineFilename */
9689 if (mUserData->s.fDirectoryIncludesUUID)
9690 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9691 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9692 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9693 /** @todo hack, make somehow use of ComposeMachineFilename */
9694 if (mUserData->s.fDirectoryIncludesUUID)
9695 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9696 configDir = configFile;
9697 configDir.stripFilename();
9698 newConfigDir = configDir;
9699 if ( configDir.length() >= groupPlusName.length()
9700 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9701 groupPlusName.c_str()))
9702 {
9703 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9704 Utf8Str newConfigBaseDir(newConfigDir);
9705 newConfigDir.append(newGroupPlusName);
9706 /* consistency: use \ if appropriate on the platform */
9707 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9708 /* new dir and old dir cannot be equal here because of 'if'
9709 * above and because name != newName */
9710 Assert(configDir != newConfigDir);
9711 if (!fSettingsFileIsNew)
9712 {
9713 /* perform real rename only if the machine is not new */
9714 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9715 if ( vrc == VERR_FILE_NOT_FOUND
9716 || vrc == VERR_PATH_NOT_FOUND)
9717 {
9718 /* create the parent directory, then retry renaming */
9719 Utf8Str parent(newConfigDir);
9720 parent.stripFilename();
9721 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9722 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9723 }
9724 if (RT_FAILURE(vrc))
9725 {
9726 rc = setError(E_FAIL,
9727 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9728 configDir.c_str(),
9729 newConfigDir.c_str(),
9730 vrc);
9731 break;
9732 }
9733 /* delete subdirectories which are no longer needed */
9734 Utf8Str dir(configDir);
9735 dir.stripFilename();
9736 while (dir != newConfigBaseDir && dir != ".")
9737 {
9738 vrc = RTDirRemove(dir.c_str());
9739 if (RT_FAILURE(vrc))
9740 break;
9741 dir.stripFilename();
9742 }
9743 dirRenamed = true;
9744 }
9745 }
9746
9747 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9748 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9749
9750 /* then try to rename the settings file itself */
9751 if (newConfigFile != configFile)
9752 {
9753 /* get the path to old settings file in renamed directory */
9754 configFile = Utf8StrFmt("%s%c%s",
9755 newConfigDir.c_str(),
9756 RTPATH_DELIMITER,
9757 RTPathFilename(configFile.c_str()));
9758 if (!fSettingsFileIsNew)
9759 {
9760 /* perform real rename only if the machine is not new */
9761 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9762 if (RT_FAILURE(vrc))
9763 {
9764 rc = setError(E_FAIL,
9765 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9766 configFile.c_str(),
9767 newConfigFile.c_str(),
9768 vrc);
9769 break;
9770 }
9771 fileRenamed = true;
9772 configFilePrev = configFile;
9773 configFilePrev += "-prev";
9774 newConfigFilePrev = newConfigFile;
9775 newConfigFilePrev += "-prev";
9776 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9777 }
9778 }
9779
9780 // update m_strConfigFileFull amd mConfigFile
9781 mData->m_strConfigFileFull = newConfigFile;
9782 // compute the relative path too
9783 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9784
9785 // store the old and new so that VirtualBox::i_saveSettings() can update
9786 // the media registry
9787 if ( mData->mRegistered
9788 && (configDir != newConfigDir || configFile != newConfigFile))
9789 {
9790 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9791
9792 if (pfNeedsGlobalSaveSettings)
9793 *pfNeedsGlobalSaveSettings = true;
9794 }
9795
9796 // in the saved state file path, replace the old directory with the new directory
9797 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9798 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9799
9800 // and do the same thing for the saved state file paths of all the online snapshots
9801 if (mData->mFirstSnapshot)
9802 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9803 newConfigDir.c_str());
9804 }
9805 while (0);
9806
9807 if (FAILED(rc))
9808 {
9809 /* silently try to rename everything back */
9810 if (fileRenamed)
9811 {
9812 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9813 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9814 }
9815 if (dirRenamed)
9816 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9817 }
9818
9819 if (FAILED(rc)) return rc;
9820 }
9821
9822 if (fSettingsFileIsNew)
9823 {
9824 /* create a virgin config file */
9825 int vrc = VINF_SUCCESS;
9826
9827 /* ensure the settings directory exists */
9828 Utf8Str path(mData->m_strConfigFileFull);
9829 path.stripFilename();
9830 if (!RTDirExists(path.c_str()))
9831 {
9832 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9833 if (RT_FAILURE(vrc))
9834 {
9835 return setError(E_FAIL,
9836 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9837 path.c_str(),
9838 vrc);
9839 }
9840 }
9841
9842 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9843 path = Utf8Str(mData->m_strConfigFileFull);
9844 RTFILE f = NIL_RTFILE;
9845 vrc = RTFileOpen(&f, path.c_str(),
9846 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9847 if (RT_FAILURE(vrc))
9848 return setError(E_FAIL,
9849 tr("Could not create the settings file '%s' (%Rrc)"),
9850 path.c_str(),
9851 vrc);
9852 RTFileClose(f);
9853 }
9854
9855 return rc;
9856}
9857
9858/**
9859 * Saves and commits machine data, user data and hardware data.
9860 *
9861 * Note that on failure, the data remains uncommitted.
9862 *
9863 * @a aFlags may combine the following flags:
9864 *
9865 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9866 * Used when saving settings after an operation that makes them 100%
9867 * correspond to the settings from the current snapshot.
9868 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9869 * #isReallyModified() returns false. This is necessary for cases when we
9870 * change machine data directly, not through the backup()/commit() mechanism.
9871 * - SaveS_Force: settings will be saved without doing a deep compare of the
9872 * settings structures. This is used when this is called because snapshots
9873 * have changed to avoid the overhead of the deep compare.
9874 *
9875 * @note Must be called from under this object's write lock. Locks children for
9876 * writing.
9877 *
9878 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9879 * initialized to false and that will be set to true by this function if
9880 * the caller must invoke VirtualBox::i_saveSettings() because the global
9881 * settings have changed. This will happen if a machine rename has been
9882 * saved and the global machine and media registries will therefore need
9883 * updating.
9884 */
9885HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9886 int aFlags /*= 0*/)
9887{
9888 LogFlowThisFuncEnter();
9889
9890 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9891
9892 /* make sure child objects are unable to modify the settings while we are
9893 * saving them */
9894 i_ensureNoStateDependencies();
9895
9896 AssertReturn(!i_isSnapshotMachine(),
9897 E_FAIL);
9898
9899 HRESULT rc = S_OK;
9900 bool fNeedsWrite = false;
9901
9902 /* First, prepare to save settings. It will care about renaming the
9903 * settings directory and file if the machine name was changed and about
9904 * creating a new settings file if this is a new machine. */
9905 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9906 if (FAILED(rc)) return rc;
9907
9908 // keep a pointer to the current settings structures
9909 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9910 settings::MachineConfigFile *pNewConfig = NULL;
9911
9912 try
9913 {
9914 // make a fresh one to have everyone write stuff into
9915 pNewConfig = new settings::MachineConfigFile(NULL);
9916 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9917
9918 // now go and copy all the settings data from COM to the settings structures
9919 // (this calles i_saveSettings() on all the COM objects in the machine)
9920 i_copyMachineDataToSettings(*pNewConfig);
9921
9922 if (aFlags & SaveS_ResetCurStateModified)
9923 {
9924 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9925 mData->mCurrentStateModified = FALSE;
9926 fNeedsWrite = true; // always, no need to compare
9927 }
9928 else if (aFlags & SaveS_Force)
9929 {
9930 fNeedsWrite = true; // always, no need to compare
9931 }
9932 else
9933 {
9934 if (!mData->mCurrentStateModified)
9935 {
9936 // do a deep compare of the settings that we just saved with the settings
9937 // previously stored in the config file; this invokes MachineConfigFile::operator==
9938 // which does a deep compare of all the settings, which is expensive but less expensive
9939 // than writing out XML in vain
9940 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9941
9942 // could still be modified if any settings changed
9943 mData->mCurrentStateModified = fAnySettingsChanged;
9944
9945 fNeedsWrite = fAnySettingsChanged;
9946 }
9947 else
9948 fNeedsWrite = true;
9949 }
9950
9951 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9952
9953 if (fNeedsWrite)
9954 // now spit it all out!
9955 pNewConfig->write(mData->m_strConfigFileFull);
9956
9957 mData->pMachineConfigFile = pNewConfig;
9958 delete pOldConfig;
9959 i_commit();
9960
9961 // after saving settings, we are no longer different from the XML on disk
9962 mData->flModifications = 0;
9963 }
9964 catch (HRESULT err)
9965 {
9966 // we assume that error info is set by the thrower
9967 rc = err;
9968
9969 // restore old config
9970 delete pNewConfig;
9971 mData->pMachineConfigFile = pOldConfig;
9972 }
9973 catch (...)
9974 {
9975 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9976 }
9977
9978 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9979 {
9980 /* Fire the data change event, even on failure (since we've already
9981 * committed all data). This is done only for SessionMachines because
9982 * mutable Machine instances are always not registered (i.e. private
9983 * to the client process that creates them) and thus don't need to
9984 * inform callbacks. */
9985 if (i_isSessionMachine())
9986 mParent->i_onMachineDataChange(mData->mUuid);
9987 }
9988
9989 LogFlowThisFunc(("rc=%08X\n", rc));
9990 LogFlowThisFuncLeave();
9991 return rc;
9992}
9993
9994/**
9995 * Implementation for saving the machine settings into the given
9996 * settings::MachineConfigFile instance. This copies machine extradata
9997 * from the previous machine config file in the instance data, if any.
9998 *
9999 * This gets called from two locations:
10000 *
10001 * -- Machine::i_saveSettings(), during the regular XML writing;
10002 *
10003 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10004 * exported to OVF and we write the VirtualBox proprietary XML
10005 * into a <vbox:Machine> tag.
10006 *
10007 * This routine fills all the fields in there, including snapshots, *except*
10008 * for the following:
10009 *
10010 * -- fCurrentStateModified. There is some special logic associated with that.
10011 *
10012 * The caller can then call MachineConfigFile::write() or do something else
10013 * with it.
10014 *
10015 * Caller must hold the machine lock!
10016 *
10017 * This throws XML errors and HRESULT, so the caller must have a catch block!
10018 */
10019void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10020{
10021 // deep copy extradata
10022 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10023
10024 config.uuid = mData->mUuid;
10025
10026 // copy name, description, OS type, teleport, UTC etc.
10027 config.machineUserData = mUserData->s;
10028
10029 if ( mData->mMachineState == MachineState_Saved
10030 || mData->mMachineState == MachineState_Restoring
10031 // when doing certain snapshot operations we may or may not have
10032 // a saved state in the current state, so keep everything as is
10033 || ( ( mData->mMachineState == MachineState_Snapshotting
10034 || mData->mMachineState == MachineState_DeletingSnapshot
10035 || mData->mMachineState == MachineState_RestoringSnapshot)
10036 && (!mSSData->strStateFilePath.isEmpty())
10037 )
10038 )
10039 {
10040 Assert(!mSSData->strStateFilePath.isEmpty());
10041 /* try to make the file name relative to the settings file dir */
10042 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10043 }
10044 else
10045 {
10046 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10047 config.strStateFile.setNull();
10048 }
10049
10050 if (mData->mCurrentSnapshot)
10051 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10052 else
10053 config.uuidCurrentSnapshot.clear();
10054
10055 config.timeLastStateChange = mData->mLastStateChange;
10056 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10057 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10058
10059 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10060 if (FAILED(rc)) throw rc;
10061
10062 // save machine's media registry if this is VirtualBox 4.0 or later
10063 if (config.canHaveOwnMediaRegistry())
10064 {
10065 // determine machine folder
10066 Utf8Str strMachineFolder = i_getSettingsFileFull();
10067 strMachineFolder.stripFilename();
10068 mParent->i_saveMediaRegistry(config.mediaRegistry,
10069 i_getId(), // only media with registry ID == machine UUID
10070 strMachineFolder);
10071 // this throws HRESULT
10072 }
10073
10074 // save snapshots
10075 rc = i_saveAllSnapshots(config);
10076 if (FAILED(rc)) throw rc;
10077}
10078
10079/**
10080 * Saves all snapshots of the machine into the given machine config file. Called
10081 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10082 * @param config
10083 * @return
10084 */
10085HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10086{
10087 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10088
10089 HRESULT rc = S_OK;
10090
10091 try
10092 {
10093 config.llFirstSnapshot.clear();
10094
10095 if (mData->mFirstSnapshot)
10096 {
10097 // the settings use a list for "the first snapshot"
10098 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10099
10100 // get reference to the snapshot on the list and work on that
10101 // element straight in the list to avoid excessive copying later
10102 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10103 if (FAILED(rc)) throw rc;
10104 }
10105
10106// if (mType == IsSessionMachine)
10107// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10108
10109 }
10110 catch (HRESULT err)
10111 {
10112 /* we assume that error info is set by the thrower */
10113 rc = err;
10114 }
10115 catch (...)
10116 {
10117 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10118 }
10119
10120 return rc;
10121}
10122
10123/**
10124 * Saves the VM hardware configuration. It is assumed that the
10125 * given node is empty.
10126 *
10127 * @param data Reference to the settings object for the hardware config.
10128 * @param pDbg Pointer to the settings object for the debugging config
10129 * which happens to live in mHWData.
10130 * @param pAutostart Pointer to the settings object for the autostart config
10131 * which happens to live in mHWData.
10132 */
10133HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10134 settings::Autostart *pAutostart)
10135{
10136 HRESULT rc = S_OK;
10137
10138 try
10139 {
10140 /* The hardware version attribute (optional).
10141 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10142 if ( mHWData->mHWVersion == "1"
10143 && mSSData->strStateFilePath.isEmpty()
10144 )
10145 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10146 other point needs to be found where this can be done. */
10147
10148 data.strVersion = mHWData->mHWVersion;
10149 data.uuid = mHWData->mHardwareUUID;
10150
10151 // CPU
10152 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10153 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10154 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10155 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10156 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10157 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10158 data.fPAE = !!mHWData->mPAEEnabled;
10159 data.enmLongMode = mHWData->mLongMode;
10160 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10161 data.cCPUs = mHWData->mCPUCount;
10162 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10163 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10164 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10165 data.strCpuProfile = mHWData->mCpuProfile;
10166
10167 data.llCpus.clear();
10168 if (data.fCpuHotPlug)
10169 {
10170 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10171 {
10172 if (mHWData->mCPUAttached[idx])
10173 {
10174 settings::Cpu cpu;
10175 cpu.ulId = idx;
10176 data.llCpus.push_back(cpu);
10177 }
10178 }
10179 }
10180
10181 /* Standard and Extended CPUID leafs. */
10182 data.llCpuIdLeafs.clear();
10183 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10184 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10185 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10186 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10187 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10188 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10189
10190 // memory
10191 data.ulMemorySizeMB = mHWData->mMemorySize;
10192 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10193
10194 // firmware
10195 data.firmwareType = mHWData->mFirmwareType;
10196
10197 // HID
10198 data.pointingHIDType = mHWData->mPointingHIDType;
10199 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10200
10201 // chipset
10202 data.chipsetType = mHWData->mChipsetType;
10203
10204 // paravirt
10205 data.paravirtProvider = mHWData->mParavirtProvider;
10206 data.strParavirtDebug = mHWData->mParavirtDebug;
10207
10208 // emulated USB card reader
10209 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10210
10211 // HPET
10212 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10213
10214 // boot order
10215 data.mapBootOrder.clear();
10216 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10217 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10218
10219 // display
10220 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10221 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10222 data.cMonitors = mHWData->mMonitorCount;
10223 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10224 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10225 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10226 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10227 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10228 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10229 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10230 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10231 {
10232 if (mHWData->maVideoCaptureScreens[i])
10233 ASMBitSet(&data.u64VideoCaptureScreens, i);
10234 else
10235 ASMBitClear(&data.u64VideoCaptureScreens, i);
10236 }
10237 /* store relative video capture file if possible */
10238 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10239
10240 /* VRDEServer settings (optional) */
10241 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10242 if (FAILED(rc)) throw rc;
10243
10244 /* BIOS (required) */
10245 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10246 if (FAILED(rc)) throw rc;
10247
10248 /* USB Controller (required) */
10249 data.usbSettings.llUSBControllers.clear();
10250 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10251 {
10252 ComObjPtr<USBController> ctrl = *it;
10253 settings::USBController settingsCtrl;
10254
10255 settingsCtrl.strName = ctrl->i_getName();
10256 settingsCtrl.enmType = ctrl->i_getControllerType();
10257
10258 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10259 }
10260
10261 /* USB device filters (required) */
10262 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10263 if (FAILED(rc)) throw rc;
10264
10265 /* Network adapters (required) */
10266 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10267 data.llNetworkAdapters.clear();
10268 /* Write out only the nominal number of network adapters for this
10269 * chipset type. Since Machine::commit() hasn't been called there
10270 * may be extra NIC settings in the vector. */
10271 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10272 {
10273 settings::NetworkAdapter nic;
10274 nic.ulSlot = (uint32_t)slot;
10275 /* paranoia check... must not be NULL, but must not crash either. */
10276 if (mNetworkAdapters[slot])
10277 {
10278 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10279 if (FAILED(rc)) throw rc;
10280
10281 data.llNetworkAdapters.push_back(nic);
10282 }
10283 }
10284
10285 /* Serial ports */
10286 data.llSerialPorts.clear();
10287 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10288 {
10289 if (mSerialPorts[slot]->i_hasDefaults())
10290 continue;
10291
10292 settings::SerialPort s;
10293 s.ulSlot = slot;
10294 rc = mSerialPorts[slot]->i_saveSettings(s);
10295 if (FAILED(rc)) return rc;
10296
10297 data.llSerialPorts.push_back(s);
10298 }
10299
10300 /* Parallel ports */
10301 data.llParallelPorts.clear();
10302 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10303 {
10304 if (mParallelPorts[slot]->i_hasDefaults())
10305 continue;
10306
10307 settings::ParallelPort p;
10308 p.ulSlot = slot;
10309 rc = mParallelPorts[slot]->i_saveSettings(p);
10310 if (FAILED(rc)) return rc;
10311
10312 data.llParallelPorts.push_back(p);
10313 }
10314
10315 /* Audio adapter */
10316 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10317 if (FAILED(rc)) return rc;
10318
10319 rc = i_saveStorageControllers(data.storage);
10320 if (FAILED(rc)) return rc;
10321
10322 /* Shared folders */
10323 data.llSharedFolders.clear();
10324 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10325 it != mHWData->mSharedFolders.end();
10326 ++it)
10327 {
10328 SharedFolder *pSF = *it;
10329 AutoCaller sfCaller(pSF);
10330 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10331 settings::SharedFolder sf;
10332 sf.strName = pSF->i_getName();
10333 sf.strHostPath = pSF->i_getHostPath();
10334 sf.fWritable = !!pSF->i_isWritable();
10335 sf.fAutoMount = !!pSF->i_isAutoMounted();
10336
10337 data.llSharedFolders.push_back(sf);
10338 }
10339
10340 // clipboard
10341 data.clipboardMode = mHWData->mClipboardMode;
10342
10343 // drag'n'drop
10344 data.dndMode = mHWData->mDnDMode;
10345
10346 /* Guest */
10347 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10348
10349 // IO settings
10350 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10351 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10352
10353 /* BandwidthControl (required) */
10354 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10355 if (FAILED(rc)) throw rc;
10356
10357 /* Host PCI devices */
10358 data.pciAttachments.clear();
10359 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10360 it != mHWData->mPCIDeviceAssignments.end();
10361 ++it)
10362 {
10363 ComObjPtr<PCIDeviceAttachment> pda = *it;
10364 settings::HostPCIDeviceAttachment hpda;
10365
10366 rc = pda->i_saveSettings(hpda);
10367 if (FAILED(rc)) throw rc;
10368
10369 data.pciAttachments.push_back(hpda);
10370 }
10371
10372
10373 // guest properties
10374 data.llGuestProperties.clear();
10375#ifdef VBOX_WITH_GUEST_PROPS
10376 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10377 it != mHWData->mGuestProperties.end();
10378 ++it)
10379 {
10380 HWData::GuestProperty property = it->second;
10381
10382 /* Remove transient guest properties at shutdown unless we
10383 * are saving state. Note that restoring snapshot intentionally
10384 * keeps them, they will be removed if appropriate once the final
10385 * machine state is set (as crashes etc. need to work). */
10386 if ( ( mData->mMachineState == MachineState_PoweredOff
10387 || mData->mMachineState == MachineState_Aborted
10388 || mData->mMachineState == MachineState_Teleported)
10389 && ( property.mFlags & guestProp::TRANSIENT
10390 || property.mFlags & guestProp::TRANSRESET))
10391 continue;
10392 settings::GuestProperty prop;
10393 prop.strName = it->first;
10394 prop.strValue = property.strValue;
10395 prop.timestamp = property.mTimestamp;
10396 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10397 guestProp::writeFlags(property.mFlags, szFlags);
10398 prop.strFlags = szFlags;
10399
10400 data.llGuestProperties.push_back(prop);
10401 }
10402
10403 /* I presume this doesn't require a backup(). */
10404 mData->mGuestPropertiesModified = FALSE;
10405#endif /* VBOX_WITH_GUEST_PROPS defined */
10406
10407 *pDbg = mHWData->mDebugging;
10408 *pAutostart = mHWData->mAutostart;
10409
10410 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10411 }
10412 catch(std::bad_alloc &)
10413 {
10414 return E_OUTOFMEMORY;
10415 }
10416
10417 AssertComRC(rc);
10418 return rc;
10419}
10420
10421/**
10422 * Saves the storage controller configuration.
10423 *
10424 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10425 */
10426HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10427{
10428 data.llStorageControllers.clear();
10429
10430 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10431 it != mStorageControllers->end();
10432 ++it)
10433 {
10434 HRESULT rc;
10435 ComObjPtr<StorageController> pCtl = *it;
10436
10437 settings::StorageController ctl;
10438 ctl.strName = pCtl->i_getName();
10439 ctl.controllerType = pCtl->i_getControllerType();
10440 ctl.storageBus = pCtl->i_getStorageBus();
10441 ctl.ulInstance = pCtl->i_getInstance();
10442 ctl.fBootable = pCtl->i_getBootable();
10443
10444 /* Save the port count. */
10445 ULONG portCount;
10446 rc = pCtl->COMGETTER(PortCount)(&portCount);
10447 ComAssertComRCRet(rc, rc);
10448 ctl.ulPortCount = portCount;
10449
10450 /* Save fUseHostIOCache */
10451 BOOL fUseHostIOCache;
10452 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10453 ComAssertComRCRet(rc, rc);
10454 ctl.fUseHostIOCache = !!fUseHostIOCache;
10455
10456 /* save the devices now. */
10457 rc = i_saveStorageDevices(pCtl, ctl);
10458 ComAssertComRCRet(rc, rc);
10459
10460 data.llStorageControllers.push_back(ctl);
10461 }
10462
10463 return S_OK;
10464}
10465
10466/**
10467 * Saves the hard disk configuration.
10468 */
10469HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10470 settings::StorageController &data)
10471{
10472 MediaData::AttachmentList atts;
10473
10474 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10475 if (FAILED(rc)) return rc;
10476
10477 data.llAttachedDevices.clear();
10478 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10479 it != atts.end();
10480 ++it)
10481 {
10482 settings::AttachedDevice dev;
10483 IMediumAttachment *iA = *it;
10484 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10485 Medium *pMedium = pAttach->i_getMedium();
10486
10487 dev.deviceType = pAttach->i_getType();
10488 dev.lPort = pAttach->i_getPort();
10489 dev.lDevice = pAttach->i_getDevice();
10490 dev.fPassThrough = pAttach->i_getPassthrough();
10491 dev.fHotPluggable = pAttach->i_getHotPluggable();
10492 if (pMedium)
10493 {
10494 if (pMedium->i_isHostDrive())
10495 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10496 else
10497 dev.uuid = pMedium->i_getId();
10498 dev.fTempEject = pAttach->i_getTempEject();
10499 dev.fNonRotational = pAttach->i_getNonRotational();
10500 dev.fDiscard = pAttach->i_getDiscard();
10501 }
10502
10503 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10504
10505 data.llAttachedDevices.push_back(dev);
10506 }
10507
10508 return S_OK;
10509}
10510
10511/**
10512 * Saves machine state settings as defined by aFlags
10513 * (SaveSTS_* values).
10514 *
10515 * @param aFlags Combination of SaveSTS_* flags.
10516 *
10517 * @note Locks objects for writing.
10518 */
10519HRESULT Machine::i_saveStateSettings(int aFlags)
10520{
10521 if (aFlags == 0)
10522 return S_OK;
10523
10524 AutoCaller autoCaller(this);
10525 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10526
10527 /* This object's write lock is also necessary to serialize file access
10528 * (prevent concurrent reads and writes) */
10529 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10530
10531 HRESULT rc = S_OK;
10532
10533 Assert(mData->pMachineConfigFile);
10534
10535 try
10536 {
10537 if (aFlags & SaveSTS_CurStateModified)
10538 mData->pMachineConfigFile->fCurrentStateModified = true;
10539
10540 if (aFlags & SaveSTS_StateFilePath)
10541 {
10542 if (!mSSData->strStateFilePath.isEmpty())
10543 /* try to make the file name relative to the settings file dir */
10544 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10545 else
10546 mData->pMachineConfigFile->strStateFile.setNull();
10547 }
10548
10549 if (aFlags & SaveSTS_StateTimeStamp)
10550 {
10551 Assert( mData->mMachineState != MachineState_Aborted
10552 || mSSData->strStateFilePath.isEmpty());
10553
10554 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10555
10556 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10557//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10558 }
10559
10560 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10561 }
10562 catch (...)
10563 {
10564 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10565 }
10566
10567 return rc;
10568}
10569
10570/**
10571 * Ensures that the given medium is added to a media registry. If this machine
10572 * was created with 4.0 or later, then the machine registry is used. Otherwise
10573 * the global VirtualBox media registry is used.
10574 *
10575 * Caller must NOT hold machine lock, media tree or any medium locks!
10576 *
10577 * @param pMedium
10578 */
10579void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10580{
10581 /* Paranoia checks: do not hold machine or media tree locks. */
10582 AssertReturnVoid(!isWriteLockOnCurrentThread());
10583 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10584
10585 ComObjPtr<Medium> pBase;
10586 {
10587 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10588 pBase = pMedium->i_getBase();
10589 }
10590
10591 /* Paranoia checks: do not hold medium locks. */
10592 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10593 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10594
10595 // decide which medium registry to use now that the medium is attached:
10596 Guid uuid;
10597 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10598 // machine XML is VirtualBox 4.0 or higher:
10599 uuid = i_getId(); // machine UUID
10600 else
10601 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10602
10603 if (pMedium->i_addRegistry(uuid))
10604 mParent->i_markRegistryModified(uuid);
10605
10606 /* For more complex hard disk structures it can happen that the base
10607 * medium isn't yet associated with any medium registry. Do that now. */
10608 if (pMedium != pBase)
10609 {
10610 /* Tree lock needed by Medium::addRegistry when recursing. */
10611 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10612 if (pBase->i_addRegistryRecursive(uuid))
10613 {
10614 treeLock.release();
10615 mParent->i_markRegistryModified(uuid);
10616 }
10617 }
10618}
10619
10620/**
10621 * Creates differencing hard disks for all normal hard disks attached to this
10622 * machine and a new set of attachments to refer to created disks.
10623 *
10624 * Used when taking a snapshot or when deleting the current state. Gets called
10625 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10626 *
10627 * This method assumes that mMediaData contains the original hard disk attachments
10628 * it needs to create diffs for. On success, these attachments will be replaced
10629 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10630 * called to delete created diffs which will also rollback mMediaData and restore
10631 * whatever was backed up before calling this method.
10632 *
10633 * Attachments with non-normal hard disks are left as is.
10634 *
10635 * If @a aOnline is @c false then the original hard disks that require implicit
10636 * diffs will be locked for reading. Otherwise it is assumed that they are
10637 * already locked for writing (when the VM was started). Note that in the latter
10638 * case it is responsibility of the caller to lock the newly created diffs for
10639 * writing if this method succeeds.
10640 *
10641 * @param aProgress Progress object to run (must contain at least as
10642 * many operations left as the number of hard disks
10643 * attached).
10644 * @param aOnline Whether the VM was online prior to this operation.
10645 *
10646 * @note The progress object is not marked as completed, neither on success nor
10647 * on failure. This is a responsibility of the caller.
10648 *
10649 * @note Locks this object and the media tree for writing.
10650 */
10651HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10652 ULONG aWeight,
10653 bool aOnline)
10654{
10655 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10656
10657 AutoCaller autoCaller(this);
10658 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10659
10660 AutoMultiWriteLock2 alock(this->lockHandle(),
10661 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10662
10663 /* must be in a protective state because we release the lock below */
10664 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10665 || mData->mMachineState == MachineState_OnlineSnapshotting
10666 || mData->mMachineState == MachineState_LiveSnapshotting
10667 || mData->mMachineState == MachineState_RestoringSnapshot
10668 || mData->mMachineState == MachineState_DeletingSnapshot
10669 , E_FAIL);
10670
10671 HRESULT rc = S_OK;
10672
10673 // use appropriate locked media map (online or offline)
10674 MediumLockListMap lockedMediaOffline;
10675 MediumLockListMap *lockedMediaMap;
10676 if (aOnline)
10677 lockedMediaMap = &mData->mSession.mLockedMedia;
10678 else
10679 lockedMediaMap = &lockedMediaOffline;
10680
10681 try
10682 {
10683 if (!aOnline)
10684 {
10685 /* lock all attached hard disks early to detect "in use"
10686 * situations before creating actual diffs */
10687 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10688 it != mMediaData->mAttachments.end();
10689 ++it)
10690 {
10691 MediumAttachment* pAtt = *it;
10692 if (pAtt->i_getType() == DeviceType_HardDisk)
10693 {
10694 Medium* pMedium = pAtt->i_getMedium();
10695 Assert(pMedium);
10696
10697 MediumLockList *pMediumLockList(new MediumLockList());
10698 alock.release();
10699 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10700 NULL /* pToLockWrite */,
10701 false /* fMediumLockWriteAll */,
10702 NULL,
10703 *pMediumLockList);
10704 alock.acquire();
10705 if (FAILED(rc))
10706 {
10707 delete pMediumLockList;
10708 throw rc;
10709 }
10710 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10711 if (FAILED(rc))
10712 {
10713 throw setError(rc,
10714 tr("Collecting locking information for all attached media failed"));
10715 }
10716 }
10717 }
10718
10719 /* Now lock all media. If this fails, nothing is locked. */
10720 alock.release();
10721 rc = lockedMediaMap->Lock();
10722 alock.acquire();
10723 if (FAILED(rc))
10724 {
10725 throw setError(rc,
10726 tr("Locking of attached media failed"));
10727 }
10728 }
10729
10730 /* remember the current list (note that we don't use backup() since
10731 * mMediaData may be already backed up) */
10732 MediaData::AttachmentList atts = mMediaData->mAttachments;
10733
10734 /* start from scratch */
10735 mMediaData->mAttachments.clear();
10736
10737 /* go through remembered attachments and create diffs for normal hard
10738 * disks and attach them */
10739 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10740 it != atts.end();
10741 ++it)
10742 {
10743 MediumAttachment* pAtt = *it;
10744
10745 DeviceType_T devType = pAtt->i_getType();
10746 Medium* pMedium = pAtt->i_getMedium();
10747
10748 if ( devType != DeviceType_HardDisk
10749 || pMedium == NULL
10750 || pMedium->i_getType() != MediumType_Normal)
10751 {
10752 /* copy the attachment as is */
10753
10754 /** @todo the progress object created in SessionMachine::TakeSnaphot
10755 * only expects operations for hard disks. Later other
10756 * device types need to show up in the progress as well. */
10757 if (devType == DeviceType_HardDisk)
10758 {
10759 if (pMedium == NULL)
10760 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10761 aWeight); // weight
10762 else
10763 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10764 pMedium->i_getBase()->i_getName().c_str()).raw(),
10765 aWeight); // weight
10766 }
10767
10768 mMediaData->mAttachments.push_back(pAtt);
10769 continue;
10770 }
10771
10772 /* need a diff */
10773 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10774 pMedium->i_getBase()->i_getName().c_str()).raw(),
10775 aWeight); // weight
10776
10777 Utf8Str strFullSnapshotFolder;
10778 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10779
10780 ComObjPtr<Medium> diff;
10781 diff.createObject();
10782 // store the diff in the same registry as the parent
10783 // (this cannot fail here because we can't create implicit diffs for
10784 // unregistered images)
10785 Guid uuidRegistryParent;
10786 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10787 Assert(fInRegistry); NOREF(fInRegistry);
10788 rc = diff->init(mParent,
10789 pMedium->i_getPreferredDiffFormat(),
10790 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10791 uuidRegistryParent,
10792 DeviceType_HardDisk);
10793 if (FAILED(rc)) throw rc;
10794
10795 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10796 * the push_back? Looks like we're going to release medium with the
10797 * wrong kind of lock (general issue with if we fail anywhere at all)
10798 * and an orphaned VDI in the snapshots folder. */
10799
10800 /* update the appropriate lock list */
10801 MediumLockList *pMediumLockList;
10802 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10803 AssertComRCThrowRC(rc);
10804 if (aOnline)
10805 {
10806 alock.release();
10807 /* The currently attached medium will be read-only, change
10808 * the lock type to read. */
10809 rc = pMediumLockList->Update(pMedium, false);
10810 alock.acquire();
10811 AssertComRCThrowRC(rc);
10812 }
10813
10814 /* release the locks before the potentially lengthy operation */
10815 alock.release();
10816 rc = pMedium->i_createDiffStorage(diff,
10817 pMedium->i_getPreferredDiffVariant(),
10818 pMediumLockList,
10819 NULL /* aProgress */,
10820 true /* aWait */);
10821 alock.acquire();
10822 if (FAILED(rc)) throw rc;
10823
10824 /* actual lock list update is done in Medium::commitMedia */
10825
10826 rc = diff->i_addBackReference(mData->mUuid);
10827 AssertComRCThrowRC(rc);
10828
10829 /* add a new attachment */
10830 ComObjPtr<MediumAttachment> attachment;
10831 attachment.createObject();
10832 rc = attachment->init(this,
10833 diff,
10834 pAtt->i_getControllerName(),
10835 pAtt->i_getPort(),
10836 pAtt->i_getDevice(),
10837 DeviceType_HardDisk,
10838 true /* aImplicit */,
10839 false /* aPassthrough */,
10840 false /* aTempEject */,
10841 pAtt->i_getNonRotational(),
10842 pAtt->i_getDiscard(),
10843 pAtt->i_getHotPluggable(),
10844 pAtt->i_getBandwidthGroup());
10845 if (FAILED(rc)) throw rc;
10846
10847 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10848 AssertComRCThrowRC(rc);
10849 mMediaData->mAttachments.push_back(attachment);
10850 }
10851 }
10852 catch (HRESULT aRC) { rc = aRC; }
10853
10854 /* unlock all hard disks we locked when there is no VM */
10855 if (!aOnline)
10856 {
10857 ErrorInfoKeeper eik;
10858
10859 HRESULT rc1 = lockedMediaMap->Clear();
10860 AssertComRC(rc1);
10861 }
10862
10863 return rc;
10864}
10865
10866/**
10867 * Deletes implicit differencing hard disks created either by
10868 * #i_createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10869 *
10870 * Note that to delete hard disks created by #AttachDevice() this method is
10871 * called from #fixupMedia() when the changes are rolled back.
10872 *
10873 * @note Locks this object and the media tree for writing.
10874 */
10875HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10876{
10877 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10878
10879 AutoCaller autoCaller(this);
10880 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10881
10882 AutoMultiWriteLock2 alock(this->lockHandle(),
10883 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10884
10885 /* We absolutely must have backed up state. */
10886 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10887
10888 /* Check if there are any implicitly created diff images. */
10889 bool fImplicitDiffs = false;
10890 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10891 it != mMediaData->mAttachments.end();
10892 ++it)
10893 {
10894 const ComObjPtr<MediumAttachment> &pAtt = *it;
10895 if (pAtt->i_isImplicit())
10896 {
10897 fImplicitDiffs = true;
10898 break;
10899 }
10900 }
10901 /* If there is nothing to do, leave early. This saves lots of image locking
10902 * effort. It also avoids a MachineStateChanged event without real reason.
10903 * This is important e.g. when loading a VM config, because there should be
10904 * no events. Otherwise API clients can become thoroughly confused for
10905 * inaccessible VMs (the code for loading VM configs uses this method for
10906 * cleanup if the config makes no sense), as they take such events as an
10907 * indication that the VM is alive, and they would force the VM config to
10908 * be reread, leading to an endless loop. */
10909 if (!fImplicitDiffs)
10910 return S_OK;
10911
10912 HRESULT rc = S_OK;
10913 MachineState_T oldState = mData->mMachineState;
10914
10915 /* will release the lock before the potentially lengthy operation,
10916 * so protect with the special state (unless already protected) */
10917 if ( oldState != MachineState_Snapshotting
10918 && oldState != MachineState_OnlineSnapshotting
10919 && oldState != MachineState_LiveSnapshotting
10920 && oldState != MachineState_RestoringSnapshot
10921 && oldState != MachineState_DeletingSnapshot
10922 && oldState != MachineState_DeletingSnapshotOnline
10923 && oldState != MachineState_DeletingSnapshotPaused
10924 )
10925 i_setMachineState(MachineState_SettingUp);
10926
10927 // use appropriate locked media map (online or offline)
10928 MediumLockListMap lockedMediaOffline;
10929 MediumLockListMap *lockedMediaMap;
10930 if (aOnline)
10931 lockedMediaMap = &mData->mSession.mLockedMedia;
10932 else
10933 lockedMediaMap = &lockedMediaOffline;
10934
10935 try
10936 {
10937 if (!aOnline)
10938 {
10939 /* lock all attached hard disks early to detect "in use"
10940 * situations before deleting actual diffs */
10941 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10942 it != mMediaData->mAttachments.end();
10943 ++it)
10944 {
10945 MediumAttachment* pAtt = *it;
10946 if (pAtt->i_getType() == DeviceType_HardDisk)
10947 {
10948 Medium* pMedium = pAtt->i_getMedium();
10949 Assert(pMedium);
10950
10951 MediumLockList *pMediumLockList(new MediumLockList());
10952 alock.release();
10953 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10954 NULL /* pToLockWrite */,
10955 false /* fMediumLockWriteAll */,
10956 NULL,
10957 *pMediumLockList);
10958 alock.acquire();
10959
10960 if (FAILED(rc))
10961 {
10962 delete pMediumLockList;
10963 throw rc;
10964 }
10965
10966 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10967 if (FAILED(rc))
10968 throw rc;
10969 }
10970 }
10971
10972 if (FAILED(rc))
10973 throw rc;
10974 } // end of offline
10975
10976 /* Lock lists are now up to date and include implicitly created media */
10977
10978 /* Go through remembered attachments and delete all implicitly created
10979 * diffs and fix up the attachment information */
10980 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10981 MediaData::AttachmentList implicitAtts;
10982 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10983 it != mMediaData->mAttachments.end();
10984 ++it)
10985 {
10986 ComObjPtr<MediumAttachment> pAtt = *it;
10987 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10988 if (pMedium.isNull())
10989 continue;
10990
10991 // Implicit attachments go on the list for deletion and back references are removed.
10992 if (pAtt->i_isImplicit())
10993 {
10994 /* Deassociate and mark for deletion */
10995 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10996 rc = pMedium->i_removeBackReference(mData->mUuid);
10997 if (FAILED(rc))
10998 throw rc;
10999 implicitAtts.push_back(pAtt);
11000 continue;
11001 }
11002
11003 /* Was this medium attached before? */
11004 if (!i_findAttachment(oldAtts, pMedium))
11005 {
11006 /* no: de-associate */
11007 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11008 rc = pMedium->i_removeBackReference(mData->mUuid);
11009 if (FAILED(rc))
11010 throw rc;
11011 continue;
11012 }
11013 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11014 }
11015
11016 /* If there are implicit attachments to delete, throw away the lock
11017 * map contents (which will unlock all media) since the medium
11018 * attachments will be rolled back. Below we need to completely
11019 * recreate the lock map anyway since it is infinitely complex to
11020 * do this incrementally (would need reconstructing each attachment
11021 * change, which would be extremely hairy). */
11022 if (implicitAtts.size() != 0)
11023 {
11024 ErrorInfoKeeper eik;
11025
11026 HRESULT rc1 = lockedMediaMap->Clear();
11027 AssertComRC(rc1);
11028 }
11029
11030 /* rollback hard disk changes */
11031 mMediaData.rollback();
11032
11033 MultiResult mrc(S_OK);
11034
11035 // Delete unused implicit diffs.
11036 if (implicitAtts.size() != 0)
11037 {
11038 alock.release();
11039
11040 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11041 {
11042 // Remove medium associated with this attachment.
11043 ComObjPtr<MediumAttachment> pAtt = *it;
11044 Assert(pAtt);
11045 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11046 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11047 Assert(pMedium);
11048
11049 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11050 // continue on delete failure, just collect error messages
11051 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11052 pMedium->i_getLocationFull().c_str() ));
11053 mrc = rc;
11054 }
11055 // Clear the list of deleted implicit attachments now, while not
11056 // holding the lock, as it will ultimately trigger Medium::uninit()
11057 // calls which assume that the media tree lock isn't held.
11058 implicitAtts.clear();
11059
11060 alock.acquire();
11061
11062 /* if there is a VM recreate media lock map as mentioned above,
11063 * otherwise it is a waste of time and we leave things unlocked */
11064 if (aOnline)
11065 {
11066 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11067 /* must never be NULL, but better safe than sorry */
11068 if (!pMachine.isNull())
11069 {
11070 alock.release();
11071 rc = mData->mSession.mMachine->i_lockMedia();
11072 alock.acquire();
11073 if (FAILED(rc))
11074 throw rc;
11075 }
11076 }
11077 }
11078 }
11079 catch (HRESULT aRC) {rc = aRC;}
11080
11081 if (mData->mMachineState == MachineState_SettingUp)
11082 i_setMachineState(oldState);
11083
11084 /* unlock all hard disks we locked when there is no VM */
11085 if (!aOnline)
11086 {
11087 ErrorInfoKeeper eik;
11088
11089 HRESULT rc1 = lockedMediaMap->Clear();
11090 AssertComRC(rc1);
11091 }
11092
11093 return rc;
11094}
11095
11096
11097/**
11098 * Looks through the given list of media attachments for one with the given parameters
11099 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11100 * can be searched as well if needed.
11101 *
11102 * @param list
11103 * @param aControllerName
11104 * @param aControllerPort
11105 * @param aDevice
11106 * @return
11107 */
11108MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11109 const Utf8Str &aControllerName,
11110 LONG aControllerPort,
11111 LONG aDevice)
11112{
11113 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11114 {
11115 MediumAttachment *pAttach = *it;
11116 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11117 return pAttach;
11118 }
11119
11120 return NULL;
11121}
11122
11123/**
11124 * Looks through the given list of media attachments for one with the given parameters
11125 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11126 * can be searched as well if needed.
11127 *
11128 * @param list
11129 * @param aControllerName
11130 * @param aControllerPort
11131 * @param aDevice
11132 * @return
11133 */
11134MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11135 ComObjPtr<Medium> pMedium)
11136{
11137 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11138 {
11139 MediumAttachment *pAttach = *it;
11140 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11141 if (pMediumThis == pMedium)
11142 return pAttach;
11143 }
11144
11145 return NULL;
11146}
11147
11148/**
11149 * Looks through the given list of media attachments for one with the given parameters
11150 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11151 * can be searched as well if needed.
11152 *
11153 * @param list
11154 * @param aControllerName
11155 * @param aControllerPort
11156 * @param aDevice
11157 * @return
11158 */
11159MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11160 Guid &id)
11161{
11162 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11163 {
11164 MediumAttachment *pAttach = *it;
11165 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11166 if (pMediumThis->i_getId() == id)
11167 return pAttach;
11168 }
11169
11170 return NULL;
11171}
11172
11173/**
11174 * Main implementation for Machine::DetachDevice. This also gets called
11175 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11176 *
11177 * @param pAttach Medium attachment to detach.
11178 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11179 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11180 * SnapshotMachine, and this must be its snapshot.
11181 * @return
11182 */
11183HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11184 AutoWriteLock &writeLock,
11185 Snapshot *pSnapshot)
11186{
11187 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11188 DeviceType_T mediumType = pAttach->i_getType();
11189
11190 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11191
11192 if (pAttach->i_isImplicit())
11193 {
11194 /* attempt to implicitly delete the implicitly created diff */
11195
11196 /// @todo move the implicit flag from MediumAttachment to Medium
11197 /// and forbid any hard disk operation when it is implicit. Or maybe
11198 /// a special media state for it to make it even more simple.
11199
11200 Assert(mMediaData.isBackedUp());
11201
11202 /* will release the lock before the potentially lengthy operation, so
11203 * protect with the special state */
11204 MachineState_T oldState = mData->mMachineState;
11205 i_setMachineState(MachineState_SettingUp);
11206
11207 writeLock.release();
11208
11209 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11210 true /*aWait*/);
11211
11212 writeLock.acquire();
11213
11214 i_setMachineState(oldState);
11215
11216 if (FAILED(rc)) return rc;
11217 }
11218
11219 i_setModified(IsModified_Storage);
11220 mMediaData.backup();
11221 mMediaData->mAttachments.remove(pAttach);
11222
11223 if (!oldmedium.isNull())
11224 {
11225 // if this is from a snapshot, do not defer detachment to commitMedia()
11226 if (pSnapshot)
11227 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11228 // else if non-hard disk media, do not defer detachment to commitMedia() either
11229 else if (mediumType != DeviceType_HardDisk)
11230 oldmedium->i_removeBackReference(mData->mUuid);
11231 }
11232
11233 return S_OK;
11234}
11235
11236/**
11237 * Goes thru all media of the given list and
11238 *
11239 * 1) calls i_detachDevice() on each of them for this machine and
11240 * 2) adds all Medium objects found in the process to the given list,
11241 * depending on cleanupMode.
11242 *
11243 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11244 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11245 * media to the list.
11246 *
11247 * This gets called from Machine::Unregister, both for the actual Machine and
11248 * the SnapshotMachine objects that might be found in the snapshots.
11249 *
11250 * Requires caller and locking. The machine lock must be passed in because it
11251 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11252 *
11253 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11254 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11255 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11256 * Full, then all media get added;
11257 * otherwise no media get added.
11258 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11259 * @return
11260 */
11261HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11262 Snapshot *pSnapshot,
11263 CleanupMode_T cleanupMode,
11264 MediaList &llMedia)
11265{
11266 Assert(isWriteLockOnCurrentThread());
11267
11268 HRESULT rc;
11269
11270 // make a temporary list because i_detachDevice invalidates iterators into
11271 // mMediaData->mAttachments
11272 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11273
11274 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11275 {
11276 ComObjPtr<MediumAttachment> &pAttach = *it;
11277 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11278
11279 if (!pMedium.isNull())
11280 {
11281 AutoCaller mac(pMedium);
11282 if (FAILED(mac.rc())) return mac.rc();
11283 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11284 DeviceType_T devType = pMedium->i_getDeviceType();
11285 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11286 && devType == DeviceType_HardDisk)
11287 || (cleanupMode == CleanupMode_Full)
11288 )
11289 {
11290 llMedia.push_back(pMedium);
11291 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11292 /* Not allowed to keep this lock as below we need the parent
11293 * medium lock, and the lock order is parent to child. */
11294 lock.release();
11295 /*
11296 * Search for medias which are not attached to any machine, but
11297 * in the chain to an attached disk. Mediums are only consided
11298 * if they are:
11299 * - have only one child
11300 * - no references to any machines
11301 * - are of normal medium type
11302 */
11303 while (!pParent.isNull())
11304 {
11305 AutoCaller mac1(pParent);
11306 if (FAILED(mac1.rc())) return mac1.rc();
11307 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11308 if (pParent->i_getChildren().size() == 1)
11309 {
11310 if ( pParent->i_getMachineBackRefCount() == 0
11311 && pParent->i_getType() == MediumType_Normal
11312 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11313 llMedia.push_back(pParent);
11314 }
11315 else
11316 break;
11317 pParent = pParent->i_getParent();
11318 }
11319 }
11320 }
11321
11322 // real machine: then we need to use the proper method
11323 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11324
11325 if (FAILED(rc))
11326 return rc;
11327 }
11328
11329 return S_OK;
11330}
11331
11332/**
11333 * Perform deferred hard disk detachments.
11334 *
11335 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11336 * backed up).
11337 *
11338 * If @a aOnline is @c true then this method will also unlock the old hard disks
11339 * for which the new implicit diffs were created and will lock these new diffs for
11340 * writing.
11341 *
11342 * @param aOnline Whether the VM was online prior to this operation.
11343 *
11344 * @note Locks this object for writing!
11345 */
11346void Machine::i_commitMedia(bool aOnline /*= false*/)
11347{
11348 AutoCaller autoCaller(this);
11349 AssertComRCReturnVoid(autoCaller.rc());
11350
11351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11352
11353 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11354
11355 HRESULT rc = S_OK;
11356
11357 /* no attach/detach operations -- nothing to do */
11358 if (!mMediaData.isBackedUp())
11359 return;
11360
11361 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11362 bool fMediaNeedsLocking = false;
11363
11364 /* enumerate new attachments */
11365 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11366 it != mMediaData->mAttachments.end();
11367 ++it)
11368 {
11369 MediumAttachment *pAttach = *it;
11370
11371 pAttach->i_commit();
11372
11373 Medium* pMedium = pAttach->i_getMedium();
11374 bool fImplicit = pAttach->i_isImplicit();
11375
11376 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11377 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11378 fImplicit));
11379
11380 /** @todo convert all this Machine-based voodoo to MediumAttachment
11381 * based commit logic. */
11382 if (fImplicit)
11383 {
11384 /* convert implicit attachment to normal */
11385 pAttach->i_setImplicit(false);
11386
11387 if ( aOnline
11388 && pMedium
11389 && pAttach->i_getType() == DeviceType_HardDisk
11390 )
11391 {
11392 /* update the appropriate lock list */
11393 MediumLockList *pMediumLockList;
11394 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11395 AssertComRC(rc);
11396 if (pMediumLockList)
11397 {
11398 /* unlock if there's a need to change the locking */
11399 if (!fMediaNeedsLocking)
11400 {
11401 rc = mData->mSession.mLockedMedia.Unlock();
11402 AssertComRC(rc);
11403 fMediaNeedsLocking = true;
11404 }
11405 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11406 AssertComRC(rc);
11407 rc = pMediumLockList->Append(pMedium, true);
11408 AssertComRC(rc);
11409 }
11410 }
11411
11412 continue;
11413 }
11414
11415 if (pMedium)
11416 {
11417 /* was this medium attached before? */
11418 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11419 {
11420 MediumAttachment *pOldAttach = *oldIt;
11421 if (pOldAttach->i_getMedium() == pMedium)
11422 {
11423 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11424
11425 /* yes: remove from old to avoid de-association */
11426 oldAtts.erase(oldIt);
11427 break;
11428 }
11429 }
11430 }
11431 }
11432
11433 /* enumerate remaining old attachments and de-associate from the
11434 * current machine state */
11435 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11436 {
11437 MediumAttachment *pAttach = *it;
11438 Medium* pMedium = pAttach->i_getMedium();
11439
11440 /* Detach only hard disks, since DVD/floppy media is detached
11441 * instantly in MountMedium. */
11442 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11443 {
11444 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11445
11446 /* now de-associate from the current machine state */
11447 rc = pMedium->i_removeBackReference(mData->mUuid);
11448 AssertComRC(rc);
11449
11450 if (aOnline)
11451 {
11452 /* unlock since medium is not used anymore */
11453 MediumLockList *pMediumLockList;
11454 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11455 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11456 {
11457 /* this happens for online snapshots, there the attachment
11458 * is changing, but only to a diff image created under
11459 * the old one, so there is no separate lock list */
11460 Assert(!pMediumLockList);
11461 }
11462 else
11463 {
11464 AssertComRC(rc);
11465 if (pMediumLockList)
11466 {
11467 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11468 AssertComRC(rc);
11469 }
11470 }
11471 }
11472 }
11473 }
11474
11475 /* take media locks again so that the locking state is consistent */
11476 if (fMediaNeedsLocking)
11477 {
11478 Assert(aOnline);
11479 rc = mData->mSession.mLockedMedia.Lock();
11480 AssertComRC(rc);
11481 }
11482
11483 /* commit the hard disk changes */
11484 mMediaData.commit();
11485
11486 if (i_isSessionMachine())
11487 {
11488 /*
11489 * Update the parent machine to point to the new owner.
11490 * This is necessary because the stored parent will point to the
11491 * session machine otherwise and cause crashes or errors later
11492 * when the session machine gets invalid.
11493 */
11494 /** @todo Change the MediumAttachment class to behave like any other
11495 * class in this regard by creating peer MediumAttachment
11496 * objects for session machines and share the data with the peer
11497 * machine.
11498 */
11499 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11500 it != mMediaData->mAttachments.end();
11501 ++it)
11502 (*it)->i_updateParentMachine(mPeer);
11503
11504 /* attach new data to the primary machine and reshare it */
11505 mPeer->mMediaData.attach(mMediaData);
11506 }
11507
11508 return;
11509}
11510
11511/**
11512 * Perform deferred deletion of implicitly created diffs.
11513 *
11514 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11515 * backed up).
11516 *
11517 * @note Locks this object for writing!
11518 */
11519void Machine::i_rollbackMedia()
11520{
11521 AutoCaller autoCaller(this);
11522 AssertComRCReturnVoid(autoCaller.rc());
11523
11524 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11525 LogFlowThisFunc(("Entering rollbackMedia\n"));
11526
11527 HRESULT rc = S_OK;
11528
11529 /* no attach/detach operations -- nothing to do */
11530 if (!mMediaData.isBackedUp())
11531 return;
11532
11533 /* enumerate new attachments */
11534 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11535 it != mMediaData->mAttachments.end();
11536 ++it)
11537 {
11538 MediumAttachment *pAttach = *it;
11539 /* Fix up the backrefs for DVD/floppy media. */
11540 if (pAttach->i_getType() != DeviceType_HardDisk)
11541 {
11542 Medium* pMedium = pAttach->i_getMedium();
11543 if (pMedium)
11544 {
11545 rc = pMedium->i_removeBackReference(mData->mUuid);
11546 AssertComRC(rc);
11547 }
11548 }
11549
11550 (*it)->i_rollback();
11551
11552 pAttach = *it;
11553 /* Fix up the backrefs for DVD/floppy media. */
11554 if (pAttach->i_getType() != DeviceType_HardDisk)
11555 {
11556 Medium* pMedium = pAttach->i_getMedium();
11557 if (pMedium)
11558 {
11559 rc = pMedium->i_addBackReference(mData->mUuid);
11560 AssertComRC(rc);
11561 }
11562 }
11563 }
11564
11565 /** @todo convert all this Machine-based voodoo to MediumAttachment
11566 * based rollback logic. */
11567 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11568
11569 return;
11570}
11571
11572/**
11573 * Returns true if the settings file is located in the directory named exactly
11574 * as the machine; this means, among other things, that the machine directory
11575 * should be auto-renamed.
11576 *
11577 * @param aSettingsDir if not NULL, the full machine settings file directory
11578 * name will be assigned there.
11579 *
11580 * @note Doesn't lock anything.
11581 * @note Not thread safe (must be called from this object's lock).
11582 */
11583bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11584{
11585 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11586 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11587 if (aSettingsDir)
11588 *aSettingsDir = strMachineDirName;
11589 strMachineDirName.stripPath(); // vmname
11590 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11591 strConfigFileOnly.stripPath() // vmname.vbox
11592 .stripSuffix(); // vmname
11593 /** @todo hack, make somehow use of ComposeMachineFilename */
11594 if (mUserData->s.fDirectoryIncludesUUID)
11595 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11596
11597 AssertReturn(!strMachineDirName.isEmpty(), false);
11598 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11599
11600 return strMachineDirName == strConfigFileOnly;
11601}
11602
11603/**
11604 * Discards all changes to machine settings.
11605 *
11606 * @param aNotify Whether to notify the direct session about changes or not.
11607 *
11608 * @note Locks objects for writing!
11609 */
11610void Machine::i_rollback(bool aNotify)
11611{
11612 AutoCaller autoCaller(this);
11613 AssertComRCReturn(autoCaller.rc(), (void)0);
11614
11615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11616
11617 if (!mStorageControllers.isNull())
11618 {
11619 if (mStorageControllers.isBackedUp())
11620 {
11621 /* unitialize all new devices (absent in the backed up list). */
11622 StorageControllerList::const_iterator it = mStorageControllers->begin();
11623 StorageControllerList *backedList = mStorageControllers.backedUpData();
11624 while (it != mStorageControllers->end())
11625 {
11626 if ( std::find(backedList->begin(), backedList->end(), *it)
11627 == backedList->end()
11628 )
11629 {
11630 (*it)->uninit();
11631 }
11632 ++it;
11633 }
11634
11635 /* restore the list */
11636 mStorageControllers.rollback();
11637 }
11638
11639 /* rollback any changes to devices after restoring the list */
11640 if (mData->flModifications & IsModified_Storage)
11641 {
11642 StorageControllerList::const_iterator it = mStorageControllers->begin();
11643 while (it != mStorageControllers->end())
11644 {
11645 (*it)->i_rollback();
11646 ++it;
11647 }
11648 }
11649 }
11650
11651 if (!mUSBControllers.isNull())
11652 {
11653 if (mUSBControllers.isBackedUp())
11654 {
11655 /* unitialize all new devices (absent in the backed up list). */
11656 USBControllerList::const_iterator it = mUSBControllers->begin();
11657 USBControllerList *backedList = mUSBControllers.backedUpData();
11658 while (it != mUSBControllers->end())
11659 {
11660 if ( std::find(backedList->begin(), backedList->end(), *it)
11661 == backedList->end()
11662 )
11663 {
11664 (*it)->uninit();
11665 }
11666 ++it;
11667 }
11668
11669 /* restore the list */
11670 mUSBControllers.rollback();
11671 }
11672
11673 /* rollback any changes to devices after restoring the list */
11674 if (mData->flModifications & IsModified_USB)
11675 {
11676 USBControllerList::const_iterator it = mUSBControllers->begin();
11677 while (it != mUSBControllers->end())
11678 {
11679 (*it)->i_rollback();
11680 ++it;
11681 }
11682 }
11683 }
11684
11685 mUserData.rollback();
11686
11687 mHWData.rollback();
11688
11689 if (mData->flModifications & IsModified_Storage)
11690 i_rollbackMedia();
11691
11692 if (mBIOSSettings)
11693 mBIOSSettings->i_rollback();
11694
11695 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11696 mVRDEServer->i_rollback();
11697
11698 if (mAudioAdapter)
11699 mAudioAdapter->i_rollback();
11700
11701 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11702 mUSBDeviceFilters->i_rollback();
11703
11704 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11705 mBandwidthControl->i_rollback();
11706
11707 if (!mHWData.isNull())
11708 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11709 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11710 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11711 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11712
11713 if (mData->flModifications & IsModified_NetworkAdapters)
11714 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11715 if ( mNetworkAdapters[slot]
11716 && mNetworkAdapters[slot]->i_isModified())
11717 {
11718 mNetworkAdapters[slot]->i_rollback();
11719 networkAdapters[slot] = mNetworkAdapters[slot];
11720 }
11721
11722 if (mData->flModifications & IsModified_SerialPorts)
11723 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11724 if ( mSerialPorts[slot]
11725 && mSerialPorts[slot]->i_isModified())
11726 {
11727 mSerialPorts[slot]->i_rollback();
11728 serialPorts[slot] = mSerialPorts[slot];
11729 }
11730
11731 if (mData->flModifications & IsModified_ParallelPorts)
11732 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11733 if ( mParallelPorts[slot]
11734 && mParallelPorts[slot]->i_isModified())
11735 {
11736 mParallelPorts[slot]->i_rollback();
11737 parallelPorts[slot] = mParallelPorts[slot];
11738 }
11739
11740 if (aNotify)
11741 {
11742 /* inform the direct session about changes */
11743
11744 ComObjPtr<Machine> that = this;
11745 uint32_t flModifications = mData->flModifications;
11746 alock.release();
11747
11748 if (flModifications & IsModified_SharedFolders)
11749 that->i_onSharedFolderChange();
11750
11751 if (flModifications & IsModified_VRDEServer)
11752 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11753 if (flModifications & IsModified_USB)
11754 that->i_onUSBControllerChange();
11755
11756 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11757 if (networkAdapters[slot])
11758 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11759 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11760 if (serialPorts[slot])
11761 that->i_onSerialPortChange(serialPorts[slot]);
11762 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11763 if (parallelPorts[slot])
11764 that->i_onParallelPortChange(parallelPorts[slot]);
11765
11766 if (flModifications & IsModified_Storage)
11767 that->i_onStorageControllerChange();
11768
11769#if 0
11770 if (flModifications & IsModified_BandwidthControl)
11771 that->onBandwidthControlChange();
11772#endif
11773 }
11774}
11775
11776/**
11777 * Commits all the changes to machine settings.
11778 *
11779 * Note that this operation is supposed to never fail.
11780 *
11781 * @note Locks this object and children for writing.
11782 */
11783void Machine::i_commit()
11784{
11785 AutoCaller autoCaller(this);
11786 AssertComRCReturnVoid(autoCaller.rc());
11787
11788 AutoCaller peerCaller(mPeer);
11789 AssertComRCReturnVoid(peerCaller.rc());
11790
11791 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11792
11793 /*
11794 * use safe commit to ensure Snapshot machines (that share mUserData)
11795 * will still refer to a valid memory location
11796 */
11797 mUserData.commitCopy();
11798
11799 mHWData.commit();
11800
11801 if (mMediaData.isBackedUp())
11802 i_commitMedia(Global::IsOnline(mData->mMachineState));
11803
11804 mBIOSSettings->i_commit();
11805 mVRDEServer->i_commit();
11806 mAudioAdapter->i_commit();
11807 mUSBDeviceFilters->i_commit();
11808 mBandwidthControl->i_commit();
11809
11810 /* Since mNetworkAdapters is a list which might have been changed (resized)
11811 * without using the Backupable<> template we need to handle the copying
11812 * of the list entries manually, including the creation of peers for the
11813 * new objects. */
11814 bool commitNetworkAdapters = false;
11815 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11816 if (mPeer)
11817 {
11818 /* commit everything, even the ones which will go away */
11819 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11820 mNetworkAdapters[slot]->i_commit();
11821 /* copy over the new entries, creating a peer and uninit the original */
11822 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11823 for (size_t slot = 0; slot < newSize; slot++)
11824 {
11825 /* look if this adapter has a peer device */
11826 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11827 if (!peer)
11828 {
11829 /* no peer means the adapter is a newly created one;
11830 * create a peer owning data this data share it with */
11831 peer.createObject();
11832 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11833 }
11834 mPeer->mNetworkAdapters[slot] = peer;
11835 }
11836 /* uninit any no longer needed network adapters */
11837 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11838 mNetworkAdapters[slot]->uninit();
11839 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11840 {
11841 if (mPeer->mNetworkAdapters[slot])
11842 mPeer->mNetworkAdapters[slot]->uninit();
11843 }
11844 /* Keep the original network adapter count until this point, so that
11845 * discarding a chipset type change will not lose settings. */
11846 mNetworkAdapters.resize(newSize);
11847 mPeer->mNetworkAdapters.resize(newSize);
11848 }
11849 else
11850 {
11851 /* we have no peer (our parent is the newly created machine);
11852 * just commit changes to the network adapters */
11853 commitNetworkAdapters = true;
11854 }
11855 if (commitNetworkAdapters)
11856 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11857 mNetworkAdapters[slot]->i_commit();
11858
11859 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11860 mSerialPorts[slot]->i_commit();
11861 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11862 mParallelPorts[slot]->i_commit();
11863
11864 bool commitStorageControllers = false;
11865
11866 if (mStorageControllers.isBackedUp())
11867 {
11868 mStorageControllers.commit();
11869
11870 if (mPeer)
11871 {
11872 /* Commit all changes to new controllers (this will reshare data with
11873 * peers for those who have peers) */
11874 StorageControllerList *newList = new StorageControllerList();
11875 StorageControllerList::const_iterator it = mStorageControllers->begin();
11876 while (it != mStorageControllers->end())
11877 {
11878 (*it)->i_commit();
11879
11880 /* look if this controller has a peer device */
11881 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11882 if (!peer)
11883 {
11884 /* no peer means the device is a newly created one;
11885 * create a peer owning data this device share it with */
11886 peer.createObject();
11887 peer->init(mPeer, *it, true /* aReshare */);
11888 }
11889 else
11890 {
11891 /* remove peer from the old list */
11892 mPeer->mStorageControllers->remove(peer);
11893 }
11894 /* and add it to the new list */
11895 newList->push_back(peer);
11896
11897 ++it;
11898 }
11899
11900 /* uninit old peer's controllers that are left */
11901 it = mPeer->mStorageControllers->begin();
11902 while (it != mPeer->mStorageControllers->end())
11903 {
11904 (*it)->uninit();
11905 ++it;
11906 }
11907
11908 /* attach new list of controllers to our peer */
11909 mPeer->mStorageControllers.attach(newList);
11910 }
11911 else
11912 {
11913 /* we have no peer (our parent is the newly created machine);
11914 * just commit changes to devices */
11915 commitStorageControllers = true;
11916 }
11917 }
11918 else
11919 {
11920 /* the list of controllers itself is not changed,
11921 * just commit changes to controllers themselves */
11922 commitStorageControllers = true;
11923 }
11924
11925 if (commitStorageControllers)
11926 {
11927 StorageControllerList::const_iterator it = mStorageControllers->begin();
11928 while (it != mStorageControllers->end())
11929 {
11930 (*it)->i_commit();
11931 ++it;
11932 }
11933 }
11934
11935 bool commitUSBControllers = false;
11936
11937 if (mUSBControllers.isBackedUp())
11938 {
11939 mUSBControllers.commit();
11940
11941 if (mPeer)
11942 {
11943 /* Commit all changes to new controllers (this will reshare data with
11944 * peers for those who have peers) */
11945 USBControllerList *newList = new USBControllerList();
11946 USBControllerList::const_iterator it = mUSBControllers->begin();
11947 while (it != mUSBControllers->end())
11948 {
11949 (*it)->i_commit();
11950
11951 /* look if this controller has a peer device */
11952 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11953 if (!peer)
11954 {
11955 /* no peer means the device is a newly created one;
11956 * create a peer owning data this device share it with */
11957 peer.createObject();
11958 peer->init(mPeer, *it, true /* aReshare */);
11959 }
11960 else
11961 {
11962 /* remove peer from the old list */
11963 mPeer->mUSBControllers->remove(peer);
11964 }
11965 /* and add it to the new list */
11966 newList->push_back(peer);
11967
11968 ++it;
11969 }
11970
11971 /* uninit old peer's controllers that are left */
11972 it = mPeer->mUSBControllers->begin();
11973 while (it != mPeer->mUSBControllers->end())
11974 {
11975 (*it)->uninit();
11976 ++it;
11977 }
11978
11979 /* attach new list of controllers to our peer */
11980 mPeer->mUSBControllers.attach(newList);
11981 }
11982 else
11983 {
11984 /* we have no peer (our parent is the newly created machine);
11985 * just commit changes to devices */
11986 commitUSBControllers = true;
11987 }
11988 }
11989 else
11990 {
11991 /* the list of controllers itself is not changed,
11992 * just commit changes to controllers themselves */
11993 commitUSBControllers = true;
11994 }
11995
11996 if (commitUSBControllers)
11997 {
11998 USBControllerList::const_iterator it = mUSBControllers->begin();
11999 while (it != mUSBControllers->end())
12000 {
12001 (*it)->i_commit();
12002 ++it;
12003 }
12004 }
12005
12006 if (i_isSessionMachine())
12007 {
12008 /* attach new data to the primary machine and reshare it */
12009 mPeer->mUserData.attach(mUserData);
12010 mPeer->mHWData.attach(mHWData);
12011 /* mMediaData is reshared by fixupMedia */
12012 // mPeer->mMediaData.attach(mMediaData);
12013 Assert(mPeer->mMediaData.data() == mMediaData.data());
12014 }
12015}
12016
12017/**
12018 * Copies all the hardware data from the given machine.
12019 *
12020 * Currently, only called when the VM is being restored from a snapshot. In
12021 * particular, this implies that the VM is not running during this method's
12022 * call.
12023 *
12024 * @note This method must be called from under this object's lock.
12025 *
12026 * @note This method doesn't call #commit(), so all data remains backed up and
12027 * unsaved.
12028 */
12029void Machine::i_copyFrom(Machine *aThat)
12030{
12031 AssertReturnVoid(!i_isSnapshotMachine());
12032 AssertReturnVoid(aThat->i_isSnapshotMachine());
12033
12034 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12035
12036 mHWData.assignCopy(aThat->mHWData);
12037
12038 // create copies of all shared folders (mHWData after attaching a copy
12039 // contains just references to original objects)
12040 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12041 it != mHWData->mSharedFolders.end();
12042 ++it)
12043 {
12044 ComObjPtr<SharedFolder> folder;
12045 folder.createObject();
12046 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12047 AssertComRC(rc);
12048 *it = folder;
12049 }
12050
12051 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12052 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12053 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12054 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12055 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12056
12057 /* create private copies of all controllers */
12058 mStorageControllers.backup();
12059 mStorageControllers->clear();
12060 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12061 it != aThat->mStorageControllers->end();
12062 ++it)
12063 {
12064 ComObjPtr<StorageController> ctrl;
12065 ctrl.createObject();
12066 ctrl->initCopy(this, *it);
12067 mStorageControllers->push_back(ctrl);
12068 }
12069
12070 /* create private copies of all USB controllers */
12071 mUSBControllers.backup();
12072 mUSBControllers->clear();
12073 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12074 it != aThat->mUSBControllers->end();
12075 ++it)
12076 {
12077 ComObjPtr<USBController> ctrl;
12078 ctrl.createObject();
12079 ctrl->initCopy(this, *it);
12080 mUSBControllers->push_back(ctrl);
12081 }
12082
12083 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12084 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12085 {
12086 if (mNetworkAdapters[slot].isNotNull())
12087 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12088 else
12089 {
12090 unconst(mNetworkAdapters[slot]).createObject();
12091 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12092 }
12093 }
12094 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12095 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12096 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12097 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12098}
12099
12100/**
12101 * Returns whether the given storage controller is hotplug capable.
12102 *
12103 * @returns true if the controller supports hotplugging
12104 * false otherwise.
12105 * @param enmCtrlType The controller type to check for.
12106 */
12107bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12108{
12109 ComPtr<ISystemProperties> systemProperties;
12110 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12111 if (FAILED(rc))
12112 return false;
12113
12114 BOOL aHotplugCapable = FALSE;
12115 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12116
12117 return RT_BOOL(aHotplugCapable);
12118}
12119
12120#ifdef VBOX_WITH_RESOURCE_USAGE_API
12121
12122void Machine::i_getDiskList(MediaList &list)
12123{
12124 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12125 it != mMediaData->mAttachments.end();
12126 ++it)
12127 {
12128 MediumAttachment* pAttach = *it;
12129 /* just in case */
12130 AssertContinue(pAttach);
12131
12132 AutoCaller localAutoCallerA(pAttach);
12133 if (FAILED(localAutoCallerA.rc())) continue;
12134
12135 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12136
12137 if (pAttach->i_getType() == DeviceType_HardDisk)
12138 list.push_back(pAttach->i_getMedium());
12139 }
12140}
12141
12142void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12143{
12144 AssertReturnVoid(isWriteLockOnCurrentThread());
12145 AssertPtrReturnVoid(aCollector);
12146
12147 pm::CollectorHAL *hal = aCollector->getHAL();
12148 /* Create sub metrics */
12149 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12150 "Percentage of processor time spent in user mode by the VM process.");
12151 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12152 "Percentage of processor time spent in kernel mode by the VM process.");
12153 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12154 "Size of resident portion of VM process in memory.");
12155 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12156 "Actual size of all VM disks combined.");
12157 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12158 "Network receive rate.");
12159 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12160 "Network transmit rate.");
12161 /* Create and register base metrics */
12162 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12163 cpuLoadUser, cpuLoadKernel);
12164 aCollector->registerBaseMetric(cpuLoad);
12165 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12166 ramUsageUsed);
12167 aCollector->registerBaseMetric(ramUsage);
12168 MediaList disks;
12169 i_getDiskList(disks);
12170 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12171 diskUsageUsed);
12172 aCollector->registerBaseMetric(diskUsage);
12173
12174 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12175 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12176 new pm::AggregateAvg()));
12177 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12178 new pm::AggregateMin()));
12179 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12180 new pm::AggregateMax()));
12181 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12182 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12183 new pm::AggregateAvg()));
12184 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12185 new pm::AggregateMin()));
12186 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12187 new pm::AggregateMax()));
12188
12189 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12190 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12191 new pm::AggregateAvg()));
12192 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12193 new pm::AggregateMin()));
12194 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12195 new pm::AggregateMax()));
12196
12197 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12198 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12199 new pm::AggregateAvg()));
12200 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12201 new pm::AggregateMin()));
12202 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12203 new pm::AggregateMax()));
12204
12205
12206 /* Guest metrics collector */
12207 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12208 aCollector->registerGuest(mCollectorGuest);
12209 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12210
12211 /* Create sub metrics */
12212 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12213 "Percentage of processor time spent in user mode as seen by the guest.");
12214 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12215 "Percentage of processor time spent in kernel mode as seen by the guest.");
12216 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12217 "Percentage of processor time spent idling as seen by the guest.");
12218
12219 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12220 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12221 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12222 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12223 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12224 pm::SubMetric *guestMemCache = new pm::SubMetric(
12225 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12226
12227 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12228 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12229
12230 /* Create and register base metrics */
12231 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12232 machineNetRx, machineNetTx);
12233 aCollector->registerBaseMetric(machineNetRate);
12234
12235 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12236 guestLoadUser, guestLoadKernel, guestLoadIdle);
12237 aCollector->registerBaseMetric(guestCpuLoad);
12238
12239 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12240 guestMemTotal, guestMemFree,
12241 guestMemBalloon, guestMemShared,
12242 guestMemCache, guestPagedTotal);
12243 aCollector->registerBaseMetric(guestCpuMem);
12244
12245 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12246 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12247 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12248 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12249
12250 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12251 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12252 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12253 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12254
12255 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12256 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12257 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12258 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12259
12260 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12261 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12262 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12263 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12264
12265 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12266 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12267 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12268 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12269
12270 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12271 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12272 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12273 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12274
12275 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12276 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12277 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12278 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12279
12280 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12281 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12282 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12283 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12284
12285 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12286 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12287 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12288 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12289
12290 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12291 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12292 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12293 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12294
12295 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12296 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12297 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12298 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12299}
12300
12301void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12302{
12303 AssertReturnVoid(isWriteLockOnCurrentThread());
12304
12305 if (aCollector)
12306 {
12307 aCollector->unregisterMetricsFor(aMachine);
12308 aCollector->unregisterBaseMetricsFor(aMachine);
12309 }
12310}
12311
12312#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12313
12314
12315////////////////////////////////////////////////////////////////////////////////
12316
12317DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12318
12319HRESULT SessionMachine::FinalConstruct()
12320{
12321 LogFlowThisFunc(("\n"));
12322
12323 mClientToken = NULL;
12324
12325 return BaseFinalConstruct();
12326}
12327
12328void SessionMachine::FinalRelease()
12329{
12330 LogFlowThisFunc(("\n"));
12331
12332 Assert(!mClientToken);
12333 /* paranoia, should not hang around any more */
12334 if (mClientToken)
12335 {
12336 delete mClientToken;
12337 mClientToken = NULL;
12338 }
12339
12340 uninit(Uninit::Unexpected);
12341
12342 BaseFinalRelease();
12343}
12344
12345/**
12346 * @note Must be called only by Machine::LockMachine() from its own write lock.
12347 */
12348HRESULT SessionMachine::init(Machine *aMachine)
12349{
12350 LogFlowThisFuncEnter();
12351 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12352
12353 AssertReturn(aMachine, E_INVALIDARG);
12354
12355 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12356
12357 /* Enclose the state transition NotReady->InInit->Ready */
12358 AutoInitSpan autoInitSpan(this);
12359 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12360
12361 HRESULT rc = S_OK;
12362
12363 RT_ZERO(mAuthLibCtx);
12364
12365 /* create the machine client token */
12366 try
12367 {
12368 mClientToken = new ClientToken(aMachine, this);
12369 if (!mClientToken->isReady())
12370 {
12371 delete mClientToken;
12372 mClientToken = NULL;
12373 rc = E_FAIL;
12374 }
12375 }
12376 catch (std::bad_alloc &)
12377 {
12378 rc = E_OUTOFMEMORY;
12379 }
12380 if (FAILED(rc))
12381 return rc;
12382
12383 /* memorize the peer Machine */
12384 unconst(mPeer) = aMachine;
12385 /* share the parent pointer */
12386 unconst(mParent) = aMachine->mParent;
12387
12388 /* take the pointers to data to share */
12389 mData.share(aMachine->mData);
12390 mSSData.share(aMachine->mSSData);
12391
12392 mUserData.share(aMachine->mUserData);
12393 mHWData.share(aMachine->mHWData);
12394 mMediaData.share(aMachine->mMediaData);
12395
12396 mStorageControllers.allocate();
12397 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12398 it != aMachine->mStorageControllers->end();
12399 ++it)
12400 {
12401 ComObjPtr<StorageController> ctl;
12402 ctl.createObject();
12403 ctl->init(this, *it);
12404 mStorageControllers->push_back(ctl);
12405 }
12406
12407 mUSBControllers.allocate();
12408 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12409 it != aMachine->mUSBControllers->end();
12410 ++it)
12411 {
12412 ComObjPtr<USBController> ctl;
12413 ctl.createObject();
12414 ctl->init(this, *it);
12415 mUSBControllers->push_back(ctl);
12416 }
12417
12418 unconst(mBIOSSettings).createObject();
12419 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12420 /* create another VRDEServer object that will be mutable */
12421 unconst(mVRDEServer).createObject();
12422 mVRDEServer->init(this, aMachine->mVRDEServer);
12423 /* create another audio adapter object that will be mutable */
12424 unconst(mAudioAdapter).createObject();
12425 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12426 /* create a list of serial ports that will be mutable */
12427 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12428 {
12429 unconst(mSerialPorts[slot]).createObject();
12430 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12431 }
12432 /* create a list of parallel ports that will be mutable */
12433 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12434 {
12435 unconst(mParallelPorts[slot]).createObject();
12436 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12437 }
12438
12439 /* create another USB device filters object that will be mutable */
12440 unconst(mUSBDeviceFilters).createObject();
12441 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12442
12443 /* create a list of network adapters that will be mutable */
12444 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12445 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12446 {
12447 unconst(mNetworkAdapters[slot]).createObject();
12448 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12449 }
12450
12451 /* create another bandwidth control object that will be mutable */
12452 unconst(mBandwidthControl).createObject();
12453 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12454
12455 /* default is to delete saved state on Saved -> PoweredOff transition */
12456 mRemoveSavedState = true;
12457
12458 /* Confirm a successful initialization when it's the case */
12459 autoInitSpan.setSucceeded();
12460
12461 miNATNetworksStarted = 0;
12462
12463 LogFlowThisFuncLeave();
12464 return rc;
12465}
12466
12467/**
12468 * Uninitializes this session object. If the reason is other than
12469 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12470 * or the client watcher code.
12471 *
12472 * @param aReason uninitialization reason
12473 *
12474 * @note Locks mParent + this object for writing.
12475 */
12476void SessionMachine::uninit(Uninit::Reason aReason)
12477{
12478 LogFlowThisFuncEnter();
12479 LogFlowThisFunc(("reason=%d\n", aReason));
12480
12481 /*
12482 * Strongly reference ourselves to prevent this object deletion after
12483 * mData->mSession.mMachine.setNull() below (which can release the last
12484 * reference and call the destructor). Important: this must be done before
12485 * accessing any members (and before AutoUninitSpan that does it as well).
12486 * This self reference will be released as the very last step on return.
12487 */
12488 ComObjPtr<SessionMachine> selfRef = this;
12489
12490 /* Enclose the state transition Ready->InUninit->NotReady */
12491 AutoUninitSpan autoUninitSpan(this);
12492 if (autoUninitSpan.uninitDone())
12493 {
12494 LogFlowThisFunc(("Already uninitialized\n"));
12495 LogFlowThisFuncLeave();
12496 return;
12497 }
12498
12499 if (autoUninitSpan.initFailed())
12500 {
12501 /* We've been called by init() because it's failed. It's not really
12502 * necessary (nor it's safe) to perform the regular uninit sequence
12503 * below, the following is enough.
12504 */
12505 LogFlowThisFunc(("Initialization failed.\n"));
12506 /* destroy the machine client token */
12507 if (mClientToken)
12508 {
12509 delete mClientToken;
12510 mClientToken = NULL;
12511 }
12512 uninitDataAndChildObjects();
12513 mData.free();
12514 unconst(mParent) = NULL;
12515 unconst(mPeer) = NULL;
12516 LogFlowThisFuncLeave();
12517 return;
12518 }
12519
12520 MachineState_T lastState;
12521 {
12522 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12523 lastState = mData->mMachineState;
12524 }
12525 NOREF(lastState);
12526
12527#ifdef VBOX_WITH_USB
12528 // release all captured USB devices, but do this before requesting the locks below
12529 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12530 {
12531 /* Console::captureUSBDevices() is called in the VM process only after
12532 * setting the machine state to Starting or Restoring.
12533 * Console::detachAllUSBDevices() will be called upon successful
12534 * termination. So, we need to release USB devices only if there was
12535 * an abnormal termination of a running VM.
12536 *
12537 * This is identical to SessionMachine::DetachAllUSBDevices except
12538 * for the aAbnormal argument. */
12539 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12540 AssertComRC(rc);
12541 NOREF(rc);
12542
12543 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12544 if (service)
12545 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12546 }
12547#endif /* VBOX_WITH_USB */
12548
12549 // we need to lock this object in uninit() because the lock is shared
12550 // with mPeer (as well as data we modify below). mParent lock is needed
12551 // by several calls to it, and USB needs host lock.
12552 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12553
12554#ifdef VBOX_WITH_RESOURCE_USAGE_API
12555 /*
12556 * It is safe to call Machine::i_unregisterMetrics() here because
12557 * PerformanceCollector::samplerCallback no longer accesses guest methods
12558 * holding the lock.
12559 */
12560 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12561 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12562 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12563 if (mCollectorGuest)
12564 {
12565 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12566 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12567 mCollectorGuest = NULL;
12568 }
12569#endif
12570
12571 if (aReason == Uninit::Abnormal)
12572 {
12573 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12574
12575 /* reset the state to Aborted */
12576 if (mData->mMachineState != MachineState_Aborted)
12577 i_setMachineState(MachineState_Aborted);
12578 }
12579
12580 // any machine settings modified?
12581 if (mData->flModifications)
12582 {
12583 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12584 i_rollback(false /* aNotify */);
12585 }
12586
12587 mData->mSession.mPID = NIL_RTPROCESS;
12588
12589 if (aReason == Uninit::Unexpected)
12590 {
12591 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12592 * client watcher thread to update the set of machines that have open
12593 * sessions. */
12594 mParent->i_updateClientWatcher();
12595 }
12596
12597 /* uninitialize all remote controls */
12598 if (mData->mSession.mRemoteControls.size())
12599 {
12600 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12601 mData->mSession.mRemoteControls.size()));
12602
12603 Data::Session::RemoteControlList::iterator it =
12604 mData->mSession.mRemoteControls.begin();
12605 while (it != mData->mSession.mRemoteControls.end())
12606 {
12607 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12608 HRESULT rc = (*it)->Uninitialize();
12609 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12610 if (FAILED(rc))
12611 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12612 ++it;
12613 }
12614 mData->mSession.mRemoteControls.clear();
12615 }
12616
12617 /* Remove all references to the NAT network service. The service will stop
12618 * if all references (also from other VMs) are removed. */
12619 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12620 {
12621 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12622 {
12623 BOOL enabled;
12624 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12625 if ( FAILED(hrc)
12626 || !enabled)
12627 continue;
12628
12629 NetworkAttachmentType_T type;
12630 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12631 if ( SUCCEEDED(hrc)
12632 && type == NetworkAttachmentType_NATNetwork)
12633 {
12634 Bstr name;
12635 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12636 if (SUCCEEDED(hrc))
12637 {
12638 multilock.release();
12639 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12640 mUserData->s.strName.c_str(), name.raw()));
12641 mParent->i_natNetworkRefDec(name.raw());
12642 multilock.acquire();
12643 }
12644 }
12645 }
12646 }
12647
12648 /*
12649 * An expected uninitialization can come only from #i_checkForDeath().
12650 * Otherwise it means that something's gone really wrong (for example,
12651 * the Session implementation has released the VirtualBox reference
12652 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12653 * etc). However, it's also possible, that the client releases the IPC
12654 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12655 * but the VirtualBox release event comes first to the server process.
12656 * This case is practically possible, so we should not assert on an
12657 * unexpected uninit, just log a warning.
12658 */
12659
12660 if ((aReason == Uninit::Unexpected))
12661 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12662
12663 if (aReason != Uninit::Normal)
12664 {
12665 mData->mSession.mDirectControl.setNull();
12666 }
12667 else
12668 {
12669 /* this must be null here (see #OnSessionEnd()) */
12670 Assert(mData->mSession.mDirectControl.isNull());
12671 Assert(mData->mSession.mState == SessionState_Unlocking);
12672 Assert(!mData->mSession.mProgress.isNull());
12673 }
12674 if (mData->mSession.mProgress)
12675 {
12676 if (aReason == Uninit::Normal)
12677 mData->mSession.mProgress->i_notifyComplete(S_OK);
12678 else
12679 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12680 COM_IIDOF(ISession),
12681 getComponentName(),
12682 tr("The VM session was aborted"));
12683 mData->mSession.mProgress.setNull();
12684 }
12685
12686 if (mConsoleTaskData.mProgress)
12687 {
12688 Assert(aReason == Uninit::Abnormal);
12689 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12690 COM_IIDOF(ISession),
12691 getComponentName(),
12692 tr("The VM session was aborted"));
12693 mConsoleTaskData.mProgress.setNull();
12694 }
12695
12696 /* remove the association between the peer machine and this session machine */
12697 Assert( (SessionMachine*)mData->mSession.mMachine == this
12698 || aReason == Uninit::Unexpected);
12699
12700 /* reset the rest of session data */
12701 mData->mSession.mLockType = LockType_Null;
12702 mData->mSession.mMachine.setNull();
12703 mData->mSession.mState = SessionState_Unlocked;
12704 mData->mSession.mName.setNull();
12705
12706 /* destroy the machine client token before leaving the exclusive lock */
12707 if (mClientToken)
12708 {
12709 delete mClientToken;
12710 mClientToken = NULL;
12711 }
12712
12713 /* fire an event */
12714 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12715
12716 uninitDataAndChildObjects();
12717
12718 /* free the essential data structure last */
12719 mData.free();
12720
12721 /* release the exclusive lock before setting the below two to NULL */
12722 multilock.release();
12723
12724 unconst(mParent) = NULL;
12725 unconst(mPeer) = NULL;
12726
12727 AuthLibUnload(&mAuthLibCtx);
12728
12729 LogFlowThisFuncLeave();
12730}
12731
12732// util::Lockable interface
12733////////////////////////////////////////////////////////////////////////////////
12734
12735/**
12736 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12737 * with the primary Machine instance (mPeer).
12738 */
12739RWLockHandle *SessionMachine::lockHandle() const
12740{
12741 AssertReturn(mPeer != NULL, NULL);
12742 return mPeer->lockHandle();
12743}
12744
12745// IInternalMachineControl methods
12746////////////////////////////////////////////////////////////////////////////////
12747
12748/**
12749 * Passes collected guest statistics to performance collector object
12750 */
12751HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12752 ULONG aCpuKernel, ULONG aCpuIdle,
12753 ULONG aMemTotal, ULONG aMemFree,
12754 ULONG aMemBalloon, ULONG aMemShared,
12755 ULONG aMemCache, ULONG aPageTotal,
12756 ULONG aAllocVMM, ULONG aFreeVMM,
12757 ULONG aBalloonedVMM, ULONG aSharedVMM,
12758 ULONG aVmNetRx, ULONG aVmNetTx)
12759{
12760#ifdef VBOX_WITH_RESOURCE_USAGE_API
12761 if (mCollectorGuest)
12762 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12763 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12764 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12765 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12766
12767 return S_OK;
12768#else
12769 NOREF(aValidStats);
12770 NOREF(aCpuUser);
12771 NOREF(aCpuKernel);
12772 NOREF(aCpuIdle);
12773 NOREF(aMemTotal);
12774 NOREF(aMemFree);
12775 NOREF(aMemBalloon);
12776 NOREF(aMemShared);
12777 NOREF(aMemCache);
12778 NOREF(aPageTotal);
12779 NOREF(aAllocVMM);
12780 NOREF(aFreeVMM);
12781 NOREF(aBalloonedVMM);
12782 NOREF(aSharedVMM);
12783 NOREF(aVmNetRx);
12784 NOREF(aVmNetTx);
12785 return E_NOTIMPL;
12786#endif
12787}
12788
12789////////////////////////////////////////////////////////////////////////////////
12790//
12791// SessionMachine task records
12792//
12793////////////////////////////////////////////////////////////////////////////////
12794
12795/**
12796 * Task record for saving the machine state.
12797 */
12798struct SessionMachine::SaveStateTask
12799 : public Machine::Task
12800{
12801 SaveStateTask(SessionMachine *m,
12802 Progress *p,
12803 const Utf8Str &t,
12804 Reason_T enmReason,
12805 const Utf8Str &strStateFilePath)
12806 : Task(m, p, t),
12807 m_enmReason(enmReason),
12808 m_strStateFilePath(strStateFilePath)
12809 {}
12810
12811 void handler()
12812 {
12813 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12814 }
12815
12816 Reason_T m_enmReason;
12817 Utf8Str m_strStateFilePath;
12818};
12819
12820/**
12821 * Task thread implementation for SessionMachine::SaveState(), called from
12822 * SessionMachine::taskHandler().
12823 *
12824 * @note Locks this object for writing.
12825 *
12826 * @param task
12827 * @return
12828 */
12829void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12830{
12831 LogFlowThisFuncEnter();
12832
12833 AutoCaller autoCaller(this);
12834 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12835 if (FAILED(autoCaller.rc()))
12836 {
12837 /* we might have been uninitialized because the session was accidentally
12838 * closed by the client, so don't assert */
12839 HRESULT rc = setError(E_FAIL,
12840 tr("The session has been accidentally closed"));
12841 task.m_pProgress->i_notifyComplete(rc);
12842 LogFlowThisFuncLeave();
12843 return;
12844 }
12845
12846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12847
12848 HRESULT rc = S_OK;
12849
12850 try
12851 {
12852 ComPtr<IInternalSessionControl> directControl;
12853 if (mData->mSession.mLockType == LockType_VM)
12854 directControl = mData->mSession.mDirectControl;
12855 if (directControl.isNull())
12856 throw setError(VBOX_E_INVALID_VM_STATE,
12857 tr("Trying to save state without a running VM"));
12858 alock.release();
12859 BOOL fSuspendedBySave;
12860 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12861 Assert(!fSuspendedBySave);
12862 alock.acquire();
12863
12864 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12865 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12866 throw E_FAIL);
12867
12868 if (SUCCEEDED(rc))
12869 {
12870 mSSData->strStateFilePath = task.m_strStateFilePath;
12871
12872 /* save all VM settings */
12873 rc = i_saveSettings(NULL);
12874 // no need to check whether VirtualBox.xml needs saving also since
12875 // we can't have a name change pending at this point
12876 }
12877 else
12878 {
12879 // On failure, set the state to the state we had at the beginning.
12880 i_setMachineState(task.m_machineStateBackup);
12881 i_updateMachineStateOnClient();
12882
12883 // Delete the saved state file (might have been already created).
12884 // No need to check whether this is shared with a snapshot here
12885 // because we certainly created a fresh saved state file here.
12886 RTFileDelete(task.m_strStateFilePath.c_str());
12887 }
12888 }
12889 catch (HRESULT aRC) { rc = aRC; }
12890
12891 task.m_pProgress->i_notifyComplete(rc);
12892
12893 LogFlowThisFuncLeave();
12894}
12895
12896/**
12897 * @note Locks this object for writing.
12898 */
12899HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12900{
12901 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12902}
12903
12904HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12905{
12906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12907
12908 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12909 if (FAILED(rc)) return rc;
12910
12911 if ( mData->mMachineState != MachineState_Running
12912 && mData->mMachineState != MachineState_Paused
12913 )
12914 return setError(VBOX_E_INVALID_VM_STATE,
12915 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12916 Global::stringifyMachineState(mData->mMachineState));
12917
12918 ComObjPtr<Progress> pProgress;
12919 pProgress.createObject();
12920 rc = pProgress->init(i_getVirtualBox(),
12921 static_cast<IMachine *>(this) /* aInitiator */,
12922 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12923 FALSE /* aCancelable */);
12924 if (FAILED(rc))
12925 return rc;
12926
12927 Utf8Str strStateFilePath;
12928 i_composeSavedStateFilename(strStateFilePath);
12929
12930 /* create and start the task on a separate thread (note that it will not
12931 * start working until we release alock) */
12932 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12933 rc = pTask->createThread();
12934 if (FAILED(rc))
12935 return rc;
12936
12937 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12938 i_setMachineState(MachineState_Saving);
12939 i_updateMachineStateOnClient();
12940
12941 pProgress.queryInterfaceTo(aProgress.asOutParam());
12942
12943 return S_OK;
12944}
12945
12946/**
12947 * @note Locks this object for writing.
12948 */
12949HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12950{
12951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12952
12953 HRESULT rc = i_checkStateDependency(MutableStateDep);
12954 if (FAILED(rc)) return rc;
12955
12956 if ( mData->mMachineState != MachineState_PoweredOff
12957 && mData->mMachineState != MachineState_Teleported
12958 && mData->mMachineState != MachineState_Aborted
12959 )
12960 return setError(VBOX_E_INVALID_VM_STATE,
12961 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12962 Global::stringifyMachineState(mData->mMachineState));
12963
12964 com::Utf8Str stateFilePathFull;
12965 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12966 if (RT_FAILURE(vrc))
12967 return setError(VBOX_E_FILE_ERROR,
12968 tr("Invalid saved state file path '%s' (%Rrc)"),
12969 aSavedStateFile.c_str(),
12970 vrc);
12971
12972 mSSData->strStateFilePath = stateFilePathFull;
12973
12974 /* The below i_setMachineState() will detect the state transition and will
12975 * update the settings file */
12976
12977 return i_setMachineState(MachineState_Saved);
12978}
12979
12980/**
12981 * @note Locks this object for writing.
12982 */
12983HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12984{
12985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12986
12987 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12988 if (FAILED(rc)) return rc;
12989
12990 if (mData->mMachineState != MachineState_Saved)
12991 return setError(VBOX_E_INVALID_VM_STATE,
12992 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12993 Global::stringifyMachineState(mData->mMachineState));
12994
12995 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12996
12997 /*
12998 * Saved -> PoweredOff transition will be detected in the SessionMachine
12999 * and properly handled.
13000 */
13001 rc = i_setMachineState(MachineState_PoweredOff);
13002 return rc;
13003}
13004
13005
13006/**
13007 * @note Locks the same as #i_setMachineState() does.
13008 */
13009HRESULT SessionMachine::updateState(MachineState_T aState)
13010{
13011 return i_setMachineState(aState);
13012}
13013
13014/**
13015 * @note Locks this object for writing.
13016 */
13017HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13018{
13019 IProgress* pProgress(aProgress);
13020
13021 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13022
13023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13024
13025 if (mData->mSession.mState != SessionState_Locked)
13026 return VBOX_E_INVALID_OBJECT_STATE;
13027
13028 if (!mData->mSession.mProgress.isNull())
13029 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13030
13031 /* If we didn't reference the NAT network service yet, add a reference to
13032 * force a start */
13033 if (miNATNetworksStarted < 1)
13034 {
13035 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13036 {
13037 BOOL enabled;
13038 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13039 if ( FAILED(hrc)
13040 || !enabled)
13041 continue;
13042
13043 NetworkAttachmentType_T type;
13044 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13045 if ( SUCCEEDED(hrc)
13046 && type == NetworkAttachmentType_NATNetwork)
13047 {
13048 Bstr name;
13049 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13050 if (SUCCEEDED(hrc))
13051 {
13052 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13053 mUserData->s.strName.c_str(), name.raw()));
13054 mPeer->lockHandle()->unlockWrite();
13055 mParent->i_natNetworkRefInc(name.raw());
13056#ifdef RT_LOCK_STRICT
13057 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13058#else
13059 mPeer->lockHandle()->lockWrite();
13060#endif
13061 }
13062 }
13063 }
13064 miNATNetworksStarted++;
13065 }
13066
13067 LogFlowThisFunc(("returns S_OK.\n"));
13068 return S_OK;
13069}
13070
13071/**
13072 * @note Locks this object for writing.
13073 */
13074HRESULT SessionMachine::endPowerUp(LONG aResult)
13075{
13076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13077
13078 if (mData->mSession.mState != SessionState_Locked)
13079 return VBOX_E_INVALID_OBJECT_STATE;
13080
13081 /* Finalize the LaunchVMProcess progress object. */
13082 if (mData->mSession.mProgress)
13083 {
13084 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13085 mData->mSession.mProgress.setNull();
13086 }
13087
13088 if (SUCCEEDED((HRESULT)aResult))
13089 {
13090#ifdef VBOX_WITH_RESOURCE_USAGE_API
13091 /* The VM has been powered up successfully, so it makes sense
13092 * now to offer the performance metrics for a running machine
13093 * object. Doing it earlier wouldn't be safe. */
13094 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13095 mData->mSession.mPID);
13096#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13097 }
13098
13099 return S_OK;
13100}
13101
13102/**
13103 * @note Locks this object for writing.
13104 */
13105HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13106{
13107 LogFlowThisFuncEnter();
13108
13109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13110
13111 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13112 E_FAIL);
13113
13114 /* create a progress object to track operation completion */
13115 ComObjPtr<Progress> pProgress;
13116 pProgress.createObject();
13117 pProgress->init(i_getVirtualBox(),
13118 static_cast<IMachine *>(this) /* aInitiator */,
13119 Bstr(tr("Stopping the virtual machine")).raw(),
13120 FALSE /* aCancelable */);
13121
13122 /* fill in the console task data */
13123 mConsoleTaskData.mLastState = mData->mMachineState;
13124 mConsoleTaskData.mProgress = pProgress;
13125
13126 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13127 i_setMachineState(MachineState_Stopping);
13128
13129 pProgress.queryInterfaceTo(aProgress.asOutParam());
13130
13131 return S_OK;
13132}
13133
13134/**
13135 * @note Locks this object for writing.
13136 */
13137HRESULT SessionMachine::endPoweringDown(LONG aResult,
13138 const com::Utf8Str &aErrMsg)
13139{
13140 LogFlowThisFuncEnter();
13141
13142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13143
13144 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13145 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13146 && mConsoleTaskData.mLastState != MachineState_Null,
13147 E_FAIL);
13148
13149 /*
13150 * On failure, set the state to the state we had when BeginPoweringDown()
13151 * was called (this is expected by Console::PowerDown() and the associated
13152 * task). On success the VM process already changed the state to
13153 * MachineState_PoweredOff, so no need to do anything.
13154 */
13155 if (FAILED(aResult))
13156 i_setMachineState(mConsoleTaskData.mLastState);
13157
13158 /* notify the progress object about operation completion */
13159 Assert(mConsoleTaskData.mProgress);
13160 if (SUCCEEDED(aResult))
13161 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13162 else
13163 {
13164 if (aErrMsg.length())
13165 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13166 COM_IIDOF(ISession),
13167 getComponentName(),
13168 aErrMsg.c_str());
13169 else
13170 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13171 }
13172
13173 /* clear out the temporary saved state data */
13174 mConsoleTaskData.mLastState = MachineState_Null;
13175 mConsoleTaskData.mProgress.setNull();
13176
13177 LogFlowThisFuncLeave();
13178 return S_OK;
13179}
13180
13181
13182/**
13183 * Goes through the USB filters of the given machine to see if the given
13184 * device matches any filter or not.
13185 *
13186 * @note Locks the same as USBController::hasMatchingFilter() does.
13187 */
13188HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13189 BOOL *aMatched,
13190 ULONG *aMaskedInterfaces)
13191{
13192 LogFlowThisFunc(("\n"));
13193
13194#ifdef VBOX_WITH_USB
13195 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13196#else
13197 NOREF(aDevice);
13198 NOREF(aMaskedInterfaces);
13199 *aMatched = FALSE;
13200#endif
13201
13202 return S_OK;
13203}
13204
13205/**
13206 * @note Locks the same as Host::captureUSBDevice() does.
13207 */
13208HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13209{
13210 LogFlowThisFunc(("\n"));
13211
13212#ifdef VBOX_WITH_USB
13213 /* if captureDeviceForVM() fails, it must have set extended error info */
13214 clearError();
13215 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13216 if (FAILED(rc)) return rc;
13217
13218 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13219 AssertReturn(service, E_FAIL);
13220 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13221#else
13222 NOREF(aId);
13223 return E_NOTIMPL;
13224#endif
13225}
13226
13227/**
13228 * @note Locks the same as Host::detachUSBDevice() does.
13229 */
13230HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13231 BOOL aDone)
13232{
13233 LogFlowThisFunc(("\n"));
13234
13235#ifdef VBOX_WITH_USB
13236 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13237 AssertReturn(service, E_FAIL);
13238 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13239#else
13240 NOREF(aId);
13241 NOREF(aDone);
13242 return E_NOTIMPL;
13243#endif
13244}
13245
13246/**
13247 * Inserts all machine filters to the USB proxy service and then calls
13248 * Host::autoCaptureUSBDevices().
13249 *
13250 * Called by Console from the VM process upon VM startup.
13251 *
13252 * @note Locks what called methods lock.
13253 */
13254HRESULT SessionMachine::autoCaptureUSBDevices()
13255{
13256 LogFlowThisFunc(("\n"));
13257
13258#ifdef VBOX_WITH_USB
13259 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13260 AssertComRC(rc);
13261 NOREF(rc);
13262
13263 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13264 AssertReturn(service, E_FAIL);
13265 return service->autoCaptureDevicesForVM(this);
13266#else
13267 return S_OK;
13268#endif
13269}
13270
13271/**
13272 * Removes all machine filters from the USB proxy service and then calls
13273 * Host::detachAllUSBDevices().
13274 *
13275 * Called by Console from the VM process upon normal VM termination or by
13276 * SessionMachine::uninit() upon abnormal VM termination (from under the
13277 * Machine/SessionMachine lock).
13278 *
13279 * @note Locks what called methods lock.
13280 */
13281HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13282{
13283 LogFlowThisFunc(("\n"));
13284
13285#ifdef VBOX_WITH_USB
13286 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13287 AssertComRC(rc);
13288 NOREF(rc);
13289
13290 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13291 AssertReturn(service, E_FAIL);
13292 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13293#else
13294 NOREF(aDone);
13295 return S_OK;
13296#endif
13297}
13298
13299/**
13300 * @note Locks this object for writing.
13301 */
13302HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13303 ComPtr<IProgress> &aProgress)
13304{
13305 LogFlowThisFuncEnter();
13306
13307 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13308 /*
13309 * We don't assert below because it might happen that a non-direct session
13310 * informs us it is closed right after we've been uninitialized -- it's ok.
13311 */
13312
13313 /* get IInternalSessionControl interface */
13314 ComPtr<IInternalSessionControl> control(aSession);
13315
13316 ComAssertRet(!control.isNull(), E_INVALIDARG);
13317
13318 /* Creating a Progress object requires the VirtualBox lock, and
13319 * thus locking it here is required by the lock order rules. */
13320 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13321
13322 if (control == mData->mSession.mDirectControl)
13323 {
13324 /* The direct session is being normally closed by the client process
13325 * ----------------------------------------------------------------- */
13326
13327 /* go to the closing state (essential for all open*Session() calls and
13328 * for #i_checkForDeath()) */
13329 Assert(mData->mSession.mState == SessionState_Locked);
13330 mData->mSession.mState = SessionState_Unlocking;
13331
13332 /* set direct control to NULL to release the remote instance */
13333 mData->mSession.mDirectControl.setNull();
13334 LogFlowThisFunc(("Direct control is set to NULL\n"));
13335
13336 if (mData->mSession.mProgress)
13337 {
13338 /* finalize the progress, someone might wait if a frontend
13339 * closes the session before powering on the VM. */
13340 mData->mSession.mProgress->notifyComplete(E_FAIL,
13341 COM_IIDOF(ISession),
13342 getComponentName(),
13343 tr("The VM session was closed before any attempt to power it on"));
13344 mData->mSession.mProgress.setNull();
13345 }
13346
13347 /* Create the progress object the client will use to wait until
13348 * #i_checkForDeath() is called to uninitialize this session object after
13349 * it releases the IPC semaphore.
13350 * Note! Because we're "reusing" mProgress here, this must be a proxy
13351 * object just like for LaunchVMProcess. */
13352 Assert(mData->mSession.mProgress.isNull());
13353 ComObjPtr<ProgressProxy> progress;
13354 progress.createObject();
13355 ComPtr<IUnknown> pPeer(mPeer);
13356 progress->init(mParent, pPeer,
13357 Bstr(tr("Closing session")).raw(),
13358 FALSE /* aCancelable */);
13359 progress.queryInterfaceTo(aProgress.asOutParam());
13360 mData->mSession.mProgress = progress;
13361 }
13362 else
13363 {
13364 /* the remote session is being normally closed */
13365 Data::Session::RemoteControlList::iterator it =
13366 mData->mSession.mRemoteControls.begin();
13367 while (it != mData->mSession.mRemoteControls.end())
13368 {
13369 if (control == *it)
13370 break;
13371 ++it;
13372 }
13373 BOOL found = it != mData->mSession.mRemoteControls.end();
13374 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13375 E_INVALIDARG);
13376 // This MUST be erase(it), not remove(*it) as the latter triggers a
13377 // very nasty use after free due to the place where the value "lives".
13378 mData->mSession.mRemoteControls.erase(it);
13379 }
13380
13381 /* signal the client watcher thread, because the client is going away */
13382 mParent->i_updateClientWatcher();
13383
13384 LogFlowThisFuncLeave();
13385 return S_OK;
13386}
13387
13388HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13389 std::vector<com::Utf8Str> &aValues,
13390 std::vector<LONG64> &aTimestamps,
13391 std::vector<com::Utf8Str> &aFlags)
13392{
13393 LogFlowThisFunc(("\n"));
13394
13395#ifdef VBOX_WITH_GUEST_PROPS
13396 using namespace guestProp;
13397
13398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13399
13400 size_t cEntries = mHWData->mGuestProperties.size();
13401 aNames.resize(cEntries);
13402 aValues.resize(cEntries);
13403 aTimestamps.resize(cEntries);
13404 aFlags.resize(cEntries);
13405
13406 size_t i = 0;
13407 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13408 it != mHWData->mGuestProperties.end();
13409 ++it, ++i)
13410 {
13411 char szFlags[MAX_FLAGS_LEN + 1];
13412 aNames[i] = it->first;
13413 aValues[i] = it->second.strValue;
13414 aTimestamps[i] = it->second.mTimestamp;
13415
13416 /* If it is NULL, keep it NULL. */
13417 if (it->second.mFlags)
13418 {
13419 writeFlags(it->second.mFlags, szFlags);
13420 aFlags[i] = szFlags;
13421 }
13422 else
13423 aFlags[i] = "";
13424 }
13425 return S_OK;
13426#else
13427 ReturnComNotImplemented();
13428#endif
13429}
13430
13431HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13432 const com::Utf8Str &aValue,
13433 LONG64 aTimestamp,
13434 const com::Utf8Str &aFlags)
13435{
13436 LogFlowThisFunc(("\n"));
13437
13438#ifdef VBOX_WITH_GUEST_PROPS
13439 using namespace guestProp;
13440
13441 try
13442 {
13443 /*
13444 * Convert input up front.
13445 */
13446 uint32_t fFlags = NILFLAG;
13447 if (aFlags.length())
13448 {
13449 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13450 AssertRCReturn(vrc, E_INVALIDARG);
13451 }
13452
13453 /*
13454 * Now grab the object lock, validate the state and do the update.
13455 */
13456
13457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13458
13459 if (!Global::IsOnline(mData->mMachineState))
13460 {
13461 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13462 VBOX_E_INVALID_VM_STATE);
13463 }
13464
13465 i_setModified(IsModified_MachineData);
13466 mHWData.backup();
13467
13468 bool fDelete = !aValue.length();
13469 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13470 if (it != mHWData->mGuestProperties.end())
13471 {
13472 if (!fDelete)
13473 {
13474 it->second.strValue = aValue;
13475 it->second.mTimestamp = aTimestamp;
13476 it->second.mFlags = fFlags;
13477 }
13478 else
13479 mHWData->mGuestProperties.erase(it);
13480
13481 mData->mGuestPropertiesModified = TRUE;
13482 }
13483 else if (!fDelete)
13484 {
13485 HWData::GuestProperty prop;
13486 prop.strValue = aValue;
13487 prop.mTimestamp = aTimestamp;
13488 prop.mFlags = fFlags;
13489
13490 mHWData->mGuestProperties[aName] = prop;
13491 mData->mGuestPropertiesModified = TRUE;
13492 }
13493
13494 alock.release();
13495
13496 mParent->i_onGuestPropertyChange(mData->mUuid,
13497 Bstr(aName).raw(),
13498 Bstr(aValue).raw(),
13499 Bstr(aFlags).raw());
13500 }
13501 catch (...)
13502 {
13503 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13504 }
13505 return S_OK;
13506#else
13507 ReturnComNotImplemented();
13508#endif
13509}
13510
13511
13512HRESULT SessionMachine::lockMedia()
13513{
13514 AutoMultiWriteLock2 alock(this->lockHandle(),
13515 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13516
13517 AssertReturn( mData->mMachineState == MachineState_Starting
13518 || mData->mMachineState == MachineState_Restoring
13519 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13520
13521 clearError();
13522 alock.release();
13523 return i_lockMedia();
13524}
13525
13526HRESULT SessionMachine::unlockMedia()
13527{
13528 HRESULT hrc = i_unlockMedia();
13529 return hrc;
13530}
13531
13532HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13533 ComPtr<IMediumAttachment> &aNewAttachment)
13534{
13535 // request the host lock first, since might be calling Host methods for getting host drives;
13536 // next, protect the media tree all the while we're in here, as well as our member variables
13537 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13538 this->lockHandle(),
13539 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13540
13541 IMediumAttachment *iAttach = aAttachment;
13542 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13543
13544 Bstr ctrlName;
13545 LONG lPort;
13546 LONG lDevice;
13547 bool fTempEject;
13548 {
13549 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13550
13551 /* Need to query the details first, as the IMediumAttachment reference
13552 * might be to the original settings, which we are going to change. */
13553 ctrlName = pAttach->i_getControllerName();
13554 lPort = pAttach->i_getPort();
13555 lDevice = pAttach->i_getDevice();
13556 fTempEject = pAttach->i_getTempEject();
13557 }
13558
13559 if (!fTempEject)
13560 {
13561 /* Remember previously mounted medium. The medium before taking the
13562 * backup is not necessarily the same thing. */
13563 ComObjPtr<Medium> oldmedium;
13564 oldmedium = pAttach->i_getMedium();
13565
13566 i_setModified(IsModified_Storage);
13567 mMediaData.backup();
13568
13569 // The backup operation makes the pAttach reference point to the
13570 // old settings. Re-get the correct reference.
13571 pAttach = i_findAttachment(mMediaData->mAttachments,
13572 ctrlName.raw(),
13573 lPort,
13574 lDevice);
13575
13576 {
13577 AutoCaller autoAttachCaller(this);
13578 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13579
13580 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13581 if (!oldmedium.isNull())
13582 oldmedium->i_removeBackReference(mData->mUuid);
13583
13584 pAttach->i_updateMedium(NULL);
13585 pAttach->i_updateEjected();
13586 }
13587
13588 i_setModified(IsModified_Storage);
13589 }
13590 else
13591 {
13592 {
13593 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13594 pAttach->i_updateEjected();
13595 }
13596 }
13597
13598 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13599
13600 return S_OK;
13601}
13602
13603HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13604 com::Utf8Str &aResult)
13605{
13606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13607
13608 HRESULT hr = S_OK;
13609
13610 if (!mAuthLibCtx.hAuthLibrary)
13611 {
13612 /* Load the external authentication library. */
13613 Bstr authLibrary;
13614 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13615
13616 Utf8Str filename = authLibrary;
13617
13618 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13619 if (RT_FAILURE(rc))
13620 {
13621 hr = setError(E_FAIL,
13622 tr("Could not load the external authentication library '%s' (%Rrc)"),
13623 filename.c_str(), rc);
13624 }
13625 }
13626
13627 /* The auth library might need the machine lock. */
13628 alock.release();
13629
13630 if (FAILED(hr))
13631 return hr;
13632
13633 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13634 {
13635 enum VRDEAuthParams
13636 {
13637 parmUuid = 1,
13638 parmGuestJudgement,
13639 parmUser,
13640 parmPassword,
13641 parmDomain,
13642 parmClientId
13643 };
13644
13645 AuthResult result = AuthResultAccessDenied;
13646
13647 Guid uuid(aAuthParams[parmUuid]);
13648 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13649 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13650
13651 result = AuthLibAuthenticate(&mAuthLibCtx,
13652 uuid.raw(), guestJudgement,
13653 aAuthParams[parmUser].c_str(),
13654 aAuthParams[parmPassword].c_str(),
13655 aAuthParams[parmDomain].c_str(),
13656 u32ClientId);
13657
13658 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13659 size_t cbPassword = aAuthParams[parmPassword].length();
13660 if (cbPassword)
13661 {
13662 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13663 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13664 }
13665
13666 if (result == AuthResultAccessGranted)
13667 aResult = "granted";
13668 else
13669 aResult = "denied";
13670
13671 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13672 aAuthParams[parmUser].c_str(), aResult.c_str()));
13673 }
13674 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13675 {
13676 enum VRDEAuthDisconnectParams
13677 {
13678 parmUuid = 1,
13679 parmClientId
13680 };
13681
13682 Guid uuid(aAuthParams[parmUuid]);
13683 uint32_t u32ClientId = 0;
13684 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13685 }
13686 else
13687 {
13688 hr = E_INVALIDARG;
13689 }
13690
13691 return hr;
13692}
13693
13694// public methods only for internal purposes
13695/////////////////////////////////////////////////////////////////////////////
13696
13697#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13698/**
13699 * Called from the client watcher thread to check for expected or unexpected
13700 * death of the client process that has a direct session to this machine.
13701 *
13702 * On Win32 and on OS/2, this method is called only when we've got the
13703 * mutex (i.e. the client has either died or terminated normally) so it always
13704 * returns @c true (the client is terminated, the session machine is
13705 * uninitialized).
13706 *
13707 * On other platforms, the method returns @c true if the client process has
13708 * terminated normally or abnormally and the session machine was uninitialized,
13709 * and @c false if the client process is still alive.
13710 *
13711 * @note Locks this object for writing.
13712 */
13713bool SessionMachine::i_checkForDeath()
13714{
13715 Uninit::Reason reason;
13716 bool terminated = false;
13717
13718 /* Enclose autoCaller with a block because calling uninit() from under it
13719 * will deadlock. */
13720 {
13721 AutoCaller autoCaller(this);
13722 if (!autoCaller.isOk())
13723 {
13724 /* return true if not ready, to cause the client watcher to exclude
13725 * the corresponding session from watching */
13726 LogFlowThisFunc(("Already uninitialized!\n"));
13727 return true;
13728 }
13729
13730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13731
13732 /* Determine the reason of death: if the session state is Closing here,
13733 * everything is fine. Otherwise it means that the client did not call
13734 * OnSessionEnd() before it released the IPC semaphore. This may happen
13735 * either because the client process has abnormally terminated, or
13736 * because it simply forgot to call ISession::Close() before exiting. We
13737 * threat the latter also as an abnormal termination (see
13738 * Session::uninit() for details). */
13739 reason = mData->mSession.mState == SessionState_Unlocking ?
13740 Uninit::Normal :
13741 Uninit::Abnormal;
13742
13743 if (mClientToken)
13744 terminated = mClientToken->release();
13745 } /* AutoCaller block */
13746
13747 if (terminated)
13748 uninit(reason);
13749
13750 return terminated;
13751}
13752
13753void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13754{
13755 LogFlowThisFunc(("\n"));
13756
13757 strTokenId.setNull();
13758
13759 AutoCaller autoCaller(this);
13760 AssertComRCReturnVoid(autoCaller.rc());
13761
13762 Assert(mClientToken);
13763 if (mClientToken)
13764 mClientToken->getId(strTokenId);
13765}
13766#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13767IToken *SessionMachine::i_getToken()
13768{
13769 LogFlowThisFunc(("\n"));
13770
13771 AutoCaller autoCaller(this);
13772 AssertComRCReturn(autoCaller.rc(), NULL);
13773
13774 Assert(mClientToken);
13775 if (mClientToken)
13776 return mClientToken->getToken();
13777 else
13778 return NULL;
13779}
13780#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13781
13782Machine::ClientToken *SessionMachine::i_getClientToken()
13783{
13784 LogFlowThisFunc(("\n"));
13785
13786 AutoCaller autoCaller(this);
13787 AssertComRCReturn(autoCaller.rc(), NULL);
13788
13789 return mClientToken;
13790}
13791
13792
13793/**
13794 * @note Locks this object for reading.
13795 */
13796HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13797{
13798 LogFlowThisFunc(("\n"));
13799
13800 AutoCaller autoCaller(this);
13801 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13802
13803 ComPtr<IInternalSessionControl> directControl;
13804 {
13805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13806 if (mData->mSession.mLockType == LockType_VM)
13807 directControl = mData->mSession.mDirectControl;
13808 }
13809
13810 /* ignore notifications sent after #OnSessionEnd() is called */
13811 if (!directControl)
13812 return S_OK;
13813
13814 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13815}
13816
13817/**
13818 * @note Locks this object for reading.
13819 */
13820HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13821 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13822 IN_BSTR aGuestIp, LONG aGuestPort)
13823{
13824 LogFlowThisFunc(("\n"));
13825
13826 AutoCaller autoCaller(this);
13827 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13828
13829 ComPtr<IInternalSessionControl> directControl;
13830 {
13831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13832 if (mData->mSession.mLockType == LockType_VM)
13833 directControl = mData->mSession.mDirectControl;
13834 }
13835
13836 /* ignore notifications sent after #OnSessionEnd() is called */
13837 if (!directControl)
13838 return S_OK;
13839 /*
13840 * instead acting like callback we ask IVirtualBox deliver corresponding event
13841 */
13842
13843 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13844 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13845 return S_OK;
13846}
13847
13848/**
13849 * @note Locks this object for reading.
13850 */
13851HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13852{
13853 LogFlowThisFunc(("\n"));
13854
13855 AutoCaller autoCaller(this);
13856 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13857
13858 ComPtr<IInternalSessionControl> directControl;
13859 {
13860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13861 if (mData->mSession.mLockType == LockType_VM)
13862 directControl = mData->mSession.mDirectControl;
13863 }
13864
13865 /* ignore notifications sent after #OnSessionEnd() is called */
13866 if (!directControl)
13867 return S_OK;
13868
13869 return directControl->OnSerialPortChange(serialPort);
13870}
13871
13872/**
13873 * @note Locks this object for reading.
13874 */
13875HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13876{
13877 LogFlowThisFunc(("\n"));
13878
13879 AutoCaller autoCaller(this);
13880 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13881
13882 ComPtr<IInternalSessionControl> directControl;
13883 {
13884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13885 if (mData->mSession.mLockType == LockType_VM)
13886 directControl = mData->mSession.mDirectControl;
13887 }
13888
13889 /* ignore notifications sent after #OnSessionEnd() is called */
13890 if (!directControl)
13891 return S_OK;
13892
13893 return directControl->OnParallelPortChange(parallelPort);
13894}
13895
13896/**
13897 * @note Locks this object for reading.
13898 */
13899HRESULT SessionMachine::i_onStorageControllerChange()
13900{
13901 LogFlowThisFunc(("\n"));
13902
13903 AutoCaller autoCaller(this);
13904 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13905
13906 ComPtr<IInternalSessionControl> directControl;
13907 {
13908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13909 if (mData->mSession.mLockType == LockType_VM)
13910 directControl = mData->mSession.mDirectControl;
13911 }
13912
13913 /* ignore notifications sent after #OnSessionEnd() is called */
13914 if (!directControl)
13915 return S_OK;
13916
13917 return directControl->OnStorageControllerChange();
13918}
13919
13920/**
13921 * @note Locks this object for reading.
13922 */
13923HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13924{
13925 LogFlowThisFunc(("\n"));
13926
13927 AutoCaller autoCaller(this);
13928 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13929
13930 ComPtr<IInternalSessionControl> directControl;
13931 {
13932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13933 if (mData->mSession.mLockType == LockType_VM)
13934 directControl = mData->mSession.mDirectControl;
13935 }
13936
13937 /* ignore notifications sent after #OnSessionEnd() is called */
13938 if (!directControl)
13939 return S_OK;
13940
13941 return directControl->OnMediumChange(aAttachment, aForce);
13942}
13943
13944/**
13945 * @note Locks this object for reading.
13946 */
13947HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13948{
13949 LogFlowThisFunc(("\n"));
13950
13951 AutoCaller autoCaller(this);
13952 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13953
13954 ComPtr<IInternalSessionControl> directControl;
13955 {
13956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13957 if (mData->mSession.mLockType == LockType_VM)
13958 directControl = mData->mSession.mDirectControl;
13959 }
13960
13961 /* ignore notifications sent after #OnSessionEnd() is called */
13962 if (!directControl)
13963 return S_OK;
13964
13965 return directControl->OnCPUChange(aCPU, aRemove);
13966}
13967
13968HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13969{
13970 LogFlowThisFunc(("\n"));
13971
13972 AutoCaller autoCaller(this);
13973 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13974
13975 ComPtr<IInternalSessionControl> directControl;
13976 {
13977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13978 if (mData->mSession.mLockType == LockType_VM)
13979 directControl = mData->mSession.mDirectControl;
13980 }
13981
13982 /* ignore notifications sent after #OnSessionEnd() is called */
13983 if (!directControl)
13984 return S_OK;
13985
13986 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13987}
13988
13989/**
13990 * @note Locks this object for reading.
13991 */
13992HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13993{
13994 LogFlowThisFunc(("\n"));
13995
13996 AutoCaller autoCaller(this);
13997 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13998
13999 ComPtr<IInternalSessionControl> directControl;
14000 {
14001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14002 if (mData->mSession.mLockType == LockType_VM)
14003 directControl = mData->mSession.mDirectControl;
14004 }
14005
14006 /* ignore notifications sent after #OnSessionEnd() is called */
14007 if (!directControl)
14008 return S_OK;
14009
14010 return directControl->OnVRDEServerChange(aRestart);
14011}
14012
14013/**
14014 * @note Locks this object for reading.
14015 */
14016HRESULT SessionMachine::i_onVideoCaptureChange()
14017{
14018 LogFlowThisFunc(("\n"));
14019
14020 AutoCaller autoCaller(this);
14021 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14022
14023 ComPtr<IInternalSessionControl> directControl;
14024 {
14025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14026 if (mData->mSession.mLockType == LockType_VM)
14027 directControl = mData->mSession.mDirectControl;
14028 }
14029
14030 /* ignore notifications sent after #OnSessionEnd() is called */
14031 if (!directControl)
14032 return S_OK;
14033
14034 return directControl->OnVideoCaptureChange();
14035}
14036
14037/**
14038 * @note Locks this object for reading.
14039 */
14040HRESULT SessionMachine::i_onUSBControllerChange()
14041{
14042 LogFlowThisFunc(("\n"));
14043
14044 AutoCaller autoCaller(this);
14045 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14046
14047 ComPtr<IInternalSessionControl> directControl;
14048 {
14049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14050 if (mData->mSession.mLockType == LockType_VM)
14051 directControl = mData->mSession.mDirectControl;
14052 }
14053
14054 /* ignore notifications sent after #OnSessionEnd() is called */
14055 if (!directControl)
14056 return S_OK;
14057
14058 return directControl->OnUSBControllerChange();
14059}
14060
14061/**
14062 * @note Locks this object for reading.
14063 */
14064HRESULT SessionMachine::i_onSharedFolderChange()
14065{
14066 LogFlowThisFunc(("\n"));
14067
14068 AutoCaller autoCaller(this);
14069 AssertComRCReturnRC(autoCaller.rc());
14070
14071 ComPtr<IInternalSessionControl> directControl;
14072 {
14073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14074 if (mData->mSession.mLockType == LockType_VM)
14075 directControl = mData->mSession.mDirectControl;
14076 }
14077
14078 /* ignore notifications sent after #OnSessionEnd() is called */
14079 if (!directControl)
14080 return S_OK;
14081
14082 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14083}
14084
14085/**
14086 * @note Locks this object for reading.
14087 */
14088HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14089{
14090 LogFlowThisFunc(("\n"));
14091
14092 AutoCaller autoCaller(this);
14093 AssertComRCReturnRC(autoCaller.rc());
14094
14095 ComPtr<IInternalSessionControl> directControl;
14096 {
14097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14098 if (mData->mSession.mLockType == LockType_VM)
14099 directControl = mData->mSession.mDirectControl;
14100 }
14101
14102 /* ignore notifications sent after #OnSessionEnd() is called */
14103 if (!directControl)
14104 return S_OK;
14105
14106 return directControl->OnClipboardModeChange(aClipboardMode);
14107}
14108
14109/**
14110 * @note Locks this object for reading.
14111 */
14112HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14113{
14114 LogFlowThisFunc(("\n"));
14115
14116 AutoCaller autoCaller(this);
14117 AssertComRCReturnRC(autoCaller.rc());
14118
14119 ComPtr<IInternalSessionControl> directControl;
14120 {
14121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14122 if (mData->mSession.mLockType == LockType_VM)
14123 directControl = mData->mSession.mDirectControl;
14124 }
14125
14126 /* ignore notifications sent after #OnSessionEnd() is called */
14127 if (!directControl)
14128 return S_OK;
14129
14130 return directControl->OnDnDModeChange(aDnDMode);
14131}
14132
14133/**
14134 * @note Locks this object for reading.
14135 */
14136HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14137{
14138 LogFlowThisFunc(("\n"));
14139
14140 AutoCaller autoCaller(this);
14141 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14142
14143 ComPtr<IInternalSessionControl> directControl;
14144 {
14145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14146 if (mData->mSession.mLockType == LockType_VM)
14147 directControl = mData->mSession.mDirectControl;
14148 }
14149
14150 /* ignore notifications sent after #OnSessionEnd() is called */
14151 if (!directControl)
14152 return S_OK;
14153
14154 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14155}
14156
14157/**
14158 * @note Locks this object for reading.
14159 */
14160HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14161{
14162 LogFlowThisFunc(("\n"));
14163
14164 AutoCaller autoCaller(this);
14165 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14166
14167 ComPtr<IInternalSessionControl> directControl;
14168 {
14169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14170 if (mData->mSession.mLockType == LockType_VM)
14171 directControl = mData->mSession.mDirectControl;
14172 }
14173
14174 /* ignore notifications sent after #OnSessionEnd() is called */
14175 if (!directControl)
14176 return S_OK;
14177
14178 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14179}
14180
14181/**
14182 * Returns @c true if this machine's USB controller reports it has a matching
14183 * filter for the given USB device and @c false otherwise.
14184 *
14185 * @note locks this object for reading.
14186 */
14187bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14188{
14189 AutoCaller autoCaller(this);
14190 /* silently return if not ready -- this method may be called after the
14191 * direct machine session has been called */
14192 if (!autoCaller.isOk())
14193 return false;
14194
14195#ifdef VBOX_WITH_USB
14196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14197
14198 switch (mData->mMachineState)
14199 {
14200 case MachineState_Starting:
14201 case MachineState_Restoring:
14202 case MachineState_TeleportingIn:
14203 case MachineState_Paused:
14204 case MachineState_Running:
14205 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14206 * elsewhere... */
14207 alock.release();
14208 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14209 default: break;
14210 }
14211#else
14212 NOREF(aDevice);
14213 NOREF(aMaskedIfs);
14214#endif
14215 return false;
14216}
14217
14218/**
14219 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14220 */
14221HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14222 IVirtualBoxErrorInfo *aError,
14223 ULONG aMaskedIfs,
14224 const com::Utf8Str &aCaptureFilename)
14225{
14226 LogFlowThisFunc(("\n"));
14227
14228 AutoCaller autoCaller(this);
14229
14230 /* This notification may happen after the machine object has been
14231 * uninitialized (the session was closed), so don't assert. */
14232 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14233
14234 ComPtr<IInternalSessionControl> directControl;
14235 {
14236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14237 if (mData->mSession.mLockType == LockType_VM)
14238 directControl = mData->mSession.mDirectControl;
14239 }
14240
14241 /* fail on notifications sent after #OnSessionEnd() is called, it is
14242 * expected by the caller */
14243 if (!directControl)
14244 return E_FAIL;
14245
14246 /* No locks should be held at this point. */
14247 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14248 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14249
14250 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14251}
14252
14253/**
14254 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14255 */
14256HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14257 IVirtualBoxErrorInfo *aError)
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->OnUSBDeviceDetach(aId, aError);
14284}
14285
14286// protected methods
14287/////////////////////////////////////////////////////////////////////////////
14288
14289/**
14290 * Deletes the given file if it is no longer in use by either the current machine state
14291 * (if the machine is "saved") or any of the machine's snapshots.
14292 *
14293 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14294 * but is different for each SnapshotMachine. When calling this, the order of calling this
14295 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14296 * is therefore critical. I know, it's all rather messy.
14297 *
14298 * @param strStateFile
14299 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14300 * the test for whether the saved state file is in use.
14301 */
14302void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14303 Snapshot *pSnapshotToIgnore)
14304{
14305 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14306 if ( (strStateFile.isNotEmpty())
14307 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14308 )
14309 // ... and it must also not be shared with other snapshots
14310 if ( !mData->mFirstSnapshot
14311 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14312 // this checks the SnapshotMachine's state file paths
14313 )
14314 RTFileDelete(strStateFile.c_str());
14315}
14316
14317/**
14318 * Locks the attached media.
14319 *
14320 * All attached hard disks are locked for writing and DVD/floppy are locked for
14321 * reading. Parents of attached hard disks (if any) are locked for reading.
14322 *
14323 * This method also performs accessibility check of all media it locks: if some
14324 * media is inaccessible, the method will return a failure and a bunch of
14325 * extended error info objects per each inaccessible medium.
14326 *
14327 * Note that this method is atomic: if it returns a success, all media are
14328 * locked as described above; on failure no media is locked at all (all
14329 * succeeded individual locks will be undone).
14330 *
14331 * The caller is responsible for doing the necessary state sanity checks.
14332 *
14333 * The locks made by this method must be undone by calling #unlockMedia() when
14334 * no more needed.
14335 */
14336HRESULT SessionMachine::i_lockMedia()
14337{
14338 AutoCaller autoCaller(this);
14339 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14340
14341 AutoMultiWriteLock2 alock(this->lockHandle(),
14342 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14343
14344 /* bail out if trying to lock things with already set up locking */
14345 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14346
14347 MultiResult mrc(S_OK);
14348
14349 /* Collect locking information for all medium objects attached to the VM. */
14350 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14351 it != mMediaData->mAttachments.end();
14352 ++it)
14353 {
14354 MediumAttachment* pAtt = *it;
14355 DeviceType_T devType = pAtt->i_getType();
14356 Medium *pMedium = pAtt->i_getMedium();
14357
14358 MediumLockList *pMediumLockList(new MediumLockList());
14359 // There can be attachments without a medium (floppy/dvd), and thus
14360 // it's impossible to create a medium lock list. It still makes sense
14361 // to have the empty medium lock list in the map in case a medium is
14362 // attached later.
14363 if (pMedium != NULL)
14364 {
14365 MediumType_T mediumType = pMedium->i_getType();
14366 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14367 || mediumType == MediumType_Shareable;
14368 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14369
14370 alock.release();
14371 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14372 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14373 false /* fMediumLockWriteAll */,
14374 NULL,
14375 *pMediumLockList);
14376 alock.acquire();
14377 if (FAILED(mrc))
14378 {
14379 delete pMediumLockList;
14380 mData->mSession.mLockedMedia.Clear();
14381 break;
14382 }
14383 }
14384
14385 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14386 if (FAILED(rc))
14387 {
14388 mData->mSession.mLockedMedia.Clear();
14389 mrc = setError(rc,
14390 tr("Collecting locking information for all attached media failed"));
14391 break;
14392 }
14393 }
14394
14395 if (SUCCEEDED(mrc))
14396 {
14397 /* Now lock all media. If this fails, nothing is locked. */
14398 alock.release();
14399 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14400 alock.acquire();
14401 if (FAILED(rc))
14402 {
14403 mrc = setError(rc,
14404 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14405 }
14406 }
14407
14408 return mrc;
14409}
14410
14411/**
14412 * Undoes the locks made by by #lockMedia().
14413 */
14414HRESULT SessionMachine::i_unlockMedia()
14415{
14416 AutoCaller autoCaller(this);
14417 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14418
14419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14420
14421 /* we may be holding important error info on the current thread;
14422 * preserve it */
14423 ErrorInfoKeeper eik;
14424
14425 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14426 AssertComRC(rc);
14427 return rc;
14428}
14429
14430/**
14431 * Helper to change the machine state (reimplementation).
14432 *
14433 * @note Locks this object for writing.
14434 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14435 * it can cause crashes in random places due to unexpectedly committing
14436 * the current settings. The caller is responsible for that. The call
14437 * to saveStateSettings is fine, because this method does not commit.
14438 */
14439HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14440{
14441 LogFlowThisFuncEnter();
14442 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14443
14444 AutoCaller autoCaller(this);
14445 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14446
14447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14448
14449 MachineState_T oldMachineState = mData->mMachineState;
14450
14451 AssertMsgReturn(oldMachineState != aMachineState,
14452 ("oldMachineState=%s, aMachineState=%s\n",
14453 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14454 E_FAIL);
14455
14456 HRESULT rc = S_OK;
14457
14458 int stsFlags = 0;
14459 bool deleteSavedState = false;
14460
14461 /* detect some state transitions */
14462
14463 if ( ( oldMachineState == MachineState_Saved
14464 && aMachineState == MachineState_Restoring)
14465 || ( ( oldMachineState == MachineState_PoweredOff
14466 || oldMachineState == MachineState_Teleported
14467 || oldMachineState == MachineState_Aborted
14468 )
14469 && ( aMachineState == MachineState_TeleportingIn
14470 || aMachineState == MachineState_Starting
14471 )
14472 )
14473 )
14474 {
14475 /* The EMT thread is about to start */
14476
14477 /* Nothing to do here for now... */
14478
14479 /// @todo NEWMEDIA don't let mDVDDrive and other children
14480 /// change anything when in the Starting/Restoring state
14481 }
14482 else if ( ( oldMachineState == MachineState_Running
14483 || oldMachineState == MachineState_Paused
14484 || oldMachineState == MachineState_Teleporting
14485 || oldMachineState == MachineState_OnlineSnapshotting
14486 || oldMachineState == MachineState_LiveSnapshotting
14487 || oldMachineState == MachineState_Stuck
14488 || oldMachineState == MachineState_Starting
14489 || oldMachineState == MachineState_Stopping
14490 || oldMachineState == MachineState_Saving
14491 || oldMachineState == MachineState_Restoring
14492 || oldMachineState == MachineState_TeleportingPausedVM
14493 || oldMachineState == MachineState_TeleportingIn
14494 )
14495 && ( aMachineState == MachineState_PoweredOff
14496 || aMachineState == MachineState_Saved
14497 || aMachineState == MachineState_Teleported
14498 || aMachineState == MachineState_Aborted
14499 )
14500 )
14501 {
14502 /* The EMT thread has just stopped, unlock attached media. Note that as
14503 * opposed to locking that is done from Console, we do unlocking here
14504 * because the VM process may have aborted before having a chance to
14505 * properly unlock all media it locked. */
14506
14507 unlockMedia();
14508 }
14509
14510 if (oldMachineState == MachineState_Restoring)
14511 {
14512 if (aMachineState != MachineState_Saved)
14513 {
14514 /*
14515 * delete the saved state file once the machine has finished
14516 * restoring from it (note that Console sets the state from
14517 * Restoring to Saved if the VM couldn't restore successfully,
14518 * to give the user an ability to fix an error and retry --
14519 * we keep the saved state file in this case)
14520 */
14521 deleteSavedState = true;
14522 }
14523 }
14524 else if ( oldMachineState == MachineState_Saved
14525 && ( aMachineState == MachineState_PoweredOff
14526 || aMachineState == MachineState_Aborted
14527 || aMachineState == MachineState_Teleported
14528 )
14529 )
14530 {
14531 /*
14532 * delete the saved state after SessionMachine::ForgetSavedState() is called
14533 * or if the VM process (owning a direct VM session) crashed while the
14534 * VM was Saved
14535 */
14536
14537 /// @todo (dmik)
14538 // Not sure that deleting the saved state file just because of the
14539 // client death before it attempted to restore the VM is a good
14540 // thing. But when it crashes we need to go to the Aborted state
14541 // which cannot have the saved state file associated... The only
14542 // way to fix this is to make the Aborted condition not a VM state
14543 // but a bool flag: i.e., when a crash occurs, set it to true and
14544 // change the state to PoweredOff or Saved depending on the
14545 // saved state presence.
14546
14547 deleteSavedState = true;
14548 mData->mCurrentStateModified = TRUE;
14549 stsFlags |= SaveSTS_CurStateModified;
14550 }
14551
14552 if ( aMachineState == MachineState_Starting
14553 || aMachineState == MachineState_Restoring
14554 || aMachineState == MachineState_TeleportingIn
14555 )
14556 {
14557 /* set the current state modified flag to indicate that the current
14558 * state is no more identical to the state in the
14559 * current snapshot */
14560 if (!mData->mCurrentSnapshot.isNull())
14561 {
14562 mData->mCurrentStateModified = TRUE;
14563 stsFlags |= SaveSTS_CurStateModified;
14564 }
14565 }
14566
14567 if (deleteSavedState)
14568 {
14569 if (mRemoveSavedState)
14570 {
14571 Assert(!mSSData->strStateFilePath.isEmpty());
14572
14573 // it is safe to delete the saved state file if ...
14574 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14575 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14576 // ... none of the snapshots share the saved state file
14577 )
14578 RTFileDelete(mSSData->strStateFilePath.c_str());
14579 }
14580
14581 mSSData->strStateFilePath.setNull();
14582 stsFlags |= SaveSTS_StateFilePath;
14583 }
14584
14585 /* redirect to the underlying peer machine */
14586 mPeer->i_setMachineState(aMachineState);
14587
14588 if ( oldMachineState != MachineState_RestoringSnapshot
14589 && ( aMachineState == MachineState_PoweredOff
14590 || aMachineState == MachineState_Teleported
14591 || aMachineState == MachineState_Aborted
14592 || aMachineState == MachineState_Saved))
14593 {
14594 /* the machine has stopped execution
14595 * (or the saved state file was adopted) */
14596 stsFlags |= SaveSTS_StateTimeStamp;
14597 }
14598
14599 if ( ( oldMachineState == MachineState_PoweredOff
14600 || oldMachineState == MachineState_Aborted
14601 || oldMachineState == MachineState_Teleported
14602 )
14603 && aMachineState == MachineState_Saved)
14604 {
14605 /* the saved state file was adopted */
14606 Assert(!mSSData->strStateFilePath.isEmpty());
14607 stsFlags |= SaveSTS_StateFilePath;
14608 }
14609
14610#ifdef VBOX_WITH_GUEST_PROPS
14611 if ( aMachineState == MachineState_PoweredOff
14612 || aMachineState == MachineState_Aborted
14613 || aMachineState == MachineState_Teleported)
14614 {
14615 /* Make sure any transient guest properties get removed from the
14616 * property store on shutdown. */
14617 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14618
14619 /* remove it from the settings representation */
14620 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14621 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14622 it != llGuestProperties.end();
14623 /*nothing*/)
14624 {
14625 const settings::GuestProperty &prop = *it;
14626 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14627 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14628 {
14629 it = llGuestProperties.erase(it);
14630 fNeedsSaving = true;
14631 }
14632 else
14633 {
14634 ++it;
14635 }
14636 }
14637
14638 /* Additionally remove it from the HWData representation. Required to
14639 * keep everything in sync, as this is what the API keeps using. */
14640 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14641 for (HWData::GuestPropertyMap::iterator it = llHWGuestProperties.begin();
14642 it != llHWGuestProperties.end();
14643 /*nothing*/)
14644 {
14645 uint32_t fFlags = it->second.mFlags;
14646 if ( fFlags & guestProp::TRANSIENT
14647 || fFlags & guestProp::TRANSRESET)
14648 {
14649 /* iterator where we need to continue after the erase call
14650 * (C++03 is a fact still, and it doesn't return the iterator
14651 * which would allow continuing) */
14652 HWData::GuestPropertyMap::iterator it2 = it;
14653 ++it2;
14654 llHWGuestProperties.erase(it);
14655 it = it2;
14656 fNeedsSaving = true;
14657 }
14658 else
14659 {
14660 ++it;
14661 }
14662 }
14663
14664 if (fNeedsSaving)
14665 {
14666 mData->mCurrentStateModified = TRUE;
14667 stsFlags |= SaveSTS_CurStateModified;
14668 }
14669 }
14670#endif /* VBOX_WITH_GUEST_PROPS */
14671
14672 rc = i_saveStateSettings(stsFlags);
14673
14674 if ( ( oldMachineState != MachineState_PoweredOff
14675 && oldMachineState != MachineState_Aborted
14676 && oldMachineState != MachineState_Teleported
14677 )
14678 && ( aMachineState == MachineState_PoweredOff
14679 || aMachineState == MachineState_Aborted
14680 || aMachineState == MachineState_Teleported
14681 )
14682 )
14683 {
14684 /* we've been shut down for any reason */
14685 /* no special action so far */
14686 }
14687
14688 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14689 LogFlowThisFuncLeave();
14690 return rc;
14691}
14692
14693/**
14694 * Sends the current machine state value to the VM process.
14695 *
14696 * @note Locks this object for reading, then calls a client process.
14697 */
14698HRESULT SessionMachine::i_updateMachineStateOnClient()
14699{
14700 AutoCaller autoCaller(this);
14701 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14702
14703 ComPtr<IInternalSessionControl> directControl;
14704 {
14705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14706 AssertReturn(!!mData, E_FAIL);
14707 if (mData->mSession.mLockType == LockType_VM)
14708 directControl = mData->mSession.mDirectControl;
14709
14710 /* directControl may be already set to NULL here in #OnSessionEnd()
14711 * called too early by the direct session process while there is still
14712 * some operation (like deleting the snapshot) in progress. The client
14713 * process in this case is waiting inside Session::close() for the
14714 * "end session" process object to complete, while #uninit() called by
14715 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14716 * operation to complete. For now, we accept this inconsistent behavior
14717 * and simply do nothing here. */
14718
14719 if (mData->mSession.mState == SessionState_Unlocking)
14720 return S_OK;
14721 }
14722
14723 /* ignore notifications sent after #OnSessionEnd() is called */
14724 if (!directControl)
14725 return S_OK;
14726
14727 return directControl->UpdateMachineState(mData->mMachineState);
14728}
14729
14730
14731/**
14732 * Static Machine method that can get passed to RTThreadCreate to
14733 * have a thread started for a Task. See Machine::Task.
14734 */
14735/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14736{
14737 AssertReturn(pvUser, VERR_INVALID_POINTER);
14738
14739 Task *pTask = static_cast<Task *>(pvUser);
14740 pTask->handler();
14741 /** @todo r=klaus it would be safer to update the progress object here,
14742 * as it avoids possible races due to scoping issues/tricks in the handler */
14743 // it's our responsibility to delete the task
14744 delete pTask;
14745
14746 return 0;
14747}
14748
14749/*static*/
14750HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14751{
14752 va_list args;
14753 va_start(args, pcszMsg);
14754 HRESULT rc = setErrorInternal(aResultCode,
14755 getStaticClassIID(),
14756 getStaticComponentName(),
14757 Utf8Str(pcszMsg, args),
14758 false /* aWarning */,
14759 true /* aLogIt */);
14760 va_end(args);
14761 return rc;
14762}
14763
14764
14765HRESULT Machine::updateState(MachineState_T aState)
14766{
14767 NOREF(aState);
14768 ReturnComNotImplemented();
14769}
14770
14771HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14772{
14773 NOREF(aProgress);
14774 ReturnComNotImplemented();
14775}
14776
14777HRESULT Machine::endPowerUp(LONG aResult)
14778{
14779 NOREF(aResult);
14780 ReturnComNotImplemented();
14781}
14782
14783HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14784{
14785 NOREF(aProgress);
14786 ReturnComNotImplemented();
14787}
14788
14789HRESULT Machine::endPoweringDown(LONG aResult,
14790 const com::Utf8Str &aErrMsg)
14791{
14792 NOREF(aResult);
14793 NOREF(aErrMsg);
14794 ReturnComNotImplemented();
14795}
14796
14797HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14798 BOOL *aMatched,
14799 ULONG *aMaskedInterfaces)
14800{
14801 NOREF(aDevice);
14802 NOREF(aMatched);
14803 NOREF(aMaskedInterfaces);
14804 ReturnComNotImplemented();
14805
14806}
14807
14808HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14809{
14810 NOREF(aId); NOREF(aCaptureFilename);
14811 ReturnComNotImplemented();
14812}
14813
14814HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14815 BOOL aDone)
14816{
14817 NOREF(aId);
14818 NOREF(aDone);
14819 ReturnComNotImplemented();
14820}
14821
14822HRESULT Machine::autoCaptureUSBDevices()
14823{
14824 ReturnComNotImplemented();
14825}
14826
14827HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14828{
14829 NOREF(aDone);
14830 ReturnComNotImplemented();
14831}
14832
14833HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14834 ComPtr<IProgress> &aProgress)
14835{
14836 NOREF(aSession);
14837 NOREF(aProgress);
14838 ReturnComNotImplemented();
14839}
14840
14841HRESULT Machine::finishOnlineMergeMedium()
14842{
14843 ReturnComNotImplemented();
14844}
14845
14846HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14847 std::vector<com::Utf8Str> &aValues,
14848 std::vector<LONG64> &aTimestamps,
14849 std::vector<com::Utf8Str> &aFlags)
14850{
14851 NOREF(aNames);
14852 NOREF(aValues);
14853 NOREF(aTimestamps);
14854 NOREF(aFlags);
14855 ReturnComNotImplemented();
14856}
14857
14858HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14859 const com::Utf8Str &aValue,
14860 LONG64 aTimestamp,
14861 const com::Utf8Str &aFlags)
14862{
14863 NOREF(aName);
14864 NOREF(aValue);
14865 NOREF(aTimestamp);
14866 NOREF(aFlags);
14867 ReturnComNotImplemented();
14868}
14869
14870HRESULT Machine::lockMedia()
14871{
14872 ReturnComNotImplemented();
14873}
14874
14875HRESULT Machine::unlockMedia()
14876{
14877 ReturnComNotImplemented();
14878}
14879
14880HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14881 ComPtr<IMediumAttachment> &aNewAttachment)
14882{
14883 NOREF(aAttachment);
14884 NOREF(aNewAttachment);
14885 ReturnComNotImplemented();
14886}
14887
14888HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14889 ULONG aCpuUser,
14890 ULONG aCpuKernel,
14891 ULONG aCpuIdle,
14892 ULONG aMemTotal,
14893 ULONG aMemFree,
14894 ULONG aMemBalloon,
14895 ULONG aMemShared,
14896 ULONG aMemCache,
14897 ULONG aPagedTotal,
14898 ULONG aMemAllocTotal,
14899 ULONG aMemFreeTotal,
14900 ULONG aMemBalloonTotal,
14901 ULONG aMemSharedTotal,
14902 ULONG aVmNetRx,
14903 ULONG aVmNetTx)
14904{
14905 NOREF(aValidStats);
14906 NOREF(aCpuUser);
14907 NOREF(aCpuKernel);
14908 NOREF(aCpuIdle);
14909 NOREF(aMemTotal);
14910 NOREF(aMemFree);
14911 NOREF(aMemBalloon);
14912 NOREF(aMemShared);
14913 NOREF(aMemCache);
14914 NOREF(aPagedTotal);
14915 NOREF(aMemAllocTotal);
14916 NOREF(aMemFreeTotal);
14917 NOREF(aMemBalloonTotal);
14918 NOREF(aMemSharedTotal);
14919 NOREF(aVmNetRx);
14920 NOREF(aVmNetTx);
14921 ReturnComNotImplemented();
14922}
14923
14924HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14925 com::Utf8Str &aResult)
14926{
14927 NOREF(aAuthParams);
14928 NOREF(aResult);
14929 ReturnComNotImplemented();
14930}
14931
14932HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
14933{
14934 NOREF(aFlags);
14935 ReturnComNotImplemented();
14936}
14937
14938/* This isn't handled entirely by the wrapper generator yet. */
14939#ifdef VBOX_WITH_XPCOM
14940NS_DECL_CLASSINFO(SessionMachine)
14941NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
14942
14943NS_DECL_CLASSINFO(SnapshotMachine)
14944NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
14945#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