VirtualBox

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

Last change on this file since 55854 was 55854, checked in by vboxsync, 10 years ago

Main: saved screenshot API cleanup.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 511.7 KB
Line 
1/* $Id: MachineImpl.cpp 55854 2015-05-13 14:40:26Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47
48// generated header
49#include "VBoxEvents.h"
50
51#ifdef VBOX_WITH_USB
52# include "USBProxyService.h"
53#endif
54
55#include "AutoCaller.h"
56#include "HashedPw.h"
57#include "Performance.h"
58
59#include <iprt/asm.h>
60#include <iprt/path.h>
61#include <iprt/dir.h>
62#include <iprt/env.h>
63#include <iprt/lockvalidator.h>
64#include <iprt/process.h>
65#include <iprt/cpp/utils.h>
66#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
67#include <iprt/sha.h>
68#include <iprt/string.h>
69#include <iprt/base64.h>
70
71#include <VBox/com/array.h>
72#include <VBox/com/list.h>
73
74#include <VBox/err.h>
75#include <VBox/param.h>
76#include <VBox/settings.h>
77#include <VBox/vmm/ssm.h>
78
79#ifdef VBOX_WITH_GUEST_PROPS
80# include <VBox/HostServices/GuestPropertySvc.h>
81# include <VBox/com/array.h>
82#endif
83
84#include "VBox/com/MultiResult.h"
85
86#include <algorithm>
87
88#ifdef VBOX_WITH_DTRACE_R3_MAIN
89# include "dtrace/VBoxAPI.h"
90#endif
91
92#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
93# define HOSTSUFF_EXE ".exe"
94#else /* !RT_OS_WINDOWS */
95# define HOSTSUFF_EXE ""
96#endif /* !RT_OS_WINDOWS */
97
98// defines / prototypes
99/////////////////////////////////////////////////////////////////////////////
100
101/////////////////////////////////////////////////////////////////////////////
102// Machine::Data structure
103/////////////////////////////////////////////////////////////////////////////
104
105Machine::Data::Data()
106{
107 mRegistered = FALSE;
108 pMachineConfigFile = NULL;
109 /* Contains hints on what has changed when the user is using the VM (config
110 * changes, running the VM, ...). This is used to decide if a config needs
111 * to be written to disk. */
112 flModifications = 0;
113 /* VM modification usually also trigger setting the current state to
114 * "Modified". Although this is not always the case. An e.g. is the VM
115 * initialization phase or when snapshot related data is changed. The
116 * actually behavior is controlled by the following flag. */
117 m_fAllowStateModification = false;
118 mAccessible = FALSE;
119 /* mUuid is initialized in Machine::init() */
120
121 mMachineState = MachineState_PoweredOff;
122 RTTimeNow(&mLastStateChange);
123
124 mMachineStateDeps = 0;
125 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
126 mMachineStateChangePending = 0;
127
128 mCurrentStateModified = TRUE;
129 mGuestPropertiesModified = FALSE;
130
131 mSession.mPID = NIL_RTPROCESS;
132 mSession.mLockType = LockType_Null;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mTripleFaultReset = false;
196 mHPETEnabled = false;
197 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
198 mCpuIdPortabilityLevel = 0;
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 mGuestPropertyNotificationPatterns = "";
210
211 mFirmwareType = FirmwareType_BIOS;
212 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
213 mPointingHIDType = PointingHIDType_PS2Mouse;
214 mChipsetType = ChipsetType_PIIX3;
215 mParavirtProvider = ParavirtProvider_Default;
216 mEmulatedUSBCardReaderEnabled = FALSE;
217
218 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
219 mCPUAttached[i] = false;
220
221 mIOCacheEnabled = true;
222 mIOCacheSize = 5; /* 5MB */
223}
224
225Machine::HWData::~HWData()
226{
227}
228
229/////////////////////////////////////////////////////////////////////////////
230// Machine::HDData structure
231/////////////////////////////////////////////////////////////////////////////
232
233Machine::MediaData::MediaData()
234{
235}
236
237Machine::MediaData::~MediaData()
238{
239}
240
241/////////////////////////////////////////////////////////////////////////////
242// Machine class
243/////////////////////////////////////////////////////////////////////////////
244
245// constructor / destructor
246/////////////////////////////////////////////////////////////////////////////
247
248Machine::Machine() :
249#ifdef VBOX_WITH_RESOURCE_USAGE_API
250 mCollectorGuest(NULL),
251#endif
252 mPeer(NULL),
253 mParent(NULL),
254 mSerialPorts(),
255 mParallelPorts(),
256 uRegistryNeedsSaving(0)
257{}
258
259Machine::~Machine()
260{}
261
262HRESULT Machine::FinalConstruct()
263{
264 LogFlowThisFunc(("\n"));
265 return BaseFinalConstruct();
266}
267
268void Machine::FinalRelease()
269{
270 LogFlowThisFunc(("\n"));
271 uninit();
272 BaseFinalRelease();
273}
274
275/**
276 * Initializes a new machine instance; this init() variant creates a new, empty machine.
277 * This gets called from VirtualBox::CreateMachine().
278 *
279 * @param aParent Associated parent object
280 * @param strConfigFile Local file system path to the VM settings file (can
281 * be relative to the VirtualBox config directory).
282 * @param strName name for the machine
283 * @param llGroups list of groups for the machine
284 * @param aOsType OS Type of this machine or NULL.
285 * @param aId UUID for the new machine.
286 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
287 *
288 * @return Success indicator. if not S_OK, the machine object is invalid
289 */
290HRESULT Machine::init(VirtualBox *aParent,
291 const Utf8Str &strConfigFile,
292 const Utf8Str &strName,
293 const StringsList &llGroups,
294 GuestOSType *aOsType,
295 const Guid &aId,
296 bool fForceOverwrite,
297 bool fDirectoryIncludesUUID)
298{
299 LogFlowThisFuncEnter();
300 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
301
302 /* Enclose the state transition NotReady->InInit->Ready */
303 AutoInitSpan autoInitSpan(this);
304 AssertReturn(autoInitSpan.isOk(), E_FAIL);
305
306 HRESULT rc = initImpl(aParent, strConfigFile);
307 if (FAILED(rc)) return rc;
308
309 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
310 if (FAILED(rc)) return rc;
311
312 if (SUCCEEDED(rc))
313 {
314 // create an empty machine config
315 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
316
317 rc = initDataAndChildObjects();
318 }
319
320 if (SUCCEEDED(rc))
321 {
322 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
323 mData->mAccessible = TRUE;
324
325 unconst(mData->mUuid) = aId;
326
327 mUserData->s.strName = strName;
328
329 mUserData->s.llGroups = llGroups;
330
331 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
332 // the "name sync" flag determines whether the machine directory gets renamed along
333 // with the machine file; say so if the settings file name is the same as the
334 // settings file parent directory (machine directory)
335 mUserData->s.fNameSync = i_isInOwnDir();
336
337 // initialize the default snapshots folder
338 rc = COMSETTER(SnapshotFolder)(NULL);
339 AssertComRC(rc);
340
341 if (aOsType)
342 {
343 /* Store OS type */
344 mUserData->s.strOsType = aOsType->i_id();
345
346 /* Apply BIOS defaults */
347 mBIOSSettings->i_applyDefaults(aOsType);
348
349 /* Apply network adapters defaults */
350 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
351 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
352
353 /* Apply serial port defaults */
354 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
355 mSerialPorts[slot]->i_applyDefaults(aOsType);
356
357 /* Let the OS type select 64-bit ness. */
358 mHWData->mLongMode = aOsType->i_is64Bit()
359 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
360 }
361
362 /* At this point the changing of the current state modification
363 * flag is allowed. */
364 i_allowStateModification();
365
366 /* commit all changes made during the initialization */
367 i_commit();
368 }
369
370 /* Confirm a successful initialization when it's the case */
371 if (SUCCEEDED(rc))
372 {
373 if (mData->mAccessible)
374 autoInitSpan.setSucceeded();
375 else
376 autoInitSpan.setLimited();
377 }
378
379 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
380 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
381 mData->mRegistered,
382 mData->mAccessible,
383 rc));
384
385 LogFlowThisFuncLeave();
386
387 return rc;
388}
389
390/**
391 * Initializes a new instance with data from machine XML (formerly Init_Registered).
392 * Gets called in two modes:
393 *
394 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
395 * UUID is specified and we mark the machine as "registered";
396 *
397 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
398 * and the machine remains unregistered until RegisterMachine() is called.
399 *
400 * @param aParent Associated parent object
401 * @param aConfigFile Local file system path to the VM settings file (can
402 * be relative to the VirtualBox config directory).
403 * @param aId UUID of the machine or NULL (see above).
404 *
405 * @return Success indicator. if not S_OK, the machine object is invalid
406 */
407HRESULT Machine::initFromSettings(VirtualBox *aParent,
408 const Utf8Str &strConfigFile,
409 const Guid *aId)
410{
411 LogFlowThisFuncEnter();
412 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
413
414 /* Enclose the state transition NotReady->InInit->Ready */
415 AutoInitSpan autoInitSpan(this);
416 AssertReturn(autoInitSpan.isOk(), E_FAIL);
417
418 HRESULT rc = initImpl(aParent, strConfigFile);
419 if (FAILED(rc)) return rc;
420
421 if (aId)
422 {
423 // loading a registered VM:
424 unconst(mData->mUuid) = *aId;
425 mData->mRegistered = TRUE;
426 // now load the settings from XML:
427 rc = i_registeredInit();
428 // this calls initDataAndChildObjects() and loadSettings()
429 }
430 else
431 {
432 // opening an unregistered VM (VirtualBox::OpenMachine()):
433 rc = initDataAndChildObjects();
434
435 if (SUCCEEDED(rc))
436 {
437 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
438 mData->mAccessible = TRUE;
439
440 try
441 {
442 // load and parse machine XML; this will throw on XML or logic errors
443 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
444
445 // reject VM UUID duplicates, they can happen if someone
446 // tries to register an already known VM config again
447 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
448 true /* fPermitInaccessible */,
449 false /* aDoSetError */,
450 NULL) != VBOX_E_OBJECT_NOT_FOUND)
451 {
452 throw setError(E_FAIL,
453 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
454 mData->m_strConfigFile.c_str());
455 }
456
457 // use UUID from machine config
458 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
459
460 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
461 NULL /* puuidRegistry */);
462 if (FAILED(rc)) throw rc;
463
464 /* At this point the changing of the current state modification
465 * flag is allowed. */
466 i_allowStateModification();
467
468 i_commit();
469 }
470 catch (HRESULT err)
471 {
472 /* we assume that error info is set by the thrower */
473 rc = err;
474 }
475 catch (...)
476 {
477 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
478 }
479 }
480 }
481
482 /* Confirm a successful initialization when it's the case */
483 if (SUCCEEDED(rc))
484 {
485 if (mData->mAccessible)
486 autoInitSpan.setSucceeded();
487 else
488 {
489 autoInitSpan.setLimited();
490
491 // uninit media from this machine's media registry, or else
492 // reloading the settings will fail
493 mParent->i_unregisterMachineMedia(i_getId());
494 }
495 }
496
497 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
498 "rc=%08X\n",
499 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
500 mData->mRegistered, mData->mAccessible, rc));
501
502 LogFlowThisFuncLeave();
503
504 return rc;
505}
506
507/**
508 * Initializes a new instance from a machine config that is already in memory
509 * (import OVF case). Since we are importing, the UUID in the machine
510 * config is ignored and we always generate a fresh one.
511 *
512 * @param strName Name for the new machine; this overrides what is specified in config and is used
513 * for the settings file as well.
514 * @param config Machine configuration loaded and parsed from XML.
515 *
516 * @return Success indicator. if not S_OK, the machine object is invalid
517 */
518HRESULT Machine::init(VirtualBox *aParent,
519 const Utf8Str &strName,
520 const settings::MachineConfigFile &config)
521{
522 LogFlowThisFuncEnter();
523
524 /* Enclose the state transition NotReady->InInit->Ready */
525 AutoInitSpan autoInitSpan(this);
526 AssertReturn(autoInitSpan.isOk(), E_FAIL);
527
528 Utf8Str strConfigFile;
529 aParent->i_getDefaultMachineFolder(strConfigFile);
530 strConfigFile.append(RTPATH_DELIMITER);
531 strConfigFile.append(strName);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(".vbox");
535
536 HRESULT rc = initImpl(aParent, strConfigFile);
537 if (FAILED(rc)) return rc;
538
539 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
540 if (FAILED(rc)) return rc;
541
542 rc = initDataAndChildObjects();
543
544 if (SUCCEEDED(rc))
545 {
546 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
547 mData->mAccessible = TRUE;
548
549 // create empty machine config for instance data
550 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
551
552 // generate fresh UUID, ignore machine config
553 unconst(mData->mUuid).create();
554
555 rc = i_loadMachineDataFromSettings(config,
556 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
557
558 // override VM name as well, it may be different
559 mUserData->s.strName = strName;
560
561 if (SUCCEEDED(rc))
562 {
563 /* At this point the changing of the current state modification
564 * flag is allowed. */
565 i_allowStateModification();
566
567 /* commit all changes made during the initialization */
568 i_commit();
569 }
570 }
571
572 /* Confirm a successful initialization when it's the case */
573 if (SUCCEEDED(rc))
574 {
575 if (mData->mAccessible)
576 autoInitSpan.setSucceeded();
577 else
578 {
579 /* Ignore all errors from unregistering, they would destroy
580- * the more interesting error information we already have,
581- * pinpointing the issue with the VM config. */
582 ErrorInfoKeeper eik;
583
584 autoInitSpan.setLimited();
585
586 // uninit media from this machine's media registry, or else
587 // reloading the settings will fail
588 mParent->i_unregisterMachineMedia(i_getId());
589 }
590 }
591
592 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
593 "rc=%08X\n",
594 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
595 mData->mRegistered, mData->mAccessible, rc));
596
597 LogFlowThisFuncLeave();
598
599 return rc;
600}
601
602/**
603 * Shared code between the various init() implementations.
604 * @param aParent
605 * @return
606 */
607HRESULT Machine::initImpl(VirtualBox *aParent,
608 const Utf8Str &strConfigFile)
609{
610 LogFlowThisFuncEnter();
611
612 AssertReturn(aParent, E_INVALIDARG);
613 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
614
615 HRESULT rc = S_OK;
616
617 /* share the parent weakly */
618 unconst(mParent) = aParent;
619
620 /* allocate the essential machine data structure (the rest will be
621 * allocated later by initDataAndChildObjects() */
622 mData.allocate();
623
624 /* memorize the config file name (as provided) */
625 mData->m_strConfigFile = strConfigFile;
626
627 /* get the full file name */
628 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
629 if (RT_FAILURE(vrc1))
630 return setError(VBOX_E_FILE_ERROR,
631 tr("Invalid machine settings file name '%s' (%Rrc)"),
632 strConfigFile.c_str(),
633 vrc1);
634
635 LogFlowThisFuncLeave();
636
637 return rc;
638}
639
640/**
641 * Tries to create a machine settings file in the path stored in the machine
642 * instance data. Used when a new machine is created to fail gracefully if
643 * the settings file could not be written (e.g. because machine dir is read-only).
644 * @return
645 */
646HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
647{
648 HRESULT rc = S_OK;
649
650 // when we create a new machine, we must be able to create the settings file
651 RTFILE f = NIL_RTFILE;
652 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
653 if ( RT_SUCCESS(vrc)
654 || vrc == VERR_SHARING_VIOLATION
655 )
656 {
657 if (RT_SUCCESS(vrc))
658 RTFileClose(f);
659 if (!fForceOverwrite)
660 rc = setError(VBOX_E_FILE_ERROR,
661 tr("Machine settings file '%s' already exists"),
662 mData->m_strConfigFileFull.c_str());
663 else
664 {
665 /* try to delete the config file, as otherwise the creation
666 * of a new settings file will fail. */
667 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
668 if (RT_FAILURE(vrc2))
669 rc = setError(VBOX_E_FILE_ERROR,
670 tr("Could not delete the existing settings file '%s' (%Rrc)"),
671 mData->m_strConfigFileFull.c_str(), vrc2);
672 }
673 }
674 else if ( vrc != VERR_FILE_NOT_FOUND
675 && vrc != VERR_PATH_NOT_FOUND
676 )
677 rc = setError(VBOX_E_FILE_ERROR,
678 tr("Invalid machine settings file name '%s' (%Rrc)"),
679 mData->m_strConfigFileFull.c_str(),
680 vrc);
681 return rc;
682}
683
684/**
685 * Initializes the registered machine by loading the settings file.
686 * This method is separated from #init() in order to make it possible to
687 * retry the operation after VirtualBox startup instead of refusing to
688 * startup the whole VirtualBox server in case if the settings file of some
689 * registered VM is invalid or inaccessible.
690 *
691 * @note Must be always called from this object's write lock
692 * (unless called from #init() that doesn't need any locking).
693 * @note Locks the mUSBController method for writing.
694 * @note Subclasses must not call this method.
695 */
696HRESULT Machine::i_registeredInit()
697{
698 AssertReturn(!i_isSessionMachine(), E_FAIL);
699 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
700 AssertReturn(mData->mUuid.isValid(), E_FAIL);
701 AssertReturn(!mData->mAccessible, E_FAIL);
702
703 HRESULT rc = initDataAndChildObjects();
704
705 if (SUCCEEDED(rc))
706 {
707 /* Temporarily reset the registered flag in order to let setters
708 * potentially called from loadSettings() succeed (isMutable() used in
709 * all setters will return FALSE for a Machine instance if mRegistered
710 * is TRUE). */
711 mData->mRegistered = FALSE;
712
713 try
714 {
715 // load and parse machine XML; this will throw on XML or logic errors
716 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
717
718 if (mData->mUuid != mData->pMachineConfigFile->uuid)
719 throw setError(E_FAIL,
720 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
721 mData->pMachineConfigFile->uuid.raw(),
722 mData->m_strConfigFileFull.c_str(),
723 mData->mUuid.toString().c_str(),
724 mParent->i_settingsFilePath().c_str());
725
726 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
727 NULL /* const Guid *puuidRegistry */);
728 if (FAILED(rc)) throw rc;
729 }
730 catch (HRESULT err)
731 {
732 /* we assume that error info is set by the thrower */
733 rc = err;
734 }
735 catch (...)
736 {
737 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
738 }
739
740 /* Restore the registered flag (even on failure) */
741 mData->mRegistered = TRUE;
742 }
743
744 if (SUCCEEDED(rc))
745 {
746 /* Set mAccessible to TRUE only if we successfully locked and loaded
747 * the settings file */
748 mData->mAccessible = TRUE;
749
750 /* commit all changes made during loading the settings file */
751 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
752 /// @todo r=klaus for some reason the settings loading logic backs up
753 // the settings, and therefore a commit is needed. Should probably be changed.
754 }
755 else
756 {
757 /* If the machine is registered, then, instead of returning a
758 * failure, we mark it as inaccessible and set the result to
759 * success to give it a try later */
760
761 /* fetch the current error info */
762 mData->mAccessError = com::ErrorInfo();
763 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
764 mData->mUuid.raw(),
765 mData->mAccessError.getText().raw()));
766
767 /* rollback all changes */
768 i_rollback(false /* aNotify */);
769
770 // uninit media from this machine's media registry, or else
771 // reloading the settings will fail
772 mParent->i_unregisterMachineMedia(i_getId());
773
774 /* uninitialize the common part to make sure all data is reset to
775 * default (null) values */
776 uninitDataAndChildObjects();
777
778 rc = S_OK;
779 }
780
781 return rc;
782}
783
784/**
785 * Uninitializes the instance.
786 * Called either from FinalRelease() or by the parent when it gets destroyed.
787 *
788 * @note The caller of this method must make sure that this object
789 * a) doesn't have active callers on the current thread and b) is not locked
790 * by the current thread; otherwise uninit() will hang either a) due to
791 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
792 * a dead-lock caused by this thread waiting for all callers on the other
793 * threads are done but preventing them from doing so by holding a lock.
794 */
795void Machine::uninit()
796{
797 LogFlowThisFuncEnter();
798
799 Assert(!isWriteLockOnCurrentThread());
800
801 Assert(!uRegistryNeedsSaving);
802 if (uRegistryNeedsSaving)
803 {
804 AutoCaller autoCaller(this);
805 if (SUCCEEDED(autoCaller.rc()))
806 {
807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
808 i_saveSettings(NULL, Machine::SaveS_Force);
809 }
810 }
811
812 /* Enclose the state transition Ready->InUninit->NotReady */
813 AutoUninitSpan autoUninitSpan(this);
814 if (autoUninitSpan.uninitDone())
815 return;
816
817 Assert(!i_isSnapshotMachine());
818 Assert(!i_isSessionMachine());
819 Assert(!!mData);
820
821 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
822 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
823
824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
825
826 if (!mData->mSession.mMachine.isNull())
827 {
828 /* Theoretically, this can only happen if the VirtualBox server has been
829 * terminated while there were clients running that owned open direct
830 * sessions. Since in this case we are definitely called by
831 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
832 * won't happen on the client watcher thread (because it does
833 * VirtualBox::addCaller() for the duration of the
834 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
835 * cannot happen until the VirtualBox caller is released). This is
836 * important, because SessionMachine::uninit() cannot correctly operate
837 * after we return from this method (it expects the Machine instance is
838 * still valid). We'll call it ourselves below.
839 */
840 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
841 (SessionMachine*)mData->mSession.mMachine));
842
843 if (Global::IsOnlineOrTransient(mData->mMachineState))
844 {
845 LogWarningThisFunc(("Setting state to Aborted!\n"));
846 /* set machine state using SessionMachine reimplementation */
847 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
848 }
849
850 /*
851 * Uninitialize SessionMachine using public uninit() to indicate
852 * an unexpected uninitialization.
853 */
854 mData->mSession.mMachine->uninit();
855 /* SessionMachine::uninit() must set mSession.mMachine to null */
856 Assert(mData->mSession.mMachine.isNull());
857 }
858
859 // uninit media from this machine's media registry, if they're still there
860 Guid uuidMachine(i_getId());
861
862 /* the lock is no more necessary (SessionMachine is uninitialized) */
863 alock.release();
864
865 /* XXX This will fail with
866 * "cannot be closed because it is still attached to 1 virtual machines"
867 * because at this point we did not call uninitDataAndChildObjects() yet
868 * and therefore also removeBackReference() for all these mediums was not called! */
869
870 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
871 mParent->i_unregisterMachineMedia(uuidMachine);
872
873 // has machine been modified?
874 if (mData->flModifications)
875 {
876 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
877 i_rollback(false /* aNotify */);
878 }
879
880 if (mData->mAccessible)
881 uninitDataAndChildObjects();
882
883 /* free the essential data structure last */
884 mData.free();
885
886 LogFlowThisFuncLeave();
887}
888
889// Wrapped IMachine properties
890/////////////////////////////////////////////////////////////////////////////
891HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
892{
893 /* mParent is constant during life time, no need to lock */
894 ComObjPtr<VirtualBox> pVirtualBox(mParent);
895 aParent = pVirtualBox;
896
897 return S_OK;
898}
899
900
901HRESULT Machine::getAccessible(BOOL *aAccessible)
902{
903 /* In some cases (medium registry related), it is necessary to be able to
904 * go through the list of all machines. Happens when an inaccessible VM
905 * has a sensible medium registry. */
906 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
908
909 HRESULT rc = S_OK;
910
911 if (!mData->mAccessible)
912 {
913 /* try to initialize the VM once more if not accessible */
914
915 AutoReinitSpan autoReinitSpan(this);
916 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
917
918#ifdef DEBUG
919 LogFlowThisFunc(("Dumping media backreferences\n"));
920 mParent->i_dumpAllBackRefs();
921#endif
922
923 if (mData->pMachineConfigFile)
924 {
925 // reset the XML file to force loadSettings() (called from registeredInit())
926 // to parse it again; the file might have changed
927 delete mData->pMachineConfigFile;
928 mData->pMachineConfigFile = NULL;
929 }
930
931 rc = i_registeredInit();
932
933 if (SUCCEEDED(rc) && mData->mAccessible)
934 {
935 autoReinitSpan.setSucceeded();
936
937 /* make sure interesting parties will notice the accessibility
938 * state change */
939 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
940 mParent->i_onMachineDataChange(mData->mUuid);
941 }
942 }
943
944 if (SUCCEEDED(rc))
945 *aAccessible = mData->mAccessible;
946
947 LogFlowThisFuncLeave();
948
949 return rc;
950}
951
952HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
953{
954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
955
956 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
957 {
958 /* return shortly */
959 aAccessError = NULL;
960 return S_OK;
961 }
962
963 HRESULT rc = S_OK;
964
965 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
966 rc = errorInfo.createObject();
967 if (SUCCEEDED(rc))
968 {
969 errorInfo->init(mData->mAccessError.getResultCode(),
970 mData->mAccessError.getInterfaceID().ref(),
971 Utf8Str(mData->mAccessError.getComponent()).c_str(),
972 Utf8Str(mData->mAccessError.getText()));
973 aAccessError = errorInfo;
974 }
975
976 return rc;
977}
978
979HRESULT Machine::getName(com::Utf8Str &aName)
980{
981 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
982
983 aName = mUserData->s.strName;
984
985 return S_OK;
986}
987
988HRESULT Machine::setName(const com::Utf8Str &aName)
989{
990 // prohibit setting a UUID only as the machine name, or else it can
991 // never be found by findMachine()
992 Guid test(aName);
993
994 if (test.isValid())
995 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
996
997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
998
999 HRESULT rc = i_checkStateDependency(MutableStateDep);
1000 if (FAILED(rc)) return rc;
1001
1002 i_setModified(IsModified_MachineData);
1003 mUserData.backup();
1004 mUserData->s.strName = aName;
1005
1006 return S_OK;
1007}
1008
1009HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1010{
1011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1012
1013 aDescription = mUserData->s.strDescription;
1014
1015 return S_OK;
1016}
1017
1018HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1019{
1020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1021
1022 // this can be done in principle in any state as it doesn't affect the VM
1023 // significantly, but play safe by not messing around while complex
1024 // activities are going on
1025 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1026 if (FAILED(rc)) return rc;
1027
1028 i_setModified(IsModified_MachineData);
1029 mUserData.backup();
1030 mUserData->s.strDescription = aDescription;
1031
1032 return S_OK;
1033}
1034
1035HRESULT Machine::getId(com::Guid &aId)
1036{
1037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1038
1039 aId = mData->mUuid;
1040
1041 return S_OK;
1042}
1043
1044HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1045{
1046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1047 aGroups.resize(mUserData->s.llGroups.size());
1048 size_t i = 0;
1049 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1050 it != mUserData->s.llGroups.end(); ++it, ++i)
1051 aGroups[i] = (*it);
1052
1053 return S_OK;
1054}
1055
1056HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1057{
1058 StringsList llGroups;
1059 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1060 if (FAILED(rc))
1061 return rc;
1062
1063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1064
1065 rc = i_checkStateDependency(MutableOrSavedStateDep);
1066 if (FAILED(rc)) return rc;
1067
1068 i_setModified(IsModified_MachineData);
1069 mUserData.backup();
1070 mUserData->s.llGroups = llGroups;
1071
1072 return S_OK;
1073}
1074
1075HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1076{
1077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1078
1079 aOSTypeId = mUserData->s.strOsType;
1080
1081 return S_OK;
1082}
1083
1084HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1085{
1086 /* look up the object by Id to check it is valid */
1087 ComPtr<IGuestOSType> guestOSType;
1088 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1089 if (FAILED(rc)) return rc;
1090
1091 /* when setting, always use the "etalon" value for consistency -- lookup
1092 * by ID is case-insensitive and the input value may have different case */
1093 Bstr osTypeId;
1094 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1095 if (FAILED(rc)) return rc;
1096
1097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1098
1099 rc = i_checkStateDependency(MutableStateDep);
1100 if (FAILED(rc)) return rc;
1101
1102 i_setModified(IsModified_MachineData);
1103 mUserData.backup();
1104 mUserData->s.strOsType = osTypeId;
1105
1106 return S_OK;
1107}
1108
1109HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1110{
1111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1112
1113 *aFirmwareType = mHWData->mFirmwareType;
1114
1115 return S_OK;
1116}
1117
1118HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1119{
1120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1121
1122 HRESULT rc = i_checkStateDependency(MutableStateDep);
1123 if (FAILED(rc)) return rc;
1124
1125 i_setModified(IsModified_MachineData);
1126 mHWData.backup();
1127 mHWData->mFirmwareType = aFirmwareType;
1128
1129 return S_OK;
1130}
1131
1132HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1133{
1134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1135
1136 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1137
1138 return S_OK;
1139}
1140
1141HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1142{
1143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1144
1145 HRESULT rc = i_checkStateDependency(MutableStateDep);
1146 if (FAILED(rc)) return rc;
1147
1148 i_setModified(IsModified_MachineData);
1149 mHWData.backup();
1150 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1151
1152 return S_OK;
1153}
1154
1155HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1156{
1157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1158
1159 *aPointingHIDType = mHWData->mPointingHIDType;
1160
1161 return S_OK;
1162}
1163
1164HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1165{
1166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1167
1168 HRESULT rc = i_checkStateDependency(MutableStateDep);
1169 if (FAILED(rc)) return rc;
1170
1171 i_setModified(IsModified_MachineData);
1172 mHWData.backup();
1173 mHWData->mPointingHIDType = aPointingHIDType;
1174
1175 return S_OK;
1176}
1177
1178HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1179{
1180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1181
1182 *aChipsetType = mHWData->mChipsetType;
1183
1184 return S_OK;
1185}
1186
1187HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1188{
1189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1190
1191 HRESULT rc = i_checkStateDependency(MutableStateDep);
1192 if (FAILED(rc)) return rc;
1193
1194 if (aChipsetType != mHWData->mChipsetType)
1195 {
1196 i_setModified(IsModified_MachineData);
1197 mHWData.backup();
1198 mHWData->mChipsetType = aChipsetType;
1199
1200 // Resize network adapter array, to be finalized on commit/rollback.
1201 // We must not throw away entries yet, otherwise settings are lost
1202 // without a way to roll back.
1203 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1204 size_t oldCount = mNetworkAdapters.size();
1205 if (newCount > oldCount)
1206 {
1207 mNetworkAdapters.resize(newCount);
1208 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1209 {
1210 unconst(mNetworkAdapters[slot]).createObject();
1211 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1212 }
1213 }
1214 }
1215
1216 return S_OK;
1217}
1218
1219HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1220{
1221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1222
1223 *aParavirtProvider = mHWData->mParavirtProvider;
1224
1225 return S_OK;
1226}
1227
1228HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1229{
1230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1231
1232 HRESULT rc = i_checkStateDependency(MutableStateDep);
1233 if (FAILED(rc)) return rc;
1234
1235 if (aParavirtProvider != mHWData->mParavirtProvider)
1236 {
1237 i_setModified(IsModified_MachineData);
1238 mHWData.backup();
1239 mHWData->mParavirtProvider = aParavirtProvider;
1240 }
1241
1242 return S_OK;
1243}
1244
1245HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1246{
1247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 *aParavirtProvider = mHWData->mParavirtProvider;
1250 switch (mHWData->mParavirtProvider)
1251 {
1252 case ParavirtProvider_None:
1253 case ParavirtProvider_HyperV:
1254 case ParavirtProvider_KVM:
1255 case ParavirtProvider_Minimal:
1256 break;
1257
1258 /* Resolve dynamic provider types to the effective types. */
1259 default:
1260 {
1261 ComPtr<IGuestOSType> ptrGuestOSType;
1262 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1263 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1264
1265 Bstr guestTypeFamilyId;
1266 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1267 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1268 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1269
1270 switch (mHWData->mParavirtProvider)
1271 {
1272 case ParavirtProvider_Legacy:
1273 {
1274 if (fOsXGuest)
1275 *aParavirtProvider = ParavirtProvider_Minimal;
1276 else
1277 *aParavirtProvider = ParavirtProvider_None;
1278 break;
1279 }
1280
1281 case ParavirtProvider_Default:
1282 {
1283 if (fOsXGuest)
1284 *aParavirtProvider = ParavirtProvider_Minimal;
1285 else if ( mUserData->s.strOsType == "Windows10"
1286 || mUserData->s.strOsType == "Windows10_64"
1287 || mUserData->s.strOsType == "Windows81"
1288 || mUserData->s.strOsType == "Windows81_64"
1289 || mUserData->s.strOsType == "Windows8"
1290 || mUserData->s.strOsType == "Windows8_64"
1291 || mUserData->s.strOsType == "Windows7"
1292 || mUserData->s.strOsType == "Windows7_64"
1293 || mUserData->s.strOsType == "WindowsVista"
1294 || mUserData->s.strOsType == "WindowsVista_64"
1295 || mUserData->s.strOsType == "Windows2012"
1296 || mUserData->s.strOsType == "Windows2012_64"
1297 || mUserData->s.strOsType == "Windows2008"
1298 || mUserData->s.strOsType == "Windows2008_64")
1299 {
1300 *aParavirtProvider = ParavirtProvider_HyperV;
1301 }
1302 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1303 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1304 || mUserData->s.strOsType == "Linux"
1305 || mUserData->s.strOsType == "Linux_64"
1306 || mUserData->s.strOsType == "ArchLinux"
1307 || mUserData->s.strOsType == "ArchLinux_64"
1308 || mUserData->s.strOsType == "Debian"
1309 || mUserData->s.strOsType == "Debian_64"
1310 || mUserData->s.strOsType == "Fedora"
1311 || mUserData->s.strOsType == "Fedora_64"
1312 || mUserData->s.strOsType == "Gentoo"
1313 || mUserData->s.strOsType == "Gentoo_64"
1314 || mUserData->s.strOsType == "Mandriva"
1315 || mUserData->s.strOsType == "Mandriva_64"
1316 || mUserData->s.strOsType == "OpenSUSE"
1317 || mUserData->s.strOsType == "OpenSUSE_64"
1318 || mUserData->s.strOsType == "Oracle"
1319 || mUserData->s.strOsType == "Oracle_64"
1320 || mUserData->s.strOsType == "RedHat"
1321 || mUserData->s.strOsType == "RedHat_64"
1322 || mUserData->s.strOsType == "Turbolinux"
1323 || mUserData->s.strOsType == "Turbolinux_64"
1324 || mUserData->s.strOsType == "Ubuntu"
1325 || mUserData->s.strOsType == "Ubuntu_64"
1326 || mUserData->s.strOsType == "Xandros"
1327 || mUserData->s.strOsType == "Xandros_64")
1328 {
1329 *aParavirtProvider = ParavirtProvider_KVM;
1330 }
1331 else
1332 *aParavirtProvider = ParavirtProvider_None;
1333 break;
1334 }
1335 }
1336 break;
1337 }
1338 }
1339
1340 Assert( *aParavirtProvider == ParavirtProvider_None
1341 || *aParavirtProvider == ParavirtProvider_Minimal
1342 || *aParavirtProvider == ParavirtProvider_HyperV
1343 || *aParavirtProvider == ParavirtProvider_KVM);
1344 return S_OK;
1345}
1346
1347HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1348{
1349 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1350
1351 aHardwareVersion = mHWData->mHWVersion;
1352
1353 return S_OK;
1354}
1355
1356HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1357{
1358 /* check known version */
1359 Utf8Str hwVersion = aHardwareVersion;
1360 if ( hwVersion.compare("1") != 0
1361 && hwVersion.compare("2") != 0)
1362 return setError(E_INVALIDARG,
1363 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1364
1365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1366
1367 HRESULT rc = i_checkStateDependency(MutableStateDep);
1368 if (FAILED(rc)) return rc;
1369
1370 i_setModified(IsModified_MachineData);
1371 mHWData.backup();
1372 mHWData->mHWVersion = aHardwareVersion;
1373
1374 return S_OK;
1375}
1376
1377HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1378{
1379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1380
1381 if (!mHWData->mHardwareUUID.isZero())
1382 aHardwareUUID = mHWData->mHardwareUUID;
1383 else
1384 aHardwareUUID = mData->mUuid;
1385
1386 return S_OK;
1387}
1388
1389HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1390{
1391 if (!aHardwareUUID.isValid())
1392 return E_INVALIDARG;
1393
1394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 HRESULT rc = i_checkStateDependency(MutableStateDep);
1397 if (FAILED(rc)) return rc;
1398
1399 i_setModified(IsModified_MachineData);
1400 mHWData.backup();
1401 if (aHardwareUUID == mData->mUuid)
1402 mHWData->mHardwareUUID.clear();
1403 else
1404 mHWData->mHardwareUUID = aHardwareUUID;
1405
1406 return S_OK;
1407}
1408
1409HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1410{
1411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1412
1413 *aMemorySize = mHWData->mMemorySize;
1414
1415 return S_OK;
1416}
1417
1418HRESULT Machine::setMemorySize(ULONG aMemorySize)
1419{
1420 /* check RAM limits */
1421 if ( aMemorySize < MM_RAM_MIN_IN_MB
1422 || aMemorySize > MM_RAM_MAX_IN_MB
1423 )
1424 return setError(E_INVALIDARG,
1425 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1426 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1427
1428 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1429
1430 HRESULT rc = i_checkStateDependency(MutableStateDep);
1431 if (FAILED(rc)) return rc;
1432
1433 i_setModified(IsModified_MachineData);
1434 mHWData.backup();
1435 mHWData->mMemorySize = aMemorySize;
1436
1437 return S_OK;
1438}
1439
1440HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1441{
1442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1443
1444 *aCPUCount = mHWData->mCPUCount;
1445
1446 return S_OK;
1447}
1448
1449HRESULT Machine::setCPUCount(ULONG aCPUCount)
1450{
1451 /* check CPU limits */
1452 if ( aCPUCount < SchemaDefs::MinCPUCount
1453 || aCPUCount > SchemaDefs::MaxCPUCount
1454 )
1455 return setError(E_INVALIDARG,
1456 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1457 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1458
1459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1460
1461 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1462 if (mHWData->mCPUHotPlugEnabled)
1463 {
1464 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1465 {
1466 if (mHWData->mCPUAttached[idx])
1467 return setError(E_INVALIDARG,
1468 tr("There is still a CPU attached to socket %lu."
1469 "Detach the CPU before removing the socket"),
1470 aCPUCount, idx+1);
1471 }
1472 }
1473
1474 HRESULT rc = i_checkStateDependency(MutableStateDep);
1475 if (FAILED(rc)) return rc;
1476
1477 i_setModified(IsModified_MachineData);
1478 mHWData.backup();
1479 mHWData->mCPUCount = aCPUCount;
1480
1481 return S_OK;
1482}
1483
1484HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1485{
1486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1487
1488 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1489
1490 return S_OK;
1491}
1492
1493HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1494{
1495 HRESULT rc = S_OK;
1496
1497 /* check throttle limits */
1498 if ( aCPUExecutionCap < 1
1499 || aCPUExecutionCap > 100
1500 )
1501 return setError(E_INVALIDARG,
1502 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1503 aCPUExecutionCap, 1, 100);
1504
1505 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1506
1507 alock.release();
1508 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1509 alock.acquire();
1510 if (FAILED(rc)) return rc;
1511
1512 i_setModified(IsModified_MachineData);
1513 mHWData.backup();
1514 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1515
1516 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1517 if (Global::IsOnline(mData->mMachineState))
1518 i_saveSettings(NULL);
1519
1520 return S_OK;
1521}
1522
1523HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1524{
1525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1526
1527 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1528
1529 return S_OK;
1530}
1531
1532HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1533{
1534 HRESULT rc = S_OK;
1535
1536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1537
1538 rc = i_checkStateDependency(MutableStateDep);
1539 if (FAILED(rc)) return rc;
1540
1541 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1542 {
1543 if (aCPUHotPlugEnabled)
1544 {
1545 i_setModified(IsModified_MachineData);
1546 mHWData.backup();
1547
1548 /* Add the amount of CPUs currently attached */
1549 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1550 mHWData->mCPUAttached[i] = true;
1551 }
1552 else
1553 {
1554 /*
1555 * We can disable hotplug only if the amount of maximum CPUs is equal
1556 * to the amount of attached CPUs
1557 */
1558 unsigned cCpusAttached = 0;
1559 unsigned iHighestId = 0;
1560
1561 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1562 {
1563 if (mHWData->mCPUAttached[i])
1564 {
1565 cCpusAttached++;
1566 iHighestId = i;
1567 }
1568 }
1569
1570 if ( (cCpusAttached != mHWData->mCPUCount)
1571 || (iHighestId >= mHWData->mCPUCount))
1572 return setError(E_INVALIDARG,
1573 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1574
1575 i_setModified(IsModified_MachineData);
1576 mHWData.backup();
1577 }
1578 }
1579
1580 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1581
1582 return rc;
1583}
1584
1585HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1586{
1587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1588
1589 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1590
1591 return S_OK;
1592}
1593
1594HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1595{
1596 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1597
1598 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1599 if (SUCCEEDED(hrc))
1600 {
1601 i_setModified(IsModified_MachineData);
1602 mHWData.backup();
1603 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1604 }
1605 return hrc;
1606}
1607
1608HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1609{
1610#ifdef VBOX_WITH_USB_CARDREADER
1611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1612
1613 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1614
1615 return S_OK;
1616#else
1617 NOREF(aEmulatedUSBCardReaderEnabled);
1618 return E_NOTIMPL;
1619#endif
1620}
1621
1622HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1623{
1624#ifdef VBOX_WITH_USB_CARDREADER
1625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1626
1627 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1628 if (FAILED(rc)) return rc;
1629
1630 i_setModified(IsModified_MachineData);
1631 mHWData.backup();
1632 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1633
1634 return S_OK;
1635#else
1636 NOREF(aEmulatedUSBCardReaderEnabled);
1637 return E_NOTIMPL;
1638#endif
1639}
1640
1641HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1642{
1643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1644
1645 *aHPETEnabled = mHWData->mHPETEnabled;
1646
1647 return S_OK;
1648}
1649
1650HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1651{
1652 HRESULT rc = S_OK;
1653
1654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1655
1656 rc = i_checkStateDependency(MutableStateDep);
1657 if (FAILED(rc)) return rc;
1658
1659 i_setModified(IsModified_MachineData);
1660 mHWData.backup();
1661
1662 mHWData->mHPETEnabled = aHPETEnabled;
1663
1664 return rc;
1665}
1666
1667HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1668{
1669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1670
1671 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1672 return S_OK;
1673}
1674
1675HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1676{
1677 HRESULT rc = S_OK;
1678
1679 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1680
1681 i_setModified(IsModified_MachineData);
1682 mHWData.backup();
1683 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1684
1685 alock.release();
1686 rc = i_onVideoCaptureChange();
1687 alock.acquire();
1688 if (FAILED(rc))
1689 {
1690 /*
1691 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1692 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1693 * determine if it should start or stop capturing. Therefore we need to manually
1694 * undo change.
1695 */
1696 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1697 return rc;
1698 }
1699
1700 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1701 if (Global::IsOnline(mData->mMachineState))
1702 i_saveSettings(NULL);
1703
1704 return rc;
1705}
1706
1707HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1708{
1709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1710 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1711 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1712 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1713 return S_OK;
1714}
1715
1716HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1717{
1718 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1719 bool fChanged = false;
1720
1721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1722
1723 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1724 {
1725 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1726 {
1727 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1728 fChanged = true;
1729 }
1730 }
1731 if (fChanged)
1732 {
1733 alock.release();
1734 HRESULT rc = i_onVideoCaptureChange();
1735 alock.acquire();
1736 if (FAILED(rc)) return rc;
1737 i_setModified(IsModified_MachineData);
1738
1739 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1740 if (Global::IsOnline(mData->mMachineState))
1741 i_saveSettings(NULL);
1742 }
1743
1744 return S_OK;
1745}
1746
1747HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1748{
1749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1750 if (mHWData->mVideoCaptureFile.isEmpty())
1751 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1752 else
1753 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1754 return S_OK;
1755}
1756
1757HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1758{
1759 Utf8Str strFile(aVideoCaptureFile);
1760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1761
1762 if ( Global::IsOnline(mData->mMachineState)
1763 && mHWData->mVideoCaptureEnabled)
1764 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1765
1766 if (!RTPathStartsWithRoot(strFile.c_str()))
1767 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1768
1769 if (!strFile.isEmpty())
1770 {
1771 Utf8Str defaultFile;
1772 i_getDefaultVideoCaptureFile(defaultFile);
1773 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1774 strFile.setNull();
1775 }
1776
1777 i_setModified(IsModified_MachineData);
1778 mHWData.backup();
1779 mHWData->mVideoCaptureFile = strFile;
1780
1781 return S_OK;
1782}
1783
1784HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1785{
1786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1787 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1788 return S_OK;
1789}
1790
1791HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1792{
1793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1794
1795 if ( Global::IsOnline(mData->mMachineState)
1796 && mHWData->mVideoCaptureEnabled)
1797 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1798
1799 i_setModified(IsModified_MachineData);
1800 mHWData.backup();
1801 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1802
1803 return S_OK;
1804}
1805
1806HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1807{
1808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1809 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1810 return S_OK;
1811}
1812
1813HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1814{
1815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1816
1817 if ( Global::IsOnline(mData->mMachineState)
1818 && mHWData->mVideoCaptureEnabled)
1819 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1820
1821 i_setModified(IsModified_MachineData);
1822 mHWData.backup();
1823 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1824
1825 return S_OK;
1826}
1827
1828HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1829{
1830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1831 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1832 return S_OK;
1833}
1834
1835HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1836{
1837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1838
1839 if ( Global::IsOnline(mData->mMachineState)
1840 && mHWData->mVideoCaptureEnabled)
1841 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1842
1843 i_setModified(IsModified_MachineData);
1844 mHWData.backup();
1845 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1846
1847 return S_OK;
1848}
1849
1850HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1851{
1852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1853 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1854 return S_OK;
1855}
1856
1857HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1858{
1859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1860
1861 if ( Global::IsOnline(mData->mMachineState)
1862 && mHWData->mVideoCaptureEnabled)
1863 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1864
1865 i_setModified(IsModified_MachineData);
1866 mHWData.backup();
1867 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1868
1869 return S_OK;
1870}
1871
1872HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1873{
1874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1875 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1876 return S_OK;
1877}
1878
1879HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1880{
1881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1882
1883 if ( Global::IsOnline(mData->mMachineState)
1884 && mHWData->mVideoCaptureEnabled)
1885 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1886
1887 i_setModified(IsModified_MachineData);
1888 mHWData.backup();
1889 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1890
1891 return S_OK;
1892}
1893
1894HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1895{
1896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1897 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1898 return S_OK;
1899}
1900
1901HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1902{
1903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1904
1905 if ( Global::IsOnline(mData->mMachineState)
1906 && mHWData->mVideoCaptureEnabled)
1907 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1908
1909 i_setModified(IsModified_MachineData);
1910 mHWData.backup();
1911 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1912
1913 return S_OK;
1914}
1915
1916HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1917{
1918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1919
1920 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1921 return S_OK;
1922}
1923
1924HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1925{
1926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1927
1928 if ( Global::IsOnline(mData->mMachineState)
1929 && mHWData->mVideoCaptureEnabled)
1930 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1931
1932 i_setModified(IsModified_MachineData);
1933 mHWData.backup();
1934 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1935
1936 return S_OK;
1937}
1938
1939HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1940{
1941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1942
1943 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1944
1945 return S_OK;
1946}
1947
1948HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1949{
1950 switch (aGraphicsControllerType)
1951 {
1952 case GraphicsControllerType_Null:
1953 case GraphicsControllerType_VBoxVGA:
1954#ifdef VBOX_WITH_VMSVGA
1955 case GraphicsControllerType_VMSVGA:
1956#endif
1957 break;
1958 default:
1959 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1960 }
1961
1962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1963
1964 HRESULT rc = i_checkStateDependency(MutableStateDep);
1965 if (FAILED(rc)) return rc;
1966
1967 i_setModified(IsModified_MachineData);
1968 mHWData.backup();
1969 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1970
1971 return S_OK;
1972}
1973
1974HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1975{
1976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1977
1978 *aVRAMSize = mHWData->mVRAMSize;
1979
1980 return S_OK;
1981}
1982
1983HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1984{
1985 /* check VRAM limits */
1986 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1987 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1988 return setError(E_INVALIDARG,
1989 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1990 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1991
1992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1993
1994 HRESULT rc = i_checkStateDependency(MutableStateDep);
1995 if (FAILED(rc)) return rc;
1996
1997 i_setModified(IsModified_MachineData);
1998 mHWData.backup();
1999 mHWData->mVRAMSize = aVRAMSize;
2000
2001 return S_OK;
2002}
2003
2004/** @todo this method should not be public */
2005HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2006{
2007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2008
2009 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2010
2011 return S_OK;
2012}
2013
2014/**
2015 * Set the memory balloon size.
2016 *
2017 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2018 * we have to make sure that we never call IGuest from here.
2019 */
2020HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2021{
2022 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2023#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2024 /* check limits */
2025 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2026 return setError(E_INVALIDARG,
2027 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2028 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2029
2030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2031
2032 i_setModified(IsModified_MachineData);
2033 mHWData.backup();
2034 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2035
2036 return S_OK;
2037#else
2038 NOREF(aMemoryBalloonSize);
2039 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2040#endif
2041}
2042
2043HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2044{
2045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2046
2047 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2048 return S_OK;
2049}
2050
2051HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2052{
2053#ifdef VBOX_WITH_PAGE_SHARING
2054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2055
2056 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2057 i_setModified(IsModified_MachineData);
2058 mHWData.backup();
2059 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2060 return S_OK;
2061#else
2062 NOREF(aPageFusionEnabled);
2063 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2064#endif
2065}
2066
2067HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2068{
2069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2070
2071 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2072
2073 return S_OK;
2074}
2075
2076HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2077{
2078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2079
2080 HRESULT rc = i_checkStateDependency(MutableStateDep);
2081 if (FAILED(rc)) return rc;
2082
2083 /** @todo check validity! */
2084
2085 i_setModified(IsModified_MachineData);
2086 mHWData.backup();
2087 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2088
2089 return S_OK;
2090}
2091
2092
2093HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2094{
2095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2096
2097 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2098
2099 return S_OK;
2100}
2101
2102HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2103{
2104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2105
2106 HRESULT rc = i_checkStateDependency(MutableStateDep);
2107 if (FAILED(rc)) return rc;
2108
2109 /** @todo check validity! */
2110 i_setModified(IsModified_MachineData);
2111 mHWData.backup();
2112 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2113
2114 return S_OK;
2115}
2116
2117HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2118{
2119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2120
2121 *aMonitorCount = mHWData->mMonitorCount;
2122
2123 return S_OK;
2124}
2125
2126HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2127{
2128 /* make sure monitor count is a sensible number */
2129 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2130 return setError(E_INVALIDARG,
2131 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2132 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2133
2134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2135
2136 HRESULT rc = i_checkStateDependency(MutableStateDep);
2137 if (FAILED(rc)) return rc;
2138
2139 i_setModified(IsModified_MachineData);
2140 mHWData.backup();
2141 mHWData->mMonitorCount = aMonitorCount;
2142
2143 return S_OK;
2144}
2145
2146HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2147{
2148 /* mBIOSSettings is constant during life time, no need to lock */
2149 aBIOSSettings = mBIOSSettings;
2150
2151 return S_OK;
2152}
2153
2154HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2155{
2156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2157
2158 switch (aProperty)
2159 {
2160 case CPUPropertyType_PAE:
2161 *aValue = mHWData->mPAEEnabled;
2162 break;
2163
2164 case CPUPropertyType_LongMode:
2165 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2166 *aValue = TRUE;
2167 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2168 *aValue = FALSE;
2169#if HC_ARCH_BITS == 64
2170 else
2171 *aValue = TRUE;
2172#else
2173 else
2174 {
2175 *aValue = FALSE;
2176
2177 ComPtr<IGuestOSType> ptrGuestOSType;
2178 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2179 if (SUCCEEDED(hrc2))
2180 {
2181 BOOL fIs64Bit = FALSE;
2182 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2183 if (SUCCEEDED(hrc2) && fIs64Bit)
2184 {
2185 ComObjPtr<Host> ptrHost = mParent->i_host();
2186 alock.release();
2187
2188 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2189 if (FAILED(hrc2))
2190 *aValue = FALSE;
2191 }
2192 }
2193 }
2194#endif
2195 break;
2196
2197 case CPUPropertyType_TripleFaultReset:
2198 *aValue = mHWData->mTripleFaultReset;
2199 break;
2200
2201 default:
2202 return E_INVALIDARG;
2203 }
2204 return S_OK;
2205}
2206
2207HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2208{
2209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2210
2211 HRESULT rc = i_checkStateDependency(MutableStateDep);
2212 if (FAILED(rc)) return rc;
2213
2214 switch (aProperty)
2215 {
2216 case CPUPropertyType_PAE:
2217 i_setModified(IsModified_MachineData);
2218 mHWData.backup();
2219 mHWData->mPAEEnabled = !!aValue;
2220 break;
2221
2222 case CPUPropertyType_LongMode:
2223 i_setModified(IsModified_MachineData);
2224 mHWData.backup();
2225 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2226 break;
2227
2228 case CPUPropertyType_TripleFaultReset:
2229 i_setModified(IsModified_MachineData);
2230 mHWData.backup();
2231 mHWData->mTripleFaultReset = !!aValue;
2232 break;
2233
2234 default:
2235 return E_INVALIDARG;
2236 }
2237 return S_OK;
2238}
2239
2240HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2241{
2242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2243
2244 switch(aId)
2245 {
2246 case 0x0:
2247 case 0x1:
2248 case 0x2:
2249 case 0x3:
2250 case 0x4:
2251 case 0x5:
2252 case 0x6:
2253 case 0x7:
2254 case 0x8:
2255 case 0x9:
2256 case 0xA:
2257 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2258 return E_INVALIDARG;
2259
2260 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2261 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2262 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2263 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2264 break;
2265
2266 case 0x80000000:
2267 case 0x80000001:
2268 case 0x80000002:
2269 case 0x80000003:
2270 case 0x80000004:
2271 case 0x80000005:
2272 case 0x80000006:
2273 case 0x80000007:
2274 case 0x80000008:
2275 case 0x80000009:
2276 case 0x8000000A:
2277 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2278 return E_INVALIDARG;
2279
2280 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2281 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2282 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2283 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2284 break;
2285
2286 default:
2287 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2288 }
2289 return S_OK;
2290}
2291
2292
2293HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2294{
2295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2296
2297 HRESULT rc = i_checkStateDependency(MutableStateDep);
2298 if (FAILED(rc)) return rc;
2299
2300 switch(aId)
2301 {
2302 case 0x0:
2303 case 0x1:
2304 case 0x2:
2305 case 0x3:
2306 case 0x4:
2307 case 0x5:
2308 case 0x6:
2309 case 0x7:
2310 case 0x8:
2311 case 0x9:
2312 case 0xA:
2313 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2314 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2315 i_setModified(IsModified_MachineData);
2316 mHWData.backup();
2317 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2318 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2319 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2320 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2321 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2322 break;
2323
2324 case 0x80000000:
2325 case 0x80000001:
2326 case 0x80000002:
2327 case 0x80000003:
2328 case 0x80000004:
2329 case 0x80000005:
2330 case 0x80000006:
2331 case 0x80000007:
2332 case 0x80000008:
2333 case 0x80000009:
2334 case 0x8000000A:
2335 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2336 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2337 i_setModified(IsModified_MachineData);
2338 mHWData.backup();
2339 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2340 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2341 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2342 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2343 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2344 break;
2345
2346 default:
2347 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2348 }
2349 return S_OK;
2350}
2351
2352HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2353{
2354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2355
2356 HRESULT rc = i_checkStateDependency(MutableStateDep);
2357 if (FAILED(rc)) return rc;
2358
2359 switch(aId)
2360 {
2361 case 0x0:
2362 case 0x1:
2363 case 0x2:
2364 case 0x3:
2365 case 0x4:
2366 case 0x5:
2367 case 0x6:
2368 case 0x7:
2369 case 0x8:
2370 case 0x9:
2371 case 0xA:
2372 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2373 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2374 i_setModified(IsModified_MachineData);
2375 mHWData.backup();
2376 /* Invalidate leaf. */
2377 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2378 break;
2379
2380 case 0x80000000:
2381 case 0x80000001:
2382 case 0x80000002:
2383 case 0x80000003:
2384 case 0x80000004:
2385 case 0x80000005:
2386 case 0x80000006:
2387 case 0x80000007:
2388 case 0x80000008:
2389 case 0x80000009:
2390 case 0x8000000A:
2391 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2392 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2393 i_setModified(IsModified_MachineData);
2394 mHWData.backup();
2395 /* Invalidate leaf. */
2396 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2397 break;
2398
2399 default:
2400 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2401 }
2402 return S_OK;
2403}
2404
2405HRESULT Machine::removeAllCPUIDLeaves()
2406{
2407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2408
2409 HRESULT rc = i_checkStateDependency(MutableStateDep);
2410 if (FAILED(rc)) return rc;
2411
2412 i_setModified(IsModified_MachineData);
2413 mHWData.backup();
2414
2415 /* Invalidate all standard leafs. */
2416 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2417 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2418
2419 /* Invalidate all extended leafs. */
2420 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2421 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2422
2423 return S_OK;
2424}
2425HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2426{
2427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2428
2429 switch(aProperty)
2430 {
2431 case HWVirtExPropertyType_Enabled:
2432 *aValue = mHWData->mHWVirtExEnabled;
2433 break;
2434
2435 case HWVirtExPropertyType_VPID:
2436 *aValue = mHWData->mHWVirtExVPIDEnabled;
2437 break;
2438
2439 case HWVirtExPropertyType_NestedPaging:
2440 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2441 break;
2442
2443 case HWVirtExPropertyType_UnrestrictedExecution:
2444 *aValue = mHWData->mHWVirtExUXEnabled;
2445 break;
2446
2447 case HWVirtExPropertyType_LargePages:
2448 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2449#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2450 *aValue = FALSE;
2451#endif
2452 break;
2453
2454 case HWVirtExPropertyType_Force:
2455 *aValue = mHWData->mHWVirtExForceEnabled;
2456 break;
2457
2458 default:
2459 return E_INVALIDARG;
2460 }
2461 return S_OK;
2462}
2463
2464HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2465{
2466 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2467
2468 HRESULT rc = i_checkStateDependency(MutableStateDep);
2469 if (FAILED(rc)) return rc;
2470
2471 switch(aProperty)
2472 {
2473 case HWVirtExPropertyType_Enabled:
2474 i_setModified(IsModified_MachineData);
2475 mHWData.backup();
2476 mHWData->mHWVirtExEnabled = !!aValue;
2477 break;
2478
2479 case HWVirtExPropertyType_VPID:
2480 i_setModified(IsModified_MachineData);
2481 mHWData.backup();
2482 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2483 break;
2484
2485 case HWVirtExPropertyType_NestedPaging:
2486 i_setModified(IsModified_MachineData);
2487 mHWData.backup();
2488 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2489 break;
2490
2491 case HWVirtExPropertyType_UnrestrictedExecution:
2492 i_setModified(IsModified_MachineData);
2493 mHWData.backup();
2494 mHWData->mHWVirtExUXEnabled = !!aValue;
2495 break;
2496
2497 case HWVirtExPropertyType_LargePages:
2498 i_setModified(IsModified_MachineData);
2499 mHWData.backup();
2500 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2501 break;
2502
2503 case HWVirtExPropertyType_Force:
2504 i_setModified(IsModified_MachineData);
2505 mHWData.backup();
2506 mHWData->mHWVirtExForceEnabled = !!aValue;
2507 break;
2508
2509 default:
2510 return E_INVALIDARG;
2511 }
2512
2513 return S_OK;
2514}
2515
2516HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2517{
2518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2519
2520 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2521
2522 return S_OK;
2523}
2524
2525HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2526{
2527 /* @todo (r=dmik):
2528 * 1. Allow to change the name of the snapshot folder containing snapshots
2529 * 2. Rename the folder on disk instead of just changing the property
2530 * value (to be smart and not to leave garbage). Note that it cannot be
2531 * done here because the change may be rolled back. Thus, the right
2532 * place is #saveSettings().
2533 */
2534
2535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2536
2537 HRESULT rc = i_checkStateDependency(MutableStateDep);
2538 if (FAILED(rc)) return rc;
2539
2540 if (!mData->mCurrentSnapshot.isNull())
2541 return setError(E_FAIL,
2542 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2543
2544 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2545
2546 if (strSnapshotFolder.isEmpty())
2547 strSnapshotFolder = "Snapshots";
2548 int vrc = i_calculateFullPath(strSnapshotFolder,
2549 strSnapshotFolder);
2550 if (RT_FAILURE(vrc))
2551 return setError(E_FAIL,
2552 tr("Invalid snapshot folder '%s' (%Rrc)"),
2553 strSnapshotFolder.c_str(), vrc);
2554
2555 i_setModified(IsModified_MachineData);
2556 mUserData.backup();
2557
2558 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2559
2560 return S_OK;
2561}
2562
2563HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2564{
2565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2566
2567 aMediumAttachments.resize(mMediaData->mAttachments.size());
2568 size_t i = 0;
2569 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2570 it != mMediaData->mAttachments.end(); ++it, ++i)
2571 aMediumAttachments[i] = *it;
2572
2573 return S_OK;
2574}
2575
2576HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2577{
2578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2579
2580 Assert(!!mVRDEServer);
2581
2582 aVRDEServer = mVRDEServer;
2583
2584 return S_OK;
2585}
2586
2587HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2588{
2589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2590
2591 aAudioAdapter = mAudioAdapter;
2592
2593 return S_OK;
2594}
2595
2596HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2597{
2598#ifdef VBOX_WITH_VUSB
2599 clearError();
2600 MultiResult rc(S_OK);
2601
2602# ifdef VBOX_WITH_USB
2603 rc = mParent->i_host()->i_checkUSBProxyService();
2604 if (FAILED(rc)) return rc;
2605# endif
2606
2607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2608
2609 USBControllerList data = *mUSBControllers.data();
2610 aUSBControllers.resize(data.size());
2611 size_t i = 0;
2612 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2613 aUSBControllers[i] = *it;
2614
2615 return S_OK;
2616#else
2617 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2618 * extended error info to indicate that USB is simply not available
2619 * (w/o treating it as a failure), for example, as in OSE */
2620 NOREF(aUSBControllers);
2621 ReturnComNotImplemented();
2622#endif /* VBOX_WITH_VUSB */
2623}
2624
2625HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2626{
2627#ifdef VBOX_WITH_VUSB
2628 clearError();
2629 MultiResult rc(S_OK);
2630
2631# ifdef VBOX_WITH_USB
2632 rc = mParent->i_host()->i_checkUSBProxyService();
2633 if (FAILED(rc)) return rc;
2634# endif
2635
2636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2637
2638 aUSBDeviceFilters = mUSBDeviceFilters;
2639 return rc;
2640#else
2641 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2642 * extended error info to indicate that USB is simply not available
2643 * (w/o treating it as a failure), for example, as in OSE */
2644 NOREF(aUSBDeviceFilters);
2645 ReturnComNotImplemented();
2646#endif /* VBOX_WITH_VUSB */
2647}
2648
2649HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2650{
2651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2652
2653 aSettingsFilePath = mData->m_strConfigFileFull;
2654
2655 return S_OK;
2656}
2657
2658HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2659{
2660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2661
2662 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2663 if (FAILED(rc)) return rc;
2664
2665 if (!mData->pMachineConfigFile->fileExists())
2666 // this is a new machine, and no config file exists yet:
2667 *aSettingsModified = TRUE;
2668 else
2669 *aSettingsModified = (mData->flModifications != 0);
2670
2671 return S_OK;
2672}
2673
2674HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2675{
2676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2677
2678 *aSessionState = mData->mSession.mState;
2679
2680 return S_OK;
2681}
2682
2683HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2684{
2685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2686
2687 aSessionName = mData->mSession.mName;
2688
2689 return S_OK;
2690}
2691
2692HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2693{
2694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2695
2696 *aSessionPID = mData->mSession.mPID;
2697
2698 return S_OK;
2699}
2700
2701HRESULT Machine::getState(MachineState_T *aState)
2702{
2703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2704
2705 *aState = mData->mMachineState;
2706 Assert(mData->mMachineState != MachineState_Null);
2707
2708 return S_OK;
2709}
2710
2711HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2712{
2713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2714
2715 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2716
2717 return S_OK;
2718}
2719
2720HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2721{
2722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2723
2724 aStateFilePath = mSSData->strStateFilePath;
2725
2726 return S_OK;
2727}
2728
2729HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2730{
2731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2732
2733 i_getLogFolder(aLogFolder);
2734
2735 return S_OK;
2736}
2737
2738HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2739{
2740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2741
2742 aCurrentSnapshot = mData->mCurrentSnapshot;
2743
2744 return S_OK;
2745}
2746
2747HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2748{
2749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2750
2751 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2752 ? 0
2753 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2754
2755 return S_OK;
2756}
2757
2758HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2759{
2760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2761
2762 /* Note: for machines with no snapshots, we always return FALSE
2763 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2764 * reasons :) */
2765
2766 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2767 ? FALSE
2768 : mData->mCurrentStateModified;
2769
2770 return S_OK;
2771}
2772
2773HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2774{
2775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2776
2777 aSharedFolders.resize(mHWData->mSharedFolders.size());
2778 size_t i = 0;
2779 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2780 it != mHWData->mSharedFolders.end(); ++i, ++it)
2781 aSharedFolders[i] = *it;
2782
2783 return S_OK;
2784}
2785
2786HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2787{
2788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 *aClipboardMode = mHWData->mClipboardMode;
2791
2792 return S_OK;
2793}
2794
2795HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2796{
2797 HRESULT rc = S_OK;
2798
2799 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2800
2801 alock.release();
2802 rc = i_onClipboardModeChange(aClipboardMode);
2803 alock.acquire();
2804 if (FAILED(rc)) return rc;
2805
2806 i_setModified(IsModified_MachineData);
2807 mHWData.backup();
2808 mHWData->mClipboardMode = aClipboardMode;
2809
2810 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2811 if (Global::IsOnline(mData->mMachineState))
2812 i_saveSettings(NULL);
2813
2814 return S_OK;
2815}
2816
2817HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2818{
2819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 *aDnDMode = mHWData->mDnDMode;
2822
2823 return S_OK;
2824}
2825
2826HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2827{
2828 HRESULT rc = S_OK;
2829
2830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2831
2832 alock.release();
2833 rc = i_onDnDModeChange(aDnDMode);
2834
2835 alock.acquire();
2836 if (FAILED(rc)) return rc;
2837
2838 i_setModified(IsModified_MachineData);
2839 mHWData.backup();
2840 mHWData->mDnDMode = aDnDMode;
2841
2842 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2843 if (Global::IsOnline(mData->mMachineState))
2844 i_saveSettings(NULL);
2845
2846 return S_OK;
2847}
2848
2849HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2850{
2851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2852
2853 try
2854 {
2855 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2856 }
2857 catch (...)
2858 {
2859 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2860 }
2861
2862 return S_OK;
2863}
2864
2865HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2866{
2867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2868
2869 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2870 if (FAILED(rc)) return rc;
2871
2872 i_setModified(IsModified_MachineData);
2873 mHWData.backup();
2874 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2875 return rc;
2876}
2877
2878HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2879{
2880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2881 StorageControllerList data = *mStorageControllers.data();
2882 size_t i = 0;
2883 aStorageControllers.resize(data.size());
2884 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2885 aStorageControllers[i] = *it;
2886 return S_OK;
2887}
2888
2889HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2890{
2891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2892
2893 *aEnabled = mUserData->s.fTeleporterEnabled;
2894
2895 return S_OK;
2896}
2897
2898HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2899{
2900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2901
2902 /* Only allow it to be set to true when PoweredOff or Aborted.
2903 (Clearing it is always permitted.) */
2904 if ( aTeleporterEnabled
2905 && mData->mRegistered
2906 && ( !i_isSessionMachine()
2907 || ( mData->mMachineState != MachineState_PoweredOff
2908 && mData->mMachineState != MachineState_Teleported
2909 && mData->mMachineState != MachineState_Aborted
2910 )
2911 )
2912 )
2913 return setError(VBOX_E_INVALID_VM_STATE,
2914 tr("The machine is not powered off (state is %s)"),
2915 Global::stringifyMachineState(mData->mMachineState));
2916
2917 i_setModified(IsModified_MachineData);
2918 mUserData.backup();
2919 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2920
2921 return S_OK;
2922}
2923
2924HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2925{
2926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2927
2928 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2929
2930 return S_OK;
2931}
2932
2933HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2934{
2935 if (aTeleporterPort >= _64K)
2936 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2937
2938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2939
2940 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2941 if (FAILED(rc)) return rc;
2942
2943 i_setModified(IsModified_MachineData);
2944 mUserData.backup();
2945 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2946
2947 return S_OK;
2948}
2949
2950HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2951{
2952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2953
2954 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2955
2956 return S_OK;
2957}
2958
2959HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2960{
2961 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2962
2963 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2964 if (FAILED(rc)) return rc;
2965
2966 i_setModified(IsModified_MachineData);
2967 mUserData.backup();
2968 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2969
2970 return S_OK;
2971}
2972
2973HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2974{
2975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2976 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2977
2978 return S_OK;
2979}
2980
2981HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2982{
2983 /*
2984 * Hash the password first.
2985 */
2986 com::Utf8Str aT = aTeleporterPassword;
2987
2988 if (!aT.isEmpty())
2989 {
2990 if (VBoxIsPasswordHashed(&aT))
2991 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2992 VBoxHashPassword(&aT);
2993 }
2994
2995 /*
2996 * Do the update.
2997 */
2998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2999 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3000 if (SUCCEEDED(hrc))
3001 {
3002 i_setModified(IsModified_MachineData);
3003 mUserData.backup();
3004 mUserData->s.strTeleporterPassword = aT;
3005 }
3006
3007 return hrc;
3008}
3009
3010HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3011{
3012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3013
3014 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3015 return S_OK;
3016}
3017
3018HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3019{
3020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3021
3022 /* @todo deal with running state change. */
3023 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3024 if (FAILED(rc)) return rc;
3025
3026 i_setModified(IsModified_MachineData);
3027 mUserData.backup();
3028 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3029 return S_OK;
3030}
3031
3032HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3033{
3034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3035
3036 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3037 return S_OK;
3038}
3039
3040HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3041{
3042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3043
3044 /* @todo deal with running state change. */
3045 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3046 if (FAILED(rc)) return rc;
3047
3048 i_setModified(IsModified_MachineData);
3049 mUserData.backup();
3050 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3051 return S_OK;
3052}
3053
3054HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3055{
3056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3057
3058 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3059 return S_OK;
3060}
3061
3062HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3063{
3064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3065
3066 /* @todo deal with running state change. */
3067 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3068 if (FAILED(rc)) return rc;
3069
3070 i_setModified(IsModified_MachineData);
3071 mUserData.backup();
3072 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3073 return S_OK;
3074}
3075
3076HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3077{
3078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3079
3080 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3081
3082 return S_OK;
3083}
3084
3085HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3086{
3087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3088
3089 /* @todo deal with running state change. */
3090 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3091 if (FAILED(rc)) return rc;
3092
3093 i_setModified(IsModified_MachineData);
3094 mUserData.backup();
3095 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3096
3097 return S_OK;
3098}
3099
3100HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3101{
3102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3103
3104 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3105 return S_OK;
3106}
3107
3108HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3109{
3110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3111
3112 /* @todo deal with running state change. */
3113 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3114 if (FAILED(rc)) return rc;
3115
3116 i_setModified(IsModified_MachineData);
3117 mUserData.backup();
3118 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3119 return S_OK;
3120}
3121
3122HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3123{
3124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3125
3126 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3127
3128 return S_OK;
3129}
3130
3131HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3132{
3133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3134
3135 /* Only allow it to be set to true when PoweredOff or Aborted.
3136 (Clearing it is always permitted.) */
3137 if ( aRTCUseUTC
3138 && mData->mRegistered
3139 && ( !i_isSessionMachine()
3140 || ( mData->mMachineState != MachineState_PoweredOff
3141 && mData->mMachineState != MachineState_Teleported
3142 && mData->mMachineState != MachineState_Aborted
3143 )
3144 )
3145 )
3146 return setError(VBOX_E_INVALID_VM_STATE,
3147 tr("The machine is not powered off (state is %s)"),
3148 Global::stringifyMachineState(mData->mMachineState));
3149
3150 i_setModified(IsModified_MachineData);
3151 mUserData.backup();
3152 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3153
3154 return S_OK;
3155}
3156
3157HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3158{
3159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3160
3161 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3162
3163 return S_OK;
3164}
3165
3166HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3167{
3168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3169
3170 HRESULT rc = i_checkStateDependency(MutableStateDep);
3171 if (FAILED(rc)) return rc;
3172
3173 i_setModified(IsModified_MachineData);
3174 mHWData.backup();
3175 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3176
3177 return S_OK;
3178}
3179
3180HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3181{
3182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3183
3184 *aIOCacheSize = mHWData->mIOCacheSize;
3185
3186 return S_OK;
3187}
3188
3189HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3190{
3191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3192
3193 HRESULT rc = i_checkStateDependency(MutableStateDep);
3194 if (FAILED(rc)) return rc;
3195
3196 i_setModified(IsModified_MachineData);
3197 mHWData.backup();
3198 mHWData->mIOCacheSize = aIOCacheSize;
3199
3200 return S_OK;
3201}
3202
3203
3204/**
3205 * @note Locks objects!
3206 */
3207HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3208 LockType_T aLockType)
3209{
3210 /* check the session state */
3211 SessionState_T state;
3212 HRESULT rc = aSession->COMGETTER(State)(&state);
3213 if (FAILED(rc)) return rc;
3214
3215 if (state != SessionState_Unlocked)
3216 return setError(VBOX_E_INVALID_OBJECT_STATE,
3217 tr("The given session is busy"));
3218
3219 // get the client's IInternalSessionControl interface
3220 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3221 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3222 E_INVALIDARG);
3223
3224 // session name (only used in some code paths)
3225 Utf8Str strSessionName;
3226
3227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3228
3229 if (!mData->mRegistered)
3230 return setError(E_UNEXPECTED,
3231 tr("The machine '%s' is not registered"),
3232 mUserData->s.strName.c_str());
3233
3234 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3235
3236 SessionState_T oldState = mData->mSession.mState;
3237 /* Hack: in case the session is closing and there is a progress object
3238 * which allows waiting for the session to be closed, take the opportunity
3239 * and do a limited wait (max. 1 second). This helps a lot when the system
3240 * is busy and thus session closing can take a little while. */
3241 if ( mData->mSession.mState == SessionState_Unlocking
3242 && mData->mSession.mProgress)
3243 {
3244 alock.release();
3245 mData->mSession.mProgress->WaitForCompletion(1000);
3246 alock.acquire();
3247 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3248 }
3249
3250 // try again now
3251 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3252 // (i.e. session machine exists)
3253 && (aLockType == LockType_Shared) // caller wants a shared link to the
3254 // existing session that holds the write lock:
3255 )
3256 {
3257 // OK, share the session... we are now dealing with three processes:
3258 // 1) VBoxSVC (where this code runs);
3259 // 2) process C: the caller's client process (who wants a shared session);
3260 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3261
3262 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3263 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3264 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3265 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3266 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3267
3268 /*
3269 * Release the lock before calling the client process. It's safe here
3270 * since the only thing to do after we get the lock again is to add
3271 * the remote control to the list (which doesn't directly influence
3272 * anything).
3273 */
3274 alock.release();
3275
3276 // get the console of the session holding the write lock (this is a remote call)
3277 ComPtr<IConsole> pConsoleW;
3278 if (mData->mSession.mLockType == LockType_VM)
3279 {
3280 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3281 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3282 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3283 if (FAILED(rc))
3284 // the failure may occur w/o any error info (from RPC), so provide one
3285 return setError(VBOX_E_VM_ERROR,
3286 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3287 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3288 }
3289
3290 // share the session machine and W's console with the caller's session
3291 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3292 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3293 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3294
3295 if (FAILED(rc))
3296 // the failure may occur w/o any error info (from RPC), so provide one
3297 return setError(VBOX_E_VM_ERROR,
3298 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3299 alock.acquire();
3300
3301 // need to revalidate the state after acquiring the lock again
3302 if (mData->mSession.mState != SessionState_Locked)
3303 {
3304 pSessionControl->Uninitialize();
3305 return setError(VBOX_E_INVALID_SESSION_STATE,
3306 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3307 mUserData->s.strName.c_str());
3308 }
3309
3310 // add the caller's session to the list
3311 mData->mSession.mRemoteControls.push_back(pSessionControl);
3312 }
3313 else if ( mData->mSession.mState == SessionState_Locked
3314 || mData->mSession.mState == SessionState_Unlocking
3315 )
3316 {
3317 // sharing not permitted, or machine still unlocking:
3318 return setError(VBOX_E_INVALID_OBJECT_STATE,
3319 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3320 mUserData->s.strName.c_str());
3321 }
3322 else
3323 {
3324 // machine is not locked: then write-lock the machine (create the session machine)
3325
3326 // must not be busy
3327 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3328
3329 // get the caller's session PID
3330 RTPROCESS pid = NIL_RTPROCESS;
3331 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3332 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3333 Assert(pid != NIL_RTPROCESS);
3334
3335 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3336
3337 if (fLaunchingVMProcess)
3338 {
3339 if (mData->mSession.mPID == NIL_RTPROCESS)
3340 {
3341 // two or more clients racing for a lock, the one which set the
3342 // session state to Spawning will win, the others will get an
3343 // error as we can't decide here if waiting a little would help
3344 // (only for shared locks this would avoid an error)
3345 return setError(VBOX_E_INVALID_OBJECT_STATE,
3346 tr("The machine '%s' already has a lock request pending"),
3347 mUserData->s.strName.c_str());
3348 }
3349
3350 // this machine is awaiting for a spawning session to be opened:
3351 // then the calling process must be the one that got started by
3352 // LaunchVMProcess()
3353
3354 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3355 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3356
3357#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3358 /* Hardened windows builds spawns three processes when a VM is
3359 launched, the 3rd one is the one that will end up here. */
3360 RTPROCESS ppid;
3361 int rc = RTProcQueryParent(pid, &ppid);
3362 if (RT_SUCCESS(rc))
3363 rc = RTProcQueryParent(ppid, &ppid);
3364 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3365 || rc == VERR_ACCESS_DENIED)
3366 {
3367 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3368 mData->mSession.mPID = pid;
3369 }
3370#endif
3371
3372 if (mData->mSession.mPID != pid)
3373 return setError(E_ACCESSDENIED,
3374 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3375 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3376 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3377 }
3378
3379 // create the mutable SessionMachine from the current machine
3380 ComObjPtr<SessionMachine> sessionMachine;
3381 sessionMachine.createObject();
3382 rc = sessionMachine->init(this);
3383 AssertComRC(rc);
3384
3385 /* NOTE: doing return from this function after this point but
3386 * before the end is forbidden since it may call SessionMachine::uninit()
3387 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3388 * lock while still holding the Machine lock in alock so that a deadlock
3389 * is possible due to the wrong lock order. */
3390
3391 if (SUCCEEDED(rc))
3392 {
3393 /*
3394 * Set the session state to Spawning to protect against subsequent
3395 * attempts to open a session and to unregister the machine after
3396 * we release the lock.
3397 */
3398 SessionState_T origState = mData->mSession.mState;
3399 mData->mSession.mState = SessionState_Spawning;
3400
3401#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3402 /* Get the client token ID to be passed to the client process */
3403 Utf8Str strTokenId;
3404 sessionMachine->i_getTokenId(strTokenId);
3405 Assert(!strTokenId.isEmpty());
3406#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3407 /* Get the client token to be passed to the client process */
3408 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3409 /* The token is now "owned" by pToken, fix refcount */
3410 if (!pToken.isNull())
3411 pToken->Release();
3412#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3413
3414 /*
3415 * Release the lock before calling the client process -- it will call
3416 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3417 * because the state is Spawning, so that LaunchVMProcess() and
3418 * LockMachine() calls will fail. This method, called before we
3419 * acquire the lock again, will fail because of the wrong PID.
3420 *
3421 * Note that mData->mSession.mRemoteControls accessed outside
3422 * the lock may not be modified when state is Spawning, so it's safe.
3423 */
3424 alock.release();
3425
3426 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3427#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3428 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3429#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3430 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3431 /* Now the token is owned by the client process. */
3432 pToken.setNull();
3433#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3434 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3435
3436 /* The failure may occur w/o any error info (from RPC), so provide one */
3437 if (FAILED(rc))
3438 setError(VBOX_E_VM_ERROR,
3439 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3440
3441 // get session name, either to remember or to compare against
3442 // the already known session name.
3443 {
3444 Bstr bstrSessionName;
3445 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3446 if (SUCCEEDED(rc2))
3447 strSessionName = bstrSessionName;
3448 }
3449
3450 if ( SUCCEEDED(rc)
3451 && fLaunchingVMProcess
3452 )
3453 {
3454 /* complete the remote session initialization */
3455
3456 /* get the console from the direct session */
3457 ComPtr<IConsole> console;
3458 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3459 ComAssertComRC(rc);
3460
3461 if (SUCCEEDED(rc) && !console)
3462 {
3463 ComAssert(!!console);
3464 rc = E_FAIL;
3465 }
3466
3467 /* assign machine & console to the remote session */
3468 if (SUCCEEDED(rc))
3469 {
3470 /*
3471 * after LaunchVMProcess(), the first and the only
3472 * entry in remoteControls is that remote session
3473 */
3474 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3475 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3476 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3477
3478 /* The failure may occur w/o any error info (from RPC), so provide one */
3479 if (FAILED(rc))
3480 setError(VBOX_E_VM_ERROR,
3481 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3482 }
3483
3484 if (FAILED(rc))
3485 pSessionControl->Uninitialize();
3486 }
3487
3488 /* acquire the lock again */
3489 alock.acquire();
3490
3491 /* Restore the session state */
3492 mData->mSession.mState = origState;
3493 }
3494
3495 // finalize spawning anyway (this is why we don't return on errors above)
3496 if (fLaunchingVMProcess)
3497 {
3498 Assert(mData->mSession.mName == strSessionName);
3499 /* Note that the progress object is finalized later */
3500 /** @todo Consider checking mData->mSession.mProgress for cancellation
3501 * around here. */
3502
3503 /* We don't reset mSession.mPID here because it is necessary for
3504 * SessionMachine::uninit() to reap the child process later. */
3505
3506 if (FAILED(rc))
3507 {
3508 /* Close the remote session, remove the remote control from the list
3509 * and reset session state to Closed (@note keep the code in sync
3510 * with the relevant part in checkForSpawnFailure()). */
3511
3512 Assert(mData->mSession.mRemoteControls.size() == 1);
3513 if (mData->mSession.mRemoteControls.size() == 1)
3514 {
3515 ErrorInfoKeeper eik;
3516 mData->mSession.mRemoteControls.front()->Uninitialize();
3517 }
3518
3519 mData->mSession.mRemoteControls.clear();
3520 mData->mSession.mState = SessionState_Unlocked;
3521 }
3522 }
3523 else
3524 {
3525 /* memorize PID of the directly opened session */
3526 if (SUCCEEDED(rc))
3527 mData->mSession.mPID = pid;
3528 }
3529
3530 if (SUCCEEDED(rc))
3531 {
3532 mData->mSession.mLockType = aLockType;
3533 /* memorize the direct session control and cache IUnknown for it */
3534 mData->mSession.mDirectControl = pSessionControl;
3535 mData->mSession.mState = SessionState_Locked;
3536 if (!fLaunchingVMProcess)
3537 mData->mSession.mName = strSessionName;
3538 /* associate the SessionMachine with this Machine */
3539 mData->mSession.mMachine = sessionMachine;
3540
3541 /* request an IUnknown pointer early from the remote party for later
3542 * identity checks (it will be internally cached within mDirectControl
3543 * at least on XPCOM) */
3544 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3545 NOREF(unk);
3546 }
3547
3548 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3549 * would break the lock order */
3550 alock.release();
3551
3552 /* uninitialize the created session machine on failure */
3553 if (FAILED(rc))
3554 sessionMachine->uninit();
3555 }
3556
3557 if (SUCCEEDED(rc))
3558 {
3559 /*
3560 * tell the client watcher thread to update the set of
3561 * machines that have open sessions
3562 */
3563 mParent->i_updateClientWatcher();
3564
3565 if (oldState != SessionState_Locked)
3566 /* fire an event */
3567 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3568 }
3569
3570 return rc;
3571}
3572
3573/**
3574 * @note Locks objects!
3575 */
3576HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3577 const com::Utf8Str &aName,
3578 const com::Utf8Str &aEnvironment,
3579 ComPtr<IProgress> &aProgress)
3580{
3581 Utf8Str strFrontend(aName);
3582 /* "emergencystop" doesn't need the session, so skip the checks/interface
3583 * retrieval. This code doesn't quite fit in here, but introducing a
3584 * special API method would be even more effort, and would require explicit
3585 * support by every API client. It's better to hide the feature a bit. */
3586 if (strFrontend != "emergencystop")
3587 CheckComArgNotNull(aSession);
3588
3589 HRESULT rc = S_OK;
3590 if (strFrontend.isEmpty())
3591 {
3592 Bstr bstrFrontend;
3593 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3594 if (FAILED(rc))
3595 return rc;
3596 strFrontend = bstrFrontend;
3597 if (strFrontend.isEmpty())
3598 {
3599 ComPtr<ISystemProperties> systemProperties;
3600 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3601 if (FAILED(rc))
3602 return rc;
3603 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3604 if (FAILED(rc))
3605 return rc;
3606 strFrontend = bstrFrontend;
3607 }
3608 /* paranoia - emergencystop is not a valid default */
3609 if (strFrontend == "emergencystop")
3610 strFrontend = Utf8Str::Empty;
3611 }
3612 /* default frontend: Qt GUI */
3613 if (strFrontend.isEmpty())
3614 strFrontend = "GUI/Qt";
3615
3616 if (strFrontend != "emergencystop")
3617 {
3618 /* check the session state */
3619 SessionState_T state;
3620 rc = aSession->COMGETTER(State)(&state);
3621 if (FAILED(rc))
3622 return rc;
3623
3624 if (state != SessionState_Unlocked)
3625 return setError(VBOX_E_INVALID_OBJECT_STATE,
3626 tr("The given session is busy"));
3627
3628 /* get the IInternalSessionControl interface */
3629 ComPtr<IInternalSessionControl> control(aSession);
3630 ComAssertMsgRet(!control.isNull(),
3631 ("No IInternalSessionControl interface"),
3632 E_INVALIDARG);
3633
3634 /* get the teleporter enable state for the progress object init. */
3635 BOOL fTeleporterEnabled;
3636 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3637 if (FAILED(rc))
3638 return rc;
3639
3640 /* create a progress object */
3641 ComObjPtr<ProgressProxy> progress;
3642 progress.createObject();
3643 rc = progress->init(mParent,
3644 static_cast<IMachine*>(this),
3645 Bstr(tr("Starting VM")).raw(),
3646 TRUE /* aCancelable */,
3647 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3648 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3649 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3650 2 /* uFirstOperationWeight */,
3651 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3652
3653 if (SUCCEEDED(rc))
3654 {
3655 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3656 if (SUCCEEDED(rc))
3657 {
3658 aProgress = progress;
3659
3660 /* signal the client watcher thread */
3661 mParent->i_updateClientWatcher();
3662
3663 /* fire an event */
3664 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3665 }
3666 }
3667 }
3668 else
3669 {
3670 /* no progress object - either instant success or failure */
3671 aProgress = NULL;
3672
3673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3674
3675 if (mData->mSession.mState != SessionState_Locked)
3676 return setError(VBOX_E_INVALID_OBJECT_STATE,
3677 tr("The machine '%s' is not locked by a session"),
3678 mUserData->s.strName.c_str());
3679
3680 /* must have a VM process associated - do not kill normal API clients
3681 * with an open session */
3682 if (!Global::IsOnline(mData->mMachineState))
3683 return setError(VBOX_E_INVALID_OBJECT_STATE,
3684 tr("The machine '%s' does not have a VM process"),
3685 mUserData->s.strName.c_str());
3686
3687 /* forcibly terminate the VM process */
3688 if (mData->mSession.mPID != NIL_RTPROCESS)
3689 RTProcTerminate(mData->mSession.mPID);
3690
3691 /* signal the client watcher thread, as most likely the client has
3692 * been terminated */
3693 mParent->i_updateClientWatcher();
3694 }
3695
3696 return rc;
3697}
3698
3699HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3700{
3701 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3702 return setError(E_INVALIDARG,
3703 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3704 aPosition, SchemaDefs::MaxBootPosition);
3705
3706 if (aDevice == DeviceType_USB)
3707 return setError(E_NOTIMPL,
3708 tr("Booting from USB device is currently not supported"));
3709
3710 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3711
3712 HRESULT rc = i_checkStateDependency(MutableStateDep);
3713 if (FAILED(rc)) return rc;
3714
3715 i_setModified(IsModified_MachineData);
3716 mHWData.backup();
3717 mHWData->mBootOrder[aPosition - 1] = aDevice;
3718
3719 return S_OK;
3720}
3721
3722HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3723{
3724 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3725 return setError(E_INVALIDARG,
3726 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3727 aPosition, SchemaDefs::MaxBootPosition);
3728
3729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3730
3731 *aDevice = mHWData->mBootOrder[aPosition - 1];
3732
3733 return S_OK;
3734}
3735
3736HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3737 LONG aControllerPort,
3738 LONG aDevice,
3739 DeviceType_T aType,
3740 const ComPtr<IMedium> &aMedium)
3741{
3742 IMedium *aM = aMedium;
3743 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3744 aName.c_str(), aControllerPort, aDevice, aType, aM));
3745
3746 // request the host lock first, since might be calling Host methods for getting host drives;
3747 // next, protect the media tree all the while we're in here, as well as our member variables
3748 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3749 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3750
3751 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3752 if (FAILED(rc)) return rc;
3753
3754 /// @todo NEWMEDIA implicit machine registration
3755 if (!mData->mRegistered)
3756 return setError(VBOX_E_INVALID_OBJECT_STATE,
3757 tr("Cannot attach storage devices to an unregistered machine"));
3758
3759 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3760
3761 /* Check for an existing controller. */
3762 ComObjPtr<StorageController> ctl;
3763 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3764 if (FAILED(rc)) return rc;
3765
3766 StorageControllerType_T ctrlType;
3767 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3768 if (FAILED(rc))
3769 return setError(E_FAIL,
3770 tr("Could not get type of controller '%s'"),
3771 aName.c_str());
3772
3773 bool fSilent = false;
3774 Utf8Str strReconfig;
3775
3776 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3777 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3778 if ( mData->mMachineState == MachineState_Paused
3779 && strReconfig == "1")
3780 fSilent = true;
3781
3782 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3783 bool fHotplug = false;
3784 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3785 fHotplug = true;
3786
3787 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3788 return setError(VBOX_E_INVALID_VM_STATE,
3789 tr("Controller '%s' does not support hotplugging"),
3790 aName.c_str());
3791
3792 // check that the port and device are not out of range
3793 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3794 if (FAILED(rc)) return rc;
3795
3796 /* check if the device slot is already busy */
3797 MediumAttachment *pAttachTemp;
3798 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3799 Bstr(aName).raw(),
3800 aControllerPort,
3801 aDevice)))
3802 {
3803 Medium *pMedium = pAttachTemp->i_getMedium();
3804 if (pMedium)
3805 {
3806 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3807 return setError(VBOX_E_OBJECT_IN_USE,
3808 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3809 pMedium->i_getLocationFull().c_str(),
3810 aControllerPort,
3811 aDevice,
3812 aName.c_str());
3813 }
3814 else
3815 return setError(VBOX_E_OBJECT_IN_USE,
3816 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3817 aControllerPort, aDevice, aName.c_str());
3818 }
3819
3820 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3821 if (aMedium && medium.isNull())
3822 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3823
3824 AutoCaller mediumCaller(medium);
3825 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3826
3827 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3828
3829 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3830 && !medium.isNull()
3831 )
3832 return setError(VBOX_E_OBJECT_IN_USE,
3833 tr("Medium '%s' is already attached to this virtual machine"),
3834 medium->i_getLocationFull().c_str());
3835
3836 if (!medium.isNull())
3837 {
3838 MediumType_T mtype = medium->i_getType();
3839 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3840 // For DVDs it's not written to the config file, so needs no global config
3841 // version bump. For floppies it's a new attribute "type", which is ignored
3842 // by older VirtualBox version, so needs no global config version bump either.
3843 // For hard disks this type is not accepted.
3844 if (mtype == MediumType_MultiAttach)
3845 {
3846 // This type is new with VirtualBox 4.0 and therefore requires settings
3847 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3848 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3849 // two reasons: The medium type is a property of the media registry tree, which
3850 // can reside in the global config file (for pre-4.0 media); we would therefore
3851 // possibly need to bump the global config version. We don't want to do that though
3852 // because that might make downgrading to pre-4.0 impossible.
3853 // As a result, we can only use these two new types if the medium is NOT in the
3854 // global registry:
3855 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3856 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3857 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3858 )
3859 return setError(VBOX_E_INVALID_OBJECT_STATE,
3860 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3861 "to machines that were created with VirtualBox 4.0 or later"),
3862 medium->i_getLocationFull().c_str());
3863 }
3864 }
3865
3866 bool fIndirect = false;
3867 if (!medium.isNull())
3868 fIndirect = medium->i_isReadOnly();
3869 bool associate = true;
3870
3871 do
3872 {
3873 if ( aType == DeviceType_HardDisk
3874 && mMediaData.isBackedUp())
3875 {
3876 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3877
3878 /* check if the medium was attached to the VM before we started
3879 * changing attachments in which case the attachment just needs to
3880 * be restored */
3881 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3882 {
3883 AssertReturn(!fIndirect, E_FAIL);
3884
3885 /* see if it's the same bus/channel/device */
3886 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3887 {
3888 /* the simplest case: restore the whole attachment
3889 * and return, nothing else to do */
3890 mMediaData->mAttachments.push_back(pAttachTemp);
3891
3892 /* Reattach the medium to the VM. */
3893 if (fHotplug || fSilent)
3894 {
3895 mediumLock.release();
3896 treeLock.release();
3897 alock.release();
3898
3899 MediumLockList *pMediumLockList(new MediumLockList());
3900
3901 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3902 true /* fMediumLockWrite */,
3903 false /* fMediumLockWriteAll */,
3904 NULL,
3905 *pMediumLockList);
3906 alock.acquire();
3907 if (FAILED(rc))
3908 delete pMediumLockList;
3909 else
3910 {
3911 mData->mSession.mLockedMedia.Unlock();
3912 alock.release();
3913 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3914 mData->mSession.mLockedMedia.Lock();
3915 alock.acquire();
3916 }
3917 alock.release();
3918
3919 if (SUCCEEDED(rc))
3920 {
3921 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3922 /* Remove lock list in case of error. */
3923 if (FAILED(rc))
3924 {
3925 mData->mSession.mLockedMedia.Unlock();
3926 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3927 mData->mSession.mLockedMedia.Lock();
3928 }
3929 }
3930 }
3931
3932 return S_OK;
3933 }
3934
3935 /* bus/channel/device differ; we need a new attachment object,
3936 * but don't try to associate it again */
3937 associate = false;
3938 break;
3939 }
3940 }
3941
3942 /* go further only if the attachment is to be indirect */
3943 if (!fIndirect)
3944 break;
3945
3946 /* perform the so called smart attachment logic for indirect
3947 * attachments. Note that smart attachment is only applicable to base
3948 * hard disks. */
3949
3950 if (medium->i_getParent().isNull())
3951 {
3952 /* first, investigate the backup copy of the current hard disk
3953 * attachments to make it possible to re-attach existing diffs to
3954 * another device slot w/o losing their contents */
3955 if (mMediaData.isBackedUp())
3956 {
3957 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3958
3959 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3960 uint32_t foundLevel = 0;
3961
3962 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3963 {
3964 uint32_t level = 0;
3965 MediumAttachment *pAttach = *it;
3966 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3967 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3968 if (pMedium.isNull())
3969 continue;
3970
3971 if (pMedium->i_getBase(&level) == medium)
3972 {
3973 /* skip the hard disk if its currently attached (we
3974 * cannot attach the same hard disk twice) */
3975 if (i_findAttachment(mMediaData->mAttachments,
3976 pMedium))
3977 continue;
3978
3979 /* matched device, channel and bus (i.e. attached to the
3980 * same place) will win and immediately stop the search;
3981 * otherwise the attachment that has the youngest
3982 * descendant of medium will be used
3983 */
3984 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3985 {
3986 /* the simplest case: restore the whole attachment
3987 * and return, nothing else to do */
3988 mMediaData->mAttachments.push_back(*it);
3989
3990 /* Reattach the medium to the VM. */
3991 if (fHotplug || fSilent)
3992 {
3993 mediumLock.release();
3994 treeLock.release();
3995 alock.release();
3996
3997 MediumLockList *pMediumLockList(new MediumLockList());
3998
3999 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4000 true /* fMediumLockWrite */,
4001 false /* fMediumLockWriteAll */,
4002 NULL,
4003 *pMediumLockList);
4004 alock.acquire();
4005 if (FAILED(rc))
4006 delete pMediumLockList;
4007 else
4008 {
4009 mData->mSession.mLockedMedia.Unlock();
4010 alock.release();
4011 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4012 mData->mSession.mLockedMedia.Lock();
4013 alock.acquire();
4014 }
4015 alock.release();
4016
4017 if (SUCCEEDED(rc))
4018 {
4019 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4020 /* Remove lock list in case of error. */
4021 if (FAILED(rc))
4022 {
4023 mData->mSession.mLockedMedia.Unlock();
4024 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4025 mData->mSession.mLockedMedia.Lock();
4026 }
4027 }
4028 }
4029
4030 return S_OK;
4031 }
4032 else if ( foundIt == oldAtts.end()
4033 || level > foundLevel /* prefer younger */
4034 )
4035 {
4036 foundIt = it;
4037 foundLevel = level;
4038 }
4039 }
4040 }
4041
4042 if (foundIt != oldAtts.end())
4043 {
4044 /* use the previously attached hard disk */
4045 medium = (*foundIt)->i_getMedium();
4046 mediumCaller.attach(medium);
4047 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4048 mediumLock.attach(medium);
4049 /* not implicit, doesn't require association with this VM */
4050 fIndirect = false;
4051 associate = false;
4052 /* go right to the MediumAttachment creation */
4053 break;
4054 }
4055 }
4056
4057 /* must give up the medium lock and medium tree lock as below we
4058 * go over snapshots, which needs a lock with higher lock order. */
4059 mediumLock.release();
4060 treeLock.release();
4061
4062 /* then, search through snapshots for the best diff in the given
4063 * hard disk's chain to base the new diff on */
4064
4065 ComObjPtr<Medium> base;
4066 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4067 while (snap)
4068 {
4069 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4070
4071 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4072
4073 MediumAttachment *pAttachFound = NULL;
4074 uint32_t foundLevel = 0;
4075
4076 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4077 {
4078 MediumAttachment *pAttach = *it;
4079 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4080 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4081 if (pMedium.isNull())
4082 continue;
4083
4084 uint32_t level = 0;
4085 if (pMedium->i_getBase(&level) == medium)
4086 {
4087 /* matched device, channel and bus (i.e. attached to the
4088 * same place) will win and immediately stop the search;
4089 * otherwise the attachment that has the youngest
4090 * descendant of medium will be used
4091 */
4092 if ( pAttach->i_getDevice() == aDevice
4093 && pAttach->i_getPort() == aControllerPort
4094 && pAttach->i_getControllerName() == aName
4095 )
4096 {
4097 pAttachFound = pAttach;
4098 break;
4099 }
4100 else if ( !pAttachFound
4101 || level > foundLevel /* prefer younger */
4102 )
4103 {
4104 pAttachFound = pAttach;
4105 foundLevel = level;
4106 }
4107 }
4108 }
4109
4110 if (pAttachFound)
4111 {
4112 base = pAttachFound->i_getMedium();
4113 break;
4114 }
4115
4116 snap = snap->i_getParent();
4117 }
4118
4119 /* re-lock medium tree and the medium, as we need it below */
4120 treeLock.acquire();
4121 mediumLock.acquire();
4122
4123 /* found a suitable diff, use it as a base */
4124 if (!base.isNull())
4125 {
4126 medium = base;
4127 mediumCaller.attach(medium);
4128 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4129 mediumLock.attach(medium);
4130 }
4131 }
4132
4133 Utf8Str strFullSnapshotFolder;
4134 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4135
4136 ComObjPtr<Medium> diff;
4137 diff.createObject();
4138 // store this diff in the same registry as the parent
4139 Guid uuidRegistryParent;
4140 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4141 {
4142 // parent image has no registry: this can happen if we're attaching a new immutable
4143 // image that has not yet been attached (medium then points to the base and we're
4144 // creating the diff image for the immutable, and the parent is not yet registered);
4145 // put the parent in the machine registry then
4146 mediumLock.release();
4147 treeLock.release();
4148 alock.release();
4149 i_addMediumToRegistry(medium);
4150 alock.acquire();
4151 treeLock.acquire();
4152 mediumLock.acquire();
4153 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4154 }
4155 rc = diff->init(mParent,
4156 medium->i_getPreferredDiffFormat(),
4157 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4158 uuidRegistryParent,
4159 DeviceType_HardDisk);
4160 if (FAILED(rc)) return rc;
4161
4162 /* Apply the normal locking logic to the entire chain. */
4163 MediumLockList *pMediumLockList(new MediumLockList());
4164 mediumLock.release();
4165 treeLock.release();
4166 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4167 true /* fMediumLockWrite */,
4168 false /* fMediumLockWriteAll */,
4169 medium,
4170 *pMediumLockList);
4171 treeLock.acquire();
4172 mediumLock.acquire();
4173 if (SUCCEEDED(rc))
4174 {
4175 mediumLock.release();
4176 treeLock.release();
4177 rc = pMediumLockList->Lock();
4178 treeLock.acquire();
4179 mediumLock.acquire();
4180 if (FAILED(rc))
4181 setError(rc,
4182 tr("Could not lock medium when creating diff '%s'"),
4183 diff->i_getLocationFull().c_str());
4184 else
4185 {
4186 /* will release the lock before the potentially lengthy
4187 * operation, so protect with the special state */
4188 MachineState_T oldState = mData->mMachineState;
4189 i_setMachineState(MachineState_SettingUp);
4190
4191 mediumLock.release();
4192 treeLock.release();
4193 alock.release();
4194
4195 rc = medium->i_createDiffStorage(diff,
4196 MediumVariant_Standard,
4197 pMediumLockList,
4198 NULL /* aProgress */,
4199 true /* aWait */);
4200
4201 alock.acquire();
4202 treeLock.acquire();
4203 mediumLock.acquire();
4204
4205 i_setMachineState(oldState);
4206 }
4207 }
4208
4209 /* Unlock the media and free the associated memory. */
4210 delete pMediumLockList;
4211
4212 if (FAILED(rc)) return rc;
4213
4214 /* use the created diff for the actual attachment */
4215 medium = diff;
4216 mediumCaller.attach(medium);
4217 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4218 mediumLock.attach(medium);
4219 }
4220 while (0);
4221
4222 ComObjPtr<MediumAttachment> attachment;
4223 attachment.createObject();
4224 rc = attachment->init(this,
4225 medium,
4226 aName,
4227 aControllerPort,
4228 aDevice,
4229 aType,
4230 fIndirect,
4231 false /* fPassthrough */,
4232 false /* fTempEject */,
4233 false /* fNonRotational */,
4234 false /* fDiscard */,
4235 fHotplug /* fHotPluggable */,
4236 Utf8Str::Empty);
4237 if (FAILED(rc)) return rc;
4238
4239 if (associate && !medium.isNull())
4240 {
4241 // as the last step, associate the medium to the VM
4242 rc = medium->i_addBackReference(mData->mUuid);
4243 // here we can fail because of Deleting, or being in process of creating a Diff
4244 if (FAILED(rc)) return rc;
4245
4246 mediumLock.release();
4247 treeLock.release();
4248 alock.release();
4249 i_addMediumToRegistry(medium);
4250 alock.acquire();
4251 treeLock.acquire();
4252 mediumLock.acquire();
4253 }
4254
4255 /* success: finally remember the attachment */
4256 i_setModified(IsModified_Storage);
4257 mMediaData.backup();
4258 mMediaData->mAttachments.push_back(attachment);
4259
4260 mediumLock.release();
4261 treeLock.release();
4262 alock.release();
4263
4264 if (fHotplug || fSilent)
4265 {
4266 if (!medium.isNull())
4267 {
4268 MediumLockList *pMediumLockList(new MediumLockList());
4269
4270 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4271 true /* fMediumLockWrite */,
4272 false /* fMediumLockWriteAll */,
4273 NULL,
4274 *pMediumLockList);
4275 alock.acquire();
4276 if (FAILED(rc))
4277 delete pMediumLockList;
4278 else
4279 {
4280 mData->mSession.mLockedMedia.Unlock();
4281 alock.release();
4282 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4283 mData->mSession.mLockedMedia.Lock();
4284 alock.acquire();
4285 }
4286 alock.release();
4287 }
4288
4289 if (SUCCEEDED(rc))
4290 {
4291 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4292 /* Remove lock list in case of error. */
4293 if (FAILED(rc))
4294 {
4295 mData->mSession.mLockedMedia.Unlock();
4296 mData->mSession.mLockedMedia.Remove(attachment);
4297 mData->mSession.mLockedMedia.Lock();
4298 }
4299 }
4300 }
4301
4302 /* Save modified registries, but skip this machine as it's the caller's
4303 * job to save its settings like all other settings changes. */
4304 mParent->i_unmarkRegistryModified(i_getId());
4305 mParent->i_saveModifiedRegistries();
4306
4307 return rc;
4308}
4309
4310HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4311 LONG aDevice)
4312{
4313 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4314 aName.c_str(), aControllerPort, aDevice));
4315
4316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4317
4318 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4319 if (FAILED(rc)) return rc;
4320
4321 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4322
4323 /* Check for an existing controller. */
4324 ComObjPtr<StorageController> ctl;
4325 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4326 if (FAILED(rc)) return rc;
4327
4328 StorageControllerType_T ctrlType;
4329 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4330 if (FAILED(rc))
4331 return setError(E_FAIL,
4332 tr("Could not get type of controller '%s'"),
4333 aName.c_str());
4334
4335 bool fSilent = false;
4336 Utf8Str strReconfig;
4337
4338 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4339 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4340 if ( mData->mMachineState == MachineState_Paused
4341 && strReconfig == "1")
4342 fSilent = true;
4343
4344 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4345 bool fHotplug = false;
4346 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4347 fHotplug = true;
4348
4349 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4350 return setError(VBOX_E_INVALID_VM_STATE,
4351 tr("Controller '%s' does not support hotplugging"),
4352 aName.c_str());
4353
4354 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4355 Bstr(aName).raw(),
4356 aControllerPort,
4357 aDevice);
4358 if (!pAttach)
4359 return setError(VBOX_E_OBJECT_NOT_FOUND,
4360 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4361 aDevice, aControllerPort, aName.c_str());
4362
4363 if (fHotplug && !pAttach->i_getHotPluggable())
4364 return setError(VBOX_E_NOT_SUPPORTED,
4365 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4366 aDevice, aControllerPort, aName.c_str());
4367
4368 /*
4369 * The VM has to detach the device before we delete any implicit diffs.
4370 * If this fails we can roll back without loosing data.
4371 */
4372 if (fHotplug || fSilent)
4373 {
4374 alock.release();
4375 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4376 alock.acquire();
4377 }
4378 if (FAILED(rc)) return rc;
4379
4380 /* If we are here everything went well and we can delete the implicit now. */
4381 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4382
4383 alock.release();
4384
4385 /* Save modified registries, but skip this machine as it's the caller's
4386 * job to save its settings like all other settings changes. */
4387 mParent->i_unmarkRegistryModified(i_getId());
4388 mParent->i_saveModifiedRegistries();
4389
4390 return rc;
4391}
4392
4393HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4394 LONG aDevice, BOOL aPassthrough)
4395{
4396 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4397 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4398
4399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4400
4401 HRESULT rc = i_checkStateDependency(MutableStateDep);
4402 if (FAILED(rc)) return rc;
4403
4404 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4405
4406 if (Global::IsOnlineOrTransient(mData->mMachineState))
4407 return setError(VBOX_E_INVALID_VM_STATE,
4408 tr("Invalid machine state: %s"),
4409 Global::stringifyMachineState(mData->mMachineState));
4410
4411 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4412 Bstr(aName).raw(),
4413 aControllerPort,
4414 aDevice);
4415 if (!pAttach)
4416 return setError(VBOX_E_OBJECT_NOT_FOUND,
4417 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4418 aDevice, aControllerPort, aName.c_str());
4419
4420
4421 i_setModified(IsModified_Storage);
4422 mMediaData.backup();
4423
4424 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4425
4426 if (pAttach->i_getType() != DeviceType_DVD)
4427 return setError(E_INVALIDARG,
4428 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4429 aDevice, aControllerPort, aName.c_str());
4430 pAttach->i_updatePassthrough(!!aPassthrough);
4431
4432 return S_OK;
4433}
4434
4435HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4436 LONG aDevice, BOOL aTemporaryEject)
4437{
4438
4439 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4440 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4441
4442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4443
4444 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4445 if (FAILED(rc)) return rc;
4446
4447 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4448 Bstr(aName).raw(),
4449 aControllerPort,
4450 aDevice);
4451 if (!pAttach)
4452 return setError(VBOX_E_OBJECT_NOT_FOUND,
4453 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4454 aDevice, aControllerPort, aName.c_str());
4455
4456
4457 i_setModified(IsModified_Storage);
4458 mMediaData.backup();
4459
4460 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4461
4462 if (pAttach->i_getType() != DeviceType_DVD)
4463 return setError(E_INVALIDARG,
4464 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4465 aDevice, aControllerPort, aName.c_str());
4466 pAttach->i_updateTempEject(!!aTemporaryEject);
4467
4468 return S_OK;
4469}
4470
4471HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4472 LONG aDevice, BOOL aNonRotational)
4473{
4474
4475 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4476 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4477
4478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4479
4480 HRESULT rc = i_checkStateDependency(MutableStateDep);
4481 if (FAILED(rc)) return rc;
4482
4483 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4484
4485 if (Global::IsOnlineOrTransient(mData->mMachineState))
4486 return setError(VBOX_E_INVALID_VM_STATE,
4487 tr("Invalid machine state: %s"),
4488 Global::stringifyMachineState(mData->mMachineState));
4489
4490 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4491 Bstr(aName).raw(),
4492 aControllerPort,
4493 aDevice);
4494 if (!pAttach)
4495 return setError(VBOX_E_OBJECT_NOT_FOUND,
4496 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4497 aDevice, aControllerPort, aName.c_str());
4498
4499
4500 i_setModified(IsModified_Storage);
4501 mMediaData.backup();
4502
4503 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4504
4505 if (pAttach->i_getType() != DeviceType_HardDisk)
4506 return setError(E_INVALIDARG,
4507 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"),
4508 aDevice, aControllerPort, aName.c_str());
4509 pAttach->i_updateNonRotational(!!aNonRotational);
4510
4511 return S_OK;
4512}
4513
4514HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4515 LONG aDevice, BOOL aDiscard)
4516{
4517
4518 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4519 aName.c_str(), aControllerPort, aDevice, aDiscard));
4520
4521 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4522
4523 HRESULT rc = i_checkStateDependency(MutableStateDep);
4524 if (FAILED(rc)) return rc;
4525
4526 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4527
4528 if (Global::IsOnlineOrTransient(mData->mMachineState))
4529 return setError(VBOX_E_INVALID_VM_STATE,
4530 tr("Invalid machine state: %s"),
4531 Global::stringifyMachineState(mData->mMachineState));
4532
4533 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4534 Bstr(aName).raw(),
4535 aControllerPort,
4536 aDevice);
4537 if (!pAttach)
4538 return setError(VBOX_E_OBJECT_NOT_FOUND,
4539 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4540 aDevice, aControllerPort, aName.c_str());
4541
4542
4543 i_setModified(IsModified_Storage);
4544 mMediaData.backup();
4545
4546 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4547
4548 if (pAttach->i_getType() != DeviceType_HardDisk)
4549 return setError(E_INVALIDARG,
4550 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"),
4551 aDevice, aControllerPort, aName.c_str());
4552 pAttach->i_updateDiscard(!!aDiscard);
4553
4554 return S_OK;
4555}
4556
4557HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4558 LONG aDevice, BOOL aHotPluggable)
4559{
4560 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4561 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4562
4563 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4564
4565 HRESULT rc = i_checkStateDependency(MutableStateDep);
4566 if (FAILED(rc)) return rc;
4567
4568 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4569
4570 if (Global::IsOnlineOrTransient(mData->mMachineState))
4571 return setError(VBOX_E_INVALID_VM_STATE,
4572 tr("Invalid machine state: %s"),
4573 Global::stringifyMachineState(mData->mMachineState));
4574
4575 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4576 Bstr(aName).raw(),
4577 aControllerPort,
4578 aDevice);
4579 if (!pAttach)
4580 return setError(VBOX_E_OBJECT_NOT_FOUND,
4581 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4582 aDevice, aControllerPort, aName.c_str());
4583
4584 /* Check for an existing controller. */
4585 ComObjPtr<StorageController> ctl;
4586 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4587 if (FAILED(rc)) return rc;
4588
4589 StorageControllerType_T ctrlType;
4590 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4591 if (FAILED(rc))
4592 return setError(E_FAIL,
4593 tr("Could not get type of controller '%s'"),
4594 aName.c_str());
4595
4596 if (!i_isControllerHotplugCapable(ctrlType))
4597 return setError(VBOX_E_NOT_SUPPORTED,
4598 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4599 aName.c_str());
4600
4601 i_setModified(IsModified_Storage);
4602 mMediaData.backup();
4603
4604 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4605
4606 if (pAttach->i_getType() == DeviceType_Floppy)
4607 return setError(E_INVALIDARG,
4608 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"),
4609 aDevice, aControllerPort, aName.c_str());
4610 pAttach->i_updateHotPluggable(!!aHotPluggable);
4611
4612 return S_OK;
4613}
4614
4615HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4616 LONG aDevice)
4617{
4618 int rc = S_OK;
4619 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4620 aName.c_str(), aControllerPort, aDevice));
4621
4622 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4623
4624 return rc;
4625}
4626
4627HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4628 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4629{
4630 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4631 aName.c_str(), aControllerPort, aDevice));
4632
4633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4634
4635 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4636 if (FAILED(rc)) return rc;
4637
4638 if (Global::IsOnlineOrTransient(mData->mMachineState))
4639 return setError(VBOX_E_INVALID_VM_STATE,
4640 tr("Invalid machine state: %s"),
4641 Global::stringifyMachineState(mData->mMachineState));
4642
4643 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4644 Bstr(aName).raw(),
4645 aControllerPort,
4646 aDevice);
4647 if (!pAttach)
4648 return setError(VBOX_E_OBJECT_NOT_FOUND,
4649 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4650 aDevice, aControllerPort, aName.c_str());
4651
4652
4653 i_setModified(IsModified_Storage);
4654 mMediaData.backup();
4655
4656 IBandwidthGroup *iB = aBandwidthGroup;
4657 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4658 if (aBandwidthGroup && group.isNull())
4659 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4660
4661 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4662
4663 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4664 if (strBandwidthGroupOld.isNotEmpty())
4665 {
4666 /* Get the bandwidth group object and release it - this must not fail. */
4667 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4668 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4669 Assert(SUCCEEDED(rc));
4670
4671 pBandwidthGroupOld->i_release();
4672 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4673 }
4674
4675 if (!group.isNull())
4676 {
4677 group->i_reference();
4678 pAttach->i_updateBandwidthGroup(group->i_getName());
4679 }
4680
4681 return S_OK;
4682}
4683
4684HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4685 LONG aControllerPort,
4686 LONG aDevice,
4687 DeviceType_T aType)
4688{
4689 HRESULT rc = S_OK;
4690
4691 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4692 aName.c_str(), aControllerPort, aDevice, aType));
4693
4694 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4695
4696 return rc;
4697}
4698
4699
4700HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4701 LONG aControllerPort,
4702 LONG aDevice,
4703 BOOL aForce)
4704{
4705 int rc = S_OK;
4706 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4707 aName.c_str(), aControllerPort, aForce));
4708
4709 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4710
4711 return rc;
4712}
4713
4714HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4715 LONG aControllerPort,
4716 LONG aDevice,
4717 const ComPtr<IMedium> &aMedium,
4718 BOOL aForce)
4719{
4720 int rc = S_OK;
4721 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4722 aName.c_str(), aControllerPort, aDevice, aForce));
4723
4724 // request the host lock first, since might be calling Host methods for getting host drives;
4725 // next, protect the media tree all the while we're in here, as well as our member variables
4726 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4727 this->lockHandle(),
4728 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4729
4730 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4731 Bstr(aName).raw(),
4732 aControllerPort,
4733 aDevice);
4734 if (pAttach.isNull())
4735 return setError(VBOX_E_OBJECT_NOT_FOUND,
4736 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4737 aDevice, aControllerPort, aName.c_str());
4738
4739 /* Remember previously mounted medium. The medium before taking the
4740 * backup is not necessarily the same thing. */
4741 ComObjPtr<Medium> oldmedium;
4742 oldmedium = pAttach->i_getMedium();
4743
4744 IMedium *iM = aMedium;
4745 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4746 if (aMedium && pMedium.isNull())
4747 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4748
4749 AutoCaller mediumCaller(pMedium);
4750 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4751
4752 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4753 if (pMedium)
4754 {
4755 DeviceType_T mediumType = pAttach->i_getType();
4756 switch (mediumType)
4757 {
4758 case DeviceType_DVD:
4759 case DeviceType_Floppy:
4760 break;
4761
4762 default:
4763 return setError(VBOX_E_INVALID_OBJECT_STATE,
4764 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4765 aControllerPort,
4766 aDevice,
4767 aName.c_str());
4768 }
4769 }
4770
4771 i_setModified(IsModified_Storage);
4772 mMediaData.backup();
4773
4774 {
4775 // The backup operation makes the pAttach reference point to the
4776 // old settings. Re-get the correct reference.
4777 pAttach = i_findAttachment(mMediaData->mAttachments,
4778 Bstr(aName).raw(),
4779 aControllerPort,
4780 aDevice);
4781 if (!oldmedium.isNull())
4782 oldmedium->i_removeBackReference(mData->mUuid);
4783 if (!pMedium.isNull())
4784 {
4785 pMedium->i_addBackReference(mData->mUuid);
4786
4787 mediumLock.release();
4788 multiLock.release();
4789 i_addMediumToRegistry(pMedium);
4790 multiLock.acquire();
4791 mediumLock.acquire();
4792 }
4793
4794 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4795 pAttach->i_updateMedium(pMedium);
4796 }
4797
4798 i_setModified(IsModified_Storage);
4799
4800 mediumLock.release();
4801 multiLock.release();
4802 rc = i_onMediumChange(pAttach, aForce);
4803 multiLock.acquire();
4804 mediumLock.acquire();
4805
4806 /* On error roll back this change only. */
4807 if (FAILED(rc))
4808 {
4809 if (!pMedium.isNull())
4810 pMedium->i_removeBackReference(mData->mUuid);
4811 pAttach = i_findAttachment(mMediaData->mAttachments,
4812 Bstr(aName).raw(),
4813 aControllerPort,
4814 aDevice);
4815 /* If the attachment is gone in the meantime, bail out. */
4816 if (pAttach.isNull())
4817 return rc;
4818 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4819 if (!oldmedium.isNull())
4820 oldmedium->i_addBackReference(mData->mUuid);
4821 pAttach->i_updateMedium(oldmedium);
4822 }
4823
4824 mediumLock.release();
4825 multiLock.release();
4826
4827 /* Save modified registries, but skip this machine as it's the caller's
4828 * job to save its settings like all other settings changes. */
4829 mParent->i_unmarkRegistryModified(i_getId());
4830 mParent->i_saveModifiedRegistries();
4831
4832 return rc;
4833}
4834HRESULT Machine::getMedium(const com::Utf8Str &aName,
4835 LONG aControllerPort,
4836 LONG aDevice,
4837 ComPtr<IMedium> &aMedium)
4838{
4839 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4840 aName.c_str(), aControllerPort, aDevice));
4841
4842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4843
4844 aMedium = NULL;
4845
4846 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4847 Bstr(aName).raw(),
4848 aControllerPort,
4849 aDevice);
4850 if (pAttach.isNull())
4851 return setError(VBOX_E_OBJECT_NOT_FOUND,
4852 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4853 aDevice, aControllerPort, aName.c_str());
4854
4855 aMedium = pAttach->i_getMedium();
4856
4857 return S_OK;
4858}
4859
4860HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4861{
4862
4863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4864
4865 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4866
4867 return S_OK;
4868}
4869
4870HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4871{
4872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4873
4874 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4875
4876 return S_OK;
4877}
4878
4879HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4880{
4881 /* Do not assert if slot is out of range, just return the advertised
4882 status. testdriver/vbox.py triggers this in logVmInfo. */
4883 if (aSlot >= mNetworkAdapters.size())
4884 return setError(E_INVALIDARG,
4885 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4886 aSlot, mNetworkAdapters.size());
4887
4888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4889
4890 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4891
4892 return S_OK;
4893}
4894
4895HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4896{
4897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4898
4899 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4900 size_t i = 0;
4901 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4902 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4903 ++it, ++i)
4904 aKeys[i] = it->first;
4905
4906 return S_OK;
4907}
4908
4909 /**
4910 * @note Locks this object for reading.
4911 */
4912HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4913 com::Utf8Str &aValue)
4914{
4915 /* start with nothing found */
4916 aValue = "";
4917
4918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4919
4920 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4921 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4922 // found:
4923 aValue = it->second; // source is a Utf8Str
4924
4925 /* return the result to caller (may be empty) */
4926 return S_OK;
4927}
4928
4929 /**
4930 * @note Locks mParent for writing + this object for writing.
4931 */
4932HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4933{
4934 Utf8Str strOldValue; // empty
4935
4936 // locking note: we only hold the read lock briefly to look up the old value,
4937 // then release it and call the onExtraCanChange callbacks. There is a small
4938 // chance of a race insofar as the callback might be called twice if two callers
4939 // change the same key at the same time, but that's a much better solution
4940 // than the deadlock we had here before. The actual changing of the extradata
4941 // is then performed under the write lock and race-free.
4942
4943 // look up the old value first; if nothing has changed then we need not do anything
4944 {
4945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4946
4947 // For snapshots don't even think about allowing changes, extradata
4948 // is global for a machine, so there is nothing snapshot specific.
4949 if (i_isSnapshotMachine())
4950 return setError(VBOX_E_INVALID_VM_STATE,
4951 tr("Cannot set extradata for a snapshot"));
4952
4953 // check if the right IMachine instance is used
4954 if (mData->mRegistered && !i_isSessionMachine())
4955 return setError(VBOX_E_INVALID_VM_STATE,
4956 tr("Cannot set extradata for an immutable machine"));
4957
4958 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4959 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4960 strOldValue = it->second;
4961 }
4962
4963 bool fChanged;
4964 if ((fChanged = (strOldValue != aValue)))
4965 {
4966 // ask for permission from all listeners outside the locks;
4967 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4968 // lock to copy the list of callbacks to invoke
4969 Bstr error;
4970 Bstr bstrValue(aValue);
4971
4972 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4973 {
4974 const char *sep = error.isEmpty() ? "" : ": ";
4975 CBSTR err = error.raw();
4976 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4977 sep, err));
4978 return setError(E_ACCESSDENIED,
4979 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4980 aKey.c_str(),
4981 aValue.c_str(),
4982 sep,
4983 err);
4984 }
4985
4986 // data is changing and change not vetoed: then write it out under the lock
4987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4988
4989 if (aValue.isEmpty())
4990 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4991 else
4992 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4993 // creates a new key if needed
4994
4995 bool fNeedsGlobalSaveSettings = false;
4996 // This saving of settings is tricky: there is no "old state" for the
4997 // extradata items at all (unlike all other settings), so the old/new
4998 // settings comparison would give a wrong result!
4999 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5000
5001 if (fNeedsGlobalSaveSettings)
5002 {
5003 // save the global settings; for that we should hold only the VirtualBox lock
5004 alock.release();
5005 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5006 mParent->i_saveSettings();
5007 }
5008 }
5009
5010 // fire notification outside the lock
5011 if (fChanged)
5012 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5013
5014 return S_OK;
5015}
5016
5017HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5018{
5019 aProgress = NULL;
5020 NOREF(aSettingsFilePath);
5021 ReturnComNotImplemented();
5022}
5023
5024HRESULT Machine::saveSettings()
5025{
5026 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5027
5028 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5029 if (FAILED(rc)) return rc;
5030
5031 /* the settings file path may never be null */
5032 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5033
5034 /* save all VM data excluding snapshots */
5035 bool fNeedsGlobalSaveSettings = false;
5036 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5037 mlock.release();
5038
5039 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5040 {
5041 // save the global settings; for that we should hold only the VirtualBox lock
5042 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5043 rc = mParent->i_saveSettings();
5044 }
5045
5046 return rc;
5047}
5048
5049
5050HRESULT Machine::discardSettings()
5051{
5052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5053
5054 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5055 if (FAILED(rc)) return rc;
5056
5057 /*
5058 * during this rollback, the session will be notified if data has
5059 * been actually changed
5060 */
5061 i_rollback(true /* aNotify */);
5062
5063 return S_OK;
5064}
5065
5066/** @note Locks objects! */
5067HRESULT Machine::unregister(AutoCaller &autoCaller,
5068 CleanupMode_T aCleanupMode,
5069 std::vector<ComPtr<IMedium> > &aMedia)
5070{
5071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5072
5073 Guid id(i_getId());
5074
5075 if (mData->mSession.mState != SessionState_Unlocked)
5076 return setError(VBOX_E_INVALID_OBJECT_STATE,
5077 tr("Cannot unregister the machine '%s' while it is locked"),
5078 mUserData->s.strName.c_str());
5079
5080 // wait for state dependents to drop to zero
5081 i_ensureNoStateDependencies();
5082
5083 if (!mData->mAccessible)
5084 {
5085 // inaccessible maschines can only be unregistered; uninitialize ourselves
5086 // here because currently there may be no unregistered that are inaccessible
5087 // (this state combination is not supported). Note releasing the caller and
5088 // leaving the lock before calling uninit()
5089 alock.release();
5090 autoCaller.release();
5091
5092 uninit();
5093
5094 mParent->i_unregisterMachine(this, id);
5095 // calls VirtualBox::i_saveSettings()
5096
5097 return S_OK;
5098 }
5099
5100 HRESULT rc = S_OK;
5101
5102 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5103 // discard saved state
5104 if (mData->mMachineState == MachineState_Saved)
5105 {
5106 // add the saved state file to the list of files the caller should delete
5107 Assert(!mSSData->strStateFilePath.isEmpty());
5108 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5109
5110 mSSData->strStateFilePath.setNull();
5111
5112 // unconditionally set the machine state to powered off, we now
5113 // know no session has locked the machine
5114 mData->mMachineState = MachineState_PoweredOff;
5115 }
5116
5117 size_t cSnapshots = 0;
5118 if (mData->mFirstSnapshot)
5119 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5120 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5121 // fail now before we start detaching media
5122 return setError(VBOX_E_INVALID_OBJECT_STATE,
5123 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5124 mUserData->s.strName.c_str(), cSnapshots);
5125
5126 // This list collects the medium objects from all medium attachments
5127 // which we will detach from the machine and its snapshots, in a specific
5128 // order which allows for closing all media without getting "media in use"
5129 // errors, simply by going through the list from the front to the back:
5130 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5131 // and must be closed before the parent media from the snapshots, or closing the parents
5132 // will fail because they still have children);
5133 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5134 // the root ("first") snapshot of the machine.
5135 MediaList llMedia;
5136
5137 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5138 && mMediaData->mAttachments.size()
5139 )
5140 {
5141 // we have media attachments: detach them all and add the Medium objects to our list
5142 if (aCleanupMode != CleanupMode_UnregisterOnly)
5143 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5144 else
5145 return setError(VBOX_E_INVALID_OBJECT_STATE,
5146 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5147 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5148 }
5149
5150 if (cSnapshots)
5151 {
5152 // add the media from the medium attachments of the snapshots to llMedia
5153 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5154 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5155 // into the children first
5156
5157 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5158 MachineState_T oldState = mData->mMachineState;
5159 mData->mMachineState = MachineState_DeletingSnapshot;
5160
5161 // make a copy of the first snapshot so the refcount does not drop to 0
5162 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5163 // because of the AutoCaller voodoo)
5164 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5165
5166 // GO!
5167 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5168
5169 mData->mMachineState = oldState;
5170 }
5171
5172 if (FAILED(rc))
5173 {
5174 i_rollbackMedia();
5175 return rc;
5176 }
5177
5178 // commit all the media changes made above
5179 i_commitMedia();
5180
5181 mData->mRegistered = false;
5182
5183 // machine lock no longer needed
5184 alock.release();
5185
5186 // return media to caller
5187 size_t i = 0;
5188 aMedia.resize(llMedia.size());
5189 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5190 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5191
5192 mParent->i_unregisterMachine(this, id);
5193 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5194
5195 return S_OK;
5196}
5197
5198/**
5199 * Task record for deleting a machine config.
5200 */
5201struct Machine::DeleteConfigTask
5202 : public Machine::Task
5203{
5204 DeleteConfigTask(Machine *m,
5205 Progress *p,
5206 const Utf8Str &t,
5207 const RTCList<ComPtr<IMedium> > &llMediums,
5208 const StringsList &llFilesToDelete)
5209 : Task(m, p, t),
5210 m_llMediums(llMediums),
5211 m_llFilesToDelete(llFilesToDelete)
5212 {}
5213
5214 void handler()
5215 {
5216 m_pMachine->i_deleteConfigHandler(*this);
5217 }
5218
5219 RTCList<ComPtr<IMedium> > m_llMediums;
5220 StringsList m_llFilesToDelete;
5221};
5222
5223/**
5224 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5225 * SessionMachine::taskHandler().
5226 *
5227 * @note Locks this object for writing.
5228 *
5229 * @param task
5230 * @return
5231 */
5232void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5233{
5234 LogFlowThisFuncEnter();
5235
5236 AutoCaller autoCaller(this);
5237 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5238 if (FAILED(autoCaller.rc()))
5239 {
5240 /* we might have been uninitialized because the session was accidentally
5241 * closed by the client, so don't assert */
5242 HRESULT rc = setError(E_FAIL,
5243 tr("The session has been accidentally closed"));
5244 task.m_pProgress->i_notifyComplete(rc);
5245 LogFlowThisFuncLeave();
5246 return;
5247 }
5248
5249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5250
5251 HRESULT rc = S_OK;
5252
5253 try
5254 {
5255 ULONG uLogHistoryCount = 3;
5256 ComPtr<ISystemProperties> systemProperties;
5257 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5258 if (FAILED(rc)) throw rc;
5259
5260 if (!systemProperties.isNull())
5261 {
5262 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5263 if (FAILED(rc)) throw rc;
5264 }
5265
5266 MachineState_T oldState = mData->mMachineState;
5267 i_setMachineState(MachineState_SettingUp);
5268 alock.release();
5269 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5270 {
5271 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5272 {
5273 AutoCaller mac(pMedium);
5274 if (FAILED(mac.rc())) throw mac.rc();
5275 Utf8Str strLocation = pMedium->i_getLocationFull();
5276 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5277 if (FAILED(rc)) throw rc;
5278 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5279 }
5280 if (pMedium->i_isMediumFormatFile())
5281 {
5282 ComPtr<IProgress> pProgress2;
5283 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5284 if (FAILED(rc)) throw rc;
5285 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5286 if (FAILED(rc)) throw rc;
5287 /* Check the result of the asynchronous process. */
5288 LONG iRc;
5289 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5290 if (FAILED(rc)) throw rc;
5291 /* If the thread of the progress object has an error, then
5292 * retrieve the error info from there, or it'll be lost. */
5293 if (FAILED(iRc))
5294 throw setError(ProgressErrorInfo(pProgress2));
5295 }
5296
5297 /* Close the medium, deliberately without checking the return
5298 * code, and without leaving any trace in the error info, as
5299 * a failure here is a very minor issue, which shouldn't happen
5300 * as above we even managed to delete the medium. */
5301 {
5302 ErrorInfoKeeper eik;
5303 pMedium->Close();
5304 }
5305 }
5306 i_setMachineState(oldState);
5307 alock.acquire();
5308
5309 // delete the files pushed on the task list by Machine::Delete()
5310 // (this includes saved states of the machine and snapshots and
5311 // medium storage files from the IMedium list passed in, and the
5312 // machine XML file)
5313 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5314 while (it != task.m_llFilesToDelete.end())
5315 {
5316 const Utf8Str &strFile = *it;
5317 LogFunc(("Deleting file %s\n", strFile.c_str()));
5318 int vrc = RTFileDelete(strFile.c_str());
5319 if (RT_FAILURE(vrc))
5320 throw setError(VBOX_E_IPRT_ERROR,
5321 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5322
5323 ++it;
5324 if (it == task.m_llFilesToDelete.end())
5325 {
5326 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5327 if (FAILED(rc)) throw rc;
5328 break;
5329 }
5330
5331 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5332 if (FAILED(rc)) throw rc;
5333 }
5334
5335 /* delete the settings only when the file actually exists */
5336 if (mData->pMachineConfigFile->fileExists())
5337 {
5338 /* Delete any backup or uncommitted XML files. Ignore failures.
5339 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5340 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5341 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5342 RTFileDelete(otherXml.c_str());
5343 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5344 RTFileDelete(otherXml.c_str());
5345
5346 /* delete the Logs folder, nothing important should be left
5347 * there (we don't check for errors because the user might have
5348 * some private files there that we don't want to delete) */
5349 Utf8Str logFolder;
5350 getLogFolder(logFolder);
5351 Assert(logFolder.length());
5352 if (RTDirExists(logFolder.c_str()))
5353 {
5354 /* Delete all VBox.log[.N] files from the Logs folder
5355 * (this must be in sync with the rotation logic in
5356 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5357 * files that may have been created by the GUI. */
5358 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5359 logFolder.c_str(), RTPATH_DELIMITER);
5360 RTFileDelete(log.c_str());
5361 log = Utf8StrFmt("%s%cVBox.png",
5362 logFolder.c_str(), RTPATH_DELIMITER);
5363 RTFileDelete(log.c_str());
5364 for (int i = uLogHistoryCount; i > 0; i--)
5365 {
5366 log = Utf8StrFmt("%s%cVBox.log.%d",
5367 logFolder.c_str(), RTPATH_DELIMITER, i);
5368 RTFileDelete(log.c_str());
5369 log = Utf8StrFmt("%s%cVBox.png.%d",
5370 logFolder.c_str(), RTPATH_DELIMITER, i);
5371 RTFileDelete(log.c_str());
5372 }
5373#if defined(RT_OS_WINDOWS)
5374 log = Utf8StrFmt("%s%cVBoxStartup.log",
5375 logFolder.c_str(), RTPATH_DELIMITER);
5376 RTFileDelete(log.c_str());
5377#endif
5378
5379 RTDirRemove(logFolder.c_str());
5380 }
5381
5382 /* delete the Snapshots folder, nothing important should be left
5383 * there (we don't check for errors because the user might have
5384 * some private files there that we don't want to delete) */
5385 Utf8Str strFullSnapshotFolder;
5386 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5387 Assert(!strFullSnapshotFolder.isEmpty());
5388 if (RTDirExists(strFullSnapshotFolder.c_str()))
5389 RTDirRemove(strFullSnapshotFolder.c_str());
5390
5391 // delete the directory that contains the settings file, but only
5392 // if it matches the VM name
5393 Utf8Str settingsDir;
5394 if (i_isInOwnDir(&settingsDir))
5395 RTDirRemove(settingsDir.c_str());
5396 }
5397
5398 alock.release();
5399
5400 mParent->i_saveModifiedRegistries();
5401 }
5402 catch (HRESULT aRC) { rc = aRC; }
5403
5404 task.m_pProgress->i_notifyComplete(rc);
5405
5406 LogFlowThisFuncLeave();
5407}
5408
5409HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5410{
5411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5412
5413 HRESULT rc = i_checkStateDependency(MutableStateDep);
5414 if (FAILED(rc)) return rc;
5415
5416 if (mData->mRegistered)
5417 return setError(VBOX_E_INVALID_VM_STATE,
5418 tr("Cannot delete settings of a registered machine"));
5419
5420 // collect files to delete
5421 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5422 if (mData->pMachineConfigFile->fileExists())
5423 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5424
5425 RTCList<ComPtr<IMedium> > llMediums;
5426 for (size_t i = 0; i < aMedia.size(); ++i)
5427 {
5428 IMedium *pIMedium(aMedia[i]);
5429 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5430 if (pMedium.isNull())
5431 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5432 SafeArray<BSTR> ids;
5433 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5434 if (FAILED(rc)) return rc;
5435 /* At this point the medium should not have any back references
5436 * anymore. If it has it is attached to another VM and *must* not
5437 * deleted. */
5438 if (ids.size() < 1)
5439 llMediums.append(pMedium);
5440 }
5441
5442 ComObjPtr<Progress> pProgress;
5443 pProgress.createObject();
5444 rc = pProgress->init(i_getVirtualBox(),
5445 static_cast<IMachine*>(this) /* aInitiator */,
5446 Bstr(tr("Deleting files")).raw(),
5447 true /* fCancellable */,
5448 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5449 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5450 if (FAILED(rc))
5451 return rc;
5452
5453 /* create and start the task on a separate thread (note that it will not
5454 * start working until we release alock) */
5455 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5456 rc = pTask->createThread();
5457 if (FAILED(rc))
5458 return rc;
5459
5460 pProgress.queryInterfaceTo(aProgress.asOutParam());
5461
5462 LogFlowFuncLeave();
5463
5464 return S_OK;
5465}
5466
5467HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5468{
5469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5470
5471 ComObjPtr<Snapshot> pSnapshot;
5472 HRESULT rc;
5473
5474 if (aNameOrId.isEmpty())
5475 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5476 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5477 else
5478 {
5479 Guid uuid(aNameOrId);
5480 if (uuid.isValid())
5481 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5482 else
5483 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5484 }
5485 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5486
5487 return rc;
5488}
5489
5490HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5491{
5492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5493
5494 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5495 if (FAILED(rc)) return rc;
5496
5497 ComObjPtr<SharedFolder> sharedFolder;
5498 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5499 if (SUCCEEDED(rc))
5500 return setError(VBOX_E_OBJECT_IN_USE,
5501 tr("Shared folder named '%s' already exists"),
5502 aName.c_str());
5503
5504 sharedFolder.createObject();
5505 rc = sharedFolder->init(i_getMachine(),
5506 aName,
5507 aHostPath,
5508 !!aWritable,
5509 !!aAutomount,
5510 true /* fFailOnError */);
5511 if (FAILED(rc)) return rc;
5512
5513 i_setModified(IsModified_SharedFolders);
5514 mHWData.backup();
5515 mHWData->mSharedFolders.push_back(sharedFolder);
5516
5517 /* inform the direct session if any */
5518 alock.release();
5519 i_onSharedFolderChange();
5520
5521 return S_OK;
5522}
5523
5524HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5525{
5526 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5527
5528 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5529 if (FAILED(rc)) return rc;
5530
5531 ComObjPtr<SharedFolder> sharedFolder;
5532 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5533 if (FAILED(rc)) return rc;
5534
5535 i_setModified(IsModified_SharedFolders);
5536 mHWData.backup();
5537 mHWData->mSharedFolders.remove(sharedFolder);
5538
5539 /* inform the direct session if any */
5540 alock.release();
5541 i_onSharedFolderChange();
5542
5543 return S_OK;
5544}
5545
5546HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5547{
5548 /* start with No */
5549 *aCanShow = FALSE;
5550
5551 ComPtr<IInternalSessionControl> directControl;
5552 {
5553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5554
5555 if (mData->mSession.mState != SessionState_Locked)
5556 return setError(VBOX_E_INVALID_VM_STATE,
5557 tr("Machine is not locked for session (session state: %s)"),
5558 Global::stringifySessionState(mData->mSession.mState));
5559
5560 if (mData->mSession.mLockType == LockType_VM)
5561 directControl = mData->mSession.mDirectControl;
5562 }
5563
5564 /* ignore calls made after #OnSessionEnd() is called */
5565 if (!directControl)
5566 return S_OK;
5567
5568 LONG64 dummy;
5569 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5570}
5571
5572HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5573{
5574 ComPtr<IInternalSessionControl> directControl;
5575 {
5576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5577
5578 if (mData->mSession.mState != SessionState_Locked)
5579 return setError(E_FAIL,
5580 tr("Machine is not locked for session (session state: %s)"),
5581 Global::stringifySessionState(mData->mSession.mState));
5582
5583 if (mData->mSession.mLockType == LockType_VM)
5584 directControl = mData->mSession.mDirectControl;
5585 }
5586
5587 /* ignore calls made after #OnSessionEnd() is called */
5588 if (!directControl)
5589 return S_OK;
5590
5591 BOOL dummy;
5592 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5593}
5594
5595#ifdef VBOX_WITH_GUEST_PROPS
5596/**
5597 * Look up a guest property in VBoxSVC's internal structures.
5598 */
5599HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5600 com::Utf8Str &aValue,
5601 LONG64 *aTimestamp,
5602 com::Utf8Str &aFlags) const
5603{
5604 using namespace guestProp;
5605
5606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5607 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5608
5609 if (it != mHWData->mGuestProperties.end())
5610 {
5611 char szFlags[MAX_FLAGS_LEN + 1];
5612 aValue = it->second.strValue;
5613 *aTimestamp = it->second.mTimestamp;
5614 writeFlags(it->second.mFlags, szFlags);
5615 aFlags = Utf8Str(szFlags);
5616 }
5617
5618 return S_OK;
5619}
5620
5621/**
5622 * Query the VM that a guest property belongs to for the property.
5623 * @returns E_ACCESSDENIED if the VM process is not available or not
5624 * currently handling queries and the lookup should then be done in
5625 * VBoxSVC.
5626 */
5627HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5628 com::Utf8Str &aValue,
5629 LONG64 *aTimestamp,
5630 com::Utf8Str &aFlags) const
5631{
5632 HRESULT rc = S_OK;
5633 BSTR bValue = NULL;
5634 BSTR bFlags = NULL;
5635
5636 ComPtr<IInternalSessionControl> directControl;
5637 {
5638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5639 if (mData->mSession.mLockType == LockType_VM)
5640 directControl = mData->mSession.mDirectControl;
5641 }
5642
5643 /* ignore calls made after #OnSessionEnd() is called */
5644 if (!directControl)
5645 rc = E_ACCESSDENIED;
5646 else
5647 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5648 0 /* accessMode */,
5649 &bValue, aTimestamp, &bFlags);
5650
5651 aValue = bValue;
5652 aFlags = bFlags;
5653
5654 return rc;
5655}
5656#endif // VBOX_WITH_GUEST_PROPS
5657
5658HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5659 com::Utf8Str &aValue,
5660 LONG64 *aTimestamp,
5661 com::Utf8Str &aFlags)
5662{
5663#ifndef VBOX_WITH_GUEST_PROPS
5664 ReturnComNotImplemented();
5665#else // VBOX_WITH_GUEST_PROPS
5666
5667 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5668
5669 if (rc == E_ACCESSDENIED)
5670 /* The VM is not running or the service is not (yet) accessible */
5671 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5672 return rc;
5673#endif // VBOX_WITH_GUEST_PROPS
5674}
5675
5676HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5677{
5678 LONG64 dummyTimestamp;
5679 com::Utf8Str dummyFlags;
5680 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5681 return rc;
5682
5683}
5684HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5685{
5686 com::Utf8Str dummyFlags;
5687 com::Utf8Str dummyValue;
5688 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5689 return rc;
5690}
5691
5692#ifdef VBOX_WITH_GUEST_PROPS
5693/**
5694 * Set a guest property in VBoxSVC's internal structures.
5695 */
5696HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5697 const com::Utf8Str &aFlags, bool fDelete)
5698{
5699 using namespace guestProp;
5700
5701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5702 HRESULT rc = S_OK;
5703
5704 rc = i_checkStateDependency(MutableOrSavedStateDep);
5705 if (FAILED(rc)) return rc;
5706
5707 try
5708 {
5709 uint32_t fFlags = NILFLAG;
5710 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5711 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5712
5713 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5714 if (it == mHWData->mGuestProperties.end())
5715 {
5716 if (!fDelete)
5717 {
5718 i_setModified(IsModified_MachineData);
5719 mHWData.backupEx();
5720
5721 RTTIMESPEC time;
5722 HWData::GuestProperty prop;
5723 prop.strValue = Bstr(aValue).raw();
5724 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5725 prop.mFlags = fFlags;
5726 mHWData->mGuestProperties[aName] = prop;
5727 }
5728 }
5729 else
5730 {
5731 if (it->second.mFlags & (RDONLYHOST))
5732 {
5733 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5734 }
5735 else
5736 {
5737 i_setModified(IsModified_MachineData);
5738 mHWData.backupEx();
5739
5740 /* The backupEx() operation invalidates our iterator,
5741 * so get a new one. */
5742 it = mHWData->mGuestProperties.find(aName);
5743 Assert(it != mHWData->mGuestProperties.end());
5744
5745 if (!fDelete)
5746 {
5747 RTTIMESPEC time;
5748 it->second.strValue = aValue;
5749 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5750 it->second.mFlags = fFlags;
5751 }
5752 else
5753 mHWData->mGuestProperties.erase(it);
5754 }
5755 }
5756
5757 if ( SUCCEEDED(rc)
5758 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5759 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5760 RTSTR_MAX,
5761 aName.c_str(),
5762 RTSTR_MAX,
5763 NULL)
5764 )
5765 )
5766 {
5767 alock.release();
5768
5769 mParent->i_onGuestPropertyChange(mData->mUuid,
5770 Bstr(aName).raw(),
5771 Bstr(aValue).raw(),
5772 Bstr(aFlags).raw());
5773 }
5774 }
5775 catch (std::bad_alloc &)
5776 {
5777 rc = E_OUTOFMEMORY;
5778 }
5779
5780 return rc;
5781}
5782
5783/**
5784 * Set a property on the VM that that property belongs to.
5785 * @returns E_ACCESSDENIED if the VM process is not available or not
5786 * currently handling queries and the setting should then be done in
5787 * VBoxSVC.
5788 */
5789HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5790 const com::Utf8Str &aFlags, bool fDelete)
5791{
5792 HRESULT rc;
5793
5794 try
5795 {
5796 ComPtr<IInternalSessionControl> directControl;
5797 {
5798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5799 if (mData->mSession.mLockType == LockType_VM)
5800 directControl = mData->mSession.mDirectControl;
5801 }
5802
5803 BSTR dummy = NULL; /* will not be changed (setter) */
5804 LONG64 dummy64;
5805 if (!directControl)
5806 rc = E_ACCESSDENIED;
5807 else
5808 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5809 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5810 fDelete? 2: 1 /* accessMode */,
5811 &dummy, &dummy64, &dummy);
5812 }
5813 catch (std::bad_alloc &)
5814 {
5815 rc = E_OUTOFMEMORY;
5816 }
5817
5818 return rc;
5819}
5820#endif // VBOX_WITH_GUEST_PROPS
5821
5822HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5823 const com::Utf8Str &aFlags)
5824{
5825#ifndef VBOX_WITH_GUEST_PROPS
5826 ReturnComNotImplemented();
5827#else // VBOX_WITH_GUEST_PROPS
5828 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5829 if (rc == E_ACCESSDENIED)
5830 /* The VM is not running or the service is not (yet) accessible */
5831 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5832 return rc;
5833#endif // VBOX_WITH_GUEST_PROPS
5834}
5835
5836HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5837{
5838 return setGuestProperty(aProperty, aValue, "");
5839}
5840
5841HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5842{
5843#ifndef VBOX_WITH_GUEST_PROPS
5844 ReturnComNotImplemented();
5845#else // VBOX_WITH_GUEST_PROPS
5846 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5847 if (rc == E_ACCESSDENIED)
5848 /* The VM is not running or the service is not (yet) accessible */
5849 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5850 return rc;
5851#endif // VBOX_WITH_GUEST_PROPS
5852}
5853
5854#ifdef VBOX_WITH_GUEST_PROPS
5855/**
5856 * Enumerate the guest properties in VBoxSVC's internal structures.
5857 */
5858HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5859 std::vector<com::Utf8Str> &aNames,
5860 std::vector<com::Utf8Str> &aValues,
5861 std::vector<LONG64> &aTimestamps,
5862 std::vector<com::Utf8Str> &aFlags)
5863{
5864 using namespace guestProp;
5865
5866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5867 Utf8Str strPatterns(aPatterns);
5868
5869 HWData::GuestPropertyMap propMap;
5870
5871 /*
5872 * Look for matching patterns and build up a list.
5873 */
5874 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5875 while (it != mHWData->mGuestProperties.end())
5876 {
5877 if ( strPatterns.isEmpty()
5878 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5879 RTSTR_MAX,
5880 it->first.c_str(),
5881 RTSTR_MAX,
5882 NULL)
5883 )
5884 propMap.insert(*it);
5885 ++it;
5886 }
5887
5888 alock.release();
5889
5890 /*
5891 * And build up the arrays for returning the property information.
5892 */
5893 size_t cEntries = propMap.size();
5894
5895 aNames.resize(cEntries);
5896 aValues.resize(cEntries);
5897 aTimestamps.resize(cEntries);
5898 aFlags.resize(cEntries);
5899
5900 char szFlags[MAX_FLAGS_LEN + 1];
5901 size_t i= 0;
5902 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5903 {
5904 aNames[i] = it->first;
5905 aValues[i] = it->second.strValue;
5906 aTimestamps[i] = it->second.mTimestamp;
5907 writeFlags(it->second.mFlags, szFlags);
5908 aFlags[i] = Utf8Str(szFlags);
5909 }
5910
5911 return S_OK;
5912}
5913
5914/**
5915 * Enumerate the properties managed by a VM.
5916 * @returns E_ACCESSDENIED if the VM process is not available or not
5917 * currently handling queries and the setting should then be done in
5918 * VBoxSVC.
5919 */
5920HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5921 std::vector<com::Utf8Str> &aNames,
5922 std::vector<com::Utf8Str> &aValues,
5923 std::vector<LONG64> &aTimestamps,
5924 std::vector<com::Utf8Str> &aFlags)
5925{
5926 HRESULT rc;
5927 ComPtr<IInternalSessionControl> directControl;
5928 {
5929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5930 if (mData->mSession.mLockType == LockType_VM)
5931 directControl = mData->mSession.mDirectControl;
5932 }
5933
5934 com::SafeArray<BSTR> bNames;
5935 com::SafeArray<BSTR> bValues;
5936 com::SafeArray<LONG64> bTimestamps;
5937 com::SafeArray<BSTR> bFlags;
5938
5939 if (!directControl)
5940 rc = E_ACCESSDENIED;
5941 else
5942 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5943 ComSafeArrayAsOutParam(bNames),
5944 ComSafeArrayAsOutParam(bValues),
5945 ComSafeArrayAsOutParam(bTimestamps),
5946 ComSafeArrayAsOutParam(bFlags));
5947 size_t i;
5948 aNames.resize(bNames.size());
5949 for (i = 0; i < bNames.size(); ++i)
5950 aNames[i] = Utf8Str(bNames[i]);
5951 aValues.resize(bValues.size());
5952 for (i = 0; i < bValues.size(); ++i)
5953 aValues[i] = Utf8Str(bValues[i]);
5954 aTimestamps.resize(bTimestamps.size());
5955 for (i = 0; i < bTimestamps.size(); ++i)
5956 aTimestamps[i] = bTimestamps[i];
5957 aFlags.resize(bFlags.size());
5958 for (i = 0; i < bFlags.size(); ++i)
5959 aFlags[i] = Utf8Str(bFlags[i]);
5960
5961 return rc;
5962}
5963#endif // VBOX_WITH_GUEST_PROPS
5964HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5965 std::vector<com::Utf8Str> &aNames,
5966 std::vector<com::Utf8Str> &aValues,
5967 std::vector<LONG64> &aTimestamps,
5968 std::vector<com::Utf8Str> &aFlags)
5969{
5970#ifndef VBOX_WITH_GUEST_PROPS
5971 ReturnComNotImplemented();
5972#else // VBOX_WITH_GUEST_PROPS
5973
5974 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5975
5976 if (rc == E_ACCESSDENIED)
5977 /* The VM is not running or the service is not (yet) accessible */
5978 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5979 return rc;
5980#endif // VBOX_WITH_GUEST_PROPS
5981}
5982
5983HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5984 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5985{
5986 MediaData::AttachmentList atts;
5987
5988 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5989 if (FAILED(rc)) return rc;
5990
5991 size_t i = 0;
5992 aMediumAttachments.resize(atts.size());
5993 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5994 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5995
5996 return S_OK;
5997}
5998
5999HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6000 LONG aControllerPort,
6001 LONG aDevice,
6002 ComPtr<IMediumAttachment> &aAttachment)
6003{
6004 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6005 aName.c_str(), aControllerPort, aDevice));
6006
6007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6008
6009 aAttachment = NULL;
6010
6011 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
6012 Bstr(aName).raw(),
6013 aControllerPort,
6014 aDevice);
6015 if (pAttach.isNull())
6016 return setError(VBOX_E_OBJECT_NOT_FOUND,
6017 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6018 aDevice, aControllerPort, aName.c_str());
6019
6020 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6021
6022 return S_OK;
6023}
6024
6025
6026HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6027 StorageBus_T aConnectionType,
6028 ComPtr<IStorageController> &aController)
6029{
6030 if ( (aConnectionType <= StorageBus_Null)
6031 || (aConnectionType > StorageBus_USB))
6032 return setError(E_INVALIDARG,
6033 tr("Invalid connection type: %d"),
6034 aConnectionType);
6035
6036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6037
6038 HRESULT rc = i_checkStateDependency(MutableStateDep);
6039 if (FAILED(rc)) return rc;
6040
6041 /* try to find one with the name first. */
6042 ComObjPtr<StorageController> ctrl;
6043
6044 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6045 if (SUCCEEDED(rc))
6046 return setError(VBOX_E_OBJECT_IN_USE,
6047 tr("Storage controller named '%s' already exists"),
6048 aName.c_str());
6049
6050 ctrl.createObject();
6051
6052 /* get a new instance number for the storage controller */
6053 ULONG ulInstance = 0;
6054 bool fBootable = true;
6055 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6056 it != mStorageControllers->end();
6057 ++it)
6058 {
6059 if ((*it)->i_getStorageBus() == aConnectionType)
6060 {
6061 ULONG ulCurInst = (*it)->i_getInstance();
6062
6063 if (ulCurInst >= ulInstance)
6064 ulInstance = ulCurInst + 1;
6065
6066 /* Only one controller of each type can be marked as bootable. */
6067 if ((*it)->i_getBootable())
6068 fBootable = false;
6069 }
6070 }
6071
6072 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6073 if (FAILED(rc)) return rc;
6074
6075 i_setModified(IsModified_Storage);
6076 mStorageControllers.backup();
6077 mStorageControllers->push_back(ctrl);
6078
6079 ctrl.queryInterfaceTo(aController.asOutParam());
6080
6081 /* inform the direct session if any */
6082 alock.release();
6083 i_onStorageControllerChange();
6084
6085 return S_OK;
6086}
6087
6088HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6089 ComPtr<IStorageController> &aStorageController)
6090{
6091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6092
6093 ComObjPtr<StorageController> ctrl;
6094
6095 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6096 if (SUCCEEDED(rc))
6097 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6098
6099 return rc;
6100}
6101
6102HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6103 ComPtr<IStorageController> &aStorageController)
6104{
6105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6106
6107 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6108 it != mStorageControllers->end();
6109 ++it)
6110 {
6111 if ((*it)->i_getInstance() == aInstance)
6112 {
6113 (*it).queryInterfaceTo(aStorageController.asOutParam());
6114 return S_OK;
6115 }
6116 }
6117
6118 return setError(VBOX_E_OBJECT_NOT_FOUND,
6119 tr("Could not find a storage controller with instance number '%lu'"),
6120 aInstance);
6121}
6122
6123HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6124{
6125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6126
6127 HRESULT rc = i_checkStateDependency(MutableStateDep);
6128 if (FAILED(rc)) return rc;
6129
6130 ComObjPtr<StorageController> ctrl;
6131
6132 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6133 if (SUCCEEDED(rc))
6134 {
6135 /* Ensure that only one controller of each type is marked as bootable. */
6136 if (aBootable == TRUE)
6137 {
6138 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6139 it != mStorageControllers->end();
6140 ++it)
6141 {
6142 ComObjPtr<StorageController> aCtrl = (*it);
6143
6144 if ( (aCtrl->i_getName() != aName)
6145 && aCtrl->i_getBootable() == TRUE
6146 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6147 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6148 {
6149 aCtrl->i_setBootable(FALSE);
6150 break;
6151 }
6152 }
6153 }
6154
6155 if (SUCCEEDED(rc))
6156 {
6157 ctrl->i_setBootable(aBootable);
6158 i_setModified(IsModified_Storage);
6159 }
6160 }
6161
6162 if (SUCCEEDED(rc))
6163 {
6164 /* inform the direct session if any */
6165 alock.release();
6166 i_onStorageControllerChange();
6167 }
6168
6169 return rc;
6170}
6171
6172HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6173{
6174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6175
6176 HRESULT rc = i_checkStateDependency(MutableStateDep);
6177 if (FAILED(rc)) return rc;
6178
6179 ComObjPtr<StorageController> ctrl;
6180 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6181 if (FAILED(rc)) return rc;
6182
6183 {
6184 /* find all attached devices to the appropriate storage controller and detach them all */
6185 // make a temporary list because detachDevice invalidates iterators into
6186 // mMediaData->mAttachments
6187 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6188
6189 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6190 it != llAttachments2.end();
6191 ++it)
6192 {
6193 MediumAttachment *pAttachTemp = *it;
6194
6195 AutoCaller localAutoCaller(pAttachTemp);
6196 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6197
6198 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6199
6200 if (pAttachTemp->i_getControllerName() == aName)
6201 {
6202 rc = i_detachDevice(pAttachTemp, alock, NULL);
6203 if (FAILED(rc)) return rc;
6204 }
6205 }
6206 }
6207
6208 /* We can remove it now. */
6209 i_setModified(IsModified_Storage);
6210 mStorageControllers.backup();
6211
6212 ctrl->i_unshare();
6213
6214 mStorageControllers->remove(ctrl);
6215
6216 /* inform the direct session if any */
6217 alock.release();
6218 i_onStorageControllerChange();
6219
6220 return S_OK;
6221}
6222
6223HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6224 ComPtr<IUSBController> &aController)
6225{
6226 if ( (aType <= USBControllerType_Null)
6227 || (aType >= USBControllerType_Last))
6228 return setError(E_INVALIDARG,
6229 tr("Invalid USB controller type: %d"),
6230 aType);
6231
6232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6233
6234 HRESULT rc = i_checkStateDependency(MutableStateDep);
6235 if (FAILED(rc)) return rc;
6236
6237 /* try to find one with the same type first. */
6238 ComObjPtr<USBController> ctrl;
6239
6240 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6241 if (SUCCEEDED(rc))
6242 return setError(VBOX_E_OBJECT_IN_USE,
6243 tr("USB controller named '%s' already exists"),
6244 aName.c_str());
6245
6246 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6247 ULONG maxInstances;
6248 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6249 if (FAILED(rc))
6250 return rc;
6251
6252 ULONG cInstances = i_getUSBControllerCountByType(aType);
6253 if (cInstances >= maxInstances)
6254 return setError(E_INVALIDARG,
6255 tr("Too many USB controllers of this type"));
6256
6257 ctrl.createObject();
6258
6259 rc = ctrl->init(this, aName, aType);
6260 if (FAILED(rc)) return rc;
6261
6262 i_setModified(IsModified_USB);
6263 mUSBControllers.backup();
6264 mUSBControllers->push_back(ctrl);
6265
6266 ctrl.queryInterfaceTo(aController.asOutParam());
6267
6268 /* inform the direct session if any */
6269 alock.release();
6270 i_onUSBControllerChange();
6271
6272 return S_OK;
6273}
6274
6275HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6276{
6277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6278
6279 ComObjPtr<USBController> ctrl;
6280
6281 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6282 if (SUCCEEDED(rc))
6283 ctrl.queryInterfaceTo(aController.asOutParam());
6284
6285 return rc;
6286}
6287
6288HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6289 ULONG *aControllers)
6290{
6291 if ( (aType <= USBControllerType_Null)
6292 || (aType >= USBControllerType_Last))
6293 return setError(E_INVALIDARG,
6294 tr("Invalid USB controller type: %d"),
6295 aType);
6296
6297 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6298
6299 ComObjPtr<USBController> ctrl;
6300
6301 *aControllers = i_getUSBControllerCountByType(aType);
6302
6303 return S_OK;
6304}
6305
6306HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6307{
6308
6309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6310
6311 HRESULT rc = i_checkStateDependency(MutableStateDep);
6312 if (FAILED(rc)) return rc;
6313
6314 ComObjPtr<USBController> ctrl;
6315 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6316 if (FAILED(rc)) return rc;
6317
6318 i_setModified(IsModified_USB);
6319 mUSBControllers.backup();
6320
6321 ctrl->i_unshare();
6322
6323 mUSBControllers->remove(ctrl);
6324
6325 /* inform the direct session if any */
6326 alock.release();
6327 i_onUSBControllerChange();
6328
6329 return S_OK;
6330}
6331
6332HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6333 ULONG *aOriginX,
6334 ULONG *aOriginY,
6335 ULONG *aWidth,
6336 ULONG *aHeight,
6337 BOOL *aEnabled)
6338{
6339 uint32_t u32OriginX= 0;
6340 uint32_t u32OriginY= 0;
6341 uint32_t u32Width = 0;
6342 uint32_t u32Height = 0;
6343 uint16_t u16Flags = 0;
6344
6345 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6346 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6347 if (RT_FAILURE(vrc))
6348 {
6349#ifdef RT_OS_WINDOWS
6350 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6351 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6352 * So just assign fEnable to TRUE again.
6353 * The right fix would be to change GUI API wrappers to make sure that parameters
6354 * are changed only if API succeeds.
6355 */
6356 *aEnabled = TRUE;
6357#endif
6358 return setError(VBOX_E_IPRT_ERROR,
6359 tr("Saved guest size is not available (%Rrc)"),
6360 vrc);
6361 }
6362
6363 *aOriginX = u32OriginX;
6364 *aOriginY = u32OriginY;
6365 *aWidth = u32Width;
6366 *aHeight = u32Height;
6367 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6368
6369 return S_OK;
6370}
6371
6372HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6373 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6374{
6375 if (aScreenId != 0)
6376 return E_NOTIMPL;
6377
6378 if ( aBitmapFormat != BitmapFormat_BGR0
6379 && aBitmapFormat != BitmapFormat_BGRA
6380 && aBitmapFormat != BitmapFormat_RGBA
6381 && aBitmapFormat != BitmapFormat_PNG)
6382 return setError(E_NOTIMPL,
6383 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6384
6385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6386
6387 uint8_t *pu8Data = NULL;
6388 uint32_t cbData = 0;
6389 uint32_t u32Width = 0;
6390 uint32_t u32Height = 0;
6391
6392 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6393
6394 if (RT_FAILURE(vrc))
6395 return setError(VBOX_E_IPRT_ERROR,
6396 tr("Saved thumbnail data is not available (%Rrc)"),
6397 vrc);
6398
6399 HRESULT hr = S_OK;
6400
6401 *aWidth = u32Width;
6402 *aHeight = u32Height;
6403
6404 if (cbData > 0)
6405 {
6406 /* Convert pixels to the format expected by the API caller. */
6407 if (aBitmapFormat == BitmapFormat_BGR0)
6408 {
6409 /* [0] B, [1] G, [2] R, [3] 0. */
6410 aData.resize(cbData);
6411 memcpy(&aData.front(), pu8Data, cbData);
6412 }
6413 else if (aBitmapFormat == BitmapFormat_BGRA)
6414 {
6415 /* [0] B, [1] G, [2] R, [3] A. */
6416 aData.resize(cbData);
6417 for (uint32_t i = 0; i < cbData; i += 4)
6418 {
6419 aData[i] = pu8Data[i];
6420 aData[i + 1] = pu8Data[i + 1];
6421 aData[i + 2] = pu8Data[i + 2];
6422 aData[i + 3] = 0xff;
6423 }
6424 }
6425 else if (aBitmapFormat == BitmapFormat_RGBA)
6426 {
6427 /* [0] R, [1] G, [2] B, [3] A. */
6428 aData.resize(cbData);
6429 for (uint32_t i = 0; i < cbData; i += 4)
6430 {
6431 aData[i] = pu8Data[i + 2];
6432 aData[i + 1] = pu8Data[i + 1];
6433 aData[i + 2] = pu8Data[i];
6434 aData[i + 3] = 0xff;
6435 }
6436 }
6437 else if (aBitmapFormat == BitmapFormat_PNG)
6438 {
6439 uint8_t *pu8PNG = NULL;
6440 uint32_t cbPNG = 0;
6441 uint32_t cxPNG = 0;
6442 uint32_t cyPNG = 0;
6443
6444 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6445
6446 if (RT_SUCCESS(vrc))
6447 {
6448 aData.resize(cbPNG);
6449 if (cbPNG)
6450 memcpy(&aData.front(), pu8PNG, cbPNG);
6451 }
6452 else
6453 hr = setError(VBOX_E_IPRT_ERROR,
6454 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6455 vrc);
6456
6457 RTMemFree(pu8PNG);
6458 }
6459 }
6460
6461 freeSavedDisplayScreenshot(pu8Data);
6462
6463 return hr;
6464}
6465
6466HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6467 ULONG *aWidth,
6468 ULONG *aHeight,
6469 std::vector<BitmapFormat_T> &aBitmapFormats)
6470{
6471 if (aScreenId != 0)
6472 return E_NOTIMPL;
6473
6474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6475
6476 uint8_t *pu8Data = NULL;
6477 uint32_t cbData = 0;
6478 uint32_t u32Width = 0;
6479 uint32_t u32Height = 0;
6480
6481 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6482
6483 if (RT_FAILURE(vrc))
6484 return setError(VBOX_E_IPRT_ERROR,
6485 tr("Saved screenshot data is not available (%Rrc)"),
6486 vrc);
6487
6488 *aWidth = u32Width;
6489 *aHeight = u32Height;
6490 aBitmapFormats.resize(1);
6491 aBitmapFormats[0] = BitmapFormat_PNG;
6492
6493 freeSavedDisplayScreenshot(pu8Data);
6494
6495 return S_OK;
6496}
6497
6498HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6499 BitmapFormat_T aBitmapFormat,
6500 ULONG *aWidth,
6501 ULONG *aHeight,
6502 std::vector<BYTE> &aData)
6503{
6504 if (aScreenId != 0)
6505 return E_NOTIMPL;
6506
6507 if (aBitmapFormat != BitmapFormat_PNG)
6508 return E_NOTIMPL;
6509
6510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6511
6512 uint8_t *pu8Data = NULL;
6513 uint32_t cbData = 0;
6514 uint32_t u32Width = 0;
6515 uint32_t u32Height = 0;
6516
6517 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6518
6519 if (RT_FAILURE(vrc))
6520 return setError(VBOX_E_IPRT_ERROR,
6521 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6522 vrc);
6523
6524 *aWidth = u32Width;
6525 *aHeight = u32Height;
6526
6527 aData.resize(cbData);
6528 if (cbData)
6529 memcpy(&aData.front(), pu8Data, cbData);
6530
6531 freeSavedDisplayScreenshot(pu8Data);
6532
6533 return S_OK;
6534}
6535
6536HRESULT Machine::hotPlugCPU(ULONG aCpu)
6537{
6538 HRESULT rc = S_OK;
6539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6540
6541 if (!mHWData->mCPUHotPlugEnabled)
6542 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6543
6544 if (aCpu >= mHWData->mCPUCount)
6545 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6546
6547 if (mHWData->mCPUAttached[aCpu])
6548 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6549
6550 alock.release();
6551 rc = i_onCPUChange(aCpu, false);
6552 alock.acquire();
6553 if (FAILED(rc)) return rc;
6554
6555 i_setModified(IsModified_MachineData);
6556 mHWData.backup();
6557 mHWData->mCPUAttached[aCpu] = true;
6558
6559 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6560 if (Global::IsOnline(mData->mMachineState))
6561 i_saveSettings(NULL);
6562
6563 return S_OK;
6564}
6565
6566HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6567{
6568 HRESULT rc = S_OK;
6569
6570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6571
6572 if (!mHWData->mCPUHotPlugEnabled)
6573 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6574
6575 if (aCpu >= SchemaDefs::MaxCPUCount)
6576 return setError(E_INVALIDARG,
6577 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6578 SchemaDefs::MaxCPUCount);
6579
6580 if (!mHWData->mCPUAttached[aCpu])
6581 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6582
6583 /* CPU 0 can't be detached */
6584 if (aCpu == 0)
6585 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6586
6587 alock.release();
6588 rc = i_onCPUChange(aCpu, true);
6589 alock.acquire();
6590 if (FAILED(rc)) return rc;
6591
6592 i_setModified(IsModified_MachineData);
6593 mHWData.backup();
6594 mHWData->mCPUAttached[aCpu] = false;
6595
6596 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6597 if (Global::IsOnline(mData->mMachineState))
6598 i_saveSettings(NULL);
6599
6600 return S_OK;
6601}
6602
6603HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6604{
6605 *aAttached = false;
6606
6607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6608
6609 /* If hotplug is enabled the CPU is always enabled. */
6610 if (!mHWData->mCPUHotPlugEnabled)
6611 {
6612 if (aCpu < mHWData->mCPUCount)
6613 *aAttached = true;
6614 }
6615 else
6616 {
6617 if (aCpu < SchemaDefs::MaxCPUCount)
6618 *aAttached = mHWData->mCPUAttached[aCpu];
6619 }
6620
6621 return S_OK;
6622}
6623
6624HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6625{
6626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6627
6628 Utf8Str log = i_queryLogFilename(aIdx);
6629 if (!RTFileExists(log.c_str()))
6630 log.setNull();
6631 aFilename = log;
6632
6633 return S_OK;
6634}
6635
6636HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6637{
6638 if (aSize < 0)
6639 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6640
6641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6642
6643 HRESULT rc = S_OK;
6644 Utf8Str log = i_queryLogFilename(aIdx);
6645
6646 /* do not unnecessarily hold the lock while doing something which does
6647 * not need the lock and potentially takes a long time. */
6648 alock.release();
6649
6650 /* Limit the chunk size to 32K for now, as that gives better performance
6651 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6652 * One byte expands to approx. 25 bytes of breathtaking XML. */
6653 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6654 aData.resize(cbData);
6655
6656 RTFILE LogFile;
6657 int vrc = RTFileOpen(&LogFile, log.c_str(),
6658 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6659 if (RT_SUCCESS(vrc))
6660 {
6661 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6662 if (RT_SUCCESS(vrc))
6663 aData.resize(cbData);
6664 else
6665 rc = setError(VBOX_E_IPRT_ERROR,
6666 tr("Could not read log file '%s' (%Rrc)"),
6667 log.c_str(), vrc);
6668 RTFileClose(LogFile);
6669 }
6670 else
6671 rc = setError(VBOX_E_IPRT_ERROR,
6672 tr("Could not open log file '%s' (%Rrc)"),
6673 log.c_str(), vrc);
6674
6675 if (FAILED(rc))
6676 aData.resize(0);
6677
6678 return rc;
6679}
6680
6681
6682/**
6683 * Currently this method doesn't attach device to the running VM,
6684 * just makes sure it's plugged on next VM start.
6685 */
6686HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6687{
6688 // lock scope
6689 {
6690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6691
6692 HRESULT rc = i_checkStateDependency(MutableStateDep);
6693 if (FAILED(rc)) return rc;
6694
6695 ChipsetType_T aChipset = ChipsetType_PIIX3;
6696 COMGETTER(ChipsetType)(&aChipset);
6697
6698 if (aChipset != ChipsetType_ICH9)
6699 {
6700 return setError(E_INVALIDARG,
6701 tr("Host PCI attachment only supported with ICH9 chipset"));
6702 }
6703
6704 // check if device with this host PCI address already attached
6705 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6706 it != mHWData->mPCIDeviceAssignments.end();
6707 ++it)
6708 {
6709 LONG iHostAddress = -1;
6710 ComPtr<PCIDeviceAttachment> pAttach;
6711 pAttach = *it;
6712 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6713 if (iHostAddress == aHostAddress)
6714 return setError(E_INVALIDARG,
6715 tr("Device with host PCI address already attached to this VM"));
6716 }
6717
6718 ComObjPtr<PCIDeviceAttachment> pda;
6719 char name[32];
6720
6721 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6722 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6723 Bstr bname(name);
6724 pda.createObject();
6725 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6726 i_setModified(IsModified_MachineData);
6727 mHWData.backup();
6728 mHWData->mPCIDeviceAssignments.push_back(pda);
6729 }
6730
6731 return S_OK;
6732}
6733
6734/**
6735 * Currently this method doesn't detach device from the running VM,
6736 * just makes sure it's not plugged on next VM start.
6737 */
6738HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6739{
6740 ComObjPtr<PCIDeviceAttachment> pAttach;
6741 bool fRemoved = false;
6742 HRESULT rc;
6743
6744 // lock scope
6745 {
6746 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6747
6748 rc = i_checkStateDependency(MutableStateDep);
6749 if (FAILED(rc)) return rc;
6750
6751 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6752 it != mHWData->mPCIDeviceAssignments.end();
6753 ++it)
6754 {
6755 LONG iHostAddress = -1;
6756 pAttach = *it;
6757 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6758 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6759 {
6760 i_setModified(IsModified_MachineData);
6761 mHWData.backup();
6762 mHWData->mPCIDeviceAssignments.remove(pAttach);
6763 fRemoved = true;
6764 break;
6765 }
6766 }
6767 }
6768
6769
6770 /* Fire event outside of the lock */
6771 if (fRemoved)
6772 {
6773 Assert(!pAttach.isNull());
6774 ComPtr<IEventSource> es;
6775 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6776 Assert(SUCCEEDED(rc));
6777 Bstr mid;
6778 rc = this->COMGETTER(Id)(mid.asOutParam());
6779 Assert(SUCCEEDED(rc));
6780 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6781 }
6782
6783 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6784 tr("No host PCI device %08x attached"),
6785 aHostAddress
6786 );
6787}
6788
6789HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6790{
6791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6792
6793 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6794
6795 size_t i = 0;
6796 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6797 it != mHWData->mPCIDeviceAssignments.end();
6798 ++i, ++it)
6799 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6800
6801 return S_OK;
6802}
6803
6804HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6805{
6806 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6807
6808 return S_OK;
6809}
6810
6811HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6812{
6813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6814
6815 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6816
6817 return S_OK;
6818}
6819
6820HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6821{
6822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6823 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6824 if (SUCCEEDED(hrc))
6825 {
6826 hrc = mHWData.backupEx();
6827 if (SUCCEEDED(hrc))
6828 {
6829 i_setModified(IsModified_MachineData);
6830 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6831 }
6832 }
6833 return hrc;
6834}
6835
6836HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6837{
6838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6839 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6840 return S_OK;
6841}
6842
6843HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6844{
6845 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6846 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6847 if (SUCCEEDED(hrc))
6848 {
6849 hrc = mHWData.backupEx();
6850 if (SUCCEEDED(hrc))
6851 {
6852 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6853 if (SUCCEEDED(hrc))
6854 i_setModified(IsModified_MachineData);
6855 }
6856 }
6857 return hrc;
6858}
6859
6860HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6861{
6862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6863
6864 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6865
6866 return S_OK;
6867}
6868
6869HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6870{
6871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6872 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6873 if (SUCCEEDED(hrc))
6874 {
6875 hrc = mHWData.backupEx();
6876 if (SUCCEEDED(hrc))
6877 {
6878 i_setModified(IsModified_MachineData);
6879 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6880 }
6881 }
6882 return hrc;
6883}
6884
6885HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6886{
6887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6888
6889 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6890
6891 return S_OK;
6892}
6893
6894HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6895{
6896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6897
6898 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6899 if ( SUCCEEDED(hrc)
6900 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6901 {
6902 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6903 int vrc;
6904
6905 if (aAutostartEnabled)
6906 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6907 else
6908 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6909
6910 if (RT_SUCCESS(vrc))
6911 {
6912 hrc = mHWData.backupEx();
6913 if (SUCCEEDED(hrc))
6914 {
6915 i_setModified(IsModified_MachineData);
6916 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6917 }
6918 }
6919 else if (vrc == VERR_NOT_SUPPORTED)
6920 hrc = setError(VBOX_E_NOT_SUPPORTED,
6921 tr("The VM autostart feature is not supported on this platform"));
6922 else if (vrc == VERR_PATH_NOT_FOUND)
6923 hrc = setError(E_FAIL,
6924 tr("The path to the autostart database is not set"));
6925 else
6926 hrc = setError(E_UNEXPECTED,
6927 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6928 aAutostartEnabled ? "Adding" : "Removing",
6929 mUserData->s.strName.c_str(), vrc);
6930 }
6931 return hrc;
6932}
6933
6934HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6935{
6936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6937
6938 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6939
6940 return S_OK;
6941}
6942
6943HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6944{
6945 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6946 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6947 if (SUCCEEDED(hrc))
6948 {
6949 hrc = mHWData.backupEx();
6950 if (SUCCEEDED(hrc))
6951 {
6952 i_setModified(IsModified_MachineData);
6953 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6954 }
6955 }
6956 return hrc;
6957}
6958
6959HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6960{
6961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6962
6963 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6964
6965 return S_OK;
6966}
6967
6968HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6969{
6970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6971 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6972 if ( SUCCEEDED(hrc)
6973 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6974 {
6975 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6976 int vrc;
6977
6978 if (aAutostopType != AutostopType_Disabled)
6979 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6980 else
6981 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6982
6983 if (RT_SUCCESS(vrc))
6984 {
6985 hrc = mHWData.backupEx();
6986 if (SUCCEEDED(hrc))
6987 {
6988 i_setModified(IsModified_MachineData);
6989 mHWData->mAutostart.enmAutostopType = aAutostopType;
6990 }
6991 }
6992 else if (vrc == VERR_NOT_SUPPORTED)
6993 hrc = setError(VBOX_E_NOT_SUPPORTED,
6994 tr("The VM autostop feature is not supported on this platform"));
6995 else if (vrc == VERR_PATH_NOT_FOUND)
6996 hrc = setError(E_FAIL,
6997 tr("The path to the autostart database is not set"));
6998 else
6999 hrc = setError(E_UNEXPECTED,
7000 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7001 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7002 mUserData->s.strName.c_str(), vrc);
7003 }
7004 return hrc;
7005}
7006
7007HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7008{
7009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7010
7011 aDefaultFrontend = mHWData->mDefaultFrontend;
7012
7013 return S_OK;
7014}
7015
7016HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7017{
7018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7019 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7020 if (SUCCEEDED(hrc))
7021 {
7022 hrc = mHWData.backupEx();
7023 if (SUCCEEDED(hrc))
7024 {
7025 i_setModified(IsModified_MachineData);
7026 mHWData->mDefaultFrontend = aDefaultFrontend;
7027 }
7028 }
7029 return hrc;
7030}
7031
7032HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7033{
7034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7035 size_t cbIcon = mUserData->mIcon.size();
7036 aIcon.resize(cbIcon);
7037 if (cbIcon)
7038 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7039 return S_OK;
7040}
7041
7042HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7043{
7044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7045 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7046 if (SUCCEEDED(hrc))
7047 {
7048 i_setModified(IsModified_MachineData);
7049 mUserData.backup();
7050 size_t cbIcon = aIcon.size();
7051 mUserData->mIcon.resize(cbIcon);
7052 if (cbIcon)
7053 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7054 }
7055 return hrc;
7056}
7057
7058HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7059{
7060#ifdef VBOX_WITH_USB
7061 *aUSBProxyAvailable = true;
7062#else
7063 *aUSBProxyAvailable = false;
7064#endif
7065 return S_OK;
7066}
7067
7068HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7069 ComPtr<IProgress> &aProgress)
7070{
7071 ComObjPtr<Progress> pP;
7072 Progress *ppP = pP;
7073 IProgress *iP = static_cast<IProgress *>(ppP);
7074 IProgress **pProgress = &iP;
7075
7076 IMachine *pTarget = aTarget;
7077
7078 /* Convert the options. */
7079 RTCList<CloneOptions_T> optList;
7080 if (aOptions.size())
7081 for (size_t i = 0; i < aOptions.size(); ++i)
7082 optList.append(aOptions[i]);
7083
7084 if (optList.contains(CloneOptions_Link))
7085 {
7086 if (!i_isSnapshotMachine())
7087 return setError(E_INVALIDARG,
7088 tr("Linked clone can only be created from a snapshot"));
7089 if (aMode != CloneMode_MachineState)
7090 return setError(E_INVALIDARG,
7091 tr("Linked clone can only be created for a single machine state"));
7092 }
7093 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7094
7095 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7096
7097 HRESULT rc = pWorker->start(pProgress);
7098
7099 pP = static_cast<Progress *>(*pProgress);
7100 pP.queryInterfaceTo(aProgress.asOutParam());
7101
7102 return rc;
7103
7104}
7105
7106HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7107{
7108 NOREF(aProgress);
7109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7110
7111 // This check should always fail.
7112 HRESULT rc = i_checkStateDependency(MutableStateDep);
7113 if (FAILED(rc)) return rc;
7114
7115 AssertFailedReturn(E_NOTIMPL);
7116}
7117
7118HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7119{
7120 NOREF(aSavedStateFile);
7121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7122
7123 // This check should always fail.
7124 HRESULT rc = i_checkStateDependency(MutableStateDep);
7125 if (FAILED(rc)) return rc;
7126
7127 AssertFailedReturn(E_NOTIMPL);
7128}
7129
7130HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7131{
7132 NOREF(aFRemoveFile);
7133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7134
7135 // This check should always fail.
7136 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7137 if (FAILED(rc)) return rc;
7138
7139 AssertFailedReturn(E_NOTIMPL);
7140}
7141
7142// public methods for internal purposes
7143/////////////////////////////////////////////////////////////////////////////
7144
7145/**
7146 * Adds the given IsModified_* flag to the dirty flags of the machine.
7147 * This must be called either during i_loadSettings or under the machine write lock.
7148 * @param fl
7149 */
7150void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7151{
7152 mData->flModifications |= fl;
7153 if (fAllowStateModification && i_isStateModificationAllowed())
7154 mData->mCurrentStateModified = true;
7155}
7156
7157/**
7158 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7159 * care of the write locking.
7160 *
7161 * @param fModifications The flag to add.
7162 */
7163void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7164{
7165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7166 i_setModified(fModification, fAllowStateModification);
7167}
7168
7169/**
7170 * Saves the registry entry of this machine to the given configuration node.
7171 *
7172 * @param aEntryNode Node to save the registry entry to.
7173 *
7174 * @note locks this object for reading.
7175 */
7176HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7177{
7178 AutoLimitedCaller autoCaller(this);
7179 AssertComRCReturnRC(autoCaller.rc());
7180
7181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7182
7183 data.uuid = mData->mUuid;
7184 data.strSettingsFile = mData->m_strConfigFile;
7185
7186 return S_OK;
7187}
7188
7189/**
7190 * Calculates the absolute path of the given path taking the directory of the
7191 * machine settings file as the current directory.
7192 *
7193 * @param aPath Path to calculate the absolute path for.
7194 * @param aResult Where to put the result (used only on success, can be the
7195 * same Utf8Str instance as passed in @a aPath).
7196 * @return IPRT result.
7197 *
7198 * @note Locks this object for reading.
7199 */
7200int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7201{
7202 AutoCaller autoCaller(this);
7203 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7204
7205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7206
7207 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7208
7209 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7210
7211 strSettingsDir.stripFilename();
7212 char folder[RTPATH_MAX];
7213 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7214 if (RT_SUCCESS(vrc))
7215 aResult = folder;
7216
7217 return vrc;
7218}
7219
7220/**
7221 * Copies strSource to strTarget, making it relative to the machine folder
7222 * if it is a subdirectory thereof, or simply copying it otherwise.
7223 *
7224 * @param strSource Path to evaluate and copy.
7225 * @param strTarget Buffer to receive target path.
7226 *
7227 * @note Locks this object for reading.
7228 */
7229void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7230 Utf8Str &strTarget)
7231{
7232 AutoCaller autoCaller(this);
7233 AssertComRCReturn(autoCaller.rc(), (void)0);
7234
7235 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7236
7237 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7238 // use strTarget as a temporary buffer to hold the machine settings dir
7239 strTarget = mData->m_strConfigFileFull;
7240 strTarget.stripFilename();
7241 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7242 {
7243 // is relative: then append what's left
7244 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7245 // for empty paths (only possible for subdirs) use "." to avoid
7246 // triggering default settings for not present config attributes.
7247 if (strTarget.isEmpty())
7248 strTarget = ".";
7249 }
7250 else
7251 // is not relative: then overwrite
7252 strTarget = strSource;
7253}
7254
7255/**
7256 * Returns the full path to the machine's log folder in the
7257 * \a aLogFolder argument.
7258 */
7259void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7260{
7261 AutoCaller autoCaller(this);
7262 AssertComRCReturnVoid(autoCaller.rc());
7263
7264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7265
7266 char szTmp[RTPATH_MAX];
7267 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7268 if (RT_SUCCESS(vrc))
7269 {
7270 if (szTmp[0] && !mUserData.isNull())
7271 {
7272 char szTmp2[RTPATH_MAX];
7273 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7274 if (RT_SUCCESS(vrc))
7275 aLogFolder = BstrFmt("%s%c%s",
7276 szTmp2,
7277 RTPATH_DELIMITER,
7278 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7279 }
7280 else
7281 vrc = VERR_PATH_IS_RELATIVE;
7282 }
7283
7284 if (RT_FAILURE(vrc))
7285 {
7286 // fallback if VBOX_USER_LOGHOME is not set or invalid
7287 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7288 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7289 aLogFolder.append(RTPATH_DELIMITER);
7290 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7291 }
7292}
7293
7294/**
7295 * Returns the full path to the machine's log file for an given index.
7296 */
7297Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7298 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7299{
7300 Utf8Str logFolder;
7301 getLogFolder(logFolder);
7302 Assert(logFolder.length());
7303 Utf8Str log;
7304 if (idx == 0)
7305 log = Utf8StrFmt("%s%cVBox.log",
7306 logFolder.c_str(), RTPATH_DELIMITER);
7307 else
7308 log = Utf8StrFmt("%s%cVBox.log.%d",
7309 logFolder.c_str(), RTPATH_DELIMITER, idx);
7310 return log;
7311}
7312
7313/**
7314 * Returns the full path to the machine's (hardened) startup log file.
7315 */
7316Utf8Str Machine::i_getStartupLogFilename(void)
7317{
7318 Utf8Str strFilename;
7319 getLogFolder(strFilename);
7320 Assert(strFilename.length());
7321 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7322 return strFilename;
7323}
7324
7325
7326/**
7327 * Composes a unique saved state filename based on the current system time. The filename is
7328 * granular to the second so this will work so long as no more than one snapshot is taken on
7329 * a machine per second.
7330 *
7331 * Before version 4.1, we used this formula for saved state files:
7332 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7333 * which no longer works because saved state files can now be shared between the saved state of the
7334 * "saved" machine and an online snapshot, and the following would cause problems:
7335 * 1) save machine
7336 * 2) create online snapshot from that machine state --> reusing saved state file
7337 * 3) save machine again --> filename would be reused, breaking the online snapshot
7338 *
7339 * So instead we now use a timestamp.
7340 *
7341 * @param str
7342 */
7343
7344void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7345{
7346 AutoCaller autoCaller(this);
7347 AssertComRCReturnVoid(autoCaller.rc());
7348
7349 {
7350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7351 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7352 }
7353
7354 RTTIMESPEC ts;
7355 RTTimeNow(&ts);
7356 RTTIME time;
7357 RTTimeExplode(&time, &ts);
7358
7359 strStateFilePath += RTPATH_DELIMITER;
7360 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7361 time.i32Year, time.u8Month, time.u8MonthDay,
7362 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7363}
7364
7365/**
7366 * Returns the full path to the default video capture file.
7367 */
7368void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7369{
7370 AutoCaller autoCaller(this);
7371 AssertComRCReturnVoid(autoCaller.rc());
7372
7373 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7374
7375 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7376 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7377 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7378}
7379
7380/**
7381 * Returns whether at least one USB controller is present for the VM.
7382 */
7383bool Machine::i_isUSBControllerPresent()
7384{
7385 AutoCaller autoCaller(this);
7386 AssertComRCReturn(autoCaller.rc(), false);
7387
7388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7389
7390 return (mUSBControllers->size() > 0);
7391}
7392
7393/**
7394 * @note Locks this object for writing, calls the client process
7395 * (inside the lock).
7396 */
7397HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7398 const Utf8Str &strFrontend,
7399 const Utf8Str &strEnvironment,
7400 ProgressProxy *aProgress)
7401{
7402 LogFlowThisFuncEnter();
7403
7404 AssertReturn(aControl, E_FAIL);
7405 AssertReturn(aProgress, E_FAIL);
7406 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7407
7408 AutoCaller autoCaller(this);
7409 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7410
7411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7412
7413 if (!mData->mRegistered)
7414 return setError(E_UNEXPECTED,
7415 tr("The machine '%s' is not registered"),
7416 mUserData->s.strName.c_str());
7417
7418 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7419
7420 /* The process started when launching a VM with separate UI/VM processes is always
7421 * the UI process, i.e. needs special handling as it won't claim the session. */
7422 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7423
7424 if (fSeparate)
7425 {
7426 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName == "headless")
7427 return setError(VBOX_E_INVALID_OBJECT_STATE,
7428 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7429 mUserData->s.strName.c_str());
7430 }
7431 else
7432 {
7433 if ( mData->mSession.mState == SessionState_Locked
7434 || mData->mSession.mState == SessionState_Spawning
7435 || mData->mSession.mState == SessionState_Unlocking)
7436 return setError(VBOX_E_INVALID_OBJECT_STATE,
7437 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7438 mUserData->s.strName.c_str());
7439
7440 /* may not be busy */
7441 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7442 }
7443
7444 /* get the path to the executable */
7445 char szPath[RTPATH_MAX];
7446 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7447 size_t cchBufLeft = strlen(szPath);
7448 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7449 szPath[cchBufLeft] = 0;
7450 char *pszNamePart = szPath + cchBufLeft;
7451 cchBufLeft = sizeof(szPath) - cchBufLeft;
7452
7453 int vrc = VINF_SUCCESS;
7454 RTPROCESS pid = NIL_RTPROCESS;
7455
7456 RTENV env = RTENV_DEFAULT;
7457
7458 if (!strEnvironment.isEmpty())
7459 {
7460 char *newEnvStr = NULL;
7461
7462 do
7463 {
7464 /* clone the current environment */
7465 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7466 AssertRCBreakStmt(vrc2, vrc = vrc2);
7467
7468 newEnvStr = RTStrDup(strEnvironment.c_str());
7469 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7470
7471 /* put new variables to the environment
7472 * (ignore empty variable names here since RTEnv API
7473 * intentionally doesn't do that) */
7474 char *var = newEnvStr;
7475 for (char *p = newEnvStr; *p; ++p)
7476 {
7477 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7478 {
7479 *p = '\0';
7480 if (*var)
7481 {
7482 char *val = strchr(var, '=');
7483 if (val)
7484 {
7485 *val++ = '\0';
7486 vrc2 = RTEnvSetEx(env, var, val);
7487 }
7488 else
7489 vrc2 = RTEnvUnsetEx(env, var);
7490 if (RT_FAILURE(vrc2))
7491 break;
7492 }
7493 var = p + 1;
7494 }
7495 }
7496 if (RT_SUCCESS(vrc2) && *var)
7497 vrc2 = RTEnvPutEx(env, var);
7498
7499 AssertRCBreakStmt(vrc2, vrc = vrc2);
7500 }
7501 while (0);
7502
7503 if (newEnvStr != NULL)
7504 RTStrFree(newEnvStr);
7505 }
7506
7507 /* Hardened startup logging */
7508#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7509 Utf8Str strSupStartLogArg("--sup-startup-log=");
7510 {
7511 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7512 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7513 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7514 {
7515 Utf8Str strStartupLogDir = strStartupLogFile;
7516 strStartupLogDir.stripFilename();
7517 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7518 file without stripping the file. */
7519 }
7520 strSupStartLogArg.append(strStartupLogFile);
7521 }
7522 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7523#else
7524 const char *pszSupStartupLogArg = NULL;
7525#endif
7526
7527 Utf8Str strCanonicalName;
7528
7529#ifdef VBOX_WITH_QTGUI
7530 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7531 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7532 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7533 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7534 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7535 {
7536 strCanonicalName = "GUI/Qt";
7537# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7538 /* Modify the base path so that we don't need to use ".." below. */
7539 RTPathStripTrailingSlash(szPath);
7540 RTPathStripFilename(szPath);
7541 cchBufLeft = strlen(szPath);
7542 pszNamePart = szPath + cchBufLeft;
7543 cchBufLeft = sizeof(szPath) - cchBufLeft;
7544
7545# define OSX_APP_NAME "VirtualBoxVM"
7546# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7547
7548 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7549 if ( strAppOverride.contains(".")
7550 || strAppOverride.contains("/")
7551 || strAppOverride.contains("\\")
7552 || strAppOverride.contains(":"))
7553 strAppOverride.setNull();
7554 Utf8Str strAppPath;
7555 if (!strAppOverride.isEmpty())
7556 {
7557 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7558 Utf8Str strFullPath(szPath);
7559 strFullPath.append(strAppPath);
7560 /* there is a race, but people using this deserve the failure */
7561 if (!RTFileExists(strFullPath.c_str()))
7562 strAppOverride.setNull();
7563 }
7564 if (strAppOverride.isEmpty())
7565 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7566 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7567 strcpy(pszNamePart, strAppPath.c_str());
7568# else
7569 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7570 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7571 strcpy(pszNamePart, s_szVirtualBox_exe);
7572# endif
7573
7574 Utf8Str idStr = mData->mUuid.toString();
7575 const char *apszArgs[] =
7576 {
7577 szPath,
7578 "--comment", mUserData->s.strName.c_str(),
7579 "--startvm", idStr.c_str(),
7580 "--no-startvm-errormsgbox",
7581 NULL, /* For "--separate". */
7582 NULL, /* For "--sup-startup-log". */
7583 NULL
7584 };
7585 unsigned iArg = 6;
7586 if (fSeparate)
7587 apszArgs[iArg++] = "--separate";
7588 apszArgs[iArg++] = pszSupStartupLogArg;
7589
7590 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7591 }
7592#else /* !VBOX_WITH_QTGUI */
7593 if (0)
7594 ;
7595#endif /* VBOX_WITH_QTGUI */
7596
7597 else
7598
7599#ifdef VBOX_WITH_VBOXSDL
7600 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7601 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7602 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7603 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7604 {
7605 strCanonicalName = "GUI/SDL";
7606 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7607 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7608 strcpy(pszNamePart, s_szVBoxSDL_exe);
7609
7610 Utf8Str idStr = mData->mUuid.toString();
7611 const char *apszArgs[] =
7612 {
7613 szPath,
7614 "--comment", mUserData->s.strName.c_str(),
7615 "--startvm", idStr.c_str(),
7616 NULL, /* For "--separate". */
7617 NULL, /* For "--sup-startup-log". */
7618 NULL
7619 };
7620 unsigned iArg = 5;
7621 if (fSeparate)
7622 apszArgs[iArg++] = "--separate";
7623 apszArgs[iArg++] = pszSupStartupLogArg;
7624
7625 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7626 }
7627#else /* !VBOX_WITH_VBOXSDL */
7628 if (0)
7629 ;
7630#endif /* !VBOX_WITH_VBOXSDL */
7631
7632 else
7633
7634#ifdef VBOX_WITH_HEADLESS
7635 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7636 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7637 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7638 )
7639 {
7640 strCanonicalName = "headless";
7641 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7642 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7643 * and a VM works even if the server has not been installed.
7644 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7645 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7646 * differently in 4.0 and 3.x.
7647 */
7648 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7649 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7650 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7651
7652 Utf8Str idStr = mData->mUuid.toString();
7653 const char *apszArgs[] =
7654 {
7655 szPath,
7656 "--comment", mUserData->s.strName.c_str(),
7657 "--startvm", idStr.c_str(),
7658 "--vrde", "config",
7659 NULL, /* For "--capture". */
7660 NULL, /* For "--sup-startup-log". */
7661 NULL
7662 };
7663 unsigned iArg = 7;
7664 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7665 apszArgs[iArg++] = "--capture";
7666 apszArgs[iArg++] = pszSupStartupLogArg;
7667
7668# ifdef RT_OS_WINDOWS
7669 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7670# else
7671 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7672# endif
7673 }
7674#else /* !VBOX_WITH_HEADLESS */
7675 if (0)
7676 ;
7677#endif /* !VBOX_WITH_HEADLESS */
7678 else
7679 {
7680 RTEnvDestroy(env);
7681 return setError(E_INVALIDARG,
7682 tr("Invalid frontend name: '%s'"),
7683 strFrontend.c_str());
7684 }
7685
7686 RTEnvDestroy(env);
7687
7688 if (RT_FAILURE(vrc))
7689 return setError(VBOX_E_IPRT_ERROR,
7690 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7691 mUserData->s.strName.c_str(), vrc);
7692
7693 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7694
7695 if (!fSeparate)
7696 {
7697 /*
7698 * Note that we don't release the lock here before calling the client,
7699 * because it doesn't need to call us back if called with a NULL argument.
7700 * Releasing the lock here is dangerous because we didn't prepare the
7701 * launch data yet, but the client we've just started may happen to be
7702 * too fast and call LockMachine() that will fail (because of PID, etc.),
7703 * so that the Machine will never get out of the Spawning session state.
7704 */
7705
7706 /* inform the session that it will be a remote one */
7707 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7708#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7709 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7710#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7711 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7712#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7713 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7714
7715 if (FAILED(rc))
7716 {
7717 /* restore the session state */
7718 mData->mSession.mState = SessionState_Unlocked;
7719 alock.release();
7720 mParent->i_addProcessToReap(pid);
7721 /* The failure may occur w/o any error info (from RPC), so provide one */
7722 return setError(VBOX_E_VM_ERROR,
7723 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7724 }
7725
7726 /* attach launch data to the machine */
7727 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7728 mData->mSession.mRemoteControls.push_back(aControl);
7729 mData->mSession.mProgress = aProgress;
7730 mData->mSession.mPID = pid;
7731 mData->mSession.mState = SessionState_Spawning;
7732 Assert(strCanonicalName.isNotEmpty());
7733 mData->mSession.mName = strCanonicalName;
7734 }
7735 else
7736 {
7737 /* For separate UI process we declare the launch as completed instantly, as the
7738 * actual headless VM start may or may not come. No point in remembering anything
7739 * yet, as what matters for us is when the headless VM gets started. */
7740 aProgress->i_notifyComplete(S_OK);
7741 }
7742
7743 alock.release();
7744 mParent->i_addProcessToReap(pid);
7745
7746 LogFlowThisFuncLeave();
7747 return S_OK;
7748}
7749
7750/**
7751 * Returns @c true if the given session machine instance has an open direct
7752 * session (and optionally also for direct sessions which are closing) and
7753 * returns the session control machine instance if so.
7754 *
7755 * Note that when the method returns @c false, the arguments remain unchanged.
7756 *
7757 * @param aMachine Session machine object.
7758 * @param aControl Direct session control object (optional).
7759 * @param aAllowClosing If true then additionally a session which is currently
7760 * being closed will also be allowed.
7761 *
7762 * @note locks this object for reading.
7763 */
7764bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7765 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7766 bool aAllowClosing /*= false*/)
7767{
7768 AutoLimitedCaller autoCaller(this);
7769 AssertComRCReturn(autoCaller.rc(), false);
7770
7771 /* just return false for inaccessible machines */
7772 if (getObjectState().getState() != ObjectState::Ready)
7773 return false;
7774
7775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7776
7777 if ( ( mData->mSession.mState == SessionState_Locked
7778 && mData->mSession.mLockType == LockType_VM)
7779 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7780 )
7781 {
7782 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7783
7784 aMachine = mData->mSession.mMachine;
7785
7786 if (aControl != NULL)
7787 *aControl = mData->mSession.mDirectControl;
7788
7789 return true;
7790 }
7791
7792 return false;
7793}
7794
7795/**
7796 * Returns @c true if the given machine has an spawning direct session.
7797 *
7798 * @note locks this object for reading.
7799 */
7800bool Machine::i_isSessionSpawning()
7801{
7802 AutoLimitedCaller autoCaller(this);
7803 AssertComRCReturn(autoCaller.rc(), false);
7804
7805 /* just return false for inaccessible machines */
7806 if (getObjectState().getState() != ObjectState::Ready)
7807 return false;
7808
7809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7810
7811 if (mData->mSession.mState == SessionState_Spawning)
7812 return true;
7813
7814 return false;
7815}
7816
7817/**
7818 * Called from the client watcher thread to check for unexpected client process
7819 * death during Session_Spawning state (e.g. before it successfully opened a
7820 * direct session).
7821 *
7822 * On Win32 and on OS/2, this method is called only when we've got the
7823 * direct client's process termination notification, so it always returns @c
7824 * true.
7825 *
7826 * On other platforms, this method returns @c true if the client process is
7827 * terminated and @c false if it's still alive.
7828 *
7829 * @note Locks this object for writing.
7830 */
7831bool Machine::i_checkForSpawnFailure()
7832{
7833 AutoCaller autoCaller(this);
7834 if (!autoCaller.isOk())
7835 {
7836 /* nothing to do */
7837 LogFlowThisFunc(("Already uninitialized!\n"));
7838 return true;
7839 }
7840
7841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7842
7843 if (mData->mSession.mState != SessionState_Spawning)
7844 {
7845 /* nothing to do */
7846 LogFlowThisFunc(("Not spawning any more!\n"));
7847 return true;
7848 }
7849
7850 HRESULT rc = S_OK;
7851
7852 /* PID not yet initialized, skip check. */
7853 if (mData->mSession.mPID == NIL_RTPROCESS)
7854 return false;
7855
7856 RTPROCSTATUS status;
7857 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7858
7859 if (vrc != VERR_PROCESS_RUNNING)
7860 {
7861 Utf8Str strExtraInfo;
7862
7863#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7864 /* If the startup logfile exists and is of non-zero length, tell the
7865 user to look there for more details to encourage them to attach it
7866 when reporting startup issues. */
7867 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7868 uint64_t cbStartupLogFile = 0;
7869 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7870 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7871 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7872#endif
7873
7874 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7875 rc = setError(E_FAIL,
7876 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7877 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7878 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7879 rc = setError(E_FAIL,
7880 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7881 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7882 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7883 rc = setError(E_FAIL,
7884 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7885 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7886 else
7887 rc = setError(E_FAIL,
7888 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7889 i_getName().c_str(), vrc, strExtraInfo.c_str());
7890 }
7891
7892 if (FAILED(rc))
7893 {
7894 /* Close the remote session, remove the remote control from the list
7895 * and reset session state to Closed (@note keep the code in sync with
7896 * the relevant part in LockMachine()). */
7897
7898 Assert(mData->mSession.mRemoteControls.size() == 1);
7899 if (mData->mSession.mRemoteControls.size() == 1)
7900 {
7901 ErrorInfoKeeper eik;
7902 mData->mSession.mRemoteControls.front()->Uninitialize();
7903 }
7904
7905 mData->mSession.mRemoteControls.clear();
7906 mData->mSession.mState = SessionState_Unlocked;
7907
7908 /* finalize the progress after setting the state */
7909 if (!mData->mSession.mProgress.isNull())
7910 {
7911 mData->mSession.mProgress->notifyComplete(rc);
7912 mData->mSession.mProgress.setNull();
7913 }
7914
7915 mData->mSession.mPID = NIL_RTPROCESS;
7916
7917 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7918 return true;
7919 }
7920
7921 return false;
7922}
7923
7924/**
7925 * Checks whether the machine can be registered. If so, commits and saves
7926 * all settings.
7927 *
7928 * @note Must be called from mParent's write lock. Locks this object and
7929 * children for writing.
7930 */
7931HRESULT Machine::i_prepareRegister()
7932{
7933 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7934
7935 AutoLimitedCaller autoCaller(this);
7936 AssertComRCReturnRC(autoCaller.rc());
7937
7938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7939
7940 /* wait for state dependents to drop to zero */
7941 i_ensureNoStateDependencies();
7942
7943 if (!mData->mAccessible)
7944 return setError(VBOX_E_INVALID_OBJECT_STATE,
7945 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7946 mUserData->s.strName.c_str(),
7947 mData->mUuid.toString().c_str());
7948
7949 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7950
7951 if (mData->mRegistered)
7952 return setError(VBOX_E_INVALID_OBJECT_STATE,
7953 tr("The machine '%s' with UUID {%s} is already registered"),
7954 mUserData->s.strName.c_str(),
7955 mData->mUuid.toString().c_str());
7956
7957 HRESULT rc = S_OK;
7958
7959 // Ensure the settings are saved. If we are going to be registered and
7960 // no config file exists yet, create it by calling i_saveSettings() too.
7961 if ( (mData->flModifications)
7962 || (!mData->pMachineConfigFile->fileExists())
7963 )
7964 {
7965 rc = i_saveSettings(NULL);
7966 // no need to check whether VirtualBox.xml needs saving too since
7967 // we can't have a machine XML file rename pending
7968 if (FAILED(rc)) return rc;
7969 }
7970
7971 /* more config checking goes here */
7972
7973 if (SUCCEEDED(rc))
7974 {
7975 /* we may have had implicit modifications we want to fix on success */
7976 i_commit();
7977
7978 mData->mRegistered = true;
7979 }
7980 else
7981 {
7982 /* we may have had implicit modifications we want to cancel on failure*/
7983 i_rollback(false /* aNotify */);
7984 }
7985
7986 return rc;
7987}
7988
7989/**
7990 * Increases the number of objects dependent on the machine state or on the
7991 * registered state. Guarantees that these two states will not change at least
7992 * until #releaseStateDependency() is called.
7993 *
7994 * Depending on the @a aDepType value, additional state checks may be made.
7995 * These checks will set extended error info on failure. See
7996 * #checkStateDependency() for more info.
7997 *
7998 * If this method returns a failure, the dependency is not added and the caller
7999 * is not allowed to rely on any particular machine state or registration state
8000 * value and may return the failed result code to the upper level.
8001 *
8002 * @param aDepType Dependency type to add.
8003 * @param aState Current machine state (NULL if not interested).
8004 * @param aRegistered Current registered state (NULL if not interested).
8005 *
8006 * @note Locks this object for writing.
8007 */
8008HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8009 MachineState_T *aState /* = NULL */,
8010 BOOL *aRegistered /* = NULL */)
8011{
8012 AutoCaller autoCaller(this);
8013 AssertComRCReturnRC(autoCaller.rc());
8014
8015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8016
8017 HRESULT rc = i_checkStateDependency(aDepType);
8018 if (FAILED(rc)) return rc;
8019
8020 {
8021 if (mData->mMachineStateChangePending != 0)
8022 {
8023 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8024 * drop to zero so don't add more. It may make sense to wait a bit
8025 * and retry before reporting an error (since the pending state
8026 * transition should be really quick) but let's just assert for
8027 * now to see if it ever happens on practice. */
8028
8029 AssertFailed();
8030
8031 return setError(E_ACCESSDENIED,
8032 tr("Machine state change is in progress. Please retry the operation later."));
8033 }
8034
8035 ++mData->mMachineStateDeps;
8036 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8037 }
8038
8039 if (aState)
8040 *aState = mData->mMachineState;
8041 if (aRegistered)
8042 *aRegistered = mData->mRegistered;
8043
8044 return S_OK;
8045}
8046
8047/**
8048 * Decreases the number of objects dependent on the machine state.
8049 * Must always complete the #addStateDependency() call after the state
8050 * dependency is no more necessary.
8051 */
8052void Machine::i_releaseStateDependency()
8053{
8054 AutoCaller autoCaller(this);
8055 AssertComRCReturnVoid(autoCaller.rc());
8056
8057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8058
8059 /* releaseStateDependency() w/o addStateDependency()? */
8060 AssertReturnVoid(mData->mMachineStateDeps != 0);
8061 -- mData->mMachineStateDeps;
8062
8063 if (mData->mMachineStateDeps == 0)
8064 {
8065 /* inform i_ensureNoStateDependencies() that there are no more deps */
8066 if (mData->mMachineStateChangePending != 0)
8067 {
8068 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8069 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8070 }
8071 }
8072}
8073
8074Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8075{
8076 /* start with nothing found */
8077 Utf8Str strResult("");
8078
8079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8080
8081 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8082 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8083 // found:
8084 strResult = it->second; // source is a Utf8Str
8085
8086 return strResult;
8087}
8088
8089// protected methods
8090/////////////////////////////////////////////////////////////////////////////
8091
8092/**
8093 * Performs machine state checks based on the @a aDepType value. If a check
8094 * fails, this method will set extended error info, otherwise it will return
8095 * S_OK. It is supposed, that on failure, the caller will immediately return
8096 * the return value of this method to the upper level.
8097 *
8098 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8099 *
8100 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8101 * current state of this machine object allows to change settings of the
8102 * machine (i.e. the machine is not registered, or registered but not running
8103 * and not saved). It is useful to call this method from Machine setters
8104 * before performing any change.
8105 *
8106 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8107 * as for MutableStateDep except that if the machine is saved, S_OK is also
8108 * returned. This is useful in setters which allow changing machine
8109 * properties when it is in the saved state.
8110 *
8111 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8112 * if the current state of this machine object allows to change runtime
8113 * changeable settings of the machine (i.e. the machine is not registered, or
8114 * registered but either running or not running and not saved). It is useful
8115 * to call this method from Machine setters before performing any changes to
8116 * runtime changeable settings.
8117 *
8118 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8119 * the same as for MutableOrRunningStateDep except that if the machine is
8120 * saved, S_OK is also returned. This is useful in setters which allow
8121 * changing runtime and saved state changeable machine properties.
8122 *
8123 * @param aDepType Dependency type to check.
8124 *
8125 * @note Non Machine based classes should use #addStateDependency() and
8126 * #releaseStateDependency() methods or the smart AutoStateDependency
8127 * template.
8128 *
8129 * @note This method must be called from under this object's read or write
8130 * lock.
8131 */
8132HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8133{
8134 switch (aDepType)
8135 {
8136 case AnyStateDep:
8137 {
8138 break;
8139 }
8140 case MutableStateDep:
8141 {
8142 if ( mData->mRegistered
8143 && ( !i_isSessionMachine()
8144 || ( mData->mMachineState != MachineState_Aborted
8145 && mData->mMachineState != MachineState_Teleported
8146 && mData->mMachineState != MachineState_PoweredOff
8147 )
8148 )
8149 )
8150 return setError(VBOX_E_INVALID_VM_STATE,
8151 tr("The machine is not mutable (state is %s)"),
8152 Global::stringifyMachineState(mData->mMachineState));
8153 break;
8154 }
8155 case MutableOrSavedStateDep:
8156 {
8157 if ( mData->mRegistered
8158 && ( !i_isSessionMachine()
8159 || ( mData->mMachineState != MachineState_Aborted
8160 && mData->mMachineState != MachineState_Teleported
8161 && mData->mMachineState != MachineState_Saved
8162 && mData->mMachineState != MachineState_PoweredOff
8163 )
8164 )
8165 )
8166 return setError(VBOX_E_INVALID_VM_STATE,
8167 tr("The machine is not mutable (state is %s)"),
8168 Global::stringifyMachineState(mData->mMachineState));
8169 break;
8170 }
8171 case MutableOrRunningStateDep:
8172 {
8173 if ( mData->mRegistered
8174 && ( !i_isSessionMachine()
8175 || ( mData->mMachineState != MachineState_Aborted
8176 && mData->mMachineState != MachineState_Teleported
8177 && mData->mMachineState != MachineState_PoweredOff
8178 && !Global::IsOnline(mData->mMachineState)
8179 )
8180 )
8181 )
8182 return setError(VBOX_E_INVALID_VM_STATE,
8183 tr("The machine is not mutable (state is %s)"),
8184 Global::stringifyMachineState(mData->mMachineState));
8185 break;
8186 }
8187 case MutableOrSavedOrRunningStateDep:
8188 {
8189 if ( mData->mRegistered
8190 && ( !i_isSessionMachine()
8191 || ( mData->mMachineState != MachineState_Aborted
8192 && mData->mMachineState != MachineState_Teleported
8193 && mData->mMachineState != MachineState_Saved
8194 && mData->mMachineState != MachineState_PoweredOff
8195 && !Global::IsOnline(mData->mMachineState)
8196 )
8197 )
8198 )
8199 return setError(VBOX_E_INVALID_VM_STATE,
8200 tr("The machine is not mutable (state is %s)"),
8201 Global::stringifyMachineState(mData->mMachineState));
8202 break;
8203 }
8204 }
8205
8206 return S_OK;
8207}
8208
8209/**
8210 * Helper to initialize all associated child objects and allocate data
8211 * structures.
8212 *
8213 * This method must be called as a part of the object's initialization procedure
8214 * (usually done in the #init() method).
8215 *
8216 * @note Must be called only from #init() or from #registeredInit().
8217 */
8218HRESULT Machine::initDataAndChildObjects()
8219{
8220 AutoCaller autoCaller(this);
8221 AssertComRCReturnRC(autoCaller.rc());
8222 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8223 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8224
8225 AssertReturn(!mData->mAccessible, E_FAIL);
8226
8227 /* allocate data structures */
8228 mSSData.allocate();
8229 mUserData.allocate();
8230 mHWData.allocate();
8231 mMediaData.allocate();
8232 mStorageControllers.allocate();
8233 mUSBControllers.allocate();
8234
8235 /* initialize mOSTypeId */
8236 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8237
8238 /* create associated BIOS settings object */
8239 unconst(mBIOSSettings).createObject();
8240 mBIOSSettings->init(this);
8241
8242 /* create an associated VRDE object (default is disabled) */
8243 unconst(mVRDEServer).createObject();
8244 mVRDEServer->init(this);
8245
8246 /* create associated serial port objects */
8247 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8248 {
8249 unconst(mSerialPorts[slot]).createObject();
8250 mSerialPorts[slot]->init(this, slot);
8251 }
8252
8253 /* create associated parallel port objects */
8254 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8255 {
8256 unconst(mParallelPorts[slot]).createObject();
8257 mParallelPorts[slot]->init(this, slot);
8258 }
8259
8260 /* create the audio adapter object (always present, default is disabled) */
8261 unconst(mAudioAdapter).createObject();
8262 mAudioAdapter->init(this);
8263
8264 /* create the USB device filters object (always present) */
8265 unconst(mUSBDeviceFilters).createObject();
8266 mUSBDeviceFilters->init(this);
8267
8268 /* create associated network adapter objects */
8269 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8270 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8271 {
8272 unconst(mNetworkAdapters[slot]).createObject();
8273 mNetworkAdapters[slot]->init(this, slot);
8274 }
8275
8276 /* create the bandwidth control */
8277 unconst(mBandwidthControl).createObject();
8278 mBandwidthControl->init(this);
8279
8280 return S_OK;
8281}
8282
8283/**
8284 * Helper to uninitialize all associated child objects and to free all data
8285 * structures.
8286 *
8287 * This method must be called as a part of the object's uninitialization
8288 * procedure (usually done in the #uninit() method).
8289 *
8290 * @note Must be called only from #uninit() or from #registeredInit().
8291 */
8292void Machine::uninitDataAndChildObjects()
8293{
8294 AutoCaller autoCaller(this);
8295 AssertComRCReturnVoid(autoCaller.rc());
8296 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8297 || getObjectState().getState() == ObjectState::Limited);
8298
8299 /* tell all our other child objects we've been uninitialized */
8300 if (mBandwidthControl)
8301 {
8302 mBandwidthControl->uninit();
8303 unconst(mBandwidthControl).setNull();
8304 }
8305
8306 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8307 {
8308 if (mNetworkAdapters[slot])
8309 {
8310 mNetworkAdapters[slot]->uninit();
8311 unconst(mNetworkAdapters[slot]).setNull();
8312 }
8313 }
8314
8315 if (mUSBDeviceFilters)
8316 {
8317 mUSBDeviceFilters->uninit();
8318 unconst(mUSBDeviceFilters).setNull();
8319 }
8320
8321 if (mAudioAdapter)
8322 {
8323 mAudioAdapter->uninit();
8324 unconst(mAudioAdapter).setNull();
8325 }
8326
8327 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8328 {
8329 if (mParallelPorts[slot])
8330 {
8331 mParallelPorts[slot]->uninit();
8332 unconst(mParallelPorts[slot]).setNull();
8333 }
8334 }
8335
8336 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8337 {
8338 if (mSerialPorts[slot])
8339 {
8340 mSerialPorts[slot]->uninit();
8341 unconst(mSerialPorts[slot]).setNull();
8342 }
8343 }
8344
8345 if (mVRDEServer)
8346 {
8347 mVRDEServer->uninit();
8348 unconst(mVRDEServer).setNull();
8349 }
8350
8351 if (mBIOSSettings)
8352 {
8353 mBIOSSettings->uninit();
8354 unconst(mBIOSSettings).setNull();
8355 }
8356
8357 /* Deassociate media (only when a real Machine or a SnapshotMachine
8358 * instance is uninitialized; SessionMachine instances refer to real
8359 * Machine media). This is necessary for a clean re-initialization of
8360 * the VM after successfully re-checking the accessibility state. Note
8361 * that in case of normal Machine or SnapshotMachine uninitialization (as
8362 * a result of unregistering or deleting the snapshot), outdated media
8363 * attachments will already be uninitialized and deleted, so this
8364 * code will not affect them. */
8365 if ( !!mMediaData
8366 && (!i_isSessionMachine())
8367 )
8368 {
8369 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8370 it != mMediaData->mAttachments.end();
8371 ++it)
8372 {
8373 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8374 if (pMedium.isNull())
8375 continue;
8376 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8377 AssertComRC(rc);
8378 }
8379 }
8380
8381 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8382 {
8383 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8384 if (mData->mFirstSnapshot)
8385 {
8386 // snapshots tree is protected by machine write lock; strictly
8387 // this isn't necessary here since we're deleting the entire
8388 // machine, but otherwise we assert in Snapshot::uninit()
8389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8390 mData->mFirstSnapshot->uninit();
8391 mData->mFirstSnapshot.setNull();
8392 }
8393
8394 mData->mCurrentSnapshot.setNull();
8395 }
8396
8397 /* free data structures (the essential mData structure is not freed here
8398 * since it may be still in use) */
8399 mMediaData.free();
8400 mStorageControllers.free();
8401 mUSBControllers.free();
8402 mHWData.free();
8403 mUserData.free();
8404 mSSData.free();
8405}
8406
8407/**
8408 * Returns a pointer to the Machine object for this machine that acts like a
8409 * parent for complex machine data objects such as shared folders, etc.
8410 *
8411 * For primary Machine objects and for SnapshotMachine objects, returns this
8412 * object's pointer itself. For SessionMachine objects, returns the peer
8413 * (primary) machine pointer.
8414 */
8415Machine* Machine::i_getMachine()
8416{
8417 if (i_isSessionMachine())
8418 return (Machine*)mPeer;
8419 return this;
8420}
8421
8422/**
8423 * Makes sure that there are no machine state dependents. If necessary, waits
8424 * for the number of dependents to drop to zero.
8425 *
8426 * Make sure this method is called from under this object's write lock to
8427 * guarantee that no new dependents may be added when this method returns
8428 * control to the caller.
8429 *
8430 * @note Locks this object for writing. The lock will be released while waiting
8431 * (if necessary).
8432 *
8433 * @warning To be used only in methods that change the machine state!
8434 */
8435void Machine::i_ensureNoStateDependencies()
8436{
8437 AssertReturnVoid(isWriteLockOnCurrentThread());
8438
8439 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8440
8441 /* Wait for all state dependents if necessary */
8442 if (mData->mMachineStateDeps != 0)
8443 {
8444 /* lazy semaphore creation */
8445 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8446 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8447
8448 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8449 mData->mMachineStateDeps));
8450
8451 ++mData->mMachineStateChangePending;
8452
8453 /* reset the semaphore before waiting, the last dependent will signal
8454 * it */
8455 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8456
8457 alock.release();
8458
8459 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8460
8461 alock.acquire();
8462
8463 -- mData->mMachineStateChangePending;
8464 }
8465}
8466
8467/**
8468 * Changes the machine state and informs callbacks.
8469 *
8470 * This method is not intended to fail so it either returns S_OK or asserts (and
8471 * returns a failure).
8472 *
8473 * @note Locks this object for writing.
8474 */
8475HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8476{
8477 LogFlowThisFuncEnter();
8478 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8479 Assert(aMachineState != MachineState_Null);
8480
8481 AutoCaller autoCaller(this);
8482 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8483
8484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8485
8486 /* wait for state dependents to drop to zero */
8487 i_ensureNoStateDependencies();
8488
8489 MachineState_T const enmOldState = mData->mMachineState;
8490 if (enmOldState != aMachineState)
8491 {
8492 mData->mMachineState = aMachineState;
8493 RTTimeNow(&mData->mLastStateChange);
8494
8495#ifdef VBOX_WITH_DTRACE_R3_MAIN
8496 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8497#endif
8498 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8499 }
8500
8501 LogFlowThisFuncLeave();
8502 return S_OK;
8503}
8504
8505/**
8506 * Searches for a shared folder with the given logical name
8507 * in the collection of shared folders.
8508 *
8509 * @param aName logical name of the shared folder
8510 * @param aSharedFolder where to return the found object
8511 * @param aSetError whether to set the error info if the folder is
8512 * not found
8513 * @return
8514 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8515 *
8516 * @note
8517 * must be called from under the object's lock!
8518 */
8519HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8520 ComObjPtr<SharedFolder> &aSharedFolder,
8521 bool aSetError /* = false */)
8522{
8523 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8524 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8525 it != mHWData->mSharedFolders.end();
8526 ++it)
8527 {
8528 SharedFolder *pSF = *it;
8529 AutoCaller autoCaller(pSF);
8530 if (pSF->i_getName() == aName)
8531 {
8532 aSharedFolder = pSF;
8533 rc = S_OK;
8534 break;
8535 }
8536 }
8537
8538 if (aSetError && FAILED(rc))
8539 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8540
8541 return rc;
8542}
8543
8544/**
8545 * Initializes all machine instance data from the given settings structures
8546 * from XML. The exception is the machine UUID which needs special handling
8547 * depending on the caller's use case, so the caller needs to set that herself.
8548 *
8549 * This gets called in several contexts during machine initialization:
8550 *
8551 * -- When machine XML exists on disk already and needs to be loaded into memory,
8552 * for example, from registeredInit() to load all registered machines on
8553 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8554 * attached to the machine should be part of some media registry already.
8555 *
8556 * -- During OVF import, when a machine config has been constructed from an
8557 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8558 * ensure that the media listed as attachments in the config (which have
8559 * been imported from the OVF) receive the correct registry ID.
8560 *
8561 * -- During VM cloning.
8562 *
8563 * @param config Machine settings from XML.
8564 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8565 * for each attached medium in the config.
8566 * @return
8567 */
8568HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8569 const Guid *puuidRegistry)
8570{
8571 // copy name, description, OS type, teleporter, UTC etc.
8572 mUserData->s = config.machineUserData;
8573
8574 // Decode the Icon overide data from config userdata and set onto Machine.
8575 #define DECODE_STR_MAX _1M
8576 const char* pszStr = config.machineUserData.ovIcon.c_str();
8577 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8578 if (cbOut > DECODE_STR_MAX)
8579 return setError(E_FAIL,
8580 tr("Icon Data too long.'%d' > '%d'"),
8581 cbOut,
8582 DECODE_STR_MAX);
8583 mUserData->mIcon.resize(cbOut);
8584 int vrc = VINF_SUCCESS;
8585 if (cbOut)
8586 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8587 if (RT_FAILURE(vrc))
8588 {
8589 mUserData->mIcon.resize(0);
8590 return setError(E_FAIL,
8591 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8592 pszStr,
8593 vrc);
8594 }
8595
8596 // look up the object by Id to check it is valid
8597 ComPtr<IGuestOSType> guestOSType;
8598 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8599 guestOSType.asOutParam());
8600 if (FAILED(rc)) return rc;
8601
8602 // stateFile (optional)
8603 if (config.strStateFile.isEmpty())
8604 mSSData->strStateFilePath.setNull();
8605 else
8606 {
8607 Utf8Str stateFilePathFull(config.strStateFile);
8608 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8609 if (RT_FAILURE(vrc))
8610 return setError(E_FAIL,
8611 tr("Invalid saved state file path '%s' (%Rrc)"),
8612 config.strStateFile.c_str(),
8613 vrc);
8614 mSSData->strStateFilePath = stateFilePathFull;
8615 }
8616
8617 // snapshot folder needs special processing so set it again
8618 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8619 if (FAILED(rc)) return rc;
8620
8621 /* Copy the extra data items (Not in any case config is already the same as
8622 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8623 * make sure the extra data map is copied). */
8624 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8625
8626 /* currentStateModified (optional, default is true) */
8627 mData->mCurrentStateModified = config.fCurrentStateModified;
8628
8629 mData->mLastStateChange = config.timeLastStateChange;
8630
8631 /*
8632 * note: all mUserData members must be assigned prior this point because
8633 * we need to commit changes in order to let mUserData be shared by all
8634 * snapshot machine instances.
8635 */
8636 mUserData.commitCopy();
8637
8638 // machine registry, if present (must be loaded before snapshots)
8639 if (config.canHaveOwnMediaRegistry())
8640 {
8641 // determine machine folder
8642 Utf8Str strMachineFolder = i_getSettingsFileFull();
8643 strMachineFolder.stripFilename();
8644 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8645 config.mediaRegistry,
8646 strMachineFolder);
8647 if (FAILED(rc)) return rc;
8648 }
8649
8650 /* Snapshot node (optional) */
8651 size_t cRootSnapshots;
8652 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8653 {
8654 // there must be only one root snapshot
8655 Assert(cRootSnapshots == 1);
8656
8657 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8658
8659 rc = i_loadSnapshot(snap,
8660 config.uuidCurrentSnapshot,
8661 NULL); // no parent == first snapshot
8662 if (FAILED(rc)) return rc;
8663 }
8664
8665 // hardware data
8666 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8667 if (FAILED(rc)) return rc;
8668
8669 // load storage controllers
8670 rc = i_loadStorageControllers(config.storageMachine,
8671 puuidRegistry,
8672 NULL /* puuidSnapshot */);
8673 if (FAILED(rc)) return rc;
8674
8675 /*
8676 * NOTE: the assignment below must be the last thing to do,
8677 * otherwise it will be not possible to change the settings
8678 * somewhere in the code above because all setters will be
8679 * blocked by i_checkStateDependency(MutableStateDep).
8680 */
8681
8682 /* set the machine state to Aborted or Saved when appropriate */
8683 if (config.fAborted)
8684 {
8685 mSSData->strStateFilePath.setNull();
8686
8687 /* no need to use i_setMachineState() during init() */
8688 mData->mMachineState = MachineState_Aborted;
8689 }
8690 else if (!mSSData->strStateFilePath.isEmpty())
8691 {
8692 /* no need to use i_setMachineState() during init() */
8693 mData->mMachineState = MachineState_Saved;
8694 }
8695
8696 // after loading settings, we are no longer different from the XML on disk
8697 mData->flModifications = 0;
8698
8699 return S_OK;
8700}
8701
8702/**
8703 * Recursively loads all snapshots starting from the given.
8704 *
8705 * @param aNode <Snapshot> node.
8706 * @param aCurSnapshotId Current snapshot ID from the settings file.
8707 * @param aParentSnapshot Parent snapshot.
8708 */
8709HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8710 const Guid &aCurSnapshotId,
8711 Snapshot *aParentSnapshot)
8712{
8713 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8714 AssertReturn(!i_isSessionMachine(), E_FAIL);
8715
8716 HRESULT rc = S_OK;
8717
8718 Utf8Str strStateFile;
8719 if (!data.strStateFile.isEmpty())
8720 {
8721 /* optional */
8722 strStateFile = data.strStateFile;
8723 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8724 if (RT_FAILURE(vrc))
8725 return setError(E_FAIL,
8726 tr("Invalid saved state file path '%s' (%Rrc)"),
8727 strStateFile.c_str(),
8728 vrc);
8729 }
8730
8731 /* create a snapshot machine object */
8732 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8733 pSnapshotMachine.createObject();
8734 rc = pSnapshotMachine->initFromSettings(this,
8735 data.hardware,
8736 &data.debugging,
8737 &data.autostart,
8738 data.storage,
8739 data.uuid.ref(),
8740 strStateFile);
8741 if (FAILED(rc)) return rc;
8742
8743 /* create a snapshot object */
8744 ComObjPtr<Snapshot> pSnapshot;
8745 pSnapshot.createObject();
8746 /* initialize the snapshot */
8747 rc = pSnapshot->init(mParent, // VirtualBox object
8748 data.uuid,
8749 data.strName,
8750 data.strDescription,
8751 data.timestamp,
8752 pSnapshotMachine,
8753 aParentSnapshot);
8754 if (FAILED(rc)) return rc;
8755
8756 /* memorize the first snapshot if necessary */
8757 if (!mData->mFirstSnapshot)
8758 mData->mFirstSnapshot = pSnapshot;
8759
8760 /* memorize the current snapshot when appropriate */
8761 if ( !mData->mCurrentSnapshot
8762 && pSnapshot->i_getId() == aCurSnapshotId
8763 )
8764 mData->mCurrentSnapshot = pSnapshot;
8765
8766 // now create the children
8767 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8768 it != data.llChildSnapshots.end();
8769 ++it)
8770 {
8771 const settings::Snapshot &childData = *it;
8772 // recurse
8773 rc = i_loadSnapshot(childData,
8774 aCurSnapshotId,
8775 pSnapshot); // parent = the one we created above
8776 if (FAILED(rc)) return rc;
8777 }
8778
8779 return rc;
8780}
8781
8782/**
8783 * Loads settings into mHWData.
8784 *
8785 * @param data Reference to the hardware settings.
8786 * @param pDbg Pointer to the debugging settings.
8787 * @param pAutostart Pointer to the autostart settings.
8788 */
8789HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8790 const settings::Autostart *pAutostart)
8791{
8792 AssertReturn(!i_isSessionMachine(), E_FAIL);
8793
8794 HRESULT rc = S_OK;
8795
8796 try
8797 {
8798 /* The hardware version attribute (optional). */
8799 mHWData->mHWVersion = data.strVersion;
8800 mHWData->mHardwareUUID = data.uuid;
8801
8802 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8803 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8804 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8805 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8806 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8807 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8808 mHWData->mPAEEnabled = data.fPAE;
8809 mHWData->mLongMode = data.enmLongMode;
8810 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8811 mHWData->mCPUCount = data.cCPUs;
8812 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8813 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8814 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8815
8816 // cpu
8817 if (mHWData->mCPUHotPlugEnabled)
8818 {
8819 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8820 it != data.llCpus.end();
8821 ++it)
8822 {
8823 const settings::Cpu &cpu = *it;
8824
8825 mHWData->mCPUAttached[cpu.ulId] = true;
8826 }
8827 }
8828
8829 // cpuid leafs
8830 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8831 it != data.llCpuIdLeafs.end();
8832 ++it)
8833 {
8834 const settings::CpuIdLeaf &leaf = *it;
8835
8836 switch (leaf.ulId)
8837 {
8838 case 0x0:
8839 case 0x1:
8840 case 0x2:
8841 case 0x3:
8842 case 0x4:
8843 case 0x5:
8844 case 0x6:
8845 case 0x7:
8846 case 0x8:
8847 case 0x9:
8848 case 0xA:
8849 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8850 break;
8851
8852 case 0x80000000:
8853 case 0x80000001:
8854 case 0x80000002:
8855 case 0x80000003:
8856 case 0x80000004:
8857 case 0x80000005:
8858 case 0x80000006:
8859 case 0x80000007:
8860 case 0x80000008:
8861 case 0x80000009:
8862 case 0x8000000A:
8863 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8864 break;
8865
8866 default:
8867 /* just ignore */
8868 break;
8869 }
8870 }
8871
8872 mHWData->mMemorySize = data.ulMemorySizeMB;
8873 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8874
8875 // boot order
8876 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8877 {
8878 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8879 if (it == data.mapBootOrder.end())
8880 mHWData->mBootOrder[i] = DeviceType_Null;
8881 else
8882 mHWData->mBootOrder[i] = it->second;
8883 }
8884
8885 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8886 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8887 mHWData->mMonitorCount = data.cMonitors;
8888 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8889 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8890 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8891 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8892 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8893 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8894 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8895 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8896 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8897 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8898 if (!data.strVideoCaptureFile.isEmpty())
8899 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8900 else
8901 mHWData->mVideoCaptureFile.setNull();
8902 mHWData->mFirmwareType = data.firmwareType;
8903 mHWData->mPointingHIDType = data.pointingHIDType;
8904 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8905 mHWData->mChipsetType = data.chipsetType;
8906 mHWData->mParavirtProvider = data.paravirtProvider;
8907 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8908 mHWData->mHPETEnabled = data.fHPETEnabled;
8909
8910 /* VRDEServer */
8911 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8912 if (FAILED(rc)) return rc;
8913
8914 /* BIOS */
8915 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8916 if (FAILED(rc)) return rc;
8917
8918 // Bandwidth control (must come before network adapters)
8919 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8920 if (FAILED(rc)) return rc;
8921
8922 /* Shared folders */
8923 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8924 it != data.usbSettings.llUSBControllers.end();
8925 ++it)
8926 {
8927 const settings::USBController &settingsCtrl = *it;
8928 ComObjPtr<USBController> newCtrl;
8929
8930 newCtrl.createObject();
8931 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8932 mUSBControllers->push_back(newCtrl);
8933 }
8934
8935 /* USB device filters */
8936 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8937 if (FAILED(rc)) return rc;
8938
8939 // network adapters
8940 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8941 size_t oldCount = mNetworkAdapters.size();
8942 if (newCount > oldCount)
8943 {
8944 mNetworkAdapters.resize(newCount);
8945 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8946 {
8947 unconst(mNetworkAdapters[slot]).createObject();
8948 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8949 }
8950 }
8951 else if (newCount < oldCount)
8952 mNetworkAdapters.resize(newCount);
8953 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8954 it != data.llNetworkAdapters.end();
8955 ++it)
8956 {
8957 const settings::NetworkAdapter &nic = *it;
8958
8959 /* slot unicity is guaranteed by XML Schema */
8960 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8961 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8962 if (FAILED(rc)) return rc;
8963 }
8964
8965 // serial ports
8966 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8967 it != data.llSerialPorts.end();
8968 ++it)
8969 {
8970 const settings::SerialPort &s = *it;
8971
8972 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8973 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8974 if (FAILED(rc)) return rc;
8975 }
8976
8977 // parallel ports (optional)
8978 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8979 it != data.llParallelPorts.end();
8980 ++it)
8981 {
8982 const settings::ParallelPort &p = *it;
8983
8984 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8985 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8986 if (FAILED(rc)) return rc;
8987 }
8988
8989 /* AudioAdapter */
8990 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8991 if (FAILED(rc)) return rc;
8992
8993 /* Shared folders */
8994 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8995 it != data.llSharedFolders.end();
8996 ++it)
8997 {
8998 const settings::SharedFolder &sf = *it;
8999
9000 ComObjPtr<SharedFolder> sharedFolder;
9001 /* Check for double entries. Not allowed! */
9002 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9003 if (SUCCEEDED(rc))
9004 return setError(VBOX_E_OBJECT_IN_USE,
9005 tr("Shared folder named '%s' already exists"),
9006 sf.strName.c_str());
9007
9008 /* Create the new shared folder. Don't break on error. This will be
9009 * reported when the machine starts. */
9010 sharedFolder.createObject();
9011 rc = sharedFolder->init(i_getMachine(),
9012 sf.strName,
9013 sf.strHostPath,
9014 RT_BOOL(sf.fWritable),
9015 RT_BOOL(sf.fAutoMount),
9016 false /* fFailOnError */);
9017 if (FAILED(rc)) return rc;
9018 mHWData->mSharedFolders.push_back(sharedFolder);
9019 }
9020
9021 // Clipboard
9022 mHWData->mClipboardMode = data.clipboardMode;
9023
9024 // drag'n'drop
9025 mHWData->mDnDMode = data.dndMode;
9026
9027 // guest settings
9028 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9029
9030 // IO settings
9031 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9032 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9033
9034 // Host PCI devices
9035 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9036 it != data.pciAttachments.end();
9037 ++it)
9038 {
9039 const settings::HostPCIDeviceAttachment &hpda = *it;
9040 ComObjPtr<PCIDeviceAttachment> pda;
9041
9042 pda.createObject();
9043 pda->i_loadSettings(this, hpda);
9044 mHWData->mPCIDeviceAssignments.push_back(pda);
9045 }
9046
9047 /*
9048 * (The following isn't really real hardware, but it lives in HWData
9049 * for reasons of convenience.)
9050 */
9051
9052#ifdef VBOX_WITH_GUEST_PROPS
9053 /* Guest properties (optional) */
9054
9055 /* Only load transient guest properties for configs which have saved
9056 * state, because there shouldn't be any for powered off VMs. The same
9057 * logic applies for snapshots, as offline snapshots shouldn't have
9058 * any such properties. They confuse the code in various places.
9059 * Note: can't rely on the machine state, as it isn't set yet. */
9060 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9061 /* apologies for the hacky unconst() usage, but this needs hacking
9062 * actually inconsistent settings into consistency, otherwise there
9063 * will be some corner cases where the inconsistency survives
9064 * surprisingly long without getting fixed, especially for snapshots
9065 * as there are no config changes. */
9066 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9067 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
9068 it != llGuestProperties.end();
9069 /*nothing*/)
9070 {
9071 const settings::GuestProperty &prop = *it;
9072 uint32_t fFlags = guestProp::NILFLAG;
9073 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9074 if ( fSkipTransientGuestProperties
9075 && ( fFlags & guestProp::TRANSIENT
9076 || fFlags & guestProp::TRANSRESET))
9077 {
9078 it = llGuestProperties.erase(it);
9079 continue;
9080 }
9081 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9082 mHWData->mGuestProperties[prop.strName] = property;
9083 ++it;
9084 }
9085
9086 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9087#endif /* VBOX_WITH_GUEST_PROPS defined */
9088
9089 rc = i_loadDebugging(pDbg);
9090 if (FAILED(rc))
9091 return rc;
9092
9093 mHWData->mAutostart = *pAutostart;
9094
9095 /* default frontend */
9096 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9097 }
9098 catch(std::bad_alloc &)
9099 {
9100 return E_OUTOFMEMORY;
9101 }
9102
9103 AssertComRC(rc);
9104 return rc;
9105}
9106
9107/**
9108 * Called from Machine::loadHardware() to load the debugging settings of the
9109 * machine.
9110 *
9111 * @param pDbg Pointer to the settings.
9112 */
9113HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9114{
9115 mHWData->mDebugging = *pDbg;
9116 /* no more processing currently required, this will probably change. */
9117 return S_OK;
9118}
9119
9120/**
9121 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9122 *
9123 * @param data
9124 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9125 * @param puuidSnapshot
9126 * @return
9127 */
9128HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9129 const Guid *puuidRegistry,
9130 const Guid *puuidSnapshot)
9131{
9132 AssertReturn(!i_isSessionMachine(), E_FAIL);
9133
9134 HRESULT rc = S_OK;
9135
9136 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9137 it != data.llStorageControllers.end();
9138 ++it)
9139 {
9140 const settings::StorageController &ctlData = *it;
9141
9142 ComObjPtr<StorageController> pCtl;
9143 /* Try to find one with the name first. */
9144 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9145 if (SUCCEEDED(rc))
9146 return setError(VBOX_E_OBJECT_IN_USE,
9147 tr("Storage controller named '%s' already exists"),
9148 ctlData.strName.c_str());
9149
9150 pCtl.createObject();
9151 rc = pCtl->init(this,
9152 ctlData.strName,
9153 ctlData.storageBus,
9154 ctlData.ulInstance,
9155 ctlData.fBootable);
9156 if (FAILED(rc)) return rc;
9157
9158 mStorageControllers->push_back(pCtl);
9159
9160 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9161 if (FAILED(rc)) return rc;
9162
9163 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9164 if (FAILED(rc)) return rc;
9165
9166 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9167 if (FAILED(rc)) return rc;
9168
9169 /* Set IDE emulation settings (only for AHCI controller). */
9170 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9171 {
9172 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9173 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9174 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9175 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9176 )
9177 return rc;
9178 }
9179
9180 /* Load the attached devices now. */
9181 rc = i_loadStorageDevices(pCtl,
9182 ctlData,
9183 puuidRegistry,
9184 puuidSnapshot);
9185 if (FAILED(rc)) return rc;
9186 }
9187
9188 return S_OK;
9189}
9190
9191/**
9192 * Called from i_loadStorageControllers for a controller's devices.
9193 *
9194 * @param aStorageController
9195 * @param data
9196 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9197 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9198 * @return
9199 */
9200HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9201 const settings::StorageController &data,
9202 const Guid *puuidRegistry,
9203 const Guid *puuidSnapshot)
9204{
9205 HRESULT rc = S_OK;
9206
9207 /* paranoia: detect duplicate attachments */
9208 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9209 it != data.llAttachedDevices.end();
9210 ++it)
9211 {
9212 const settings::AttachedDevice &ad = *it;
9213
9214 for (settings::AttachedDevicesList::const_iterator it2 = it;
9215 it2 != data.llAttachedDevices.end();
9216 ++it2)
9217 {
9218 if (it == it2)
9219 continue;
9220
9221 const settings::AttachedDevice &ad2 = *it2;
9222
9223 if ( ad.lPort == ad2.lPort
9224 && ad.lDevice == ad2.lDevice)
9225 {
9226 return setError(E_FAIL,
9227 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9228 aStorageController->i_getName().c_str(),
9229 ad.lPort,
9230 ad.lDevice,
9231 mUserData->s.strName.c_str());
9232 }
9233 }
9234 }
9235
9236 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9237 it != data.llAttachedDevices.end();
9238 ++it)
9239 {
9240 const settings::AttachedDevice &dev = *it;
9241 ComObjPtr<Medium> medium;
9242
9243 switch (dev.deviceType)
9244 {
9245 case DeviceType_Floppy:
9246 case DeviceType_DVD:
9247 if (dev.strHostDriveSrc.isNotEmpty())
9248 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9249 false /* fRefresh */, medium);
9250 else
9251 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9252 dev.uuid,
9253 false /* fRefresh */,
9254 false /* aSetError */,
9255 medium);
9256 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9257 // This is not an error. The host drive or UUID might have vanished, so just go
9258 // ahead without this removeable medium attachment
9259 rc = S_OK;
9260 break;
9261
9262 case DeviceType_HardDisk:
9263 {
9264 /* find a hard disk by UUID */
9265 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9266 if (FAILED(rc))
9267 {
9268 if (i_isSnapshotMachine())
9269 {
9270 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9271 // so the user knows that the bad disk is in a snapshot somewhere
9272 com::ErrorInfo info;
9273 return setError(E_FAIL,
9274 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9275 puuidSnapshot->raw(),
9276 info.getText().raw());
9277 }
9278 else
9279 return rc;
9280 }
9281
9282 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9283
9284 if (medium->i_getType() == MediumType_Immutable)
9285 {
9286 if (i_isSnapshotMachine())
9287 return setError(E_FAIL,
9288 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9289 "of the virtual machine '%s' ('%s')"),
9290 medium->i_getLocationFull().c_str(),
9291 dev.uuid.raw(),
9292 puuidSnapshot->raw(),
9293 mUserData->s.strName.c_str(),
9294 mData->m_strConfigFileFull.c_str());
9295
9296 return setError(E_FAIL,
9297 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9298 medium->i_getLocationFull().c_str(),
9299 dev.uuid.raw(),
9300 mUserData->s.strName.c_str(),
9301 mData->m_strConfigFileFull.c_str());
9302 }
9303
9304 if (medium->i_getType() == MediumType_MultiAttach)
9305 {
9306 if (i_isSnapshotMachine())
9307 return setError(E_FAIL,
9308 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9309 "of the virtual machine '%s' ('%s')"),
9310 medium->i_getLocationFull().c_str(),
9311 dev.uuid.raw(),
9312 puuidSnapshot->raw(),
9313 mUserData->s.strName.c_str(),
9314 mData->m_strConfigFileFull.c_str());
9315
9316 return setError(E_FAIL,
9317 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9318 medium->i_getLocationFull().c_str(),
9319 dev.uuid.raw(),
9320 mUserData->s.strName.c_str(),
9321 mData->m_strConfigFileFull.c_str());
9322 }
9323
9324 if ( !i_isSnapshotMachine()
9325 && medium->i_getChildren().size() != 0
9326 )
9327 return setError(E_FAIL,
9328 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9329 "because it has %d differencing child hard disks"),
9330 medium->i_getLocationFull().c_str(),
9331 dev.uuid.raw(),
9332 mUserData->s.strName.c_str(),
9333 mData->m_strConfigFileFull.c_str(),
9334 medium->i_getChildren().size());
9335
9336 if (i_findAttachment(mMediaData->mAttachments,
9337 medium))
9338 return setError(E_FAIL,
9339 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9340 medium->i_getLocationFull().c_str(),
9341 dev.uuid.raw(),
9342 mUserData->s.strName.c_str(),
9343 mData->m_strConfigFileFull.c_str());
9344
9345 break;
9346 }
9347
9348 default:
9349 return setError(E_FAIL,
9350 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9351 medium->i_getLocationFull().c_str(),
9352 mUserData->s.strName.c_str(),
9353 mData->m_strConfigFileFull.c_str());
9354 }
9355
9356 if (FAILED(rc))
9357 break;
9358
9359 /* Bandwidth groups are loaded at this point. */
9360 ComObjPtr<BandwidthGroup> pBwGroup;
9361
9362 if (!dev.strBwGroup.isEmpty())
9363 {
9364 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9365 if (FAILED(rc))
9366 return setError(E_FAIL,
9367 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9368 medium->i_getLocationFull().c_str(),
9369 dev.strBwGroup.c_str(),
9370 mUserData->s.strName.c_str(),
9371 mData->m_strConfigFileFull.c_str());
9372 pBwGroup->i_reference();
9373 }
9374
9375 const Bstr controllerName = aStorageController->i_getName();
9376 ComObjPtr<MediumAttachment> pAttachment;
9377 pAttachment.createObject();
9378 rc = pAttachment->init(this,
9379 medium,
9380 controllerName,
9381 dev.lPort,
9382 dev.lDevice,
9383 dev.deviceType,
9384 false,
9385 dev.fPassThrough,
9386 dev.fTempEject,
9387 dev.fNonRotational,
9388 dev.fDiscard,
9389 dev.fHotPluggable,
9390 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9391 if (FAILED(rc)) break;
9392
9393 /* associate the medium with this machine and snapshot */
9394 if (!medium.isNull())
9395 {
9396 AutoCaller medCaller(medium);
9397 if (FAILED(medCaller.rc())) return medCaller.rc();
9398 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9399
9400 if (i_isSnapshotMachine())
9401 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9402 else
9403 rc = medium->i_addBackReference(mData->mUuid);
9404 /* If the medium->addBackReference fails it sets an appropriate
9405 * error message, so no need to do any guesswork here. */
9406
9407 if (puuidRegistry)
9408 // caller wants registry ID to be set on all attached media (OVF import case)
9409 medium->i_addRegistry(*puuidRegistry);
9410 }
9411
9412 if (FAILED(rc))
9413 break;
9414
9415 /* back up mMediaData to let registeredInit() properly rollback on failure
9416 * (= limited accessibility) */
9417 i_setModified(IsModified_Storage);
9418 mMediaData.backup();
9419 mMediaData->mAttachments.push_back(pAttachment);
9420 }
9421
9422 return rc;
9423}
9424
9425/**
9426 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9427 *
9428 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9429 * @param aSnapshot where to return the found snapshot
9430 * @param aSetError true to set extended error info on failure
9431 */
9432HRESULT Machine::i_findSnapshotById(const Guid &aId,
9433 ComObjPtr<Snapshot> &aSnapshot,
9434 bool aSetError /* = false */)
9435{
9436 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9437
9438 if (!mData->mFirstSnapshot)
9439 {
9440 if (aSetError)
9441 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9442 return E_FAIL;
9443 }
9444
9445 if (aId.isZero())
9446 aSnapshot = mData->mFirstSnapshot;
9447 else
9448 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9449
9450 if (!aSnapshot)
9451 {
9452 if (aSetError)
9453 return setError(E_FAIL,
9454 tr("Could not find a snapshot with UUID {%s}"),
9455 aId.toString().c_str());
9456 return E_FAIL;
9457 }
9458
9459 return S_OK;
9460}
9461
9462/**
9463 * Returns the snapshot with the given name or fails of no such snapshot.
9464 *
9465 * @param aName snapshot name to find
9466 * @param aSnapshot where to return the found snapshot
9467 * @param aSetError true to set extended error info on failure
9468 */
9469HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9470 ComObjPtr<Snapshot> &aSnapshot,
9471 bool aSetError /* = false */)
9472{
9473 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9474
9475 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9476
9477 if (!mData->mFirstSnapshot)
9478 {
9479 if (aSetError)
9480 return setError(VBOX_E_OBJECT_NOT_FOUND,
9481 tr("This machine does not have any snapshots"));
9482 return VBOX_E_OBJECT_NOT_FOUND;
9483 }
9484
9485 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9486
9487 if (!aSnapshot)
9488 {
9489 if (aSetError)
9490 return setError(VBOX_E_OBJECT_NOT_FOUND,
9491 tr("Could not find a snapshot named '%s'"), strName.c_str());
9492 return VBOX_E_OBJECT_NOT_FOUND;
9493 }
9494
9495 return S_OK;
9496}
9497
9498/**
9499 * Returns a storage controller object with the given name.
9500 *
9501 * @param aName storage controller name to find
9502 * @param aStorageController where to return the found storage controller
9503 * @param aSetError true to set extended error info on failure
9504 */
9505HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9506 ComObjPtr<StorageController> &aStorageController,
9507 bool aSetError /* = false */)
9508{
9509 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9510
9511 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9512 it != mStorageControllers->end();
9513 ++it)
9514 {
9515 if ((*it)->i_getName() == aName)
9516 {
9517 aStorageController = (*it);
9518 return S_OK;
9519 }
9520 }
9521
9522 if (aSetError)
9523 return setError(VBOX_E_OBJECT_NOT_FOUND,
9524 tr("Could not find a storage controller named '%s'"),
9525 aName.c_str());
9526 return VBOX_E_OBJECT_NOT_FOUND;
9527}
9528
9529/**
9530 * Returns a USB controller object with the given name.
9531 *
9532 * @param aName USB controller name to find
9533 * @param aUSBController where to return the found USB controller
9534 * @param aSetError true to set extended error info on failure
9535 */
9536HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9537 ComObjPtr<USBController> &aUSBController,
9538 bool aSetError /* = false */)
9539{
9540 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9541
9542 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9543 it != mUSBControllers->end();
9544 ++it)
9545 {
9546 if ((*it)->i_getName() == aName)
9547 {
9548 aUSBController = (*it);
9549 return S_OK;
9550 }
9551 }
9552
9553 if (aSetError)
9554 return setError(VBOX_E_OBJECT_NOT_FOUND,
9555 tr("Could not find a storage controller named '%s'"),
9556 aName.c_str());
9557 return VBOX_E_OBJECT_NOT_FOUND;
9558}
9559
9560/**
9561 * Returns the number of USB controller instance of the given type.
9562 *
9563 * @param enmType USB controller type.
9564 */
9565ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9566{
9567 ULONG cCtrls = 0;
9568
9569 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9570 it != mUSBControllers->end();
9571 ++it)
9572 {
9573 if ((*it)->i_getControllerType() == enmType)
9574 cCtrls++;
9575 }
9576
9577 return cCtrls;
9578}
9579
9580HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9581 MediaData::AttachmentList &atts)
9582{
9583 AutoCaller autoCaller(this);
9584 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9585
9586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9587
9588 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9589 it != mMediaData->mAttachments.end();
9590 ++it)
9591 {
9592 const ComObjPtr<MediumAttachment> &pAtt = *it;
9593 // should never happen, but deal with NULL pointers in the list.
9594 AssertStmt(!pAtt.isNull(), continue);
9595
9596 // getControllerName() needs caller+read lock
9597 AutoCaller autoAttCaller(pAtt);
9598 if (FAILED(autoAttCaller.rc()))
9599 {
9600 atts.clear();
9601 return autoAttCaller.rc();
9602 }
9603 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9604
9605 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9606 atts.push_back(pAtt);
9607 }
9608
9609 return S_OK;
9610}
9611
9612
9613/**
9614 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9615 * file if the machine name was changed and about creating a new settings file
9616 * if this is a new machine.
9617 *
9618 * @note Must be never called directly but only from #saveSettings().
9619 */
9620HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9621{
9622 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9623
9624 HRESULT rc = S_OK;
9625
9626 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9627
9628 /// @todo need to handle primary group change, too
9629
9630 /* attempt to rename the settings file if machine name is changed */
9631 if ( mUserData->s.fNameSync
9632 && mUserData.isBackedUp()
9633 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9634 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9635 )
9636 {
9637 bool dirRenamed = false;
9638 bool fileRenamed = false;
9639
9640 Utf8Str configFile, newConfigFile;
9641 Utf8Str configFilePrev, newConfigFilePrev;
9642 Utf8Str configDir, newConfigDir;
9643
9644 do
9645 {
9646 int vrc = VINF_SUCCESS;
9647
9648 Utf8Str name = mUserData.backedUpData()->s.strName;
9649 Utf8Str newName = mUserData->s.strName;
9650 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9651 if (group == "/")
9652 group.setNull();
9653 Utf8Str newGroup = mUserData->s.llGroups.front();
9654 if (newGroup == "/")
9655 newGroup.setNull();
9656
9657 configFile = mData->m_strConfigFileFull;
9658
9659 /* first, rename the directory if it matches the group and machine name */
9660 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9661 group.c_str(), RTPATH_DELIMITER, name.c_str());
9662 /** @todo hack, make somehow use of ComposeMachineFilename */
9663 if (mUserData->s.fDirectoryIncludesUUID)
9664 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9665 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9666 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9667 /** @todo hack, make somehow use of ComposeMachineFilename */
9668 if (mUserData->s.fDirectoryIncludesUUID)
9669 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9670 configDir = configFile;
9671 configDir.stripFilename();
9672 newConfigDir = configDir;
9673 if ( configDir.length() >= groupPlusName.length()
9674 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9675 groupPlusName.c_str()))
9676 {
9677 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9678 Utf8Str newConfigBaseDir(newConfigDir);
9679 newConfigDir.append(newGroupPlusName);
9680 /* consistency: use \ if appropriate on the platform */
9681 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9682 /* new dir and old dir cannot be equal here because of 'if'
9683 * above and because name != newName */
9684 Assert(configDir != newConfigDir);
9685 if (!fSettingsFileIsNew)
9686 {
9687 /* perform real rename only if the machine is not new */
9688 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9689 if ( vrc == VERR_FILE_NOT_FOUND
9690 || vrc == VERR_PATH_NOT_FOUND)
9691 {
9692 /* create the parent directory, then retry renaming */
9693 Utf8Str parent(newConfigDir);
9694 parent.stripFilename();
9695 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9696 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9697 }
9698 if (RT_FAILURE(vrc))
9699 {
9700 rc = setError(E_FAIL,
9701 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9702 configDir.c_str(),
9703 newConfigDir.c_str(),
9704 vrc);
9705 break;
9706 }
9707 /* delete subdirectories which are no longer needed */
9708 Utf8Str dir(configDir);
9709 dir.stripFilename();
9710 while (dir != newConfigBaseDir && dir != ".")
9711 {
9712 vrc = RTDirRemove(dir.c_str());
9713 if (RT_FAILURE(vrc))
9714 break;
9715 dir.stripFilename();
9716 }
9717 dirRenamed = true;
9718 }
9719 }
9720
9721 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9722 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9723
9724 /* then try to rename the settings file itself */
9725 if (newConfigFile != configFile)
9726 {
9727 /* get the path to old settings file in renamed directory */
9728 configFile = Utf8StrFmt("%s%c%s",
9729 newConfigDir.c_str(),
9730 RTPATH_DELIMITER,
9731 RTPathFilename(configFile.c_str()));
9732 if (!fSettingsFileIsNew)
9733 {
9734 /* perform real rename only if the machine is not new */
9735 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9736 if (RT_FAILURE(vrc))
9737 {
9738 rc = setError(E_FAIL,
9739 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9740 configFile.c_str(),
9741 newConfigFile.c_str(),
9742 vrc);
9743 break;
9744 }
9745 fileRenamed = true;
9746 configFilePrev = configFile;
9747 configFilePrev += "-prev";
9748 newConfigFilePrev = newConfigFile;
9749 newConfigFilePrev += "-prev";
9750 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9751 }
9752 }
9753
9754 // update m_strConfigFileFull amd mConfigFile
9755 mData->m_strConfigFileFull = newConfigFile;
9756 // compute the relative path too
9757 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9758
9759 // store the old and new so that VirtualBox::i_saveSettings() can update
9760 // the media registry
9761 if ( mData->mRegistered
9762 && (configDir != newConfigDir || configFile != newConfigFile))
9763 {
9764 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9765
9766 if (pfNeedsGlobalSaveSettings)
9767 *pfNeedsGlobalSaveSettings = true;
9768 }
9769
9770 // in the saved state file path, replace the old directory with the new directory
9771 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9772 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9773
9774 // and do the same thing for the saved state file paths of all the online snapshots
9775 if (mData->mFirstSnapshot)
9776 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9777 newConfigDir.c_str());
9778 }
9779 while (0);
9780
9781 if (FAILED(rc))
9782 {
9783 /* silently try to rename everything back */
9784 if (fileRenamed)
9785 {
9786 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9787 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9788 }
9789 if (dirRenamed)
9790 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9791 }
9792
9793 if (FAILED(rc)) return rc;
9794 }
9795
9796 if (fSettingsFileIsNew)
9797 {
9798 /* create a virgin config file */
9799 int vrc = VINF_SUCCESS;
9800
9801 /* ensure the settings directory exists */
9802 Utf8Str path(mData->m_strConfigFileFull);
9803 path.stripFilename();
9804 if (!RTDirExists(path.c_str()))
9805 {
9806 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9807 if (RT_FAILURE(vrc))
9808 {
9809 return setError(E_FAIL,
9810 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9811 path.c_str(),
9812 vrc);
9813 }
9814 }
9815
9816 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9817 path = Utf8Str(mData->m_strConfigFileFull);
9818 RTFILE f = NIL_RTFILE;
9819 vrc = RTFileOpen(&f, path.c_str(),
9820 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9821 if (RT_FAILURE(vrc))
9822 return setError(E_FAIL,
9823 tr("Could not create the settings file '%s' (%Rrc)"),
9824 path.c_str(),
9825 vrc);
9826 RTFileClose(f);
9827 }
9828
9829 return rc;
9830}
9831
9832/**
9833 * Saves and commits machine data, user data and hardware data.
9834 *
9835 * Note that on failure, the data remains uncommitted.
9836 *
9837 * @a aFlags may combine the following flags:
9838 *
9839 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9840 * Used when saving settings after an operation that makes them 100%
9841 * correspond to the settings from the current snapshot.
9842 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9843 * #isReallyModified() returns false. This is necessary for cases when we
9844 * change machine data directly, not through the backup()/commit() mechanism.
9845 * - SaveS_Force: settings will be saved without doing a deep compare of the
9846 * settings structures. This is used when this is called because snapshots
9847 * have changed to avoid the overhead of the deep compare.
9848 *
9849 * @note Must be called from under this object's write lock. Locks children for
9850 * writing.
9851 *
9852 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9853 * initialized to false and that will be set to true by this function if
9854 * the caller must invoke VirtualBox::i_saveSettings() because the global
9855 * settings have changed. This will happen if a machine rename has been
9856 * saved and the global machine and media registries will therefore need
9857 * updating.
9858 */
9859HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9860 int aFlags /*= 0*/)
9861{
9862 LogFlowThisFuncEnter();
9863
9864 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9865
9866 /* make sure child objects are unable to modify the settings while we are
9867 * saving them */
9868 i_ensureNoStateDependencies();
9869
9870 AssertReturn(!i_isSnapshotMachine(),
9871 E_FAIL);
9872
9873 HRESULT rc = S_OK;
9874 bool fNeedsWrite = false;
9875
9876 /* First, prepare to save settings. It will care about renaming the
9877 * settings directory and file if the machine name was changed and about
9878 * creating a new settings file if this is a new machine. */
9879 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9880 if (FAILED(rc)) return rc;
9881
9882 // keep a pointer to the current settings structures
9883 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9884 settings::MachineConfigFile *pNewConfig = NULL;
9885
9886 try
9887 {
9888 // make a fresh one to have everyone write stuff into
9889 pNewConfig = new settings::MachineConfigFile(NULL);
9890 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9891
9892 // now go and copy all the settings data from COM to the settings structures
9893 // (this calles i_saveSettings() on all the COM objects in the machine)
9894 i_copyMachineDataToSettings(*pNewConfig);
9895
9896 if (aFlags & SaveS_ResetCurStateModified)
9897 {
9898 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9899 mData->mCurrentStateModified = FALSE;
9900 fNeedsWrite = true; // always, no need to compare
9901 }
9902 else if (aFlags & SaveS_Force)
9903 {
9904 fNeedsWrite = true; // always, no need to compare
9905 }
9906 else
9907 {
9908 if (!mData->mCurrentStateModified)
9909 {
9910 // do a deep compare of the settings that we just saved with the settings
9911 // previously stored in the config file; this invokes MachineConfigFile::operator==
9912 // which does a deep compare of all the settings, which is expensive but less expensive
9913 // than writing out XML in vain
9914 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9915
9916 // could still be modified if any settings changed
9917 mData->mCurrentStateModified = fAnySettingsChanged;
9918
9919 fNeedsWrite = fAnySettingsChanged;
9920 }
9921 else
9922 fNeedsWrite = true;
9923 }
9924
9925 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9926
9927 if (fNeedsWrite)
9928 // now spit it all out!
9929 pNewConfig->write(mData->m_strConfigFileFull);
9930
9931 mData->pMachineConfigFile = pNewConfig;
9932 delete pOldConfig;
9933 i_commit();
9934
9935 // after saving settings, we are no longer different from the XML on disk
9936 mData->flModifications = 0;
9937 }
9938 catch (HRESULT err)
9939 {
9940 // we assume that error info is set by the thrower
9941 rc = err;
9942
9943 // restore old config
9944 delete pNewConfig;
9945 mData->pMachineConfigFile = pOldConfig;
9946 }
9947 catch (...)
9948 {
9949 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9950 }
9951
9952 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9953 {
9954 /* Fire the data change event, even on failure (since we've already
9955 * committed all data). This is done only for SessionMachines because
9956 * mutable Machine instances are always not registered (i.e. private
9957 * to the client process that creates them) and thus don't need to
9958 * inform callbacks. */
9959 if (i_isSessionMachine())
9960 mParent->i_onMachineDataChange(mData->mUuid);
9961 }
9962
9963 LogFlowThisFunc(("rc=%08X\n", rc));
9964 LogFlowThisFuncLeave();
9965 return rc;
9966}
9967
9968/**
9969 * Implementation for saving the machine settings into the given
9970 * settings::MachineConfigFile instance. This copies machine extradata
9971 * from the previous machine config file in the instance data, if any.
9972 *
9973 * This gets called from two locations:
9974 *
9975 * -- Machine::i_saveSettings(), during the regular XML writing;
9976 *
9977 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9978 * exported to OVF and we write the VirtualBox proprietary XML
9979 * into a <vbox:Machine> tag.
9980 *
9981 * This routine fills all the fields in there, including snapshots, *except*
9982 * for the following:
9983 *
9984 * -- fCurrentStateModified. There is some special logic associated with that.
9985 *
9986 * The caller can then call MachineConfigFile::write() or do something else
9987 * with it.
9988 *
9989 * Caller must hold the machine lock!
9990 *
9991 * This throws XML errors and HRESULT, so the caller must have a catch block!
9992 */
9993void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9994{
9995 // deep copy extradata
9996 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9997
9998 config.uuid = mData->mUuid;
9999
10000 // copy name, description, OS type, teleport, UTC etc.
10001 config.machineUserData = mUserData->s;
10002
10003 // Encode the Icon Override data from Machine and store on config userdata.
10004 std::vector<BYTE> iconByte;
10005 getIcon(iconByte);
10006 ssize_t cbData = iconByte.size();
10007 if (cbData > 0)
10008 {
10009 ssize_t cchOut = RTBase64EncodedLength(cbData);
10010 Utf8Str strIconData;
10011 strIconData.reserve(cchOut+1);
10012 int vrc = RTBase64Encode(&iconByte.front(), cbData,
10013 strIconData.mutableRaw(), strIconData.capacity(),
10014 NULL);
10015 if (RT_FAILURE(vrc))
10016 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10017 strIconData.jolt();
10018 config.machineUserData.ovIcon = strIconData;
10019 }
10020 else
10021 config.machineUserData.ovIcon.setNull();
10022
10023 if ( mData->mMachineState == MachineState_Saved
10024 || mData->mMachineState == MachineState_Restoring
10025 // when doing certain snapshot operations we may or may not have
10026 // a saved state in the current state, so keep everything as is
10027 || ( ( mData->mMachineState == MachineState_Snapshotting
10028 || mData->mMachineState == MachineState_DeletingSnapshot
10029 || mData->mMachineState == MachineState_RestoringSnapshot)
10030 && (!mSSData->strStateFilePath.isEmpty())
10031 )
10032 )
10033 {
10034 Assert(!mSSData->strStateFilePath.isEmpty());
10035 /* try to make the file name relative to the settings file dir */
10036 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10037 }
10038 else
10039 {
10040 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10041 config.strStateFile.setNull();
10042 }
10043
10044 if (mData->mCurrentSnapshot)
10045 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10046 else
10047 config.uuidCurrentSnapshot.clear();
10048
10049 config.timeLastStateChange = mData->mLastStateChange;
10050 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10051 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10052
10053 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10054 if (FAILED(rc)) throw rc;
10055
10056 rc = i_saveStorageControllers(config.storageMachine);
10057 if (FAILED(rc)) throw rc;
10058
10059 // save machine's media registry if this is VirtualBox 4.0 or later
10060 if (config.canHaveOwnMediaRegistry())
10061 {
10062 // determine machine folder
10063 Utf8Str strMachineFolder = i_getSettingsFileFull();
10064 strMachineFolder.stripFilename();
10065 mParent->i_saveMediaRegistry(config.mediaRegistry,
10066 i_getId(), // only media with registry ID == machine UUID
10067 strMachineFolder);
10068 // this throws HRESULT
10069 }
10070
10071 // save snapshots
10072 rc = i_saveAllSnapshots(config);
10073 if (FAILED(rc)) throw rc;
10074}
10075
10076/**
10077 * Saves all snapshots of the machine into the given machine config file. Called
10078 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10079 * @param config
10080 * @return
10081 */
10082HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10083{
10084 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10085
10086 HRESULT rc = S_OK;
10087
10088 try
10089 {
10090 config.llFirstSnapshot.clear();
10091
10092 if (mData->mFirstSnapshot)
10093 {
10094 // the settings use a list for "the first snapshot"
10095 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
10096
10097 // get reference to the snapshot on the list and work on that
10098 // element straight in the list to avoid excessive copying later
10099 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10100 if (FAILED(rc)) throw rc;
10101 }
10102
10103// if (mType == IsSessionMachine)
10104// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10105
10106 }
10107 catch (HRESULT err)
10108 {
10109 /* we assume that error info is set by the thrower */
10110 rc = err;
10111 }
10112 catch (...)
10113 {
10114 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10115 }
10116
10117 return rc;
10118}
10119
10120/**
10121 * Saves the VM hardware configuration. It is assumed that the
10122 * given node is empty.
10123 *
10124 * @param data Reference to the settings object for the hardware config.
10125 * @param pDbg Pointer to the settings object for the debugging config
10126 * which happens to live in mHWData.
10127 * @param pAutostart Pointer to the settings object for the autostart config
10128 * which happens to live in mHWData.
10129 */
10130HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10131 settings::Autostart *pAutostart)
10132{
10133 HRESULT rc = S_OK;
10134
10135 try
10136 {
10137 /* The hardware version attribute (optional).
10138 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10139 if ( mHWData->mHWVersion == "1"
10140 && mSSData->strStateFilePath.isEmpty()
10141 )
10142 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10143 other point needs to be found where this can be done. */
10144
10145 data.strVersion = mHWData->mHWVersion;
10146 data.uuid = mHWData->mHardwareUUID;
10147
10148 // CPU
10149 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10150 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10151 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10152 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10153 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10154 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10155 data.fPAE = !!mHWData->mPAEEnabled;
10156 data.enmLongMode = mHWData->mLongMode;
10157 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10158 data.cCPUs = mHWData->mCPUCount;
10159 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10160 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10161 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10162
10163 data.llCpus.clear();
10164 if (data.fCpuHotPlug)
10165 {
10166 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10167 {
10168 if (mHWData->mCPUAttached[idx])
10169 {
10170 settings::Cpu cpu;
10171 cpu.ulId = idx;
10172 data.llCpus.push_back(cpu);
10173 }
10174 }
10175 }
10176
10177 /* Standard and Extended CPUID leafs. */
10178 data.llCpuIdLeafs.clear();
10179 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10180 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10181 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10182 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10183 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10184 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10185
10186 // memory
10187 data.ulMemorySizeMB = mHWData->mMemorySize;
10188 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10189
10190 // firmware
10191 data.firmwareType = mHWData->mFirmwareType;
10192
10193 // HID
10194 data.pointingHIDType = mHWData->mPointingHIDType;
10195 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10196
10197 // chipset
10198 data.chipsetType = mHWData->mChipsetType;
10199
10200 // paravirt
10201 data.paravirtProvider = mHWData->mParavirtProvider;
10202
10203
10204 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10205
10206 // HPET
10207 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10208
10209 // boot order
10210 data.mapBootOrder.clear();
10211 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10212 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10213
10214 // display
10215 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10216 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10217 data.cMonitors = mHWData->mMonitorCount;
10218 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10219 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10220 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10221 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10222 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10223 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10224 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10225 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10226 {
10227 if (mHWData->maVideoCaptureScreens[i])
10228 ASMBitSet(&data.u64VideoCaptureScreens, i);
10229 else
10230 ASMBitClear(&data.u64VideoCaptureScreens, i);
10231 }
10232 /* store relative video capture file if possible */
10233 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10234
10235 /* VRDEServer settings (optional) */
10236 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10237 if (FAILED(rc)) throw rc;
10238
10239 /* BIOS (required) */
10240 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10241 if (FAILED(rc)) throw rc;
10242
10243 /* USB Controller (required) */
10244 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10245 {
10246 ComObjPtr<USBController> ctrl = *it;
10247 settings::USBController settingsCtrl;
10248
10249 settingsCtrl.strName = ctrl->i_getName();
10250 settingsCtrl.enmType = ctrl->i_getControllerType();
10251
10252 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10253 }
10254
10255 /* USB device filters (required) */
10256 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10257 if (FAILED(rc)) throw rc;
10258
10259 /* Network adapters (required) */
10260 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10261 data.llNetworkAdapters.clear();
10262 /* Write out only the nominal number of network adapters for this
10263 * chipset type. Since Machine::commit() hasn't been called there
10264 * may be extra NIC settings in the vector. */
10265 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10266 {
10267 settings::NetworkAdapter nic;
10268 nic.ulSlot = (uint32_t)slot;
10269 /* paranoia check... must not be NULL, but must not crash either. */
10270 if (mNetworkAdapters[slot])
10271 {
10272 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10273 if (FAILED(rc)) throw rc;
10274
10275 data.llNetworkAdapters.push_back(nic);
10276 }
10277 }
10278
10279 /* Serial ports */
10280 data.llSerialPorts.clear();
10281 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10282 {
10283 settings::SerialPort s;
10284 s.ulSlot = slot;
10285 rc = mSerialPorts[slot]->i_saveSettings(s);
10286 if (FAILED(rc)) return rc;
10287
10288 data.llSerialPorts.push_back(s);
10289 }
10290
10291 /* Parallel ports */
10292 data.llParallelPorts.clear();
10293 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10294 {
10295 settings::ParallelPort p;
10296 p.ulSlot = slot;
10297 rc = mParallelPorts[slot]->i_saveSettings(p);
10298 if (FAILED(rc)) return rc;
10299
10300 data.llParallelPorts.push_back(p);
10301 }
10302
10303 /* Audio adapter */
10304 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10305 if (FAILED(rc)) return rc;
10306
10307 /* Shared folders */
10308 data.llSharedFolders.clear();
10309 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10310 it != mHWData->mSharedFolders.end();
10311 ++it)
10312 {
10313 SharedFolder *pSF = *it;
10314 AutoCaller sfCaller(pSF);
10315 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10316 settings::SharedFolder sf;
10317 sf.strName = pSF->i_getName();
10318 sf.strHostPath = pSF->i_getHostPath();
10319 sf.fWritable = !!pSF->i_isWritable();
10320 sf.fAutoMount = !!pSF->i_isAutoMounted();
10321
10322 data.llSharedFolders.push_back(sf);
10323 }
10324
10325 // clipboard
10326 data.clipboardMode = mHWData->mClipboardMode;
10327
10328 // drag'n'drop
10329 data.dndMode = mHWData->mDnDMode;
10330
10331 /* Guest */
10332 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10333
10334 // IO settings
10335 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10336 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10337
10338 /* BandwidthControl (required) */
10339 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10340 if (FAILED(rc)) throw rc;
10341
10342 /* Host PCI devices */
10343 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10344 it != mHWData->mPCIDeviceAssignments.end();
10345 ++it)
10346 {
10347 ComObjPtr<PCIDeviceAttachment> pda = *it;
10348 settings::HostPCIDeviceAttachment hpda;
10349
10350 rc = pda->i_saveSettings(hpda);
10351 if (FAILED(rc)) throw rc;
10352
10353 data.pciAttachments.push_back(hpda);
10354 }
10355
10356
10357 // guest properties
10358 data.llGuestProperties.clear();
10359#ifdef VBOX_WITH_GUEST_PROPS
10360 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10361 it != mHWData->mGuestProperties.end();
10362 ++it)
10363 {
10364 HWData::GuestProperty property = it->second;
10365
10366 /* Remove transient guest properties at shutdown unless we
10367 * are saving state. Note that restoring snapshot intentionally
10368 * keeps them, they will be removed if appropriate once the final
10369 * machine state is set (as crashes etc. need to work). */
10370 if ( ( mData->mMachineState == MachineState_PoweredOff
10371 || mData->mMachineState == MachineState_Aborted
10372 || mData->mMachineState == MachineState_Teleported)
10373 && ( property.mFlags & guestProp::TRANSIENT
10374 || property.mFlags & guestProp::TRANSRESET))
10375 continue;
10376 settings::GuestProperty prop;
10377 prop.strName = it->first;
10378 prop.strValue = property.strValue;
10379 prop.timestamp = property.mTimestamp;
10380 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10381 guestProp::writeFlags(property.mFlags, szFlags);
10382 prop.strFlags = szFlags;
10383
10384 data.llGuestProperties.push_back(prop);
10385 }
10386
10387 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10388 /* I presume this doesn't require a backup(). */
10389 mData->mGuestPropertiesModified = FALSE;
10390#endif /* VBOX_WITH_GUEST_PROPS defined */
10391
10392 *pDbg = mHWData->mDebugging;
10393 *pAutostart = mHWData->mAutostart;
10394
10395 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10396 }
10397 catch(std::bad_alloc &)
10398 {
10399 return E_OUTOFMEMORY;
10400 }
10401
10402 AssertComRC(rc);
10403 return rc;
10404}
10405
10406/**
10407 * Saves the storage controller configuration.
10408 *
10409 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10410 */
10411HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10412{
10413 data.llStorageControllers.clear();
10414
10415 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10416 it != mStorageControllers->end();
10417 ++it)
10418 {
10419 HRESULT rc;
10420 ComObjPtr<StorageController> pCtl = *it;
10421
10422 settings::StorageController ctl;
10423 ctl.strName = pCtl->i_getName();
10424 ctl.controllerType = pCtl->i_getControllerType();
10425 ctl.storageBus = pCtl->i_getStorageBus();
10426 ctl.ulInstance = pCtl->i_getInstance();
10427 ctl.fBootable = pCtl->i_getBootable();
10428
10429 /* Save the port count. */
10430 ULONG portCount;
10431 rc = pCtl->COMGETTER(PortCount)(&portCount);
10432 ComAssertComRCRet(rc, rc);
10433 ctl.ulPortCount = portCount;
10434
10435 /* Save fUseHostIOCache */
10436 BOOL fUseHostIOCache;
10437 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10438 ComAssertComRCRet(rc, rc);
10439 ctl.fUseHostIOCache = !!fUseHostIOCache;
10440
10441 /* Save IDE emulation settings. */
10442 if (ctl.controllerType == StorageControllerType_IntelAhci)
10443 {
10444 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10445 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10446 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10447 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10448 )
10449 ComAssertComRCRet(rc, rc);
10450 }
10451
10452 /* save the devices now. */
10453 rc = i_saveStorageDevices(pCtl, ctl);
10454 ComAssertComRCRet(rc, rc);
10455
10456 data.llStorageControllers.push_back(ctl);
10457 }
10458
10459 return S_OK;
10460}
10461
10462/**
10463 * Saves the hard disk configuration.
10464 */
10465HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10466 settings::StorageController &data)
10467{
10468 MediaData::AttachmentList atts;
10469
10470 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10471 if (FAILED(rc)) return rc;
10472
10473 data.llAttachedDevices.clear();
10474 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10475 it != atts.end();
10476 ++it)
10477 {
10478 settings::AttachedDevice dev;
10479 IMediumAttachment *iA = *it;
10480 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10481 Medium *pMedium = pAttach->i_getMedium();
10482
10483 dev.deviceType = pAttach->i_getType();
10484 dev.lPort = pAttach->i_getPort();
10485 dev.lDevice = pAttach->i_getDevice();
10486 dev.fPassThrough = pAttach->i_getPassthrough();
10487 dev.fHotPluggable = pAttach->i_getHotPluggable();
10488 if (pMedium)
10489 {
10490 if (pMedium->i_isHostDrive())
10491 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10492 else
10493 dev.uuid = pMedium->i_getId();
10494 dev.fTempEject = pAttach->i_getTempEject();
10495 dev.fNonRotational = pAttach->i_getNonRotational();
10496 dev.fDiscard = pAttach->i_getDiscard();
10497 }
10498
10499 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10500
10501 data.llAttachedDevices.push_back(dev);
10502 }
10503
10504 return S_OK;
10505}
10506
10507/**
10508 * Saves machine state settings as defined by aFlags
10509 * (SaveSTS_* values).
10510 *
10511 * @param aFlags Combination of SaveSTS_* flags.
10512 *
10513 * @note Locks objects for writing.
10514 */
10515HRESULT Machine::i_saveStateSettings(int aFlags)
10516{
10517 if (aFlags == 0)
10518 return S_OK;
10519
10520 AutoCaller autoCaller(this);
10521 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10522
10523 /* This object's write lock is also necessary to serialize file access
10524 * (prevent concurrent reads and writes) */
10525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10526
10527 HRESULT rc = S_OK;
10528
10529 Assert(mData->pMachineConfigFile);
10530
10531 try
10532 {
10533 if (aFlags & SaveSTS_CurStateModified)
10534 mData->pMachineConfigFile->fCurrentStateModified = true;
10535
10536 if (aFlags & SaveSTS_StateFilePath)
10537 {
10538 if (!mSSData->strStateFilePath.isEmpty())
10539 /* try to make the file name relative to the settings file dir */
10540 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10541 else
10542 mData->pMachineConfigFile->strStateFile.setNull();
10543 }
10544
10545 if (aFlags & SaveSTS_StateTimeStamp)
10546 {
10547 Assert( mData->mMachineState != MachineState_Aborted
10548 || mSSData->strStateFilePath.isEmpty());
10549
10550 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10551
10552 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10553//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10554 }
10555
10556 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10557 }
10558 catch (...)
10559 {
10560 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10561 }
10562
10563 return rc;
10564}
10565
10566/**
10567 * Ensures that the given medium is added to a media registry. If this machine
10568 * was created with 4.0 or later, then the machine registry is used. Otherwise
10569 * the global VirtualBox media registry is used.
10570 *
10571 * Caller must NOT hold machine lock, media tree or any medium locks!
10572 *
10573 * @param pMedium
10574 */
10575void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10576{
10577 /* Paranoia checks: do not hold machine or media tree locks. */
10578 AssertReturnVoid(!isWriteLockOnCurrentThread());
10579 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10580
10581 ComObjPtr<Medium> pBase;
10582 {
10583 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10584 pBase = pMedium->i_getBase();
10585 }
10586
10587 /* Paranoia checks: do not hold medium locks. */
10588 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10589 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10590
10591 // decide which medium registry to use now that the medium is attached:
10592 Guid uuid;
10593 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10594 // machine XML is VirtualBox 4.0 or higher:
10595 uuid = i_getId(); // machine UUID
10596 else
10597 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10598
10599 if (pMedium->i_addRegistry(uuid))
10600 mParent->i_markRegistryModified(uuid);
10601
10602 /* For more complex hard disk structures it can happen that the base
10603 * medium isn't yet associated with any medium registry. Do that now. */
10604 if (pMedium != pBase)
10605 {
10606 /* Tree lock needed by Medium::addRegistry when recursing. */
10607 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10608 if (pBase->i_addRegistryRecursive(uuid))
10609 {
10610 treeLock.release();
10611 mParent->i_markRegistryModified(uuid);
10612 }
10613 }
10614}
10615
10616/**
10617 * Creates differencing hard disks for all normal hard disks attached to this
10618 * machine and a new set of attachments to refer to created disks.
10619 *
10620 * Used when taking a snapshot or when deleting the current state. Gets called
10621 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10622 *
10623 * This method assumes that mMediaData contains the original hard disk attachments
10624 * it needs to create diffs for. On success, these attachments will be replaced
10625 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10626 * called to delete created diffs which will also rollback mMediaData and restore
10627 * whatever was backed up before calling this method.
10628 *
10629 * Attachments with non-normal hard disks are left as is.
10630 *
10631 * If @a aOnline is @c false then the original hard disks that require implicit
10632 * diffs will be locked for reading. Otherwise it is assumed that they are
10633 * already locked for writing (when the VM was started). Note that in the latter
10634 * case it is responsibility of the caller to lock the newly created diffs for
10635 * writing if this method succeeds.
10636 *
10637 * @param aProgress Progress object to run (must contain at least as
10638 * many operations left as the number of hard disks
10639 * attached).
10640 * @param aOnline Whether the VM was online prior to this operation.
10641 *
10642 * @note The progress object is not marked as completed, neither on success nor
10643 * on failure. This is a responsibility of the caller.
10644 *
10645 * @note Locks this object and the media tree for writing.
10646 */
10647HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10648 ULONG aWeight,
10649 bool aOnline)
10650{
10651 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10652
10653 AutoCaller autoCaller(this);
10654 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10655
10656 AutoMultiWriteLock2 alock(this->lockHandle(),
10657 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10658
10659 /* must be in a protective state because we release the lock below */
10660 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10661 || mData->mMachineState == MachineState_OnlineSnapshotting
10662 || mData->mMachineState == MachineState_LiveSnapshotting
10663 || mData->mMachineState == MachineState_RestoringSnapshot
10664 || mData->mMachineState == MachineState_DeletingSnapshot
10665 , E_FAIL);
10666
10667 HRESULT rc = S_OK;
10668
10669 // use appropriate locked media map (online or offline)
10670 MediumLockListMap lockedMediaOffline;
10671 MediumLockListMap *lockedMediaMap;
10672 if (aOnline)
10673 lockedMediaMap = &mData->mSession.mLockedMedia;
10674 else
10675 lockedMediaMap = &lockedMediaOffline;
10676
10677 try
10678 {
10679 if (!aOnline)
10680 {
10681 /* lock all attached hard disks early to detect "in use"
10682 * situations before creating actual diffs */
10683 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10684 it != mMediaData->mAttachments.end();
10685 ++it)
10686 {
10687 MediumAttachment* pAtt = *it;
10688 if (pAtt->i_getType() == DeviceType_HardDisk)
10689 {
10690 Medium* pMedium = pAtt->i_getMedium();
10691 Assert(pMedium);
10692
10693 MediumLockList *pMediumLockList(new MediumLockList());
10694 alock.release();
10695 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10696 false /* fMediumLockWrite */,
10697 false /* fMediumLockWriteAll */,
10698 NULL,
10699 *pMediumLockList);
10700 alock.acquire();
10701 if (FAILED(rc))
10702 {
10703 delete pMediumLockList;
10704 throw rc;
10705 }
10706 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10707 if (FAILED(rc))
10708 {
10709 throw setError(rc,
10710 tr("Collecting locking information for all attached media failed"));
10711 }
10712 }
10713 }
10714
10715 /* Now lock all media. If this fails, nothing is locked. */
10716 alock.release();
10717 rc = lockedMediaMap->Lock();
10718 alock.acquire();
10719 if (FAILED(rc))
10720 {
10721 throw setError(rc,
10722 tr("Locking of attached media failed"));
10723 }
10724 }
10725
10726 /* remember the current list (note that we don't use backup() since
10727 * mMediaData may be already backed up) */
10728 MediaData::AttachmentList atts = mMediaData->mAttachments;
10729
10730 /* start from scratch */
10731 mMediaData->mAttachments.clear();
10732
10733 /* go through remembered attachments and create diffs for normal hard
10734 * disks and attach them */
10735 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10736 it != atts.end();
10737 ++it)
10738 {
10739 MediumAttachment* pAtt = *it;
10740
10741 DeviceType_T devType = pAtt->i_getType();
10742 Medium* pMedium = pAtt->i_getMedium();
10743
10744 if ( devType != DeviceType_HardDisk
10745 || pMedium == NULL
10746 || pMedium->i_getType() != MediumType_Normal)
10747 {
10748 /* copy the attachment as is */
10749
10750 /** @todo the progress object created in SessionMachine::TakeSnaphot
10751 * only expects operations for hard disks. Later other
10752 * device types need to show up in the progress as well. */
10753 if (devType == DeviceType_HardDisk)
10754 {
10755 if (pMedium == NULL)
10756 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10757 aWeight); // weight
10758 else
10759 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10760 pMedium->i_getBase()->i_getName().c_str()).raw(),
10761 aWeight); // weight
10762 }
10763
10764 mMediaData->mAttachments.push_back(pAtt);
10765 continue;
10766 }
10767
10768 /* need a diff */
10769 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10770 pMedium->i_getBase()->i_getName().c_str()).raw(),
10771 aWeight); // weight
10772
10773 Utf8Str strFullSnapshotFolder;
10774 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10775
10776 ComObjPtr<Medium> diff;
10777 diff.createObject();
10778 // store the diff in the same registry as the parent
10779 // (this cannot fail here because we can't create implicit diffs for
10780 // unregistered images)
10781 Guid uuidRegistryParent;
10782 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10783 Assert(fInRegistry); NOREF(fInRegistry);
10784 rc = diff->init(mParent,
10785 pMedium->i_getPreferredDiffFormat(),
10786 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10787 uuidRegistryParent,
10788 DeviceType_HardDisk);
10789 if (FAILED(rc)) throw rc;
10790
10791 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10792 * the push_back? Looks like we're going to release medium with the
10793 * wrong kind of lock (general issue with if we fail anywhere at all)
10794 * and an orphaned VDI in the snapshots folder. */
10795
10796 /* update the appropriate lock list */
10797 MediumLockList *pMediumLockList;
10798 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10799 AssertComRCThrowRC(rc);
10800 if (aOnline)
10801 {
10802 alock.release();
10803 /* The currently attached medium will be read-only, change
10804 * the lock type to read. */
10805 rc = pMediumLockList->Update(pMedium, false);
10806 alock.acquire();
10807 AssertComRCThrowRC(rc);
10808 }
10809
10810 /* release the locks before the potentially lengthy operation */
10811 alock.release();
10812 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10813 pMediumLockList,
10814 NULL /* aProgress */,
10815 true /* aWait */);
10816 alock.acquire();
10817 if (FAILED(rc)) throw rc;
10818
10819 /* actual lock list update is done in Medium::commitMedia */
10820
10821 rc = diff->i_addBackReference(mData->mUuid);
10822 AssertComRCThrowRC(rc);
10823
10824 /* add a new attachment */
10825 ComObjPtr<MediumAttachment> attachment;
10826 attachment.createObject();
10827 rc = attachment->init(this,
10828 diff,
10829 pAtt->i_getControllerName(),
10830 pAtt->i_getPort(),
10831 pAtt->i_getDevice(),
10832 DeviceType_HardDisk,
10833 true /* aImplicit */,
10834 false /* aPassthrough */,
10835 false /* aTempEject */,
10836 pAtt->i_getNonRotational(),
10837 pAtt->i_getDiscard(),
10838 pAtt->i_getHotPluggable(),
10839 pAtt->i_getBandwidthGroup());
10840 if (FAILED(rc)) throw rc;
10841
10842 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10843 AssertComRCThrowRC(rc);
10844 mMediaData->mAttachments.push_back(attachment);
10845 }
10846 }
10847 catch (HRESULT aRC) { rc = aRC; }
10848
10849 /* unlock all hard disks we locked when there is no VM */
10850 if (!aOnline)
10851 {
10852 ErrorInfoKeeper eik;
10853
10854 HRESULT rc1 = lockedMediaMap->Clear();
10855 AssertComRC(rc1);
10856 }
10857
10858 return rc;
10859}
10860
10861/**
10862 * Deletes implicit differencing hard disks created either by
10863 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10864 *
10865 * Note that to delete hard disks created by #AttachDevice() this method is
10866 * called from #fixupMedia() when the changes are rolled back.
10867 *
10868 * @note Locks this object and the media tree for writing.
10869 */
10870HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10871{
10872 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10873
10874 AutoCaller autoCaller(this);
10875 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10876
10877 AutoMultiWriteLock2 alock(this->lockHandle(),
10878 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10879
10880 /* We absolutely must have backed up state. */
10881 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10882
10883 /* Check if there are any implicitly created diff images. */
10884 bool fImplicitDiffs = false;
10885 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10886 it != mMediaData->mAttachments.end();
10887 ++it)
10888 {
10889 const ComObjPtr<MediumAttachment> &pAtt = *it;
10890 if (pAtt->i_isImplicit())
10891 {
10892 fImplicitDiffs = true;
10893 break;
10894 }
10895 }
10896 /* If there is nothing to do, leave early. This saves lots of image locking
10897 * effort. It also avoids a MachineStateChanged event without real reason.
10898 * This is important e.g. when loading a VM config, because there should be
10899 * no events. Otherwise API clients can become thoroughly confused for
10900 * inaccessible VMs (the code for loading VM configs uses this method for
10901 * cleanup if the config makes no sense), as they take such events as an
10902 * indication that the VM is alive, and they would force the VM config to
10903 * be reread, leading to an endless loop. */
10904 if (!fImplicitDiffs)
10905 return S_OK;
10906
10907 HRESULT rc = S_OK;
10908 MachineState_T oldState = mData->mMachineState;
10909
10910 /* will release the lock before the potentially lengthy operation,
10911 * so protect with the special state (unless already protected) */
10912 if ( oldState != MachineState_Snapshotting
10913 && oldState != MachineState_OnlineSnapshotting
10914 && oldState != MachineState_LiveSnapshotting
10915 && oldState != MachineState_RestoringSnapshot
10916 && oldState != MachineState_DeletingSnapshot
10917 && oldState != MachineState_DeletingSnapshotOnline
10918 && oldState != MachineState_DeletingSnapshotPaused
10919 )
10920 i_setMachineState(MachineState_SettingUp);
10921
10922 // use appropriate locked media map (online or offline)
10923 MediumLockListMap lockedMediaOffline;
10924 MediumLockListMap *lockedMediaMap;
10925 if (aOnline)
10926 lockedMediaMap = &mData->mSession.mLockedMedia;
10927 else
10928 lockedMediaMap = &lockedMediaOffline;
10929
10930 try
10931 {
10932 if (!aOnline)
10933 {
10934 /* lock all attached hard disks early to detect "in use"
10935 * situations before deleting actual diffs */
10936 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10937 it != mMediaData->mAttachments.end();
10938 ++it)
10939 {
10940 MediumAttachment* pAtt = *it;
10941 if (pAtt->i_getType() == DeviceType_HardDisk)
10942 {
10943 Medium* pMedium = pAtt->i_getMedium();
10944 Assert(pMedium);
10945
10946 MediumLockList *pMediumLockList(new MediumLockList());
10947 alock.release();
10948 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10949 false /* fMediumLockWrite */,
10950 false /* fMediumLockWriteAll */,
10951 NULL,
10952 *pMediumLockList);
10953 alock.acquire();
10954
10955 if (FAILED(rc))
10956 {
10957 delete pMediumLockList;
10958 throw rc;
10959 }
10960
10961 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10962 if (FAILED(rc))
10963 throw rc;
10964 }
10965 }
10966
10967 if (FAILED(rc))
10968 throw rc;
10969 } // end of offline
10970
10971 /* Lock lists are now up to date and include implicitly created media */
10972
10973 /* Go through remembered attachments and delete all implicitly created
10974 * diffs and fix up the attachment information */
10975 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10976 MediaData::AttachmentList implicitAtts;
10977 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10978 it != mMediaData->mAttachments.end();
10979 ++it)
10980 {
10981 ComObjPtr<MediumAttachment> pAtt = *it;
10982 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10983 if (pMedium.isNull())
10984 continue;
10985
10986 // Implicit attachments go on the list for deletion and back references are removed.
10987 if (pAtt->i_isImplicit())
10988 {
10989 /* Deassociate and mark for deletion */
10990 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10991 rc = pMedium->i_removeBackReference(mData->mUuid);
10992 if (FAILED(rc))
10993 throw rc;
10994 implicitAtts.push_back(pAtt);
10995 continue;
10996 }
10997
10998 /* Was this medium attached before? */
10999 if (!i_findAttachment(oldAtts, pMedium))
11000 {
11001 /* no: de-associate */
11002 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11003 rc = pMedium->i_removeBackReference(mData->mUuid);
11004 if (FAILED(rc))
11005 throw rc;
11006 continue;
11007 }
11008 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11009 }
11010
11011 /* If there are implicit attachments to delete, throw away the lock
11012 * map contents (which will unlock all media) since the medium
11013 * attachments will be rolled back. Below we need to completely
11014 * recreate the lock map anyway since it is infinitely complex to
11015 * do this incrementally (would need reconstructing each attachment
11016 * change, which would be extremely hairy). */
11017 if (implicitAtts.size() != 0)
11018 {
11019 ErrorInfoKeeper eik;
11020
11021 HRESULT rc1 = lockedMediaMap->Clear();
11022 AssertComRC(rc1);
11023 }
11024
11025 /* rollback hard disk changes */
11026 mMediaData.rollback();
11027
11028 MultiResult mrc(S_OK);
11029
11030 // Delete unused implicit diffs.
11031 if (implicitAtts.size() != 0)
11032 {
11033 alock.release();
11034
11035 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11036 {
11037 // Remove medium associated with this attachment.
11038 ComObjPtr<MediumAttachment> pAtt = *it;
11039 Assert(pAtt);
11040 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11041 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11042 Assert(pMedium);
11043
11044 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11045 // continue on delete failure, just collect error messages
11046 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11047 pMedium->i_getLocationFull().c_str() ));
11048 mrc = rc;
11049 }
11050 // Clear the list of deleted implicit attachments now, while not
11051 // holding the lock, as it will ultimately trigger Medium::uninit()
11052 // calls which assume that the media tree lock isn't held.
11053 implicitAtts.clear();
11054
11055 alock.acquire();
11056
11057 /* if there is a VM recreate media lock map as mentioned above,
11058 * otherwise it is a waste of time and we leave things unlocked */
11059 if (aOnline)
11060 {
11061 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11062 /* must never be NULL, but better safe than sorry */
11063 if (!pMachine.isNull())
11064 {
11065 alock.release();
11066 rc = mData->mSession.mMachine->i_lockMedia();
11067 alock.acquire();
11068 if (FAILED(rc))
11069 throw rc;
11070 }
11071 }
11072 }
11073 }
11074 catch (HRESULT aRC) {rc = aRC;}
11075
11076 if (mData->mMachineState == MachineState_SettingUp)
11077 i_setMachineState(oldState);
11078
11079 /* unlock all hard disks we locked when there is no VM */
11080 if (!aOnline)
11081 {
11082 ErrorInfoKeeper eik;
11083
11084 HRESULT rc1 = lockedMediaMap->Clear();
11085 AssertComRC(rc1);
11086 }
11087
11088 return rc;
11089}
11090
11091
11092/**
11093 * Looks through the given list of media attachments for one with the given parameters
11094 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11095 * can be searched as well if needed.
11096 *
11097 * @param list
11098 * @param aControllerName
11099 * @param aControllerPort
11100 * @param aDevice
11101 * @return
11102 */
11103MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11104 IN_BSTR aControllerName,
11105 LONG aControllerPort,
11106 LONG aDevice)
11107{
11108 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11109 {
11110 MediumAttachment *pAttach = *it;
11111 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11112 return pAttach;
11113 }
11114
11115 return NULL;
11116}
11117
11118/**
11119 * Looks through the given list of media attachments for one with the given parameters
11120 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11121 * can be searched as well if needed.
11122 *
11123 * @param list
11124 * @param aControllerName
11125 * @param aControllerPort
11126 * @param aDevice
11127 * @return
11128 */
11129MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11130 ComObjPtr<Medium> pMedium)
11131{
11132 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11133 {
11134 MediumAttachment *pAttach = *it;
11135 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11136 if (pMediumThis == pMedium)
11137 return pAttach;
11138 }
11139
11140 return NULL;
11141}
11142
11143/**
11144 * Looks through the given list of media attachments for one with the given parameters
11145 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11146 * can be searched as well if needed.
11147 *
11148 * @param list
11149 * @param aControllerName
11150 * @param aControllerPort
11151 * @param aDevice
11152 * @return
11153 */
11154MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11155 Guid &id)
11156{
11157 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11158 {
11159 MediumAttachment *pAttach = *it;
11160 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11161 if (pMediumThis->i_getId() == id)
11162 return pAttach;
11163 }
11164
11165 return NULL;
11166}
11167
11168/**
11169 * Main implementation for Machine::DetachDevice. This also gets called
11170 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11171 *
11172 * @param pAttach Medium attachment to detach.
11173 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11174 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11175 * SnapshotMachine, and this must be its snapshot.
11176 * @return
11177 */
11178HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11179 AutoWriteLock &writeLock,
11180 Snapshot *pSnapshot)
11181{
11182 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11183 DeviceType_T mediumType = pAttach->i_getType();
11184
11185 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11186
11187 if (pAttach->i_isImplicit())
11188 {
11189 /* attempt to implicitly delete the implicitly created diff */
11190
11191 /// @todo move the implicit flag from MediumAttachment to Medium
11192 /// and forbid any hard disk operation when it is implicit. Or maybe
11193 /// a special media state for it to make it even more simple.
11194
11195 Assert(mMediaData.isBackedUp());
11196
11197 /* will release the lock before the potentially lengthy operation, so
11198 * protect with the special state */
11199 MachineState_T oldState = mData->mMachineState;
11200 i_setMachineState(MachineState_SettingUp);
11201
11202 writeLock.release();
11203
11204 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11205 true /*aWait*/);
11206
11207 writeLock.acquire();
11208
11209 i_setMachineState(oldState);
11210
11211 if (FAILED(rc)) return rc;
11212 }
11213
11214 i_setModified(IsModified_Storage);
11215 mMediaData.backup();
11216 mMediaData->mAttachments.remove(pAttach);
11217
11218 if (!oldmedium.isNull())
11219 {
11220 // if this is from a snapshot, do not defer detachment to commitMedia()
11221 if (pSnapshot)
11222 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11223 // else if non-hard disk media, do not defer detachment to commitMedia() either
11224 else if (mediumType != DeviceType_HardDisk)
11225 oldmedium->i_removeBackReference(mData->mUuid);
11226 }
11227
11228 return S_OK;
11229}
11230
11231/**
11232 * Goes thru all media of the given list and
11233 *
11234 * 1) calls i_detachDevice() on each of them for this machine and
11235 * 2) adds all Medium objects found in the process to the given list,
11236 * depending on cleanupMode.
11237 *
11238 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11239 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11240 * media to the list.
11241 *
11242 * This gets called from Machine::Unregister, both for the actual Machine and
11243 * the SnapshotMachine objects that might be found in the snapshots.
11244 *
11245 * Requires caller and locking. The machine lock must be passed in because it
11246 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11247 *
11248 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11249 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11250 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11251 * Full, then all media get added;
11252 * otherwise no media get added.
11253 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11254 * @return
11255 */
11256HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11257 Snapshot *pSnapshot,
11258 CleanupMode_T cleanupMode,
11259 MediaList &llMedia)
11260{
11261 Assert(isWriteLockOnCurrentThread());
11262
11263 HRESULT rc;
11264
11265 // make a temporary list because i_detachDevice invalidates iterators into
11266 // mMediaData->mAttachments
11267 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11268
11269 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11270 {
11271 ComObjPtr<MediumAttachment> &pAttach = *it;
11272 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11273
11274 if (!pMedium.isNull())
11275 {
11276 AutoCaller mac(pMedium);
11277 if (FAILED(mac.rc())) return mac.rc();
11278 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11279 DeviceType_T devType = pMedium->i_getDeviceType();
11280 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11281 && devType == DeviceType_HardDisk)
11282 || (cleanupMode == CleanupMode_Full)
11283 )
11284 {
11285 llMedia.push_back(pMedium);
11286 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11287 /* Not allowed to keep this lock as below we need the parent
11288 * medium lock, and the lock order is parent to child. */
11289 lock.release();
11290 /*
11291 * Search for medias which are not attached to any machine, but
11292 * in the chain to an attached disk. Mediums are only consided
11293 * if they are:
11294 * - have only one child
11295 * - no references to any machines
11296 * - are of normal medium type
11297 */
11298 while (!pParent.isNull())
11299 {
11300 AutoCaller mac1(pParent);
11301 if (FAILED(mac1.rc())) return mac1.rc();
11302 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11303 if (pParent->i_getChildren().size() == 1)
11304 {
11305 if ( pParent->i_getMachineBackRefCount() == 0
11306 && pParent->i_getType() == MediumType_Normal
11307 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11308 llMedia.push_back(pParent);
11309 }
11310 else
11311 break;
11312 pParent = pParent->i_getParent();
11313 }
11314 }
11315 }
11316
11317 // real machine: then we need to use the proper method
11318 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11319
11320 if (FAILED(rc))
11321 return rc;
11322 }
11323
11324 return S_OK;
11325}
11326
11327/**
11328 * Perform deferred hard disk detachments.
11329 *
11330 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11331 * backed up).
11332 *
11333 * If @a aOnline is @c true then this method will also unlock the old hard disks
11334 * for which the new implicit diffs were created and will lock these new diffs for
11335 * writing.
11336 *
11337 * @param aOnline Whether the VM was online prior to this operation.
11338 *
11339 * @note Locks this object for writing!
11340 */
11341void Machine::i_commitMedia(bool aOnline /*= false*/)
11342{
11343 AutoCaller autoCaller(this);
11344 AssertComRCReturnVoid(autoCaller.rc());
11345
11346 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11347
11348 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11349
11350 HRESULT rc = S_OK;
11351
11352 /* no attach/detach operations -- nothing to do */
11353 if (!mMediaData.isBackedUp())
11354 return;
11355
11356 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11357 bool fMediaNeedsLocking = false;
11358
11359 /* enumerate new attachments */
11360 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11361 it != mMediaData->mAttachments.end();
11362 ++it)
11363 {
11364 MediumAttachment *pAttach = *it;
11365
11366 pAttach->i_commit();
11367
11368 Medium* pMedium = pAttach->i_getMedium();
11369 bool fImplicit = pAttach->i_isImplicit();
11370
11371 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11372 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11373 fImplicit));
11374
11375 /** @todo convert all this Machine-based voodoo to MediumAttachment
11376 * based commit logic. */
11377 if (fImplicit)
11378 {
11379 /* convert implicit attachment to normal */
11380 pAttach->i_setImplicit(false);
11381
11382 if ( aOnline
11383 && pMedium
11384 && pAttach->i_getType() == DeviceType_HardDisk
11385 )
11386 {
11387 /* update the appropriate lock list */
11388 MediumLockList *pMediumLockList;
11389 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11390 AssertComRC(rc);
11391 if (pMediumLockList)
11392 {
11393 /* unlock if there's a need to change the locking */
11394 if (!fMediaNeedsLocking)
11395 {
11396 rc = mData->mSession.mLockedMedia.Unlock();
11397 AssertComRC(rc);
11398 fMediaNeedsLocking = true;
11399 }
11400 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11401 AssertComRC(rc);
11402 rc = pMediumLockList->Append(pMedium, true);
11403 AssertComRC(rc);
11404 }
11405 }
11406
11407 continue;
11408 }
11409
11410 if (pMedium)
11411 {
11412 /* was this medium attached before? */
11413 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11414 {
11415 MediumAttachment *pOldAttach = *oldIt;
11416 if (pOldAttach->i_getMedium() == pMedium)
11417 {
11418 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11419
11420 /* yes: remove from old to avoid de-association */
11421 oldAtts.erase(oldIt);
11422 break;
11423 }
11424 }
11425 }
11426 }
11427
11428 /* enumerate remaining old attachments and de-associate from the
11429 * current machine state */
11430 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11431 {
11432 MediumAttachment *pAttach = *it;
11433 Medium* pMedium = pAttach->i_getMedium();
11434
11435 /* Detach only hard disks, since DVD/floppy media is detached
11436 * instantly in MountMedium. */
11437 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11438 {
11439 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11440
11441 /* now de-associate from the current machine state */
11442 rc = pMedium->i_removeBackReference(mData->mUuid);
11443 AssertComRC(rc);
11444
11445 if (aOnline)
11446 {
11447 /* unlock since medium is not used anymore */
11448 MediumLockList *pMediumLockList;
11449 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11450 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11451 {
11452 /* this happens for online snapshots, there the attachment
11453 * is changing, but only to a diff image created under
11454 * the old one, so there is no separate lock list */
11455 Assert(!pMediumLockList);
11456 }
11457 else
11458 {
11459 AssertComRC(rc);
11460 if (pMediumLockList)
11461 {
11462 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11463 AssertComRC(rc);
11464 }
11465 }
11466 }
11467 }
11468 }
11469
11470 /* take media locks again so that the locking state is consistent */
11471 if (fMediaNeedsLocking)
11472 {
11473 Assert(aOnline);
11474 rc = mData->mSession.mLockedMedia.Lock();
11475 AssertComRC(rc);
11476 }
11477
11478 /* commit the hard disk changes */
11479 mMediaData.commit();
11480
11481 if (i_isSessionMachine())
11482 {
11483 /*
11484 * Update the parent machine to point to the new owner.
11485 * This is necessary because the stored parent will point to the
11486 * session machine otherwise and cause crashes or errors later
11487 * when the session machine gets invalid.
11488 */
11489 /** @todo Change the MediumAttachment class to behave like any other
11490 * class in this regard by creating peer MediumAttachment
11491 * objects for session machines and share the data with the peer
11492 * machine.
11493 */
11494 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11495 it != mMediaData->mAttachments.end();
11496 ++it)
11497 (*it)->i_updateParentMachine(mPeer);
11498
11499 /* attach new data to the primary machine and reshare it */
11500 mPeer->mMediaData.attach(mMediaData);
11501 }
11502
11503 return;
11504}
11505
11506/**
11507 * Perform deferred deletion of implicitly created diffs.
11508 *
11509 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11510 * backed up).
11511 *
11512 * @note Locks this object for writing!
11513 */
11514void Machine::i_rollbackMedia()
11515{
11516 AutoCaller autoCaller(this);
11517 AssertComRCReturnVoid(autoCaller.rc());
11518
11519 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11520 LogFlowThisFunc(("Entering rollbackMedia\n"));
11521
11522 HRESULT rc = S_OK;
11523
11524 /* no attach/detach operations -- nothing to do */
11525 if (!mMediaData.isBackedUp())
11526 return;
11527
11528 /* enumerate new attachments */
11529 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11530 it != mMediaData->mAttachments.end();
11531 ++it)
11532 {
11533 MediumAttachment *pAttach = *it;
11534 /* Fix up the backrefs for DVD/floppy media. */
11535 if (pAttach->i_getType() != DeviceType_HardDisk)
11536 {
11537 Medium* pMedium = pAttach->i_getMedium();
11538 if (pMedium)
11539 {
11540 rc = pMedium->i_removeBackReference(mData->mUuid);
11541 AssertComRC(rc);
11542 }
11543 }
11544
11545 (*it)->i_rollback();
11546
11547 pAttach = *it;
11548 /* Fix up the backrefs for DVD/floppy media. */
11549 if (pAttach->i_getType() != DeviceType_HardDisk)
11550 {
11551 Medium* pMedium = pAttach->i_getMedium();
11552 if (pMedium)
11553 {
11554 rc = pMedium->i_addBackReference(mData->mUuid);
11555 AssertComRC(rc);
11556 }
11557 }
11558 }
11559
11560 /** @todo convert all this Machine-based voodoo to MediumAttachment
11561 * based rollback logic. */
11562 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11563
11564 return;
11565}
11566
11567/**
11568 * Returns true if the settings file is located in the directory named exactly
11569 * as the machine; this means, among other things, that the machine directory
11570 * should be auto-renamed.
11571 *
11572 * @param aSettingsDir if not NULL, the full machine settings file directory
11573 * name will be assigned there.
11574 *
11575 * @note Doesn't lock anything.
11576 * @note Not thread safe (must be called from this object's lock).
11577 */
11578bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11579{
11580 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11581 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11582 if (aSettingsDir)
11583 *aSettingsDir = strMachineDirName;
11584 strMachineDirName.stripPath(); // vmname
11585 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11586 strConfigFileOnly.stripPath() // vmname.vbox
11587 .stripSuffix(); // vmname
11588 /** @todo hack, make somehow use of ComposeMachineFilename */
11589 if (mUserData->s.fDirectoryIncludesUUID)
11590 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11591
11592 AssertReturn(!strMachineDirName.isEmpty(), false);
11593 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11594
11595 return strMachineDirName == strConfigFileOnly;
11596}
11597
11598/**
11599 * Discards all changes to machine settings.
11600 *
11601 * @param aNotify Whether to notify the direct session about changes or not.
11602 *
11603 * @note Locks objects for writing!
11604 */
11605void Machine::i_rollback(bool aNotify)
11606{
11607 AutoCaller autoCaller(this);
11608 AssertComRCReturn(autoCaller.rc(), (void)0);
11609
11610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11611
11612 if (!mStorageControllers.isNull())
11613 {
11614 if (mStorageControllers.isBackedUp())
11615 {
11616 /* unitialize all new devices (absent in the backed up list). */
11617 StorageControllerList::const_iterator it = mStorageControllers->begin();
11618 StorageControllerList *backedList = mStorageControllers.backedUpData();
11619 while (it != mStorageControllers->end())
11620 {
11621 if ( std::find(backedList->begin(), backedList->end(), *it)
11622 == backedList->end()
11623 )
11624 {
11625 (*it)->uninit();
11626 }
11627 ++it;
11628 }
11629
11630 /* restore the list */
11631 mStorageControllers.rollback();
11632 }
11633
11634 /* rollback any changes to devices after restoring the list */
11635 if (mData->flModifications & IsModified_Storage)
11636 {
11637 StorageControllerList::const_iterator it = mStorageControllers->begin();
11638 while (it != mStorageControllers->end())
11639 {
11640 (*it)->i_rollback();
11641 ++it;
11642 }
11643 }
11644 }
11645
11646 if (!mUSBControllers.isNull())
11647 {
11648 if (mUSBControllers.isBackedUp())
11649 {
11650 /* unitialize all new devices (absent in the backed up list). */
11651 USBControllerList::const_iterator it = mUSBControllers->begin();
11652 USBControllerList *backedList = mUSBControllers.backedUpData();
11653 while (it != mUSBControllers->end())
11654 {
11655 if ( std::find(backedList->begin(), backedList->end(), *it)
11656 == backedList->end()
11657 )
11658 {
11659 (*it)->uninit();
11660 }
11661 ++it;
11662 }
11663
11664 /* restore the list */
11665 mUSBControllers.rollback();
11666 }
11667
11668 /* rollback any changes to devices after restoring the list */
11669 if (mData->flModifications & IsModified_USB)
11670 {
11671 USBControllerList::const_iterator it = mUSBControllers->begin();
11672 while (it != mUSBControllers->end())
11673 {
11674 (*it)->i_rollback();
11675 ++it;
11676 }
11677 }
11678 }
11679
11680 mUserData.rollback();
11681
11682 mHWData.rollback();
11683
11684 if (mData->flModifications & IsModified_Storage)
11685 i_rollbackMedia();
11686
11687 if (mBIOSSettings)
11688 mBIOSSettings->i_rollback();
11689
11690 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11691 mVRDEServer->i_rollback();
11692
11693 if (mAudioAdapter)
11694 mAudioAdapter->i_rollback();
11695
11696 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11697 mUSBDeviceFilters->i_rollback();
11698
11699 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11700 mBandwidthControl->i_rollback();
11701
11702 if (!mHWData.isNull())
11703 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11704 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11705 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11706 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11707
11708 if (mData->flModifications & IsModified_NetworkAdapters)
11709 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11710 if ( mNetworkAdapters[slot]
11711 && mNetworkAdapters[slot]->i_isModified())
11712 {
11713 mNetworkAdapters[slot]->i_rollback();
11714 networkAdapters[slot] = mNetworkAdapters[slot];
11715 }
11716
11717 if (mData->flModifications & IsModified_SerialPorts)
11718 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11719 if ( mSerialPorts[slot]
11720 && mSerialPorts[slot]->i_isModified())
11721 {
11722 mSerialPorts[slot]->i_rollback();
11723 serialPorts[slot] = mSerialPorts[slot];
11724 }
11725
11726 if (mData->flModifications & IsModified_ParallelPorts)
11727 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11728 if ( mParallelPorts[slot]
11729 && mParallelPorts[slot]->i_isModified())
11730 {
11731 mParallelPorts[slot]->i_rollback();
11732 parallelPorts[slot] = mParallelPorts[slot];
11733 }
11734
11735 if (aNotify)
11736 {
11737 /* inform the direct session about changes */
11738
11739 ComObjPtr<Machine> that = this;
11740 uint32_t flModifications = mData->flModifications;
11741 alock.release();
11742
11743 if (flModifications & IsModified_SharedFolders)
11744 that->i_onSharedFolderChange();
11745
11746 if (flModifications & IsModified_VRDEServer)
11747 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11748 if (flModifications & IsModified_USB)
11749 that->i_onUSBControllerChange();
11750
11751 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11752 if (networkAdapters[slot])
11753 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11754 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11755 if (serialPorts[slot])
11756 that->i_onSerialPortChange(serialPorts[slot]);
11757 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11758 if (parallelPorts[slot])
11759 that->i_onParallelPortChange(parallelPorts[slot]);
11760
11761 if (flModifications & IsModified_Storage)
11762 that->i_onStorageControllerChange();
11763
11764#if 0
11765 if (flModifications & IsModified_BandwidthControl)
11766 that->onBandwidthControlChange();
11767#endif
11768 }
11769}
11770
11771/**
11772 * Commits all the changes to machine settings.
11773 *
11774 * Note that this operation is supposed to never fail.
11775 *
11776 * @note Locks this object and children for writing.
11777 */
11778void Machine::i_commit()
11779{
11780 AutoCaller autoCaller(this);
11781 AssertComRCReturnVoid(autoCaller.rc());
11782
11783 AutoCaller peerCaller(mPeer);
11784 AssertComRCReturnVoid(peerCaller.rc());
11785
11786 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11787
11788 /*
11789 * use safe commit to ensure Snapshot machines (that share mUserData)
11790 * will still refer to a valid memory location
11791 */
11792 mUserData.commitCopy();
11793
11794 mHWData.commit();
11795
11796 if (mMediaData.isBackedUp())
11797 i_commitMedia(Global::IsOnline(mData->mMachineState));
11798
11799 mBIOSSettings->i_commit();
11800 mVRDEServer->i_commit();
11801 mAudioAdapter->i_commit();
11802 mUSBDeviceFilters->i_commit();
11803 mBandwidthControl->i_commit();
11804
11805 /* Since mNetworkAdapters is a list which might have been changed (resized)
11806 * without using the Backupable<> template we need to handle the copying
11807 * of the list entries manually, including the creation of peers for the
11808 * new objects. */
11809 bool commitNetworkAdapters = false;
11810 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11811 if (mPeer)
11812 {
11813 /* commit everything, even the ones which will go away */
11814 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11815 mNetworkAdapters[slot]->i_commit();
11816 /* copy over the new entries, creating a peer and uninit the original */
11817 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11818 for (size_t slot = 0; slot < newSize; slot++)
11819 {
11820 /* look if this adapter has a peer device */
11821 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11822 if (!peer)
11823 {
11824 /* no peer means the adapter is a newly created one;
11825 * create a peer owning data this data share it with */
11826 peer.createObject();
11827 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11828 }
11829 mPeer->mNetworkAdapters[slot] = peer;
11830 }
11831 /* uninit any no longer needed network adapters */
11832 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11833 mNetworkAdapters[slot]->uninit();
11834 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11835 {
11836 if (mPeer->mNetworkAdapters[slot])
11837 mPeer->mNetworkAdapters[slot]->uninit();
11838 }
11839 /* Keep the original network adapter count until this point, so that
11840 * discarding a chipset type change will not lose settings. */
11841 mNetworkAdapters.resize(newSize);
11842 mPeer->mNetworkAdapters.resize(newSize);
11843 }
11844 else
11845 {
11846 /* we have no peer (our parent is the newly created machine);
11847 * just commit changes to the network adapters */
11848 commitNetworkAdapters = true;
11849 }
11850 if (commitNetworkAdapters)
11851 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11852 mNetworkAdapters[slot]->i_commit();
11853
11854 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11855 mSerialPorts[slot]->i_commit();
11856 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11857 mParallelPorts[slot]->i_commit();
11858
11859 bool commitStorageControllers = false;
11860
11861 if (mStorageControllers.isBackedUp())
11862 {
11863 mStorageControllers.commit();
11864
11865 if (mPeer)
11866 {
11867 /* Commit all changes to new controllers (this will reshare data with
11868 * peers for those who have peers) */
11869 StorageControllerList *newList = new StorageControllerList();
11870 StorageControllerList::const_iterator it = mStorageControllers->begin();
11871 while (it != mStorageControllers->end())
11872 {
11873 (*it)->i_commit();
11874
11875 /* look if this controller has a peer device */
11876 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11877 if (!peer)
11878 {
11879 /* no peer means the device is a newly created one;
11880 * create a peer owning data this device share it with */
11881 peer.createObject();
11882 peer->init(mPeer, *it, true /* aReshare */);
11883 }
11884 else
11885 {
11886 /* remove peer from the old list */
11887 mPeer->mStorageControllers->remove(peer);
11888 }
11889 /* and add it to the new list */
11890 newList->push_back(peer);
11891
11892 ++it;
11893 }
11894
11895 /* uninit old peer's controllers that are left */
11896 it = mPeer->mStorageControllers->begin();
11897 while (it != mPeer->mStorageControllers->end())
11898 {
11899 (*it)->uninit();
11900 ++it;
11901 }
11902
11903 /* attach new list of controllers to our peer */
11904 mPeer->mStorageControllers.attach(newList);
11905 }
11906 else
11907 {
11908 /* we have no peer (our parent is the newly created machine);
11909 * just commit changes to devices */
11910 commitStorageControllers = true;
11911 }
11912 }
11913 else
11914 {
11915 /* the list of controllers itself is not changed,
11916 * just commit changes to controllers themselves */
11917 commitStorageControllers = true;
11918 }
11919
11920 if (commitStorageControllers)
11921 {
11922 StorageControllerList::const_iterator it = mStorageControllers->begin();
11923 while (it != mStorageControllers->end())
11924 {
11925 (*it)->i_commit();
11926 ++it;
11927 }
11928 }
11929
11930 bool commitUSBControllers = false;
11931
11932 if (mUSBControllers.isBackedUp())
11933 {
11934 mUSBControllers.commit();
11935
11936 if (mPeer)
11937 {
11938 /* Commit all changes to new controllers (this will reshare data with
11939 * peers for those who have peers) */
11940 USBControllerList *newList = new USBControllerList();
11941 USBControllerList::const_iterator it = mUSBControllers->begin();
11942 while (it != mUSBControllers->end())
11943 {
11944 (*it)->i_commit();
11945
11946 /* look if this controller has a peer device */
11947 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11948 if (!peer)
11949 {
11950 /* no peer means the device is a newly created one;
11951 * create a peer owning data this device share it with */
11952 peer.createObject();
11953 peer->init(mPeer, *it, true /* aReshare */);
11954 }
11955 else
11956 {
11957 /* remove peer from the old list */
11958 mPeer->mUSBControllers->remove(peer);
11959 }
11960 /* and add it to the new list */
11961 newList->push_back(peer);
11962
11963 ++it;
11964 }
11965
11966 /* uninit old peer's controllers that are left */
11967 it = mPeer->mUSBControllers->begin();
11968 while (it != mPeer->mUSBControllers->end())
11969 {
11970 (*it)->uninit();
11971 ++it;
11972 }
11973
11974 /* attach new list of controllers to our peer */
11975 mPeer->mUSBControllers.attach(newList);
11976 }
11977 else
11978 {
11979 /* we have no peer (our parent is the newly created machine);
11980 * just commit changes to devices */
11981 commitUSBControllers = true;
11982 }
11983 }
11984 else
11985 {
11986 /* the list of controllers itself is not changed,
11987 * just commit changes to controllers themselves */
11988 commitUSBControllers = true;
11989 }
11990
11991 if (commitUSBControllers)
11992 {
11993 USBControllerList::const_iterator it = mUSBControllers->begin();
11994 while (it != mUSBControllers->end())
11995 {
11996 (*it)->i_commit();
11997 ++it;
11998 }
11999 }
12000
12001 if (i_isSessionMachine())
12002 {
12003 /* attach new data to the primary machine and reshare it */
12004 mPeer->mUserData.attach(mUserData);
12005 mPeer->mHWData.attach(mHWData);
12006 /* mMediaData is reshared by fixupMedia */
12007 // mPeer->mMediaData.attach(mMediaData);
12008 Assert(mPeer->mMediaData.data() == mMediaData.data());
12009 }
12010}
12011
12012/**
12013 * Copies all the hardware data from the given machine.
12014 *
12015 * Currently, only called when the VM is being restored from a snapshot. In
12016 * particular, this implies that the VM is not running during this method's
12017 * call.
12018 *
12019 * @note This method must be called from under this object's lock.
12020 *
12021 * @note This method doesn't call #commit(), so all data remains backed up and
12022 * unsaved.
12023 */
12024void Machine::i_copyFrom(Machine *aThat)
12025{
12026 AssertReturnVoid(!i_isSnapshotMachine());
12027 AssertReturnVoid(aThat->i_isSnapshotMachine());
12028
12029 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12030
12031 mHWData.assignCopy(aThat->mHWData);
12032
12033 // create copies of all shared folders (mHWData after attaching a copy
12034 // contains just references to original objects)
12035 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12036 it != mHWData->mSharedFolders.end();
12037 ++it)
12038 {
12039 ComObjPtr<SharedFolder> folder;
12040 folder.createObject();
12041 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12042 AssertComRC(rc);
12043 *it = folder;
12044 }
12045
12046 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12047 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12048 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12049 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12050 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12051
12052 /* create private copies of all controllers */
12053 mStorageControllers.backup();
12054 mStorageControllers->clear();
12055 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12056 it != aThat->mStorageControllers->end();
12057 ++it)
12058 {
12059 ComObjPtr<StorageController> ctrl;
12060 ctrl.createObject();
12061 ctrl->initCopy(this, *it);
12062 mStorageControllers->push_back(ctrl);
12063 }
12064
12065 /* create private copies of all USB controllers */
12066 mUSBControllers.backup();
12067 mUSBControllers->clear();
12068 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12069 it != aThat->mUSBControllers->end();
12070 ++it)
12071 {
12072 ComObjPtr<USBController> ctrl;
12073 ctrl.createObject();
12074 ctrl->initCopy(this, *it);
12075 mUSBControllers->push_back(ctrl);
12076 }
12077
12078 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12079 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12080 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12081 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12082 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12083 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12084 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12085}
12086
12087/**
12088 * Returns whether the given storage controller is hotplug capable.
12089 *
12090 * @returns true if the controller supports hotplugging
12091 * false otherwise.
12092 * @param enmCtrlType The controller type to check for.
12093 */
12094bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12095{
12096 ComPtr<ISystemProperties> systemProperties;
12097 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12098 if (FAILED(rc))
12099 return false;
12100
12101 BOOL aHotplugCapable = FALSE;
12102 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12103
12104 return RT_BOOL(aHotplugCapable);
12105}
12106
12107#ifdef VBOX_WITH_RESOURCE_USAGE_API
12108
12109void Machine::i_getDiskList(MediaList &list)
12110{
12111 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12112 it != mMediaData->mAttachments.end();
12113 ++it)
12114 {
12115 MediumAttachment* pAttach = *it;
12116 /* just in case */
12117 AssertStmt(pAttach, continue);
12118
12119 AutoCaller localAutoCallerA(pAttach);
12120 if (FAILED(localAutoCallerA.rc())) continue;
12121
12122 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12123
12124 if (pAttach->i_getType() == DeviceType_HardDisk)
12125 list.push_back(pAttach->i_getMedium());
12126 }
12127}
12128
12129void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12130{
12131 AssertReturnVoid(isWriteLockOnCurrentThread());
12132 AssertPtrReturnVoid(aCollector);
12133
12134 pm::CollectorHAL *hal = aCollector->getHAL();
12135 /* Create sub metrics */
12136 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12137 "Percentage of processor time spent in user mode by the VM process.");
12138 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12139 "Percentage of processor time spent in kernel mode by the VM process.");
12140 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12141 "Size of resident portion of VM process in memory.");
12142 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12143 "Actual size of all VM disks combined.");
12144 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12145 "Network receive rate.");
12146 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12147 "Network transmit rate.");
12148 /* Create and register base metrics */
12149 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12150 cpuLoadUser, cpuLoadKernel);
12151 aCollector->registerBaseMetric(cpuLoad);
12152 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12153 ramUsageUsed);
12154 aCollector->registerBaseMetric(ramUsage);
12155 MediaList disks;
12156 i_getDiskList(disks);
12157 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12158 diskUsageUsed);
12159 aCollector->registerBaseMetric(diskUsage);
12160
12161 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12162 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12163 new pm::AggregateAvg()));
12164 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12165 new pm::AggregateMin()));
12166 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12167 new pm::AggregateMax()));
12168 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12169 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12170 new pm::AggregateAvg()));
12171 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12172 new pm::AggregateMin()));
12173 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12174 new pm::AggregateMax()));
12175
12176 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12177 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12178 new pm::AggregateAvg()));
12179 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12180 new pm::AggregateMin()));
12181 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12182 new pm::AggregateMax()));
12183
12184 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12185 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12186 new pm::AggregateAvg()));
12187 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12188 new pm::AggregateMin()));
12189 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12190 new pm::AggregateMax()));
12191
12192
12193 /* Guest metrics collector */
12194 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12195 aCollector->registerGuest(mCollectorGuest);
12196 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12197 this, __PRETTY_FUNCTION__, mCollectorGuest));
12198
12199 /* Create sub metrics */
12200 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12201 "Percentage of processor time spent in user mode as seen by the guest.");
12202 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12203 "Percentage of processor time spent in kernel mode as seen by the guest.");
12204 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12205 "Percentage of processor time spent idling as seen by the guest.");
12206
12207 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12208 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12209 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12210 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12211 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12212 pm::SubMetric *guestMemCache = new pm::SubMetric(
12213 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12214
12215 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12216 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12217
12218 /* Create and register base metrics */
12219 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12220 machineNetRx, machineNetTx);
12221 aCollector->registerBaseMetric(machineNetRate);
12222
12223 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12224 guestLoadUser, guestLoadKernel, guestLoadIdle);
12225 aCollector->registerBaseMetric(guestCpuLoad);
12226
12227 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12228 guestMemTotal, guestMemFree,
12229 guestMemBalloon, guestMemShared,
12230 guestMemCache, guestPagedTotal);
12231 aCollector->registerBaseMetric(guestCpuMem);
12232
12233 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12234 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12235 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12236 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12237
12238 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12239 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12240 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12241 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12242
12243 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12244 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12245 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12246 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12247
12248 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12249 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12250 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12251 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12252
12253 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12254 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12255 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12256 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12257
12258 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12259 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12260 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12261 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12262
12263 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12264 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12265 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12266 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12267
12268 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12269 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12270 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12271 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12272
12273 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12274 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12275 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12276 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12277
12278 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12279 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12280 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12281 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12282
12283 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12284 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12285 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12286 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12287}
12288
12289void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12290{
12291 AssertReturnVoid(isWriteLockOnCurrentThread());
12292
12293 if (aCollector)
12294 {
12295 aCollector->unregisterMetricsFor(aMachine);
12296 aCollector->unregisterBaseMetricsFor(aMachine);
12297 }
12298}
12299
12300#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12301
12302
12303////////////////////////////////////////////////////////////////////////////////
12304
12305DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12306
12307HRESULT SessionMachine::FinalConstruct()
12308{
12309 LogFlowThisFunc(("\n"));
12310
12311 mClientToken = NULL;
12312
12313 return BaseFinalConstruct();
12314}
12315
12316void SessionMachine::FinalRelease()
12317{
12318 LogFlowThisFunc(("\n"));
12319
12320 Assert(!mClientToken);
12321 /* paranoia, should not hang around any more */
12322 if (mClientToken)
12323 {
12324 delete mClientToken;
12325 mClientToken = NULL;
12326 }
12327
12328 uninit(Uninit::Unexpected);
12329
12330 BaseFinalRelease();
12331}
12332
12333/**
12334 * @note Must be called only by Machine::LockMachine() from its own write lock.
12335 */
12336HRESULT SessionMachine::init(Machine *aMachine)
12337{
12338 LogFlowThisFuncEnter();
12339 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12340
12341 AssertReturn(aMachine, E_INVALIDARG);
12342
12343 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12344
12345 /* Enclose the state transition NotReady->InInit->Ready */
12346 AutoInitSpan autoInitSpan(this);
12347 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12348
12349 HRESULT rc = S_OK;
12350
12351 /* create the machine client token */
12352 try
12353 {
12354 mClientToken = new ClientToken(aMachine, this);
12355 if (!mClientToken->isReady())
12356 {
12357 delete mClientToken;
12358 mClientToken = NULL;
12359 rc = E_FAIL;
12360 }
12361 }
12362 catch (std::bad_alloc &)
12363 {
12364 rc = E_OUTOFMEMORY;
12365 }
12366 if (FAILED(rc))
12367 return rc;
12368
12369 /* memorize the peer Machine */
12370 unconst(mPeer) = aMachine;
12371 /* share the parent pointer */
12372 unconst(mParent) = aMachine->mParent;
12373
12374 /* take the pointers to data to share */
12375 mData.share(aMachine->mData);
12376 mSSData.share(aMachine->mSSData);
12377
12378 mUserData.share(aMachine->mUserData);
12379 mHWData.share(aMachine->mHWData);
12380 mMediaData.share(aMachine->mMediaData);
12381
12382 mStorageControllers.allocate();
12383 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12384 it != aMachine->mStorageControllers->end();
12385 ++it)
12386 {
12387 ComObjPtr<StorageController> ctl;
12388 ctl.createObject();
12389 ctl->init(this, *it);
12390 mStorageControllers->push_back(ctl);
12391 }
12392
12393 mUSBControllers.allocate();
12394 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12395 it != aMachine->mUSBControllers->end();
12396 ++it)
12397 {
12398 ComObjPtr<USBController> ctl;
12399 ctl.createObject();
12400 ctl->init(this, *it);
12401 mUSBControllers->push_back(ctl);
12402 }
12403
12404 unconst(mBIOSSettings).createObject();
12405 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12406 /* create another VRDEServer object that will be mutable */
12407 unconst(mVRDEServer).createObject();
12408 mVRDEServer->init(this, aMachine->mVRDEServer);
12409 /* create another audio adapter object that will be mutable */
12410 unconst(mAudioAdapter).createObject();
12411 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12412 /* create a list of serial ports that will be mutable */
12413 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12414 {
12415 unconst(mSerialPorts[slot]).createObject();
12416 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12417 }
12418 /* create a list of parallel ports that will be mutable */
12419 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12420 {
12421 unconst(mParallelPorts[slot]).createObject();
12422 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12423 }
12424
12425 /* create another USB device filters object that will be mutable */
12426 unconst(mUSBDeviceFilters).createObject();
12427 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12428
12429 /* create a list of network adapters that will be mutable */
12430 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12431 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12432 {
12433 unconst(mNetworkAdapters[slot]).createObject();
12434 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12435 }
12436
12437 /* create another bandwidth control object that will be mutable */
12438 unconst(mBandwidthControl).createObject();
12439 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12440
12441 /* default is to delete saved state on Saved -> PoweredOff transition */
12442 mRemoveSavedState = true;
12443
12444 /* Confirm a successful initialization when it's the case */
12445 autoInitSpan.setSucceeded();
12446
12447 miNATNetworksStarted = 0;
12448
12449 LogFlowThisFuncLeave();
12450 return rc;
12451}
12452
12453/**
12454 * Uninitializes this session object. If the reason is other than
12455 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12456 * or the client watcher code.
12457 *
12458 * @param aReason uninitialization reason
12459 *
12460 * @note Locks mParent + this object for writing.
12461 */
12462void SessionMachine::uninit(Uninit::Reason aReason)
12463{
12464 LogFlowThisFuncEnter();
12465 LogFlowThisFunc(("reason=%d\n", aReason));
12466
12467 /*
12468 * Strongly reference ourselves to prevent this object deletion after
12469 * mData->mSession.mMachine.setNull() below (which can release the last
12470 * reference and call the destructor). Important: this must be done before
12471 * accessing any members (and before AutoUninitSpan that does it as well).
12472 * This self reference will be released as the very last step on return.
12473 */
12474 ComObjPtr<SessionMachine> selfRef = this;
12475
12476 /* Enclose the state transition Ready->InUninit->NotReady */
12477 AutoUninitSpan autoUninitSpan(this);
12478 if (autoUninitSpan.uninitDone())
12479 {
12480 LogFlowThisFunc(("Already uninitialized\n"));
12481 LogFlowThisFuncLeave();
12482 return;
12483 }
12484
12485 if (autoUninitSpan.initFailed())
12486 {
12487 /* We've been called by init() because it's failed. It's not really
12488 * necessary (nor it's safe) to perform the regular uninit sequence
12489 * below, the following is enough.
12490 */
12491 LogFlowThisFunc(("Initialization failed.\n"));
12492 /* destroy the machine client token */
12493 if (mClientToken)
12494 {
12495 delete mClientToken;
12496 mClientToken = NULL;
12497 }
12498 uninitDataAndChildObjects();
12499 mData.free();
12500 unconst(mParent) = NULL;
12501 unconst(mPeer) = NULL;
12502 LogFlowThisFuncLeave();
12503 return;
12504 }
12505
12506 MachineState_T lastState;
12507 {
12508 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12509 lastState = mData->mMachineState;
12510 }
12511 NOREF(lastState);
12512
12513#ifdef VBOX_WITH_USB
12514 // release all captured USB devices, but do this before requesting the locks below
12515 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12516 {
12517 /* Console::captureUSBDevices() is called in the VM process only after
12518 * setting the machine state to Starting or Restoring.
12519 * Console::detachAllUSBDevices() will be called upon successful
12520 * termination. So, we need to release USB devices only if there was
12521 * an abnormal termination of a running VM.
12522 *
12523 * This is identical to SessionMachine::DetachAllUSBDevices except
12524 * for the aAbnormal argument. */
12525 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12526 AssertComRC(rc);
12527 NOREF(rc);
12528
12529 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12530 if (service)
12531 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12532 }
12533#endif /* VBOX_WITH_USB */
12534
12535 // we need to lock this object in uninit() because the lock is shared
12536 // with mPeer (as well as data we modify below). mParent lock is needed
12537 // by several calls to it, and USB needs host lock.
12538 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12539
12540#ifdef VBOX_WITH_RESOURCE_USAGE_API
12541 /*
12542 * It is safe to call Machine::i_unregisterMetrics() here because
12543 * PerformanceCollector::samplerCallback no longer accesses guest methods
12544 * holding the lock.
12545 */
12546 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12547 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12548 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12549 this, __PRETTY_FUNCTION__, mCollectorGuest));
12550 if (mCollectorGuest)
12551 {
12552 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12553 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12554 mCollectorGuest = NULL;
12555 }
12556#endif
12557
12558 if (aReason == Uninit::Abnormal)
12559 {
12560 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12561 Global::IsOnlineOrTransient(lastState)));
12562
12563 /* reset the state to Aborted */
12564 if (mData->mMachineState != MachineState_Aborted)
12565 i_setMachineState(MachineState_Aborted);
12566 }
12567
12568 // any machine settings modified?
12569 if (mData->flModifications)
12570 {
12571 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12572 i_rollback(false /* aNotify */);
12573 }
12574
12575 mData->mSession.mPID = NIL_RTPROCESS;
12576
12577 if (aReason == Uninit::Unexpected)
12578 {
12579 /* Uninitialization didn't come from #checkForDeath(), so tell the
12580 * client watcher thread to update the set of machines that have open
12581 * sessions. */
12582 mParent->i_updateClientWatcher();
12583 }
12584
12585 /* uninitialize all remote controls */
12586 if (mData->mSession.mRemoteControls.size())
12587 {
12588 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12589 mData->mSession.mRemoteControls.size()));
12590
12591 Data::Session::RemoteControlList::iterator it =
12592 mData->mSession.mRemoteControls.begin();
12593 while (it != mData->mSession.mRemoteControls.end())
12594 {
12595 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12596 HRESULT rc = (*it)->Uninitialize();
12597 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12598 if (FAILED(rc))
12599 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12600 ++it;
12601 }
12602 mData->mSession.mRemoteControls.clear();
12603 }
12604
12605 /* Remove all references to the NAT network service. The service will stop
12606 * if all references (also from other VMs) are removed. */
12607 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12608 {
12609 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12610 {
12611 NetworkAttachmentType_T type;
12612 HRESULT hrc;
12613
12614 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12615 if ( SUCCEEDED(hrc)
12616 && type == NetworkAttachmentType_NATNetwork)
12617 {
12618 Bstr name;
12619 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12620 if (SUCCEEDED(hrc))
12621 {
12622 multilock.release();
12623 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12624 mUserData->s.strName.c_str(), name.raw()));
12625 mParent->i_natNetworkRefDec(name.raw());
12626 multilock.acquire();
12627 }
12628 }
12629 }
12630 }
12631
12632 /*
12633 * An expected uninitialization can come only from #checkForDeath().
12634 * Otherwise it means that something's gone really wrong (for example,
12635 * the Session implementation has released the VirtualBox reference
12636 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12637 * etc). However, it's also possible, that the client releases the IPC
12638 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12639 * but the VirtualBox release event comes first to the server process.
12640 * This case is practically possible, so we should not assert on an
12641 * unexpected uninit, just log a warning.
12642 */
12643
12644 if ((aReason == Uninit::Unexpected))
12645 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12646
12647 if (aReason != Uninit::Normal)
12648 {
12649 mData->mSession.mDirectControl.setNull();
12650 }
12651 else
12652 {
12653 /* this must be null here (see #OnSessionEnd()) */
12654 Assert(mData->mSession.mDirectControl.isNull());
12655 Assert(mData->mSession.mState == SessionState_Unlocking);
12656 Assert(!mData->mSession.mProgress.isNull());
12657 }
12658 if (mData->mSession.mProgress)
12659 {
12660 if (aReason == Uninit::Normal)
12661 mData->mSession.mProgress->i_notifyComplete(S_OK);
12662 else
12663 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12664 COM_IIDOF(ISession),
12665 getComponentName(),
12666 tr("The VM session was aborted"));
12667 mData->mSession.mProgress.setNull();
12668 }
12669
12670 /* remove the association between the peer machine and this session machine */
12671 Assert( (SessionMachine*)mData->mSession.mMachine == this
12672 || aReason == Uninit::Unexpected);
12673
12674 /* reset the rest of session data */
12675 mData->mSession.mLockType = LockType_Null;
12676 mData->mSession.mMachine.setNull();
12677 mData->mSession.mState = SessionState_Unlocked;
12678 mData->mSession.mName.setNull();
12679
12680 /* destroy the machine client token before leaving the exclusive lock */
12681 if (mClientToken)
12682 {
12683 delete mClientToken;
12684 mClientToken = NULL;
12685 }
12686
12687 /* fire an event */
12688 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12689
12690 uninitDataAndChildObjects();
12691
12692 /* free the essential data structure last */
12693 mData.free();
12694
12695 /* release the exclusive lock before setting the below two to NULL */
12696 multilock.release();
12697
12698 unconst(mParent) = NULL;
12699 unconst(mPeer) = NULL;
12700
12701 LogFlowThisFuncLeave();
12702}
12703
12704// util::Lockable interface
12705////////////////////////////////////////////////////////////////////////////////
12706
12707/**
12708 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12709 * with the primary Machine instance (mPeer).
12710 */
12711RWLockHandle *SessionMachine::lockHandle() const
12712{
12713 AssertReturn(mPeer != NULL, NULL);
12714 return mPeer->lockHandle();
12715}
12716
12717// IInternalMachineControl methods
12718////////////////////////////////////////////////////////////////////////////////
12719
12720/**
12721 * Passes collected guest statistics to performance collector object
12722 */
12723HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12724 ULONG aCpuKernel, ULONG aCpuIdle,
12725 ULONG aMemTotal, ULONG aMemFree,
12726 ULONG aMemBalloon, ULONG aMemShared,
12727 ULONG aMemCache, ULONG aPageTotal,
12728 ULONG aAllocVMM, ULONG aFreeVMM,
12729 ULONG aBalloonedVMM, ULONG aSharedVMM,
12730 ULONG aVmNetRx, ULONG aVmNetTx)
12731{
12732#ifdef VBOX_WITH_RESOURCE_USAGE_API
12733 if (mCollectorGuest)
12734 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12735 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12736 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12737 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12738
12739 return S_OK;
12740#else
12741 NOREF(aValidStats);
12742 NOREF(aCpuUser);
12743 NOREF(aCpuKernel);
12744 NOREF(aCpuIdle);
12745 NOREF(aMemTotal);
12746 NOREF(aMemFree);
12747 NOREF(aMemBalloon);
12748 NOREF(aMemShared);
12749 NOREF(aMemCache);
12750 NOREF(aPageTotal);
12751 NOREF(aAllocVMM);
12752 NOREF(aFreeVMM);
12753 NOREF(aBalloonedVMM);
12754 NOREF(aSharedVMM);
12755 NOREF(aVmNetRx);
12756 NOREF(aVmNetTx);
12757 return E_NOTIMPL;
12758#endif
12759}
12760
12761////////////////////////////////////////////////////////////////////////////////
12762//
12763// SessionMachine task records
12764//
12765////////////////////////////////////////////////////////////////////////////////
12766
12767/**
12768 * Task record for saving the machine state.
12769 */
12770struct SessionMachine::SaveStateTask
12771 : public Machine::Task
12772{
12773 SaveStateTask(SessionMachine *m,
12774 Progress *p,
12775 const Utf8Str &t,
12776 Reason_T enmReason,
12777 const Utf8Str &strStateFilePath)
12778 : Task(m, p, t),
12779 m_enmReason(enmReason),
12780 m_strStateFilePath(strStateFilePath)
12781 {}
12782
12783 void handler()
12784 {
12785 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12786 }
12787
12788 Reason_T m_enmReason;
12789 Utf8Str m_strStateFilePath;
12790};
12791
12792/**
12793 * Task thread implementation for SessionMachine::SaveState(), called from
12794 * SessionMachine::taskHandler().
12795 *
12796 * @note Locks this object for writing.
12797 *
12798 * @param task
12799 * @return
12800 */
12801void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12802{
12803 LogFlowThisFuncEnter();
12804
12805 AutoCaller autoCaller(this);
12806 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12807 if (FAILED(autoCaller.rc()))
12808 {
12809 /* we might have been uninitialized because the session was accidentally
12810 * closed by the client, so don't assert */
12811 HRESULT rc = setError(E_FAIL,
12812 tr("The session has been accidentally closed"));
12813 task.m_pProgress->i_notifyComplete(rc);
12814 LogFlowThisFuncLeave();
12815 return;
12816 }
12817
12818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12819
12820 HRESULT rc = S_OK;
12821
12822 try
12823 {
12824 ComPtr<IInternalSessionControl> directControl;
12825 if (mData->mSession.mLockType == LockType_VM)
12826 directControl = mData->mSession.mDirectControl;
12827 if (directControl.isNull())
12828 throw setError(VBOX_E_INVALID_VM_STATE,
12829 tr("Trying to save state without a running VM"));
12830 alock.release();
12831 BOOL fSuspendedBySave;
12832 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12833 Assert(!fSuspendedBySave);
12834 alock.acquire();
12835
12836 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12837 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12838 throw E_FAIL);
12839
12840 if (SUCCEEDED(rc))
12841 {
12842 mSSData->strStateFilePath = task.m_strStateFilePath;
12843
12844 /* save all VM settings */
12845 rc = i_saveSettings(NULL);
12846 // no need to check whether VirtualBox.xml needs saving also since
12847 // we can't have a name change pending at this point
12848 }
12849 else
12850 {
12851 // On failure, set the state to the state we had at the beginning.
12852 i_setMachineState(task.m_machineStateBackup);
12853 i_updateMachineStateOnClient();
12854
12855 // Delete the saved state file (might have been already created).
12856 // No need to check whether this is shared with a snapshot here
12857 // because we certainly created a fresh saved state file here.
12858 RTFileDelete(task.m_strStateFilePath.c_str());
12859 }
12860 }
12861 catch (HRESULT aRC) { rc = aRC; }
12862
12863 task.m_pProgress->i_notifyComplete(rc);
12864
12865 LogFlowThisFuncLeave();
12866}
12867
12868/**
12869 * @note Locks this object for writing.
12870 */
12871HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12872{
12873 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12874}
12875
12876HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12877{
12878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12879
12880 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12881 if (FAILED(rc)) return rc;
12882
12883 if ( mData->mMachineState != MachineState_Running
12884 && mData->mMachineState != MachineState_Paused
12885 )
12886 return setError(VBOX_E_INVALID_VM_STATE,
12887 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12888 Global::stringifyMachineState(mData->mMachineState));
12889
12890 ComObjPtr<Progress> pProgress;
12891 pProgress.createObject();
12892 rc = pProgress->init(i_getVirtualBox(),
12893 static_cast<IMachine *>(this) /* aInitiator */,
12894 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12895 FALSE /* aCancelable */);
12896 if (FAILED(rc))
12897 return rc;
12898
12899 Utf8Str strStateFilePath;
12900 i_composeSavedStateFilename(strStateFilePath);
12901
12902 /* create and start the task on a separate thread (note that it will not
12903 * start working until we release alock) */
12904 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12905 rc = pTask->createThread();
12906 if (FAILED(rc))
12907 return rc;
12908
12909 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12910 i_setMachineState(MachineState_Saving);
12911 i_updateMachineStateOnClient();
12912
12913 pProgress.queryInterfaceTo(aProgress.asOutParam());
12914
12915 return S_OK;
12916}
12917
12918/**
12919 * @note Locks this object for writing.
12920 */
12921HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12922{
12923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12924
12925 HRESULT rc = i_checkStateDependency(MutableStateDep);
12926 if (FAILED(rc)) return rc;
12927
12928 if ( mData->mMachineState != MachineState_PoweredOff
12929 && mData->mMachineState != MachineState_Teleported
12930 && mData->mMachineState != MachineState_Aborted
12931 )
12932 return setError(VBOX_E_INVALID_VM_STATE,
12933 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12934 Global::stringifyMachineState(mData->mMachineState));
12935
12936 com::Utf8Str stateFilePathFull;
12937 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12938 if (RT_FAILURE(vrc))
12939 return setError(VBOX_E_FILE_ERROR,
12940 tr("Invalid saved state file path '%s' (%Rrc)"),
12941 aSavedStateFile.c_str(),
12942 vrc);
12943
12944 mSSData->strStateFilePath = stateFilePathFull;
12945
12946 /* The below i_setMachineState() will detect the state transition and will
12947 * update the settings file */
12948
12949 return i_setMachineState(MachineState_Saved);
12950}
12951
12952/**
12953 * @note Locks this object for writing.
12954 */
12955HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12956{
12957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12958
12959 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12960 if (FAILED(rc)) return rc;
12961
12962 if (mData->mMachineState != MachineState_Saved)
12963 return setError(VBOX_E_INVALID_VM_STATE,
12964 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12965 Global::stringifyMachineState(mData->mMachineState));
12966
12967 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12968
12969 /*
12970 * Saved -> PoweredOff transition will be detected in the SessionMachine
12971 * and properly handled.
12972 */
12973 rc = i_setMachineState(MachineState_PoweredOff);
12974 return rc;
12975}
12976
12977
12978/**
12979 * @note Locks the same as #i_setMachineState() does.
12980 */
12981HRESULT SessionMachine::updateState(MachineState_T aState)
12982{
12983 return i_setMachineState(aState);
12984}
12985
12986/**
12987 * @note Locks this object for writing.
12988 */
12989HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12990{
12991 IProgress* pProgress(aProgress);
12992
12993 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12994
12995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12996
12997 if (mData->mSession.mState != SessionState_Locked)
12998 return VBOX_E_INVALID_OBJECT_STATE;
12999
13000 if (!mData->mSession.mProgress.isNull())
13001 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13002
13003 /* If we didn't reference the NAT network service yet, add a reference to
13004 * force a start */
13005 if (miNATNetworksStarted < 1)
13006 {
13007 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13008 {
13009 NetworkAttachmentType_T type;
13010 HRESULT hrc;
13011 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13012 if ( SUCCEEDED(hrc)
13013 && type == NetworkAttachmentType_NATNetwork)
13014 {
13015 Bstr name;
13016 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13017 if (SUCCEEDED(hrc))
13018 {
13019 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13020 mUserData->s.strName.c_str(), name.raw()));
13021 mPeer->lockHandle()->unlockWrite();
13022 mParent->i_natNetworkRefInc(name.raw());
13023#ifdef RT_LOCK_STRICT
13024 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13025#else
13026 mPeer->lockHandle()->lockWrite();
13027#endif
13028 }
13029 }
13030 }
13031 miNATNetworksStarted++;
13032 }
13033
13034 LogFlowThisFunc(("returns S_OK.\n"));
13035 return S_OK;
13036}
13037
13038/**
13039 * @note Locks this object for writing.
13040 */
13041HRESULT SessionMachine::endPowerUp(LONG aResult)
13042{
13043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13044
13045 if (mData->mSession.mState != SessionState_Locked)
13046 return VBOX_E_INVALID_OBJECT_STATE;
13047
13048 /* Finalize the LaunchVMProcess progress object. */
13049 if (mData->mSession.mProgress)
13050 {
13051 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13052 mData->mSession.mProgress.setNull();
13053 }
13054
13055 if (SUCCEEDED((HRESULT)aResult))
13056 {
13057#ifdef VBOX_WITH_RESOURCE_USAGE_API
13058 /* The VM has been powered up successfully, so it makes sense
13059 * now to offer the performance metrics for a running machine
13060 * object. Doing it earlier wouldn't be safe. */
13061 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13062 mData->mSession.mPID);
13063#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13064 }
13065
13066 return S_OK;
13067}
13068
13069/**
13070 * @note Locks this object for writing.
13071 */
13072HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13073{
13074 LogFlowThisFuncEnter();
13075
13076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13077
13078 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13079 E_FAIL);
13080
13081 /* create a progress object to track operation completion */
13082 ComObjPtr<Progress> pProgress;
13083 pProgress.createObject();
13084 pProgress->init(i_getVirtualBox(),
13085 static_cast<IMachine *>(this) /* aInitiator */,
13086 Bstr(tr("Stopping the virtual machine")).raw(),
13087 FALSE /* aCancelable */);
13088
13089 /* fill in the console task data */
13090 mConsoleTaskData.mLastState = mData->mMachineState;
13091 mConsoleTaskData.mProgress = pProgress;
13092
13093 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13094 i_setMachineState(MachineState_Stopping);
13095
13096 pProgress.queryInterfaceTo(aProgress.asOutParam());
13097
13098 return S_OK;
13099}
13100
13101/**
13102 * @note Locks this object for writing.
13103 */
13104HRESULT SessionMachine::endPoweringDown(LONG aResult,
13105 const com::Utf8Str &aErrMsg)
13106{
13107 LogFlowThisFuncEnter();
13108
13109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13110
13111 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13112 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13113 && mConsoleTaskData.mLastState != MachineState_Null,
13114 E_FAIL);
13115
13116 /*
13117 * On failure, set the state to the state we had when BeginPoweringDown()
13118 * was called (this is expected by Console::PowerDown() and the associated
13119 * task). On success the VM process already changed the state to
13120 * MachineState_PoweredOff, so no need to do anything.
13121 */
13122 if (FAILED(aResult))
13123 i_setMachineState(mConsoleTaskData.mLastState);
13124
13125 /* notify the progress object about operation completion */
13126 Assert(mConsoleTaskData.mProgress);
13127 if (SUCCEEDED(aResult))
13128 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13129 else
13130 {
13131 if (aErrMsg.length())
13132 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13133 COM_IIDOF(ISession),
13134 getComponentName(),
13135 aErrMsg.c_str());
13136 else
13137 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13138 }
13139
13140 /* clear out the temporary saved state data */
13141 mConsoleTaskData.mLastState = MachineState_Null;
13142 mConsoleTaskData.mProgress.setNull();
13143
13144 LogFlowThisFuncLeave();
13145 return S_OK;
13146}
13147
13148
13149/**
13150 * Goes through the USB filters of the given machine to see if the given
13151 * device matches any filter or not.
13152 *
13153 * @note Locks the same as USBController::hasMatchingFilter() does.
13154 */
13155HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13156 BOOL *aMatched,
13157 ULONG *aMaskedInterfaces)
13158{
13159 LogFlowThisFunc(("\n"));
13160
13161#ifdef VBOX_WITH_USB
13162 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13163#else
13164 NOREF(aDevice);
13165 NOREF(aMaskedInterfaces);
13166 *aMatched = FALSE;
13167#endif
13168
13169 return S_OK;
13170}
13171
13172/**
13173 * @note Locks the same as Host::captureUSBDevice() does.
13174 */
13175HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13176{
13177 LogFlowThisFunc(("\n"));
13178
13179#ifdef VBOX_WITH_USB
13180 /* if captureDeviceForVM() fails, it must have set extended error info */
13181 clearError();
13182 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13183 if (FAILED(rc)) return rc;
13184
13185 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13186 AssertReturn(service, E_FAIL);
13187 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13188#else
13189 NOREF(aId);
13190 return E_NOTIMPL;
13191#endif
13192}
13193
13194/**
13195 * @note Locks the same as Host::detachUSBDevice() does.
13196 */
13197HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13198 BOOL aDone)
13199{
13200 LogFlowThisFunc(("\n"));
13201
13202#ifdef VBOX_WITH_USB
13203 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13204 AssertReturn(service, E_FAIL);
13205 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13206#else
13207 NOREF(aId);
13208 NOREF(aDone);
13209 return E_NOTIMPL;
13210#endif
13211}
13212
13213/**
13214 * Inserts all machine filters to the USB proxy service and then calls
13215 * Host::autoCaptureUSBDevices().
13216 *
13217 * Called by Console from the VM process upon VM startup.
13218 *
13219 * @note Locks what called methods lock.
13220 */
13221HRESULT SessionMachine::autoCaptureUSBDevices()
13222{
13223 LogFlowThisFunc(("\n"));
13224
13225#ifdef VBOX_WITH_USB
13226 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13227 AssertComRC(rc);
13228 NOREF(rc);
13229
13230 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13231 AssertReturn(service, E_FAIL);
13232 return service->autoCaptureDevicesForVM(this);
13233#else
13234 return S_OK;
13235#endif
13236}
13237
13238/**
13239 * Removes all machine filters from the USB proxy service and then calls
13240 * Host::detachAllUSBDevices().
13241 *
13242 * Called by Console from the VM process upon normal VM termination or by
13243 * SessionMachine::uninit() upon abnormal VM termination (from under the
13244 * Machine/SessionMachine lock).
13245 *
13246 * @note Locks what called methods lock.
13247 */
13248HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13249{
13250 LogFlowThisFunc(("\n"));
13251
13252#ifdef VBOX_WITH_USB
13253 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13254 AssertComRC(rc);
13255 NOREF(rc);
13256
13257 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13258 AssertReturn(service, E_FAIL);
13259 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13260#else
13261 NOREF(aDone);
13262 return S_OK;
13263#endif
13264}
13265
13266/**
13267 * @note Locks this object for writing.
13268 */
13269HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13270 ComPtr<IProgress> &aProgress)
13271{
13272 LogFlowThisFuncEnter();
13273
13274 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13275 /*
13276 * We don't assert below because it might happen that a non-direct session
13277 * informs us it is closed right after we've been uninitialized -- it's ok.
13278 */
13279
13280 /* get IInternalSessionControl interface */
13281 ComPtr<IInternalSessionControl> control(aSession);
13282
13283 ComAssertRet(!control.isNull(), E_INVALIDARG);
13284
13285 /* Creating a Progress object requires the VirtualBox lock, and
13286 * thus locking it here is required by the lock order rules. */
13287 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13288
13289 if (control == mData->mSession.mDirectControl)
13290 {
13291 /* The direct session is being normally closed by the client process
13292 * ----------------------------------------------------------------- */
13293
13294 /* go to the closing state (essential for all open*Session() calls and
13295 * for #checkForDeath()) */
13296 Assert(mData->mSession.mState == SessionState_Locked);
13297 mData->mSession.mState = SessionState_Unlocking;
13298
13299 /* set direct control to NULL to release the remote instance */
13300 mData->mSession.mDirectControl.setNull();
13301 LogFlowThisFunc(("Direct control is set to NULL\n"));
13302
13303 if (mData->mSession.mProgress)
13304 {
13305 /* finalize the progress, someone might wait if a frontend
13306 * closes the session before powering on the VM. */
13307 mData->mSession.mProgress->notifyComplete(E_FAIL,
13308 COM_IIDOF(ISession),
13309 getComponentName(),
13310 tr("The VM session was closed before any attempt to power it on"));
13311 mData->mSession.mProgress.setNull();
13312 }
13313
13314 /* Create the progress object the client will use to wait until
13315 * #checkForDeath() is called to uninitialize this session object after
13316 * it releases the IPC semaphore.
13317 * Note! Because we're "reusing" mProgress here, this must be a proxy
13318 * object just like for LaunchVMProcess. */
13319 Assert(mData->mSession.mProgress.isNull());
13320 ComObjPtr<ProgressProxy> progress;
13321 progress.createObject();
13322 ComPtr<IUnknown> pPeer(mPeer);
13323 progress->init(mParent, pPeer,
13324 Bstr(tr("Closing session")).raw(),
13325 FALSE /* aCancelable */);
13326 progress.queryInterfaceTo(aProgress.asOutParam());
13327 mData->mSession.mProgress = progress;
13328 }
13329 else
13330 {
13331 /* the remote session is being normally closed */
13332 Data::Session::RemoteControlList::iterator it =
13333 mData->mSession.mRemoteControls.begin();
13334 while (it != mData->mSession.mRemoteControls.end())
13335 {
13336 if (control == *it)
13337 break;
13338 ++it;
13339 }
13340 BOOL found = it != mData->mSession.mRemoteControls.end();
13341 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13342 E_INVALIDARG);
13343 // This MUST be erase(it), not remove(*it) as the latter triggers a
13344 // very nasty use after free due to the place where the value "lives".
13345 mData->mSession.mRemoteControls.erase(it);
13346 }
13347
13348 /* signal the client watcher thread, because the client is going away */
13349 mParent->i_updateClientWatcher();
13350
13351 LogFlowThisFuncLeave();
13352 return S_OK;
13353}
13354
13355HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13356 std::vector<com::Utf8Str> &aValues,
13357 std::vector<LONG64> &aTimestamps,
13358 std::vector<com::Utf8Str> &aFlags)
13359{
13360 LogFlowThisFunc(("\n"));
13361
13362#ifdef VBOX_WITH_GUEST_PROPS
13363 using namespace guestProp;
13364
13365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13366
13367 size_t cEntries = mHWData->mGuestProperties.size();
13368 aNames.resize(cEntries);
13369 aValues.resize(cEntries);
13370 aTimestamps.resize(cEntries);
13371 aFlags.resize(cEntries);
13372
13373 size_t i = 0;
13374 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13375 it != mHWData->mGuestProperties.end();
13376 ++it, ++i)
13377 {
13378 char szFlags[MAX_FLAGS_LEN + 1];
13379 aNames[i] = it->first;
13380 aValues[i] = it->second.strValue;
13381 aTimestamps[i] = it->second.mTimestamp;
13382
13383 /* If it is NULL, keep it NULL. */
13384 if (it->second.mFlags)
13385 {
13386 writeFlags(it->second.mFlags, szFlags);
13387 aFlags[i] = szFlags;
13388 }
13389 else
13390 aFlags[i] = "";
13391 }
13392 return S_OK;
13393#else
13394 ReturnComNotImplemented();
13395#endif
13396}
13397
13398HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13399 const com::Utf8Str &aValue,
13400 LONG64 aTimestamp,
13401 const com::Utf8Str &aFlags,
13402 BOOL *aNotify)
13403{
13404 LogFlowThisFunc(("\n"));
13405
13406#ifdef VBOX_WITH_GUEST_PROPS
13407 using namespace guestProp;
13408
13409 *aNotify = FALSE;
13410
13411 try
13412 {
13413 /*
13414 * Convert input up front.
13415 */
13416 uint32_t fFlags = NILFLAG;
13417 if (aFlags.length())
13418 {
13419 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13420 AssertRCReturn(vrc, E_INVALIDARG);
13421 }
13422
13423 /*
13424 * Now grab the object lock, validate the state and do the update.
13425 */
13426
13427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13428
13429 switch (mData->mMachineState)
13430 {
13431 case MachineState_Paused:
13432 case MachineState_Running:
13433 case MachineState_Teleporting:
13434 case MachineState_TeleportingPausedVM:
13435 case MachineState_OnlineSnapshotting:
13436 case MachineState_LiveSnapshotting:
13437 case MachineState_DeletingSnapshotOnline:
13438 case MachineState_DeletingSnapshotPaused:
13439 case MachineState_Saving:
13440 case MachineState_Stopping:
13441 break;
13442
13443 default:
13444 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13445 VBOX_E_INVALID_VM_STATE);
13446 }
13447
13448 i_setModified(IsModified_MachineData);
13449 mHWData.backup();
13450
13451 bool fDelete = !aValue.length();
13452 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13453 if (it != mHWData->mGuestProperties.end())
13454 {
13455 if (!fDelete)
13456 {
13457 it->second.strValue = aValue;
13458 it->second.mTimestamp = aTimestamp;
13459 it->second.mFlags = fFlags;
13460 }
13461 else
13462 mHWData->mGuestProperties.erase(it);
13463
13464 mData->mGuestPropertiesModified = TRUE;
13465 }
13466 else if (!fDelete)
13467 {
13468 HWData::GuestProperty prop;
13469 prop.strValue = aValue;
13470 prop.mTimestamp = aTimestamp;
13471 prop.mFlags = fFlags;
13472
13473 mHWData->mGuestProperties[aName] = prop;
13474 mData->mGuestPropertiesModified = TRUE;
13475 }
13476
13477 /*
13478 * Send a callback notification if appropriate
13479 */
13480 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13481 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13482 RTSTR_MAX,
13483 aName.c_str(),
13484 RTSTR_MAX, NULL)
13485 )
13486 {
13487 alock.release();
13488
13489 mParent->i_onGuestPropertyChange(mData->mUuid,
13490 Bstr(aName).raw(),
13491 Bstr(aValue).raw(),
13492 Bstr(aFlags).raw());
13493 *aNotify = TRUE;
13494 }
13495 }
13496 catch (...)
13497 {
13498 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13499 }
13500 return S_OK;
13501#else
13502 ReturnComNotImplemented();
13503#endif
13504}
13505
13506
13507HRESULT SessionMachine::lockMedia()
13508{
13509 AutoMultiWriteLock2 alock(this->lockHandle(),
13510 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13511
13512 AssertReturn( mData->mMachineState == MachineState_Starting
13513 || mData->mMachineState == MachineState_Restoring
13514 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13515
13516 clearError();
13517 alock.release();
13518 return i_lockMedia();
13519}
13520
13521HRESULT SessionMachine::unlockMedia()
13522{
13523 HRESULT hrc = i_unlockMedia();
13524 return hrc;
13525}
13526
13527HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13528 ComPtr<IMediumAttachment> &aNewAttachment)
13529{
13530 // request the host lock first, since might be calling Host methods for getting host drives;
13531 // next, protect the media tree all the while we're in here, as well as our member variables
13532 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13533 this->lockHandle(),
13534 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13535
13536 IMediumAttachment *iAttach = aAttachment;
13537 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13538
13539 Bstr ctrlName;
13540 LONG lPort;
13541 LONG lDevice;
13542 bool fTempEject;
13543 {
13544 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13545
13546 /* Need to query the details first, as the IMediumAttachment reference
13547 * might be to the original settings, which we are going to change. */
13548 ctrlName = pAttach->i_getControllerName();
13549 lPort = pAttach->i_getPort();
13550 lDevice = pAttach->i_getDevice();
13551 fTempEject = pAttach->i_getTempEject();
13552 }
13553
13554 if (!fTempEject)
13555 {
13556 /* Remember previously mounted medium. The medium before taking the
13557 * backup is not necessarily the same thing. */
13558 ComObjPtr<Medium> oldmedium;
13559 oldmedium = pAttach->i_getMedium();
13560
13561 i_setModified(IsModified_Storage);
13562 mMediaData.backup();
13563
13564 // The backup operation makes the pAttach reference point to the
13565 // old settings. Re-get the correct reference.
13566 pAttach = i_findAttachment(mMediaData->mAttachments,
13567 ctrlName.raw(),
13568 lPort,
13569 lDevice);
13570
13571 {
13572 AutoCaller autoAttachCaller(this);
13573 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13574
13575 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13576 if (!oldmedium.isNull())
13577 oldmedium->i_removeBackReference(mData->mUuid);
13578
13579 pAttach->i_updateMedium(NULL);
13580 pAttach->i_updateEjected();
13581 }
13582
13583 i_setModified(IsModified_Storage);
13584 }
13585 else
13586 {
13587 {
13588 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13589 pAttach->i_updateEjected();
13590 }
13591 }
13592
13593 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13594
13595 return S_OK;
13596}
13597
13598// public methods only for internal purposes
13599/////////////////////////////////////////////////////////////////////////////
13600
13601#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13602/**
13603 * Called from the client watcher thread to check for expected or unexpected
13604 * death of the client process that has a direct session to this machine.
13605 *
13606 * On Win32 and on OS/2, this method is called only when we've got the
13607 * mutex (i.e. the client has either died or terminated normally) so it always
13608 * returns @c true (the client is terminated, the session machine is
13609 * uninitialized).
13610 *
13611 * On other platforms, the method returns @c true if the client process has
13612 * terminated normally or abnormally and the session machine was uninitialized,
13613 * and @c false if the client process is still alive.
13614 *
13615 * @note Locks this object for writing.
13616 */
13617bool SessionMachine::i_checkForDeath()
13618{
13619 Uninit::Reason reason;
13620 bool terminated = false;
13621
13622 /* Enclose autoCaller with a block because calling uninit() from under it
13623 * will deadlock. */
13624 {
13625 AutoCaller autoCaller(this);
13626 if (!autoCaller.isOk())
13627 {
13628 /* return true if not ready, to cause the client watcher to exclude
13629 * the corresponding session from watching */
13630 LogFlowThisFunc(("Already uninitialized!\n"));
13631 return true;
13632 }
13633
13634 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13635
13636 /* Determine the reason of death: if the session state is Closing here,
13637 * everything is fine. Otherwise it means that the client did not call
13638 * OnSessionEnd() before it released the IPC semaphore. This may happen
13639 * either because the client process has abnormally terminated, or
13640 * because it simply forgot to call ISession::Close() before exiting. We
13641 * threat the latter also as an abnormal termination (see
13642 * Session::uninit() for details). */
13643 reason = mData->mSession.mState == SessionState_Unlocking ?
13644 Uninit::Normal :
13645 Uninit::Abnormal;
13646
13647 if (mClientToken)
13648 terminated = mClientToken->release();
13649 } /* AutoCaller block */
13650
13651 if (terminated)
13652 uninit(reason);
13653
13654 return terminated;
13655}
13656
13657void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13658{
13659 LogFlowThisFunc(("\n"));
13660
13661 strTokenId.setNull();
13662
13663 AutoCaller autoCaller(this);
13664 AssertComRCReturnVoid(autoCaller.rc());
13665
13666 Assert(mClientToken);
13667 if (mClientToken)
13668 mClientToken->getId(strTokenId);
13669}
13670#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13671IToken *SessionMachine::i_getToken()
13672{
13673 LogFlowThisFunc(("\n"));
13674
13675 AutoCaller autoCaller(this);
13676 AssertComRCReturn(autoCaller.rc(), NULL);
13677
13678 Assert(mClientToken);
13679 if (mClientToken)
13680 return mClientToken->getToken();
13681 else
13682 return NULL;
13683}
13684#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13685
13686Machine::ClientToken *SessionMachine::i_getClientToken()
13687{
13688 LogFlowThisFunc(("\n"));
13689
13690 AutoCaller autoCaller(this);
13691 AssertComRCReturn(autoCaller.rc(), NULL);
13692
13693 return mClientToken;
13694}
13695
13696
13697/**
13698 * @note Locks this object for reading.
13699 */
13700HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13701{
13702 LogFlowThisFunc(("\n"));
13703
13704 AutoCaller autoCaller(this);
13705 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13706
13707 ComPtr<IInternalSessionControl> directControl;
13708 {
13709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13710 if (mData->mSession.mLockType == LockType_VM)
13711 directControl = mData->mSession.mDirectControl;
13712 }
13713
13714 /* ignore notifications sent after #OnSessionEnd() is called */
13715 if (!directControl)
13716 return S_OK;
13717
13718 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13719}
13720
13721/**
13722 * @note Locks this object for reading.
13723 */
13724HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13725 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13726 IN_BSTR aGuestIp, LONG aGuestPort)
13727{
13728 LogFlowThisFunc(("\n"));
13729
13730 AutoCaller autoCaller(this);
13731 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13732
13733 ComPtr<IInternalSessionControl> directControl;
13734 {
13735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13736 if (mData->mSession.mLockType == LockType_VM)
13737 directControl = mData->mSession.mDirectControl;
13738 }
13739
13740 /* ignore notifications sent after #OnSessionEnd() is called */
13741 if (!directControl)
13742 return S_OK;
13743 /*
13744 * instead acting like callback we ask IVirtualBox deliver corresponding event
13745 */
13746
13747 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13748 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13749 return S_OK;
13750}
13751
13752/**
13753 * @note Locks this object for reading.
13754 */
13755HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13756{
13757 LogFlowThisFunc(("\n"));
13758
13759 AutoCaller autoCaller(this);
13760 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13761
13762 ComPtr<IInternalSessionControl> directControl;
13763 {
13764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13765 if (mData->mSession.mLockType == LockType_VM)
13766 directControl = mData->mSession.mDirectControl;
13767 }
13768
13769 /* ignore notifications sent after #OnSessionEnd() is called */
13770 if (!directControl)
13771 return S_OK;
13772
13773 return directControl->OnSerialPortChange(serialPort);
13774}
13775
13776/**
13777 * @note Locks this object for reading.
13778 */
13779HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13780{
13781 LogFlowThisFunc(("\n"));
13782
13783 AutoCaller autoCaller(this);
13784 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13785
13786 ComPtr<IInternalSessionControl> directControl;
13787 {
13788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13789 if (mData->mSession.mLockType == LockType_VM)
13790 directControl = mData->mSession.mDirectControl;
13791 }
13792
13793 /* ignore notifications sent after #OnSessionEnd() is called */
13794 if (!directControl)
13795 return S_OK;
13796
13797 return directControl->OnParallelPortChange(parallelPort);
13798}
13799
13800/**
13801 * @note Locks this object for reading.
13802 */
13803HRESULT SessionMachine::i_onStorageControllerChange()
13804{
13805 LogFlowThisFunc(("\n"));
13806
13807 AutoCaller autoCaller(this);
13808 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13809
13810 ComPtr<IInternalSessionControl> directControl;
13811 {
13812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13813 if (mData->mSession.mLockType == LockType_VM)
13814 directControl = mData->mSession.mDirectControl;
13815 }
13816
13817 /* ignore notifications sent after #OnSessionEnd() is called */
13818 if (!directControl)
13819 return S_OK;
13820
13821 return directControl->OnStorageControllerChange();
13822}
13823
13824/**
13825 * @note Locks this object for reading.
13826 */
13827HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13828{
13829 LogFlowThisFunc(("\n"));
13830
13831 AutoCaller autoCaller(this);
13832 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13833
13834 ComPtr<IInternalSessionControl> directControl;
13835 {
13836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13837 if (mData->mSession.mLockType == LockType_VM)
13838 directControl = mData->mSession.mDirectControl;
13839 }
13840
13841 /* ignore notifications sent after #OnSessionEnd() is called */
13842 if (!directControl)
13843 return S_OK;
13844
13845 return directControl->OnMediumChange(aAttachment, aForce);
13846}
13847
13848/**
13849 * @note Locks this object for reading.
13850 */
13851HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
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->OnCPUChange(aCPU, aRemove);
13870}
13871
13872HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13873{
13874 LogFlowThisFunc(("\n"));
13875
13876 AutoCaller autoCaller(this);
13877 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13878
13879 ComPtr<IInternalSessionControl> directControl;
13880 {
13881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13882 if (mData->mSession.mLockType == LockType_VM)
13883 directControl = mData->mSession.mDirectControl;
13884 }
13885
13886 /* ignore notifications sent after #OnSessionEnd() is called */
13887 if (!directControl)
13888 return S_OK;
13889
13890 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13891}
13892
13893/**
13894 * @note Locks this object for reading.
13895 */
13896HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13897{
13898 LogFlowThisFunc(("\n"));
13899
13900 AutoCaller autoCaller(this);
13901 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13902
13903 ComPtr<IInternalSessionControl> directControl;
13904 {
13905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13906 if (mData->mSession.mLockType == LockType_VM)
13907 directControl = mData->mSession.mDirectControl;
13908 }
13909
13910 /* ignore notifications sent after #OnSessionEnd() is called */
13911 if (!directControl)
13912 return S_OK;
13913
13914 return directControl->OnVRDEServerChange(aRestart);
13915}
13916
13917/**
13918 * @note Locks this object for reading.
13919 */
13920HRESULT SessionMachine::i_onVideoCaptureChange()
13921{
13922 LogFlowThisFunc(("\n"));
13923
13924 AutoCaller autoCaller(this);
13925 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13926
13927 ComPtr<IInternalSessionControl> directControl;
13928 {
13929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13930 if (mData->mSession.mLockType == LockType_VM)
13931 directControl = mData->mSession.mDirectControl;
13932 }
13933
13934 /* ignore notifications sent after #OnSessionEnd() is called */
13935 if (!directControl)
13936 return S_OK;
13937
13938 return directControl->OnVideoCaptureChange();
13939}
13940
13941/**
13942 * @note Locks this object for reading.
13943 */
13944HRESULT SessionMachine::i_onUSBControllerChange()
13945{
13946 LogFlowThisFunc(("\n"));
13947
13948 AutoCaller autoCaller(this);
13949 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13950
13951 ComPtr<IInternalSessionControl> directControl;
13952 {
13953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13954 if (mData->mSession.mLockType == LockType_VM)
13955 directControl = mData->mSession.mDirectControl;
13956 }
13957
13958 /* ignore notifications sent after #OnSessionEnd() is called */
13959 if (!directControl)
13960 return S_OK;
13961
13962 return directControl->OnUSBControllerChange();
13963}
13964
13965/**
13966 * @note Locks this object for reading.
13967 */
13968HRESULT SessionMachine::i_onSharedFolderChange()
13969{
13970 LogFlowThisFunc(("\n"));
13971
13972 AutoCaller autoCaller(this);
13973 AssertComRCReturnRC(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->OnSharedFolderChange(FALSE /* aGlobal */);
13987}
13988
13989/**
13990 * @note Locks this object for reading.
13991 */
13992HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13993{
13994 LogFlowThisFunc(("\n"));
13995
13996 AutoCaller autoCaller(this);
13997 AssertComRCReturnRC(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->OnClipboardModeChange(aClipboardMode);
14011}
14012
14013/**
14014 * @note Locks this object for reading.
14015 */
14016HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14017{
14018 LogFlowThisFunc(("\n"));
14019
14020 AutoCaller autoCaller(this);
14021 AssertComRCReturnRC(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->OnDnDModeChange(aDnDMode);
14035}
14036
14037/**
14038 * @note Locks this object for reading.
14039 */
14040HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
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->OnBandwidthGroupChange(aBandwidthGroup);
14059}
14060
14061/**
14062 * @note Locks this object for reading.
14063 */
14064HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14065{
14066 LogFlowThisFunc(("\n"));
14067
14068 AutoCaller autoCaller(this);
14069 AssertComRCReturn(autoCaller.rc(), 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->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14083}
14084
14085/**
14086 * Returns @c true if this machine's USB controller reports it has a matching
14087 * filter for the given USB device and @c false otherwise.
14088 *
14089 * @note locks this object for reading.
14090 */
14091bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14092{
14093 AutoCaller autoCaller(this);
14094 /* silently return if not ready -- this method may be called after the
14095 * direct machine session has been called */
14096 if (!autoCaller.isOk())
14097 return false;
14098
14099#ifdef VBOX_WITH_USB
14100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14101
14102 switch (mData->mMachineState)
14103 {
14104 case MachineState_Starting:
14105 case MachineState_Restoring:
14106 case MachineState_TeleportingIn:
14107 case MachineState_Paused:
14108 case MachineState_Running:
14109 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14110 * elsewhere... */
14111 alock.release();
14112 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14113 default: break;
14114 }
14115#else
14116 NOREF(aDevice);
14117 NOREF(aMaskedIfs);
14118#endif
14119 return false;
14120}
14121
14122/**
14123 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14124 */
14125HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14126 IVirtualBoxErrorInfo *aError,
14127 ULONG aMaskedIfs,
14128 const com::Utf8Str &aCaptureFilename)
14129{
14130 LogFlowThisFunc(("\n"));
14131
14132 AutoCaller autoCaller(this);
14133
14134 /* This notification may happen after the machine object has been
14135 * uninitialized (the session was closed), so don't assert. */
14136 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14137
14138 ComPtr<IInternalSessionControl> directControl;
14139 {
14140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14141 if (mData->mSession.mLockType == LockType_VM)
14142 directControl = mData->mSession.mDirectControl;
14143 }
14144
14145 /* fail on notifications sent after #OnSessionEnd() is called, it is
14146 * expected by the caller */
14147 if (!directControl)
14148 return E_FAIL;
14149
14150 /* No locks should be held at this point. */
14151 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14152 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14153
14154 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14155}
14156
14157/**
14158 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14159 */
14160HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14161 IVirtualBoxErrorInfo *aError)
14162{
14163 LogFlowThisFunc(("\n"));
14164
14165 AutoCaller autoCaller(this);
14166
14167 /* This notification may happen after the machine object has been
14168 * uninitialized (the session was closed), so don't assert. */
14169 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14170
14171 ComPtr<IInternalSessionControl> directControl;
14172 {
14173 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14174 if (mData->mSession.mLockType == LockType_VM)
14175 directControl = mData->mSession.mDirectControl;
14176 }
14177
14178 /* fail on notifications sent after #OnSessionEnd() is called, it is
14179 * expected by the caller */
14180 if (!directControl)
14181 return E_FAIL;
14182
14183 /* No locks should be held at this point. */
14184 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14185 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14186
14187 return directControl->OnUSBDeviceDetach(aId, aError);
14188}
14189
14190// protected methods
14191/////////////////////////////////////////////////////////////////////////////
14192
14193/**
14194 * Deletes the given file if it is no longer in use by either the current machine state
14195 * (if the machine is "saved") or any of the machine's snapshots.
14196 *
14197 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14198 * but is different for each SnapshotMachine. When calling this, the order of calling this
14199 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14200 * is therefore critical. I know, it's all rather messy.
14201 *
14202 * @param strStateFile
14203 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14204 * the test for whether the saved state file is in use.
14205 */
14206void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14207 Snapshot *pSnapshotToIgnore)
14208{
14209 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14210 if ( (strStateFile.isNotEmpty())
14211 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14212 )
14213 // ... and it must also not be shared with other snapshots
14214 if ( !mData->mFirstSnapshot
14215 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14216 // this checks the SnapshotMachine's state file paths
14217 )
14218 RTFileDelete(strStateFile.c_str());
14219}
14220
14221/**
14222 * Locks the attached media.
14223 *
14224 * All attached hard disks are locked for writing and DVD/floppy are locked for
14225 * reading. Parents of attached hard disks (if any) are locked for reading.
14226 *
14227 * This method also performs accessibility check of all media it locks: if some
14228 * media is inaccessible, the method will return a failure and a bunch of
14229 * extended error info objects per each inaccessible medium.
14230 *
14231 * Note that this method is atomic: if it returns a success, all media are
14232 * locked as described above; on failure no media is locked at all (all
14233 * succeeded individual locks will be undone).
14234 *
14235 * The caller is responsible for doing the necessary state sanity checks.
14236 *
14237 * The locks made by this method must be undone by calling #unlockMedia() when
14238 * no more needed.
14239 */
14240HRESULT SessionMachine::i_lockMedia()
14241{
14242 AutoCaller autoCaller(this);
14243 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14244
14245 AutoMultiWriteLock2 alock(this->lockHandle(),
14246 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14247
14248 /* bail out if trying to lock things with already set up locking */
14249 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14250
14251 MultiResult mrc(S_OK);
14252
14253 /* Collect locking information for all medium objects attached to the VM. */
14254 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14255 it != mMediaData->mAttachments.end();
14256 ++it)
14257 {
14258 MediumAttachment* pAtt = *it;
14259 DeviceType_T devType = pAtt->i_getType();
14260 Medium *pMedium = pAtt->i_getMedium();
14261
14262 MediumLockList *pMediumLockList(new MediumLockList());
14263 // There can be attachments without a medium (floppy/dvd), and thus
14264 // it's impossible to create a medium lock list. It still makes sense
14265 // to have the empty medium lock list in the map in case a medium is
14266 // attached later.
14267 if (pMedium != NULL)
14268 {
14269 MediumType_T mediumType = pMedium->i_getType();
14270 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14271 || mediumType == MediumType_Shareable;
14272 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14273
14274 alock.release();
14275 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14276 !fIsReadOnlyLock /* fMediumLockWrite */,
14277 false /* fMediumLockWriteAll */,
14278 NULL,
14279 *pMediumLockList);
14280 alock.acquire();
14281 if (FAILED(mrc))
14282 {
14283 delete pMediumLockList;
14284 mData->mSession.mLockedMedia.Clear();
14285 break;
14286 }
14287 }
14288
14289 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14290 if (FAILED(rc))
14291 {
14292 mData->mSession.mLockedMedia.Clear();
14293 mrc = setError(rc,
14294 tr("Collecting locking information for all attached media failed"));
14295 break;
14296 }
14297 }
14298
14299 if (SUCCEEDED(mrc))
14300 {
14301 /* Now lock all media. If this fails, nothing is locked. */
14302 alock.release();
14303 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14304 alock.acquire();
14305 if (FAILED(rc))
14306 {
14307 mrc = setError(rc,
14308 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14309 }
14310 }
14311
14312 return mrc;
14313}
14314
14315/**
14316 * Undoes the locks made by by #lockMedia().
14317 */
14318HRESULT SessionMachine::i_unlockMedia()
14319{
14320 AutoCaller autoCaller(this);
14321 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14322
14323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14324
14325 /* we may be holding important error info on the current thread;
14326 * preserve it */
14327 ErrorInfoKeeper eik;
14328
14329 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14330 AssertComRC(rc);
14331 return rc;
14332}
14333
14334/**
14335 * Helper to change the machine state (reimplementation).
14336 *
14337 * @note Locks this object for writing.
14338 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14339 * it can cause crashes in random places due to unexpectedly committing
14340 * the current settings. The caller is responsible for that. The call
14341 * to saveStateSettings is fine, because this method does not commit.
14342 */
14343HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14344{
14345 LogFlowThisFuncEnter();
14346 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14347
14348 AutoCaller autoCaller(this);
14349 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14350
14351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14352
14353 MachineState_T oldMachineState = mData->mMachineState;
14354
14355 AssertMsgReturn(oldMachineState != aMachineState,
14356 ("oldMachineState=%s, aMachineState=%s\n",
14357 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14358 E_FAIL);
14359
14360 HRESULT rc = S_OK;
14361
14362 int stsFlags = 0;
14363 bool deleteSavedState = false;
14364
14365 /* detect some state transitions */
14366
14367 if ( ( oldMachineState == MachineState_Saved
14368 && aMachineState == MachineState_Restoring)
14369 || ( ( oldMachineState == MachineState_PoweredOff
14370 || oldMachineState == MachineState_Teleported
14371 || oldMachineState == MachineState_Aborted
14372 )
14373 && ( aMachineState == MachineState_TeleportingIn
14374 || aMachineState == MachineState_Starting
14375 )
14376 )
14377 )
14378 {
14379 /* The EMT thread is about to start */
14380
14381 /* Nothing to do here for now... */
14382
14383 /// @todo NEWMEDIA don't let mDVDDrive and other children
14384 /// change anything when in the Starting/Restoring state
14385 }
14386 else if ( ( oldMachineState == MachineState_Running
14387 || oldMachineState == MachineState_Paused
14388 || oldMachineState == MachineState_Teleporting
14389 || oldMachineState == MachineState_OnlineSnapshotting
14390 || oldMachineState == MachineState_LiveSnapshotting
14391 || oldMachineState == MachineState_Stuck
14392 || oldMachineState == MachineState_Starting
14393 || oldMachineState == MachineState_Stopping
14394 || oldMachineState == MachineState_Saving
14395 || oldMachineState == MachineState_Restoring
14396 || oldMachineState == MachineState_TeleportingPausedVM
14397 || oldMachineState == MachineState_TeleportingIn
14398 )
14399 && ( aMachineState == MachineState_PoweredOff
14400 || aMachineState == MachineState_Saved
14401 || aMachineState == MachineState_Teleported
14402 || aMachineState == MachineState_Aborted
14403 )
14404 )
14405 {
14406 /* The EMT thread has just stopped, unlock attached media. Note that as
14407 * opposed to locking that is done from Console, we do unlocking here
14408 * because the VM process may have aborted before having a chance to
14409 * properly unlock all media it locked. */
14410
14411 unlockMedia();
14412 }
14413
14414 if (oldMachineState == MachineState_Restoring)
14415 {
14416 if (aMachineState != MachineState_Saved)
14417 {
14418 /*
14419 * delete the saved state file once the machine has finished
14420 * restoring from it (note that Console sets the state from
14421 * Restoring to Saved if the VM couldn't restore successfully,
14422 * to give the user an ability to fix an error and retry --
14423 * we keep the saved state file in this case)
14424 */
14425 deleteSavedState = true;
14426 }
14427 }
14428 else if ( oldMachineState == MachineState_Saved
14429 && ( aMachineState == MachineState_PoweredOff
14430 || aMachineState == MachineState_Aborted
14431 || aMachineState == MachineState_Teleported
14432 )
14433 )
14434 {
14435 /*
14436 * delete the saved state after SessionMachine::ForgetSavedState() is called
14437 * or if the VM process (owning a direct VM session) crashed while the
14438 * VM was Saved
14439 */
14440
14441 /// @todo (dmik)
14442 // Not sure that deleting the saved state file just because of the
14443 // client death before it attempted to restore the VM is a good
14444 // thing. But when it crashes we need to go to the Aborted state
14445 // which cannot have the saved state file associated... The only
14446 // way to fix this is to make the Aborted condition not a VM state
14447 // but a bool flag: i.e., when a crash occurs, set it to true and
14448 // change the state to PoweredOff or Saved depending on the
14449 // saved state presence.
14450
14451 deleteSavedState = true;
14452 mData->mCurrentStateModified = TRUE;
14453 stsFlags |= SaveSTS_CurStateModified;
14454 }
14455
14456 if ( aMachineState == MachineState_Starting
14457 || aMachineState == MachineState_Restoring
14458 || aMachineState == MachineState_TeleportingIn
14459 )
14460 {
14461 /* set the current state modified flag to indicate that the current
14462 * state is no more identical to the state in the
14463 * current snapshot */
14464 if (!mData->mCurrentSnapshot.isNull())
14465 {
14466 mData->mCurrentStateModified = TRUE;
14467 stsFlags |= SaveSTS_CurStateModified;
14468 }
14469 }
14470
14471 if (deleteSavedState)
14472 {
14473 if (mRemoveSavedState)
14474 {
14475 Assert(!mSSData->strStateFilePath.isEmpty());
14476
14477 // it is safe to delete the saved state file if ...
14478 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14479 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14480 // ... none of the snapshots share the saved state file
14481 )
14482 RTFileDelete(mSSData->strStateFilePath.c_str());
14483 }
14484
14485 mSSData->strStateFilePath.setNull();
14486 stsFlags |= SaveSTS_StateFilePath;
14487 }
14488
14489 /* redirect to the underlying peer machine */
14490 mPeer->i_setMachineState(aMachineState);
14491
14492 if ( oldMachineState != MachineState_RestoringSnapshot
14493 && ( aMachineState == MachineState_PoweredOff
14494 || aMachineState == MachineState_Teleported
14495 || aMachineState == MachineState_Aborted
14496 || aMachineState == MachineState_Saved))
14497 {
14498 /* the machine has stopped execution
14499 * (or the saved state file was adopted) */
14500 stsFlags |= SaveSTS_StateTimeStamp;
14501 }
14502
14503 if ( ( oldMachineState == MachineState_PoweredOff
14504 || oldMachineState == MachineState_Aborted
14505 || oldMachineState == MachineState_Teleported
14506 )
14507 && aMachineState == MachineState_Saved)
14508 {
14509 /* the saved state file was adopted */
14510 Assert(!mSSData->strStateFilePath.isEmpty());
14511 stsFlags |= SaveSTS_StateFilePath;
14512 }
14513
14514#ifdef VBOX_WITH_GUEST_PROPS
14515 if ( aMachineState == MachineState_PoweredOff
14516 || aMachineState == MachineState_Aborted
14517 || aMachineState == MachineState_Teleported)
14518 {
14519 /* Make sure any transient guest properties get removed from the
14520 * property store on shutdown. */
14521 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14522
14523 /* remove it from the settings representation */
14524 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14525 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14526 it != llGuestProperties.end();
14527 /*nothing*/)
14528 {
14529 const settings::GuestProperty &prop = *it;
14530 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14531 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14532 {
14533 it = llGuestProperties.erase(it);
14534 fNeedsSaving = true;
14535 }
14536 else
14537 {
14538 ++it;
14539 }
14540 }
14541
14542 /* Additionally remove it from the HWData representation. Required to
14543 * keep everything in sync, as this is what the API keeps using. */
14544 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14545 for (HWData::GuestPropertyMap::iterator it = llHWGuestProperties.begin();
14546 it != llHWGuestProperties.end();
14547 /*nothing*/)
14548 {
14549 uint32_t fFlags = it->second.mFlags;
14550 if ( fFlags & guestProp::TRANSIENT
14551 || fFlags & guestProp::TRANSRESET)
14552 {
14553 /* iterator where we need to continue after the erase call
14554 * (C++03 is a fact still, and it doesn't return the iterator
14555 * which would allow continuing) */
14556 HWData::GuestPropertyMap::iterator it2 = it;
14557 ++it2;
14558 llHWGuestProperties.erase(it);
14559 it = it2;
14560 fNeedsSaving = true;
14561 }
14562 else
14563 {
14564 ++it;
14565 }
14566 }
14567
14568 if (fNeedsSaving)
14569 {
14570 mData->mCurrentStateModified = TRUE;
14571 stsFlags |= SaveSTS_CurStateModified;
14572 }
14573 }
14574#endif /* VBOX_WITH_GUEST_PROPS */
14575
14576 rc = i_saveStateSettings(stsFlags);
14577
14578 if ( ( oldMachineState != MachineState_PoweredOff
14579 && oldMachineState != MachineState_Aborted
14580 && oldMachineState != MachineState_Teleported
14581 )
14582 && ( aMachineState == MachineState_PoweredOff
14583 || aMachineState == MachineState_Aborted
14584 || aMachineState == MachineState_Teleported
14585 )
14586 )
14587 {
14588 /* we've been shut down for any reason */
14589 /* no special action so far */
14590 }
14591
14592 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14593 LogFlowThisFuncLeave();
14594 return rc;
14595}
14596
14597/**
14598 * Sends the current machine state value to the VM process.
14599 *
14600 * @note Locks this object for reading, then calls a client process.
14601 */
14602HRESULT SessionMachine::i_updateMachineStateOnClient()
14603{
14604 AutoCaller autoCaller(this);
14605 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14606
14607 ComPtr<IInternalSessionControl> directControl;
14608 {
14609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14610 AssertReturn(!!mData, E_FAIL);
14611 if (mData->mSession.mLockType == LockType_VM)
14612 directControl = mData->mSession.mDirectControl;
14613
14614 /* directControl may be already set to NULL here in #OnSessionEnd()
14615 * called too early by the direct session process while there is still
14616 * some operation (like deleting the snapshot) in progress. The client
14617 * process in this case is waiting inside Session::close() for the
14618 * "end session" process object to complete, while #uninit() called by
14619 * #checkForDeath() on the Watcher thread is waiting for the pending
14620 * operation to complete. For now, we accept this inconsistent behavior
14621 * and simply do nothing here. */
14622
14623 if (mData->mSession.mState == SessionState_Unlocking)
14624 return S_OK;
14625 }
14626
14627 /* ignore notifications sent after #OnSessionEnd() is called */
14628 if (!directControl)
14629 return S_OK;
14630
14631 return directControl->UpdateMachineState(mData->mMachineState);
14632}
14633
14634
14635/**
14636 * Static Machine method that can get passed to RTThreadCreate to
14637 * have a thread started for a Task. See Machine::Task.
14638 */
14639/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14640{
14641 AssertReturn(pvUser, VERR_INVALID_POINTER);
14642
14643 Task *pTask = static_cast<Task *>(pvUser);
14644 pTask->handler();
14645 /** @todo r=klaus it would be safer to update the progress object here,
14646 * as it avoids possible races due to scoping issues/tricks in the handler */
14647 // it's our responsibility to delete the task
14648 delete pTask;
14649
14650 return 0;
14651}
14652
14653/*static*/
14654HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14655{
14656 va_list args;
14657 va_start(args, pcszMsg);
14658 HRESULT rc = setErrorInternal(aResultCode,
14659 getStaticClassIID(),
14660 getStaticComponentName(),
14661 Utf8Str(pcszMsg, args),
14662 false /* aWarning */,
14663 true /* aLogIt */);
14664 va_end(args);
14665 return rc;
14666}
14667
14668
14669HRESULT Machine::updateState(MachineState_T aState)
14670{
14671 NOREF(aState);
14672 ReturnComNotImplemented();
14673}
14674
14675HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14676{
14677 NOREF(aProgress);
14678 ReturnComNotImplemented();
14679}
14680
14681HRESULT Machine::endPowerUp(LONG aResult)
14682{
14683 NOREF(aResult);
14684 ReturnComNotImplemented();
14685}
14686
14687HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14688{
14689 NOREF(aProgress);
14690 ReturnComNotImplemented();
14691}
14692
14693HRESULT Machine::endPoweringDown(LONG aResult,
14694 const com::Utf8Str &aErrMsg)
14695{
14696 NOREF(aResult);
14697 NOREF(aErrMsg);
14698 ReturnComNotImplemented();
14699}
14700
14701HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14702 BOOL *aMatched,
14703 ULONG *aMaskedInterfaces)
14704{
14705 NOREF(aDevice);
14706 NOREF(aMatched);
14707 NOREF(aMaskedInterfaces);
14708 ReturnComNotImplemented();
14709
14710}
14711
14712HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14713{
14714 NOREF(aId); NOREF(aCaptureFilename);
14715 ReturnComNotImplemented();
14716}
14717
14718HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14719 BOOL aDone)
14720{
14721 NOREF(aId);
14722 NOREF(aDone);
14723 ReturnComNotImplemented();
14724}
14725
14726HRESULT Machine::autoCaptureUSBDevices()
14727{
14728 ReturnComNotImplemented();
14729}
14730
14731HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14732{
14733 NOREF(aDone);
14734 ReturnComNotImplemented();
14735}
14736
14737HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14738 ComPtr<IProgress> &aProgress)
14739{
14740 NOREF(aSession);
14741 NOREF(aProgress);
14742 ReturnComNotImplemented();
14743}
14744
14745HRESULT Machine::finishOnlineMergeMedium()
14746{
14747 ReturnComNotImplemented();
14748}
14749
14750HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14751 std::vector<com::Utf8Str> &aValues,
14752 std::vector<LONG64> &aTimestamps,
14753 std::vector<com::Utf8Str> &aFlags)
14754{
14755 NOREF(aNames);
14756 NOREF(aValues);
14757 NOREF(aTimestamps);
14758 NOREF(aFlags);
14759 ReturnComNotImplemented();
14760}
14761
14762HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14763 const com::Utf8Str &aValue,
14764 LONG64 aTimestamp,
14765 const com::Utf8Str &aFlags,
14766 BOOL *aNotify)
14767{
14768 NOREF(aName);
14769 NOREF(aValue);
14770 NOREF(aTimestamp);
14771 NOREF(aFlags);
14772 NOREF(aNotify);
14773 ReturnComNotImplemented();
14774}
14775
14776HRESULT Machine::lockMedia()
14777{
14778 ReturnComNotImplemented();
14779}
14780
14781HRESULT Machine::unlockMedia()
14782{
14783 ReturnComNotImplemented();
14784}
14785
14786HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14787 ComPtr<IMediumAttachment> &aNewAttachment)
14788{
14789 NOREF(aAttachment);
14790 NOREF(aNewAttachment);
14791 ReturnComNotImplemented();
14792}
14793
14794HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14795 ULONG aCpuUser,
14796 ULONG aCpuKernel,
14797 ULONG aCpuIdle,
14798 ULONG aMemTotal,
14799 ULONG aMemFree,
14800 ULONG aMemBalloon,
14801 ULONG aMemShared,
14802 ULONG aMemCache,
14803 ULONG aPagedTotal,
14804 ULONG aMemAllocTotal,
14805 ULONG aMemFreeTotal,
14806 ULONG aMemBalloonTotal,
14807 ULONG aMemSharedTotal,
14808 ULONG aVmNetRx,
14809 ULONG aVmNetTx)
14810{
14811 NOREF(aValidStats);
14812 NOREF(aCpuUser);
14813 NOREF(aCpuKernel);
14814 NOREF(aCpuIdle);
14815 NOREF(aMemTotal);
14816 NOREF(aMemFree);
14817 NOREF(aMemBalloon);
14818 NOREF(aMemShared);
14819 NOREF(aMemCache);
14820 NOREF(aPagedTotal);
14821 NOREF(aMemAllocTotal);
14822 NOREF(aMemFreeTotal);
14823 NOREF(aMemBalloonTotal);
14824 NOREF(aMemSharedTotal);
14825 NOREF(aVmNetRx);
14826 NOREF(aVmNetTx);
14827 ReturnComNotImplemented();
14828}
Note: See TracBrowser for help on using the repository browser.

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