VirtualBox

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

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

Main,VBoxManage,Settings: Changed the boolean syntheticCPU setting into a 32-bit CPUIDPortabilityLevel property.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 508.9 KB
Line 
1/* $Id: MachineImpl.cpp 55674 2015-05-05 17:58:10Z 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
2677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2678
2679 *aSessionState = mData->mSession.mState;
2680
2681 return S_OK;
2682}
2683
2684HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2685{
2686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2687
2688 aSessionType = mData->mSession.mType;
2689
2690 return S_OK;
2691}
2692
2693HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2694{
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696
2697 *aSessionPID = mData->mSession.mPID;
2698
2699 return S_OK;
2700}
2701
2702HRESULT Machine::getState(MachineState_T *aState)
2703{
2704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2705
2706 *aState = mData->mMachineState;
2707 Assert(mData->mMachineState != MachineState_Null);
2708
2709 return S_OK;
2710}
2711
2712HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2713{
2714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2715
2716 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2717
2718 return S_OK;
2719}
2720
2721HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2722{
2723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2724
2725 aStateFilePath = mSSData->strStateFilePath;
2726
2727 return S_OK;
2728}
2729
2730HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2731{
2732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2733
2734 i_getLogFolder(aLogFolder);
2735
2736 return S_OK;
2737}
2738
2739HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2740{
2741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2742
2743 aCurrentSnapshot = mData->mCurrentSnapshot;
2744
2745 return S_OK;
2746}
2747
2748HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2749{
2750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2751
2752 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2753 ? 0
2754 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2755
2756 return S_OK;
2757}
2758
2759HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2760{
2761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2762
2763 /* Note: for machines with no snapshots, we always return FALSE
2764 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2765 * reasons :) */
2766
2767 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2768 ? FALSE
2769 : mData->mCurrentStateModified;
2770
2771 return S_OK;
2772}
2773
2774HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2775{
2776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2777
2778 aSharedFolders.resize(mHWData->mSharedFolders.size());
2779 size_t i = 0;
2780 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2781 it != mHWData->mSharedFolders.end(); ++i, ++it)
2782 aSharedFolders[i] = *it;
2783
2784 return S_OK;
2785}
2786
2787HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2788{
2789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2790
2791 *aClipboardMode = mHWData->mClipboardMode;
2792
2793 return S_OK;
2794}
2795
2796HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2797{
2798 HRESULT rc = S_OK;
2799
2800 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2801
2802 alock.release();
2803 rc = i_onClipboardModeChange(aClipboardMode);
2804 alock.acquire();
2805 if (FAILED(rc)) return rc;
2806
2807 i_setModified(IsModified_MachineData);
2808 mHWData.backup();
2809 mHWData->mClipboardMode = aClipboardMode;
2810
2811 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2812 if (Global::IsOnline(mData->mMachineState))
2813 i_saveSettings(NULL);
2814
2815 return S_OK;
2816}
2817
2818HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2819{
2820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 *aDnDMode = mHWData->mDnDMode;
2823
2824 return S_OK;
2825}
2826
2827HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2828{
2829 HRESULT rc = S_OK;
2830
2831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2832
2833 alock.release();
2834 rc = i_onDnDModeChange(aDnDMode);
2835
2836 alock.acquire();
2837 if (FAILED(rc)) return rc;
2838
2839 i_setModified(IsModified_MachineData);
2840 mHWData.backup();
2841 mHWData->mDnDMode = aDnDMode;
2842
2843 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2844 if (Global::IsOnline(mData->mMachineState))
2845 i_saveSettings(NULL);
2846
2847 return S_OK;
2848}
2849
2850HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2851{
2852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2853
2854 try
2855 {
2856 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2857 }
2858 catch (...)
2859 {
2860 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2861 }
2862
2863 return S_OK;
2864}
2865
2866HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2867{
2868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2869
2870 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2871 if (FAILED(rc)) return rc;
2872
2873 i_setModified(IsModified_MachineData);
2874 mHWData.backup();
2875 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2876 return rc;
2877}
2878
2879HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2880{
2881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2882 StorageControllerList data = *mStorageControllers.data();
2883 size_t i = 0;
2884 aStorageControllers.resize(data.size());
2885 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2886 aStorageControllers[i] = *it;
2887 return S_OK;
2888}
2889
2890HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2891{
2892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2893
2894 *aEnabled = mUserData->s.fTeleporterEnabled;
2895
2896 return S_OK;
2897}
2898
2899HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2900{
2901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2902
2903 /* Only allow it to be set to true when PoweredOff or Aborted.
2904 (Clearing it is always permitted.) */
2905 if ( aTeleporterEnabled
2906 && mData->mRegistered
2907 && ( !i_isSessionMachine()
2908 || ( mData->mMachineState != MachineState_PoweredOff
2909 && mData->mMachineState != MachineState_Teleported
2910 && mData->mMachineState != MachineState_Aborted
2911 )
2912 )
2913 )
2914 return setError(VBOX_E_INVALID_VM_STATE,
2915 tr("The machine is not powered off (state is %s)"),
2916 Global::stringifyMachineState(mData->mMachineState));
2917
2918 i_setModified(IsModified_MachineData);
2919 mUserData.backup();
2920 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2921
2922 return S_OK;
2923}
2924
2925HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2926{
2927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2930
2931 return S_OK;
2932}
2933
2934HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2935{
2936 if (aTeleporterPort >= _64K)
2937 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2938
2939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2940
2941 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2942 if (FAILED(rc)) return rc;
2943
2944 i_setModified(IsModified_MachineData);
2945 mUserData.backup();
2946 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2947
2948 return S_OK;
2949}
2950
2951HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2952{
2953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2954
2955 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2956
2957 return S_OK;
2958}
2959
2960HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2961{
2962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2963
2964 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2965 if (FAILED(rc)) return rc;
2966
2967 i_setModified(IsModified_MachineData);
2968 mUserData.backup();
2969 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2970
2971 return S_OK;
2972}
2973
2974HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2975{
2976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2977 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2978
2979 return S_OK;
2980}
2981
2982HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2983{
2984 /*
2985 * Hash the password first.
2986 */
2987 com::Utf8Str aT = aTeleporterPassword;
2988
2989 if (!aT.isEmpty())
2990 {
2991 if (VBoxIsPasswordHashed(&aT))
2992 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2993 VBoxHashPassword(&aT);
2994 }
2995
2996 /*
2997 * Do the update.
2998 */
2999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3000 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3001 if (SUCCEEDED(hrc))
3002 {
3003 i_setModified(IsModified_MachineData);
3004 mUserData.backup();
3005 mUserData->s.strTeleporterPassword = aT;
3006 }
3007
3008 return hrc;
3009}
3010
3011HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3012{
3013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3014
3015 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3016 return S_OK;
3017}
3018
3019HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3020{
3021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3022
3023 /* @todo deal with running state change. */
3024 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3025 if (FAILED(rc)) return rc;
3026
3027 i_setModified(IsModified_MachineData);
3028 mUserData.backup();
3029 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3030 return S_OK;
3031}
3032
3033HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3034{
3035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3036
3037 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3038 return S_OK;
3039}
3040
3041HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3042{
3043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3044
3045 /* @todo deal with running state change. */
3046 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3047 if (FAILED(rc)) return rc;
3048
3049 i_setModified(IsModified_MachineData);
3050 mUserData.backup();
3051 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3052 return S_OK;
3053}
3054
3055HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3056{
3057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3058
3059 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3060 return S_OK;
3061}
3062
3063HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3064{
3065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3066
3067 /* @todo deal with running state change. */
3068 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3069 if (FAILED(rc)) return rc;
3070
3071 i_setModified(IsModified_MachineData);
3072 mUserData.backup();
3073 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3074 return S_OK;
3075}
3076
3077HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3078{
3079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3080
3081 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3082
3083 return S_OK;
3084}
3085
3086HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3087{
3088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3089
3090 /* @todo deal with running state change. */
3091 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3092 if (FAILED(rc)) return rc;
3093
3094 i_setModified(IsModified_MachineData);
3095 mUserData.backup();
3096 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3097
3098 return S_OK;
3099}
3100
3101HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3102{
3103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3104
3105 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3106 return S_OK;
3107}
3108
3109HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3110{
3111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3112
3113 /* @todo deal with running state change. */
3114 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3115 if (FAILED(rc)) return rc;
3116
3117 i_setModified(IsModified_MachineData);
3118 mUserData.backup();
3119 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3120 return S_OK;
3121}
3122
3123HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3124{
3125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3126
3127 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3128
3129 return S_OK;
3130}
3131
3132HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3133{
3134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3135
3136 /* Only allow it to be set to true when PoweredOff or Aborted.
3137 (Clearing it is always permitted.) */
3138 if ( aRTCUseUTC
3139 && mData->mRegistered
3140 && ( !i_isSessionMachine()
3141 || ( mData->mMachineState != MachineState_PoweredOff
3142 && mData->mMachineState != MachineState_Teleported
3143 && mData->mMachineState != MachineState_Aborted
3144 )
3145 )
3146 )
3147 return setError(VBOX_E_INVALID_VM_STATE,
3148 tr("The machine is not powered off (state is %s)"),
3149 Global::stringifyMachineState(mData->mMachineState));
3150
3151 i_setModified(IsModified_MachineData);
3152 mUserData.backup();
3153 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3154
3155 return S_OK;
3156}
3157
3158HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3159{
3160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3161
3162 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3163
3164 return S_OK;
3165}
3166
3167HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3168{
3169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3170
3171 HRESULT rc = i_checkStateDependency(MutableStateDep);
3172 if (FAILED(rc)) return rc;
3173
3174 i_setModified(IsModified_MachineData);
3175 mHWData.backup();
3176 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3177
3178 return S_OK;
3179}
3180
3181HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3182{
3183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3184
3185 *aIOCacheSize = mHWData->mIOCacheSize;
3186
3187 return S_OK;
3188}
3189
3190HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3191{
3192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3193
3194 HRESULT rc = i_checkStateDependency(MutableStateDep);
3195 if (FAILED(rc)) return rc;
3196
3197 i_setModified(IsModified_MachineData);
3198 mHWData.backup();
3199 mHWData->mIOCacheSize = aIOCacheSize;
3200
3201 return S_OK;
3202}
3203
3204
3205/**
3206 * @note Locks objects!
3207 */
3208HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3209 LockType_T aLockType)
3210
3211{
3212 /* check the session state */
3213 SessionState_T state;
3214 HRESULT rc = aSession->COMGETTER(State)(&state);
3215 if (FAILED(rc)) return rc;
3216
3217 if (state != SessionState_Unlocked)
3218 return setError(VBOX_E_INVALID_OBJECT_STATE,
3219 tr("The given session is busy"));
3220
3221 // get the client's IInternalSessionControl interface
3222 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3223 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3224 E_INVALIDARG);
3225
3226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3227
3228 if (!mData->mRegistered)
3229 return setError(E_UNEXPECTED,
3230 tr("The machine '%s' is not registered"),
3231 mUserData->s.strName.c_str());
3232
3233 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3234
3235 SessionState_T oldState = mData->mSession.mState;
3236 /* Hack: in case the session is closing and there is a progress object
3237 * which allows waiting for the session to be closed, take the opportunity
3238 * and do a limited wait (max. 1 second). This helps a lot when the system
3239 * is busy and thus session closing can take a little while. */
3240 if ( mData->mSession.mState == SessionState_Unlocking
3241 && mData->mSession.mProgress)
3242 {
3243 alock.release();
3244 mData->mSession.mProgress->WaitForCompletion(1000);
3245 alock.acquire();
3246 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3247 }
3248
3249 // try again now
3250 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3251 // (i.e. session machine exists)
3252 && (aLockType == LockType_Shared) // caller wants a shared link to the
3253 // existing session that holds the write lock:
3254 )
3255 {
3256 // OK, share the session... we are now dealing with three processes:
3257 // 1) VBoxSVC (where this code runs);
3258 // 2) process C: the caller's client process (who wants a shared session);
3259 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3260
3261 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3262 ComAssertRet(mData->mSession.mLockType == LockType_Write || mData->mSession.mLockType == LockType_VM, E_FAIL);
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 if ( SUCCEEDED(rc)
3442 && fLaunchingVMProcess
3443 )
3444 {
3445 /* complete the remote session initialization */
3446
3447 /* get the console from the direct session */
3448 ComPtr<IConsole> console;
3449 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3450 ComAssertComRC(rc);
3451
3452 if (SUCCEEDED(rc) && !console)
3453 {
3454 ComAssert(!!console);
3455 rc = E_FAIL;
3456 }
3457
3458 /* assign machine & console to the remote session */
3459 if (SUCCEEDED(rc))
3460 {
3461 /*
3462 * after LaunchVMProcess(), the first and the only
3463 * entry in remoteControls is that remote session
3464 */
3465 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3466 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3467 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3468
3469 /* The failure may occur w/o any error info (from RPC), so provide one */
3470 if (FAILED(rc))
3471 setError(VBOX_E_VM_ERROR,
3472 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3473 }
3474
3475 if (FAILED(rc))
3476 pSessionControl->Uninitialize();
3477 }
3478
3479 /* acquire the lock again */
3480 alock.acquire();
3481
3482 /* Restore the session state */
3483 mData->mSession.mState = origState;
3484 }
3485
3486 // finalize spawning anyway (this is why we don't return on errors above)
3487 if (fLaunchingVMProcess)
3488 {
3489 /* Note that the progress object is finalized later */
3490 /** @todo Consider checking mData->mSession.mProgress for cancellation
3491 * around here. */
3492
3493 /* We don't reset mSession.mPID here because it is necessary for
3494 * SessionMachine::uninit() to reap the child process later. */
3495
3496 if (FAILED(rc))
3497 {
3498 /* Close the remote session, remove the remote control from the list
3499 * and reset session state to Closed (@note keep the code in sync
3500 * with the relevant part in checkForSpawnFailure()). */
3501
3502 Assert(mData->mSession.mRemoteControls.size() == 1);
3503 if (mData->mSession.mRemoteControls.size() == 1)
3504 {
3505 ErrorInfoKeeper eik;
3506 mData->mSession.mRemoteControls.front()->Uninitialize();
3507 }
3508
3509 mData->mSession.mRemoteControls.clear();
3510 mData->mSession.mState = SessionState_Unlocked;
3511 }
3512 }
3513 else
3514 {
3515 /* memorize PID of the directly opened session */
3516 if (SUCCEEDED(rc))
3517 mData->mSession.mPID = pid;
3518 }
3519
3520 if (SUCCEEDED(rc))
3521 {
3522 mData->mSession.mLockType = aLockType;
3523 /* memorize the direct session control and cache IUnknown for it */
3524 mData->mSession.mDirectControl = pSessionControl;
3525 mData->mSession.mState = SessionState_Locked;
3526 /* associate the SessionMachine with this Machine */
3527 mData->mSession.mMachine = sessionMachine;
3528
3529 /* request an IUnknown pointer early from the remote party for later
3530 * identity checks (it will be internally cached within mDirectControl
3531 * at least on XPCOM) */
3532 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3533 NOREF(unk);
3534 }
3535
3536 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3537 * would break the lock order */
3538 alock.release();
3539
3540 /* uninitialize the created session machine on failure */
3541 if (FAILED(rc))
3542 sessionMachine->uninit();
3543 }
3544
3545 if (SUCCEEDED(rc))
3546 {
3547 /*
3548 * tell the client watcher thread to update the set of
3549 * machines that have open sessions
3550 */
3551 mParent->i_updateClientWatcher();
3552
3553 if (oldState != SessionState_Locked)
3554 /* fire an event */
3555 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3556 }
3557
3558 return rc;
3559}
3560
3561/**
3562 * @note Locks objects!
3563 */
3564HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3565 const com::Utf8Str &aType,
3566 const com::Utf8Str &aEnvironment,
3567 ComPtr<IProgress> &aProgress)
3568{
3569 Utf8Str strFrontend(aType);
3570 /* "emergencystop" doesn't need the session, so skip the checks/interface
3571 * retrieval. This code doesn't quite fit in here, but introducing a
3572 * special API method would be even more effort, and would require explicit
3573 * support by every API client. It's better to hide the feature a bit. */
3574 if (strFrontend != "emergencystop")
3575 CheckComArgNotNull(aSession);
3576
3577 HRESULT rc = S_OK;
3578 if (strFrontend.isEmpty())
3579 {
3580 Bstr bstrFrontend;
3581 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3582 if (FAILED(rc))
3583 return rc;
3584 strFrontend = bstrFrontend;
3585 if (strFrontend.isEmpty())
3586 {
3587 ComPtr<ISystemProperties> systemProperties;
3588 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3589 if (FAILED(rc))
3590 return rc;
3591 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3592 if (FAILED(rc))
3593 return rc;
3594 strFrontend = bstrFrontend;
3595 }
3596 /* paranoia - emergencystop is not a valid default */
3597 if (strFrontend == "emergencystop")
3598 strFrontend = Utf8Str::Empty;
3599 }
3600 /* default frontend: Qt GUI */
3601 if (strFrontend.isEmpty())
3602 strFrontend = "GUI/Qt";
3603
3604 if (strFrontend != "emergencystop")
3605 {
3606 /* check the session state */
3607 SessionState_T state;
3608 rc = aSession->COMGETTER(State)(&state);
3609 if (FAILED(rc))
3610 return rc;
3611
3612 if (state != SessionState_Unlocked)
3613 return setError(VBOX_E_INVALID_OBJECT_STATE,
3614 tr("The given session is busy"));
3615
3616 /* get the IInternalSessionControl interface */
3617 ComPtr<IInternalSessionControl> control(aSession);
3618 ComAssertMsgRet(!control.isNull(),
3619 ("No IInternalSessionControl interface"),
3620 E_INVALIDARG);
3621
3622 /* get the teleporter enable state for the progress object init. */
3623 BOOL fTeleporterEnabled;
3624 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3625 if (FAILED(rc))
3626 return rc;
3627
3628 /* create a progress object */
3629 ComObjPtr<ProgressProxy> progress;
3630 progress.createObject();
3631 rc = progress->init(mParent,
3632 static_cast<IMachine*>(this),
3633 Bstr(tr("Starting VM")).raw(),
3634 TRUE /* aCancelable */,
3635 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3636 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3637 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3638 2 /* uFirstOperationWeight */,
3639 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3640
3641 if (SUCCEEDED(rc))
3642 {
3643 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3644 if (SUCCEEDED(rc))
3645 {
3646 aProgress = progress;
3647
3648 /* signal the client watcher thread */
3649 mParent->i_updateClientWatcher();
3650
3651 /* fire an event */
3652 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3653 }
3654 }
3655 }
3656 else
3657 {
3658 /* no progress object - either instant success or failure */
3659 aProgress = NULL;
3660
3661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3662
3663 if (mData->mSession.mState != SessionState_Locked)
3664 return setError(VBOX_E_INVALID_OBJECT_STATE,
3665 tr("The machine '%s' is not locked by a session"),
3666 mUserData->s.strName.c_str());
3667
3668 /* must have a VM process associated - do not kill normal API clients
3669 * with an open session */
3670 if (!Global::IsOnline(mData->mMachineState))
3671 return setError(VBOX_E_INVALID_OBJECT_STATE,
3672 tr("The machine '%s' does not have a VM process"),
3673 mUserData->s.strName.c_str());
3674
3675 /* forcibly terminate the VM process */
3676 if (mData->mSession.mPID != NIL_RTPROCESS)
3677 RTProcTerminate(mData->mSession.mPID);
3678
3679 /* signal the client watcher thread, as most likely the client has
3680 * been terminated */
3681 mParent->i_updateClientWatcher();
3682 }
3683
3684 return rc;
3685}
3686
3687HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3688{
3689 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3690 return setError(E_INVALIDARG,
3691 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3692 aPosition, SchemaDefs::MaxBootPosition);
3693
3694 if (aDevice == DeviceType_USB)
3695 return setError(E_NOTIMPL,
3696 tr("Booting from USB device is currently not supported"));
3697
3698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3699
3700 HRESULT rc = i_checkStateDependency(MutableStateDep);
3701 if (FAILED(rc)) return rc;
3702
3703 i_setModified(IsModified_MachineData);
3704 mHWData.backup();
3705 mHWData->mBootOrder[aPosition - 1] = aDevice;
3706
3707 return S_OK;
3708}
3709
3710HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3711{
3712 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3713 return setError(E_INVALIDARG,
3714 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3715 aPosition, SchemaDefs::MaxBootPosition);
3716
3717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3718
3719 *aDevice = mHWData->mBootOrder[aPosition - 1];
3720
3721 return S_OK;
3722}
3723
3724HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3725 LONG aControllerPort,
3726 LONG aDevice,
3727 DeviceType_T aType,
3728 const ComPtr<IMedium> &aMedium)
3729{
3730 IMedium *aM = aMedium;
3731 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3732 aName.c_str(), aControllerPort, aDevice, aType, aM));
3733
3734 // request the host lock first, since might be calling Host methods for getting host drives;
3735 // next, protect the media tree all the while we're in here, as well as our member variables
3736 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3737 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3738
3739 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3740 if (FAILED(rc)) return rc;
3741
3742 /// @todo NEWMEDIA implicit machine registration
3743 if (!mData->mRegistered)
3744 return setError(VBOX_E_INVALID_OBJECT_STATE,
3745 tr("Cannot attach storage devices to an unregistered machine"));
3746
3747 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3748
3749 /* Check for an existing controller. */
3750 ComObjPtr<StorageController> ctl;
3751 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3752 if (FAILED(rc)) return rc;
3753
3754 StorageControllerType_T ctrlType;
3755 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3756 if (FAILED(rc))
3757 return setError(E_FAIL,
3758 tr("Could not get type of controller '%s'"),
3759 aName.c_str());
3760
3761 bool fSilent = false;
3762 Utf8Str strReconfig;
3763
3764 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3765 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3766 if ( mData->mMachineState == MachineState_Paused
3767 && strReconfig == "1")
3768 fSilent = true;
3769
3770 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3771 bool fHotplug = false;
3772 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3773 fHotplug = true;
3774
3775 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3776 return setError(VBOX_E_INVALID_VM_STATE,
3777 tr("Controller '%s' does not support hotplugging"),
3778 aName.c_str());
3779
3780 // check that the port and device are not out of range
3781 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3782 if (FAILED(rc)) return rc;
3783
3784 /* check if the device slot is already busy */
3785 MediumAttachment *pAttachTemp;
3786 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3787 Bstr(aName).raw(),
3788 aControllerPort,
3789 aDevice)))
3790 {
3791 Medium *pMedium = pAttachTemp->i_getMedium();
3792 if (pMedium)
3793 {
3794 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3795 return setError(VBOX_E_OBJECT_IN_USE,
3796 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3797 pMedium->i_getLocationFull().c_str(),
3798 aControllerPort,
3799 aDevice,
3800 aName.c_str());
3801 }
3802 else
3803 return setError(VBOX_E_OBJECT_IN_USE,
3804 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3805 aControllerPort, aDevice, aName.c_str());
3806 }
3807
3808 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3809 if (aMedium && medium.isNull())
3810 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3811
3812 AutoCaller mediumCaller(medium);
3813 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3814
3815 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3816
3817 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3818 && !medium.isNull()
3819 )
3820 return setError(VBOX_E_OBJECT_IN_USE,
3821 tr("Medium '%s' is already attached to this virtual machine"),
3822 medium->i_getLocationFull().c_str());
3823
3824 if (!medium.isNull())
3825 {
3826 MediumType_T mtype = medium->i_getType();
3827 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3828 // For DVDs it's not written to the config file, so needs no global config
3829 // version bump. For floppies it's a new attribute "type", which is ignored
3830 // by older VirtualBox version, so needs no global config version bump either.
3831 // For hard disks this type is not accepted.
3832 if (mtype == MediumType_MultiAttach)
3833 {
3834 // This type is new with VirtualBox 4.0 and therefore requires settings
3835 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3836 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3837 // two reasons: The medium type is a property of the media registry tree, which
3838 // can reside in the global config file (for pre-4.0 media); we would therefore
3839 // possibly need to bump the global config version. We don't want to do that though
3840 // because that might make downgrading to pre-4.0 impossible.
3841 // As a result, we can only use these two new types if the medium is NOT in the
3842 // global registry:
3843 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3844 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3845 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3846 )
3847 return setError(VBOX_E_INVALID_OBJECT_STATE,
3848 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3849 "to machines that were created with VirtualBox 4.0 or later"),
3850 medium->i_getLocationFull().c_str());
3851 }
3852 }
3853
3854 bool fIndirect = false;
3855 if (!medium.isNull())
3856 fIndirect = medium->i_isReadOnly();
3857 bool associate = true;
3858
3859 do
3860 {
3861 if ( aType == DeviceType_HardDisk
3862 && mMediaData.isBackedUp())
3863 {
3864 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3865
3866 /* check if the medium was attached to the VM before we started
3867 * changing attachments in which case the attachment just needs to
3868 * be restored */
3869 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3870 {
3871 AssertReturn(!fIndirect, E_FAIL);
3872
3873 /* see if it's the same bus/channel/device */
3874 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3875 {
3876 /* the simplest case: restore the whole attachment
3877 * and return, nothing else to do */
3878 mMediaData->mAttachments.push_back(pAttachTemp);
3879
3880 /* Reattach the medium to the VM. */
3881 if (fHotplug || fSilent)
3882 {
3883 mediumLock.release();
3884 treeLock.release();
3885 alock.release();
3886
3887 MediumLockList *pMediumLockList(new MediumLockList());
3888
3889 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3890 true /* fMediumLockWrite */,
3891 false /* fMediumLockWriteAll */,
3892 NULL,
3893 *pMediumLockList);
3894 alock.acquire();
3895 if (FAILED(rc))
3896 delete pMediumLockList;
3897 else
3898 {
3899 mData->mSession.mLockedMedia.Unlock();
3900 alock.release();
3901 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3902 mData->mSession.mLockedMedia.Lock();
3903 alock.acquire();
3904 }
3905 alock.release();
3906
3907 if (SUCCEEDED(rc))
3908 {
3909 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3910 /* Remove lock list in case of error. */
3911 if (FAILED(rc))
3912 {
3913 mData->mSession.mLockedMedia.Unlock();
3914 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3915 mData->mSession.mLockedMedia.Lock();
3916 }
3917 }
3918 }
3919
3920 return S_OK;
3921 }
3922
3923 /* bus/channel/device differ; we need a new attachment object,
3924 * but don't try to associate it again */
3925 associate = false;
3926 break;
3927 }
3928 }
3929
3930 /* go further only if the attachment is to be indirect */
3931 if (!fIndirect)
3932 break;
3933
3934 /* perform the so called smart attachment logic for indirect
3935 * attachments. Note that smart attachment is only applicable to base
3936 * hard disks. */
3937
3938 if (medium->i_getParent().isNull())
3939 {
3940 /* first, investigate the backup copy of the current hard disk
3941 * attachments to make it possible to re-attach existing diffs to
3942 * another device slot w/o losing their contents */
3943 if (mMediaData.isBackedUp())
3944 {
3945 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3946
3947 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3948 uint32_t foundLevel = 0;
3949
3950 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3951 {
3952 uint32_t level = 0;
3953 MediumAttachment *pAttach = *it;
3954 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3955 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3956 if (pMedium.isNull())
3957 continue;
3958
3959 if (pMedium->i_getBase(&level) == medium)
3960 {
3961 /* skip the hard disk if its currently attached (we
3962 * cannot attach the same hard disk twice) */
3963 if (i_findAttachment(mMediaData->mAttachments,
3964 pMedium))
3965 continue;
3966
3967 /* matched device, channel and bus (i.e. attached to the
3968 * same place) will win and immediately stop the search;
3969 * otherwise the attachment that has the youngest
3970 * descendant of medium will be used
3971 */
3972 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3973 {
3974 /* the simplest case: restore the whole attachment
3975 * and return, nothing else to do */
3976 mMediaData->mAttachments.push_back(*it);
3977
3978 /* Reattach the medium to the VM. */
3979 if (fHotplug || fSilent)
3980 {
3981 mediumLock.release();
3982 treeLock.release();
3983 alock.release();
3984
3985 MediumLockList *pMediumLockList(new MediumLockList());
3986
3987 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3988 true /* fMediumLockWrite */,
3989 false /* fMediumLockWriteAll */,
3990 NULL,
3991 *pMediumLockList);
3992 alock.acquire();
3993 if (FAILED(rc))
3994 delete pMediumLockList;
3995 else
3996 {
3997 mData->mSession.mLockedMedia.Unlock();
3998 alock.release();
3999 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4000 mData->mSession.mLockedMedia.Lock();
4001 alock.acquire();
4002 }
4003 alock.release();
4004
4005 if (SUCCEEDED(rc))
4006 {
4007 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4008 /* Remove lock list in case of error. */
4009 if (FAILED(rc))
4010 {
4011 mData->mSession.mLockedMedia.Unlock();
4012 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4013 mData->mSession.mLockedMedia.Lock();
4014 }
4015 }
4016 }
4017
4018 return S_OK;
4019 }
4020 else if ( foundIt == oldAtts.end()
4021 || level > foundLevel /* prefer younger */
4022 )
4023 {
4024 foundIt = it;
4025 foundLevel = level;
4026 }
4027 }
4028 }
4029
4030 if (foundIt != oldAtts.end())
4031 {
4032 /* use the previously attached hard disk */
4033 medium = (*foundIt)->i_getMedium();
4034 mediumCaller.attach(medium);
4035 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4036 mediumLock.attach(medium);
4037 /* not implicit, doesn't require association with this VM */
4038 fIndirect = false;
4039 associate = false;
4040 /* go right to the MediumAttachment creation */
4041 break;
4042 }
4043 }
4044
4045 /* must give up the medium lock and medium tree lock as below we
4046 * go over snapshots, which needs a lock with higher lock order. */
4047 mediumLock.release();
4048 treeLock.release();
4049
4050 /* then, search through snapshots for the best diff in the given
4051 * hard disk's chain to base the new diff on */
4052
4053 ComObjPtr<Medium> base;
4054 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4055 while (snap)
4056 {
4057 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4058
4059 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4060
4061 MediumAttachment *pAttachFound = NULL;
4062 uint32_t foundLevel = 0;
4063
4064 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4065 {
4066 MediumAttachment *pAttach = *it;
4067 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4068 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4069 if (pMedium.isNull())
4070 continue;
4071
4072 uint32_t level = 0;
4073 if (pMedium->i_getBase(&level) == medium)
4074 {
4075 /* matched device, channel and bus (i.e. attached to the
4076 * same place) will win and immediately stop the search;
4077 * otherwise the attachment that has the youngest
4078 * descendant of medium will be used
4079 */
4080 if ( pAttach->i_getDevice() == aDevice
4081 && pAttach->i_getPort() == aControllerPort
4082 && pAttach->i_getControllerName() == aName
4083 )
4084 {
4085 pAttachFound = pAttach;
4086 break;
4087 }
4088 else if ( !pAttachFound
4089 || level > foundLevel /* prefer younger */
4090 )
4091 {
4092 pAttachFound = pAttach;
4093 foundLevel = level;
4094 }
4095 }
4096 }
4097
4098 if (pAttachFound)
4099 {
4100 base = pAttachFound->i_getMedium();
4101 break;
4102 }
4103
4104 snap = snap->i_getParent();
4105 }
4106
4107 /* re-lock medium tree and the medium, as we need it below */
4108 treeLock.acquire();
4109 mediumLock.acquire();
4110
4111 /* found a suitable diff, use it as a base */
4112 if (!base.isNull())
4113 {
4114 medium = base;
4115 mediumCaller.attach(medium);
4116 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4117 mediumLock.attach(medium);
4118 }
4119 }
4120
4121 Utf8Str strFullSnapshotFolder;
4122 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4123
4124 ComObjPtr<Medium> diff;
4125 diff.createObject();
4126 // store this diff in the same registry as the parent
4127 Guid uuidRegistryParent;
4128 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4129 {
4130 // parent image has no registry: this can happen if we're attaching a new immutable
4131 // image that has not yet been attached (medium then points to the base and we're
4132 // creating the diff image for the immutable, and the parent is not yet registered);
4133 // put the parent in the machine registry then
4134 mediumLock.release();
4135 treeLock.release();
4136 alock.release();
4137 i_addMediumToRegistry(medium);
4138 alock.acquire();
4139 treeLock.acquire();
4140 mediumLock.acquire();
4141 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4142 }
4143 rc = diff->init(mParent,
4144 medium->i_getPreferredDiffFormat(),
4145 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4146 uuidRegistryParent,
4147 DeviceType_HardDisk);
4148 if (FAILED(rc)) return rc;
4149
4150 /* Apply the normal locking logic to the entire chain. */
4151 MediumLockList *pMediumLockList(new MediumLockList());
4152 mediumLock.release();
4153 treeLock.release();
4154 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4155 true /* fMediumLockWrite */,
4156 false /* fMediumLockWriteAll */,
4157 medium,
4158 *pMediumLockList);
4159 treeLock.acquire();
4160 mediumLock.acquire();
4161 if (SUCCEEDED(rc))
4162 {
4163 mediumLock.release();
4164 treeLock.release();
4165 rc = pMediumLockList->Lock();
4166 treeLock.acquire();
4167 mediumLock.acquire();
4168 if (FAILED(rc))
4169 setError(rc,
4170 tr("Could not lock medium when creating diff '%s'"),
4171 diff->i_getLocationFull().c_str());
4172 else
4173 {
4174 /* will release the lock before the potentially lengthy
4175 * operation, so protect with the special state */
4176 MachineState_T oldState = mData->mMachineState;
4177 i_setMachineState(MachineState_SettingUp);
4178
4179 mediumLock.release();
4180 treeLock.release();
4181 alock.release();
4182
4183 rc = medium->i_createDiffStorage(diff,
4184 MediumVariant_Standard,
4185 pMediumLockList,
4186 NULL /* aProgress */,
4187 true /* aWait */);
4188
4189 alock.acquire();
4190 treeLock.acquire();
4191 mediumLock.acquire();
4192
4193 i_setMachineState(oldState);
4194 }
4195 }
4196
4197 /* Unlock the media and free the associated memory. */
4198 delete pMediumLockList;
4199
4200 if (FAILED(rc)) return rc;
4201
4202 /* use the created diff for the actual attachment */
4203 medium = diff;
4204 mediumCaller.attach(medium);
4205 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4206 mediumLock.attach(medium);
4207 }
4208 while (0);
4209
4210 ComObjPtr<MediumAttachment> attachment;
4211 attachment.createObject();
4212 rc = attachment->init(this,
4213 medium,
4214 aName,
4215 aControllerPort,
4216 aDevice,
4217 aType,
4218 fIndirect,
4219 false /* fPassthrough */,
4220 false /* fTempEject */,
4221 false /* fNonRotational */,
4222 false /* fDiscard */,
4223 fHotplug /* fHotPluggable */,
4224 Utf8Str::Empty);
4225 if (FAILED(rc)) return rc;
4226
4227 if (associate && !medium.isNull())
4228 {
4229 // as the last step, associate the medium to the VM
4230 rc = medium->i_addBackReference(mData->mUuid);
4231 // here we can fail because of Deleting, or being in process of creating a Diff
4232 if (FAILED(rc)) return rc;
4233
4234 mediumLock.release();
4235 treeLock.release();
4236 alock.release();
4237 i_addMediumToRegistry(medium);
4238 alock.acquire();
4239 treeLock.acquire();
4240 mediumLock.acquire();
4241 }
4242
4243 /* success: finally remember the attachment */
4244 i_setModified(IsModified_Storage);
4245 mMediaData.backup();
4246 mMediaData->mAttachments.push_back(attachment);
4247
4248 mediumLock.release();
4249 treeLock.release();
4250 alock.release();
4251
4252 if (fHotplug || fSilent)
4253 {
4254 if (!medium.isNull())
4255 {
4256 MediumLockList *pMediumLockList(new MediumLockList());
4257
4258 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4259 true /* fMediumLockWrite */,
4260 false /* fMediumLockWriteAll */,
4261 NULL,
4262 *pMediumLockList);
4263 alock.acquire();
4264 if (FAILED(rc))
4265 delete pMediumLockList;
4266 else
4267 {
4268 mData->mSession.mLockedMedia.Unlock();
4269 alock.release();
4270 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4271 mData->mSession.mLockedMedia.Lock();
4272 alock.acquire();
4273 }
4274 alock.release();
4275 }
4276
4277 if (SUCCEEDED(rc))
4278 {
4279 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4280 /* Remove lock list in case of error. */
4281 if (FAILED(rc))
4282 {
4283 mData->mSession.mLockedMedia.Unlock();
4284 mData->mSession.mLockedMedia.Remove(attachment);
4285 mData->mSession.mLockedMedia.Lock();
4286 }
4287 }
4288 }
4289
4290 /* Save modified registries, but skip this machine as it's the caller's
4291 * job to save its settings like all other settings changes. */
4292 mParent->i_unmarkRegistryModified(i_getId());
4293 mParent->i_saveModifiedRegistries();
4294
4295 return rc;
4296}
4297
4298HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4299 LONG aDevice)
4300{
4301 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4302 aName.c_str(), aControllerPort, aDevice));
4303
4304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4305
4306 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4307 if (FAILED(rc)) return rc;
4308
4309 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4310
4311 /* Check for an existing controller. */
4312 ComObjPtr<StorageController> ctl;
4313 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4314 if (FAILED(rc)) return rc;
4315
4316 StorageControllerType_T ctrlType;
4317 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4318 if (FAILED(rc))
4319 return setError(E_FAIL,
4320 tr("Could not get type of controller '%s'"),
4321 aName.c_str());
4322
4323 bool fSilent = false;
4324 Utf8Str strReconfig;
4325
4326 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4327 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4328 if ( mData->mMachineState == MachineState_Paused
4329 && strReconfig == "1")
4330 fSilent = true;
4331
4332 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4333 bool fHotplug = false;
4334 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4335 fHotplug = true;
4336
4337 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4338 return setError(VBOX_E_INVALID_VM_STATE,
4339 tr("Controller '%s' does not support hotplugging"),
4340 aName.c_str());
4341
4342 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4343 Bstr(aName).raw(),
4344 aControllerPort,
4345 aDevice);
4346 if (!pAttach)
4347 return setError(VBOX_E_OBJECT_NOT_FOUND,
4348 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4349 aDevice, aControllerPort, aName.c_str());
4350
4351 if (fHotplug && !pAttach->i_getHotPluggable())
4352 return setError(VBOX_E_NOT_SUPPORTED,
4353 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4354 aDevice, aControllerPort, aName.c_str());
4355
4356 /*
4357 * The VM has to detach the device before we delete any implicit diffs.
4358 * If this fails we can roll back without loosing data.
4359 */
4360 if (fHotplug || fSilent)
4361 {
4362 alock.release();
4363 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4364 alock.acquire();
4365 }
4366 if (FAILED(rc)) return rc;
4367
4368 /* If we are here everything went well and we can delete the implicit now. */
4369 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4370
4371 alock.release();
4372
4373 /* Save modified registries, but skip this machine as it's the caller's
4374 * job to save its settings like all other settings changes. */
4375 mParent->i_unmarkRegistryModified(i_getId());
4376 mParent->i_saveModifiedRegistries();
4377
4378 return rc;
4379}
4380
4381HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4382 LONG aDevice, BOOL aPassthrough)
4383{
4384 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4385 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4386
4387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4388
4389 HRESULT rc = i_checkStateDependency(MutableStateDep);
4390 if (FAILED(rc)) return rc;
4391
4392 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4393
4394 if (Global::IsOnlineOrTransient(mData->mMachineState))
4395 return setError(VBOX_E_INVALID_VM_STATE,
4396 tr("Invalid machine state: %s"),
4397 Global::stringifyMachineState(mData->mMachineState));
4398
4399 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4400 Bstr(aName).raw(),
4401 aControllerPort,
4402 aDevice);
4403 if (!pAttach)
4404 return setError(VBOX_E_OBJECT_NOT_FOUND,
4405 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4406 aDevice, aControllerPort, aName.c_str());
4407
4408
4409 i_setModified(IsModified_Storage);
4410 mMediaData.backup();
4411
4412 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4413
4414 if (pAttach->i_getType() != DeviceType_DVD)
4415 return setError(E_INVALIDARG,
4416 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4417 aDevice, aControllerPort, aName.c_str());
4418 pAttach->i_updatePassthrough(!!aPassthrough);
4419
4420 return S_OK;
4421}
4422
4423HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4424 LONG aDevice, BOOL aTemporaryEject)
4425{
4426
4427 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4428 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4429
4430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4431
4432 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4433 if (FAILED(rc)) return rc;
4434
4435 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4436 Bstr(aName).raw(),
4437 aControllerPort,
4438 aDevice);
4439 if (!pAttach)
4440 return setError(VBOX_E_OBJECT_NOT_FOUND,
4441 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4442 aDevice, aControllerPort, aName.c_str());
4443
4444
4445 i_setModified(IsModified_Storage);
4446 mMediaData.backup();
4447
4448 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4449
4450 if (pAttach->i_getType() != DeviceType_DVD)
4451 return setError(E_INVALIDARG,
4452 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4453 aDevice, aControllerPort, aName.c_str());
4454 pAttach->i_updateTempEject(!!aTemporaryEject);
4455
4456 return S_OK;
4457}
4458
4459HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4460 LONG aDevice, BOOL aNonRotational)
4461{
4462
4463 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4464 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4465
4466 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4467
4468 HRESULT rc = i_checkStateDependency(MutableStateDep);
4469 if (FAILED(rc)) return rc;
4470
4471 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4472
4473 if (Global::IsOnlineOrTransient(mData->mMachineState))
4474 return setError(VBOX_E_INVALID_VM_STATE,
4475 tr("Invalid machine state: %s"),
4476 Global::stringifyMachineState(mData->mMachineState));
4477
4478 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4479 Bstr(aName).raw(),
4480 aControllerPort,
4481 aDevice);
4482 if (!pAttach)
4483 return setError(VBOX_E_OBJECT_NOT_FOUND,
4484 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4485 aDevice, aControllerPort, aName.c_str());
4486
4487
4488 i_setModified(IsModified_Storage);
4489 mMediaData.backup();
4490
4491 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4492
4493 if (pAttach->i_getType() != DeviceType_HardDisk)
4494 return setError(E_INVALIDARG,
4495 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"),
4496 aDevice, aControllerPort, aName.c_str());
4497 pAttach->i_updateNonRotational(!!aNonRotational);
4498
4499 return S_OK;
4500}
4501
4502HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4503 LONG aDevice, BOOL aDiscard)
4504{
4505
4506 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4507 aName.c_str(), aControllerPort, aDevice, aDiscard));
4508
4509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4510
4511 HRESULT rc = i_checkStateDependency(MutableStateDep);
4512 if (FAILED(rc)) return rc;
4513
4514 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4515
4516 if (Global::IsOnlineOrTransient(mData->mMachineState))
4517 return setError(VBOX_E_INVALID_VM_STATE,
4518 tr("Invalid machine state: %s"),
4519 Global::stringifyMachineState(mData->mMachineState));
4520
4521 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4522 Bstr(aName).raw(),
4523 aControllerPort,
4524 aDevice);
4525 if (!pAttach)
4526 return setError(VBOX_E_OBJECT_NOT_FOUND,
4527 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4528 aDevice, aControllerPort, aName.c_str());
4529
4530
4531 i_setModified(IsModified_Storage);
4532 mMediaData.backup();
4533
4534 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4535
4536 if (pAttach->i_getType() != DeviceType_HardDisk)
4537 return setError(E_INVALIDARG,
4538 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"),
4539 aDevice, aControllerPort, aName.c_str());
4540 pAttach->i_updateDiscard(!!aDiscard);
4541
4542 return S_OK;
4543}
4544
4545HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4546 LONG aDevice, BOOL aHotPluggable)
4547{
4548 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4549 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4550
4551 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4552
4553 HRESULT rc = i_checkStateDependency(MutableStateDep);
4554 if (FAILED(rc)) return rc;
4555
4556 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4557
4558 if (Global::IsOnlineOrTransient(mData->mMachineState))
4559 return setError(VBOX_E_INVALID_VM_STATE,
4560 tr("Invalid machine state: %s"),
4561 Global::stringifyMachineState(mData->mMachineState));
4562
4563 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4564 Bstr(aName).raw(),
4565 aControllerPort,
4566 aDevice);
4567 if (!pAttach)
4568 return setError(VBOX_E_OBJECT_NOT_FOUND,
4569 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4570 aDevice, aControllerPort, aName.c_str());
4571
4572 /* Check for an existing controller. */
4573 ComObjPtr<StorageController> ctl;
4574 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4575 if (FAILED(rc)) return rc;
4576
4577 StorageControllerType_T ctrlType;
4578 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4579 if (FAILED(rc))
4580 return setError(E_FAIL,
4581 tr("Could not get type of controller '%s'"),
4582 aName.c_str());
4583
4584 if (!i_isControllerHotplugCapable(ctrlType))
4585 return setError(VBOX_E_NOT_SUPPORTED,
4586 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4587 aName.c_str());
4588
4589 i_setModified(IsModified_Storage);
4590 mMediaData.backup();
4591
4592 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4593
4594 if (pAttach->i_getType() == DeviceType_Floppy)
4595 return setError(E_INVALIDARG,
4596 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"),
4597 aDevice, aControllerPort, aName.c_str());
4598 pAttach->i_updateHotPluggable(!!aHotPluggable);
4599
4600 return S_OK;
4601}
4602
4603HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4604 LONG aDevice)
4605{
4606 int rc = S_OK;
4607 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4608 aName.c_str(), aControllerPort, aDevice));
4609
4610 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4611
4612 return rc;
4613}
4614
4615HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4616 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4617{
4618 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4619 aName.c_str(), aControllerPort, aDevice));
4620
4621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4622
4623 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4624 if (FAILED(rc)) return rc;
4625
4626 if (Global::IsOnlineOrTransient(mData->mMachineState))
4627 return setError(VBOX_E_INVALID_VM_STATE,
4628 tr("Invalid machine state: %s"),
4629 Global::stringifyMachineState(mData->mMachineState));
4630
4631 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4632 Bstr(aName).raw(),
4633 aControllerPort,
4634 aDevice);
4635 if (!pAttach)
4636 return setError(VBOX_E_OBJECT_NOT_FOUND,
4637 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4638 aDevice, aControllerPort, aName.c_str());
4639
4640
4641 i_setModified(IsModified_Storage);
4642 mMediaData.backup();
4643
4644 IBandwidthGroup *iB = aBandwidthGroup;
4645 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4646 if (aBandwidthGroup && group.isNull())
4647 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4648
4649 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4650
4651 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4652 if (strBandwidthGroupOld.isNotEmpty())
4653 {
4654 /* Get the bandwidth group object and release it - this must not fail. */
4655 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4656 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4657 Assert(SUCCEEDED(rc));
4658
4659 pBandwidthGroupOld->i_release();
4660 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4661 }
4662
4663 if (!group.isNull())
4664 {
4665 group->i_reference();
4666 pAttach->i_updateBandwidthGroup(group->i_getName());
4667 }
4668
4669 return S_OK;
4670}
4671
4672HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4673 LONG aControllerPort,
4674 LONG aDevice,
4675 DeviceType_T aType)
4676{
4677 HRESULT rc = S_OK;
4678
4679 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4680 aName.c_str(), aControllerPort, aDevice, aType));
4681
4682 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4683
4684 return rc;
4685}
4686
4687
4688HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4689 LONG aControllerPort,
4690 LONG aDevice,
4691 BOOL aForce)
4692{
4693 int rc = S_OK;
4694 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4695 aName.c_str(), aControllerPort, aForce));
4696
4697 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4698
4699 return rc;
4700}
4701
4702HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4703 LONG aControllerPort,
4704 LONG aDevice,
4705 const ComPtr<IMedium> &aMedium,
4706 BOOL aForce)
4707{
4708 int rc = S_OK;
4709 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4710 aName.c_str(), aControllerPort, aDevice, aForce));
4711
4712 // request the host lock first, since might be calling Host methods for getting host drives;
4713 // next, protect the media tree all the while we're in here, as well as our member variables
4714 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4715 this->lockHandle(),
4716 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4717
4718 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4719 Bstr(aName).raw(),
4720 aControllerPort,
4721 aDevice);
4722 if (pAttach.isNull())
4723 return setError(VBOX_E_OBJECT_NOT_FOUND,
4724 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4725 aDevice, aControllerPort, aName.c_str());
4726
4727 /* Remember previously mounted medium. The medium before taking the
4728 * backup is not necessarily the same thing. */
4729 ComObjPtr<Medium> oldmedium;
4730 oldmedium = pAttach->i_getMedium();
4731
4732 IMedium *iM = aMedium;
4733 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4734 if (aMedium && pMedium.isNull())
4735 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4736
4737 AutoCaller mediumCaller(pMedium);
4738 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4739
4740 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4741 if (pMedium)
4742 {
4743 DeviceType_T mediumType = pAttach->i_getType();
4744 switch (mediumType)
4745 {
4746 case DeviceType_DVD:
4747 case DeviceType_Floppy:
4748 break;
4749
4750 default:
4751 return setError(VBOX_E_INVALID_OBJECT_STATE,
4752 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4753 aControllerPort,
4754 aDevice,
4755 aName.c_str());
4756 }
4757 }
4758
4759 i_setModified(IsModified_Storage);
4760 mMediaData.backup();
4761
4762 {
4763 // The backup operation makes the pAttach reference point to the
4764 // old settings. Re-get the correct reference.
4765 pAttach = i_findAttachment(mMediaData->mAttachments,
4766 Bstr(aName).raw(),
4767 aControllerPort,
4768 aDevice);
4769 if (!oldmedium.isNull())
4770 oldmedium->i_removeBackReference(mData->mUuid);
4771 if (!pMedium.isNull())
4772 {
4773 pMedium->i_addBackReference(mData->mUuid);
4774
4775 mediumLock.release();
4776 multiLock.release();
4777 i_addMediumToRegistry(pMedium);
4778 multiLock.acquire();
4779 mediumLock.acquire();
4780 }
4781
4782 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4783 pAttach->i_updateMedium(pMedium);
4784 }
4785
4786 i_setModified(IsModified_Storage);
4787
4788 mediumLock.release();
4789 multiLock.release();
4790 rc = i_onMediumChange(pAttach, aForce);
4791 multiLock.acquire();
4792 mediumLock.acquire();
4793
4794 /* On error roll back this change only. */
4795 if (FAILED(rc))
4796 {
4797 if (!pMedium.isNull())
4798 pMedium->i_removeBackReference(mData->mUuid);
4799 pAttach = i_findAttachment(mMediaData->mAttachments,
4800 Bstr(aName).raw(),
4801 aControllerPort,
4802 aDevice);
4803 /* If the attachment is gone in the meantime, bail out. */
4804 if (pAttach.isNull())
4805 return rc;
4806 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4807 if (!oldmedium.isNull())
4808 oldmedium->i_addBackReference(mData->mUuid);
4809 pAttach->i_updateMedium(oldmedium);
4810 }
4811
4812 mediumLock.release();
4813 multiLock.release();
4814
4815 /* Save modified registries, but skip this machine as it's the caller's
4816 * job to save its settings like all other settings changes. */
4817 mParent->i_unmarkRegistryModified(i_getId());
4818 mParent->i_saveModifiedRegistries();
4819
4820 return rc;
4821}
4822HRESULT Machine::getMedium(const com::Utf8Str &aName,
4823 LONG aControllerPort,
4824 LONG aDevice,
4825 ComPtr<IMedium> &aMedium)
4826{
4827 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4828 aName.c_str(), aControllerPort, aDevice));
4829
4830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4831
4832 aMedium = NULL;
4833
4834 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4835 Bstr(aName).raw(),
4836 aControllerPort,
4837 aDevice);
4838 if (pAttach.isNull())
4839 return setError(VBOX_E_OBJECT_NOT_FOUND,
4840 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4841 aDevice, aControllerPort, aName.c_str());
4842
4843 aMedium = pAttach->i_getMedium();
4844
4845 return S_OK;
4846}
4847
4848HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4849{
4850
4851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4852
4853 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4854
4855 return S_OK;
4856}
4857
4858HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4859{
4860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4861
4862 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4863
4864 return S_OK;
4865}
4866
4867HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4868{
4869 /* Do not assert if slot is out of range, just return the advertised
4870 status. testdriver/vbox.py triggers this in logVmInfo. */
4871 if (aSlot >= mNetworkAdapters.size())
4872 return setError(E_INVALIDARG,
4873 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4874 aSlot, mNetworkAdapters.size());
4875
4876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4877
4878 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4879
4880 return S_OK;
4881}
4882
4883HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4884{
4885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4886
4887 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4888 size_t i = 0;
4889 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4890 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4891 ++it, ++i)
4892 aKeys[i] = it->first;
4893
4894 return S_OK;
4895}
4896
4897 /**
4898 * @note Locks this object for reading.
4899 */
4900HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4901 com::Utf8Str &aValue)
4902{
4903 /* start with nothing found */
4904 aValue = "";
4905
4906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4907
4908 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4909 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4910 // found:
4911 aValue = it->second; // source is a Utf8Str
4912
4913 /* return the result to caller (may be empty) */
4914 return S_OK;
4915}
4916
4917 /**
4918 * @note Locks mParent for writing + this object for writing.
4919 */
4920HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4921{
4922 Utf8Str strOldValue; // empty
4923
4924 // locking note: we only hold the read lock briefly to look up the old value,
4925 // then release it and call the onExtraCanChange callbacks. There is a small
4926 // chance of a race insofar as the callback might be called twice if two callers
4927 // change the same key at the same time, but that's a much better solution
4928 // than the deadlock we had here before. The actual changing of the extradata
4929 // is then performed under the write lock and race-free.
4930
4931 // look up the old value first; if nothing has changed then we need not do anything
4932 {
4933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4934
4935 // For snapshots don't even think about allowing changes, extradata
4936 // is global for a machine, so there is nothing snapshot specific.
4937 if (i_isSnapshotMachine())
4938 return setError(VBOX_E_INVALID_VM_STATE,
4939 tr("Cannot set extradata for a snapshot"));
4940
4941 // check if the right IMachine instance is used
4942 if (mData->mRegistered && !i_isSessionMachine())
4943 return setError(VBOX_E_INVALID_VM_STATE,
4944 tr("Cannot set extradata for an immutable machine"));
4945
4946 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4947 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4948 strOldValue = it->second;
4949 }
4950
4951 bool fChanged;
4952 if ((fChanged = (strOldValue != aValue)))
4953 {
4954 // ask for permission from all listeners outside the locks;
4955 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4956 // lock to copy the list of callbacks to invoke
4957 Bstr error;
4958 Bstr bstrValue(aValue);
4959
4960 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4961 {
4962 const char *sep = error.isEmpty() ? "" : ": ";
4963 CBSTR err = error.raw();
4964 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4965 sep, err));
4966 return setError(E_ACCESSDENIED,
4967 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4968 aKey.c_str(),
4969 aValue.c_str(),
4970 sep,
4971 err);
4972 }
4973
4974 // data is changing and change not vetoed: then write it out under the lock
4975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4976
4977 if (aValue.isEmpty())
4978 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4979 else
4980 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4981 // creates a new key if needed
4982
4983 bool fNeedsGlobalSaveSettings = false;
4984 // This saving of settings is tricky: there is no "old state" for the
4985 // extradata items at all (unlike all other settings), so the old/new
4986 // settings comparison would give a wrong result!
4987 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4988
4989 if (fNeedsGlobalSaveSettings)
4990 {
4991 // save the global settings; for that we should hold only the VirtualBox lock
4992 alock.release();
4993 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4994 mParent->i_saveSettings();
4995 }
4996 }
4997
4998 // fire notification outside the lock
4999 if (fChanged)
5000 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5001
5002 return S_OK;
5003}
5004
5005HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5006{
5007 aProgress = NULL;
5008 NOREF(aSettingsFilePath);
5009 ReturnComNotImplemented();
5010}
5011
5012HRESULT Machine::saveSettings()
5013{
5014 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5015
5016 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5017 if (FAILED(rc)) return rc;
5018
5019 /* the settings file path may never be null */
5020 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5021
5022 /* save all VM data excluding snapshots */
5023 bool fNeedsGlobalSaveSettings = false;
5024 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5025 mlock.release();
5026
5027 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5028 {
5029 // save the global settings; for that we should hold only the VirtualBox lock
5030 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5031 rc = mParent->i_saveSettings();
5032 }
5033
5034 return rc;
5035}
5036
5037
5038HRESULT Machine::discardSettings()
5039{
5040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5041
5042 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5043 if (FAILED(rc)) return rc;
5044
5045 /*
5046 * during this rollback, the session will be notified if data has
5047 * been actually changed
5048 */
5049 i_rollback(true /* aNotify */);
5050
5051 return S_OK;
5052}
5053
5054/** @note Locks objects! */
5055HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
5056 std::vector<ComPtr<IMedium> > &aMedia)
5057{
5058 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5059 AutoLimitedCaller autoCaller(this);
5060 AssertComRCReturnRC(autoCaller.rc());
5061
5062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5063
5064 Guid id(i_getId());
5065
5066 if (mData->mSession.mState != SessionState_Unlocked)
5067 return setError(VBOX_E_INVALID_OBJECT_STATE,
5068 tr("Cannot unregister the machine '%s' while it is locked"),
5069 mUserData->s.strName.c_str());
5070
5071 // wait for state dependents to drop to zero
5072 i_ensureNoStateDependencies();
5073
5074 if (!mData->mAccessible)
5075 {
5076 // inaccessible maschines can only be unregistered; uninitialize ourselves
5077 // here because currently there may be no unregistered that are inaccessible
5078 // (this state combination is not supported). Note releasing the caller and
5079 // leaving the lock before calling uninit()
5080 alock.release();
5081 autoCaller.release();
5082
5083 uninit();
5084
5085 mParent->i_unregisterMachine(this, id);
5086 // calls VirtualBox::i_saveSettings()
5087
5088 return S_OK;
5089 }
5090
5091 HRESULT rc = S_OK;
5092
5093 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5094 // discard saved state
5095 if (mData->mMachineState == MachineState_Saved)
5096 {
5097 // add the saved state file to the list of files the caller should delete
5098 Assert(!mSSData->strStateFilePath.isEmpty());
5099 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5100
5101 mSSData->strStateFilePath.setNull();
5102
5103 // unconditionally set the machine state to powered off, we now
5104 // know no session has locked the machine
5105 mData->mMachineState = MachineState_PoweredOff;
5106 }
5107
5108 size_t cSnapshots = 0;
5109 if (mData->mFirstSnapshot)
5110 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5111 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5112 // fail now before we start detaching media
5113 return setError(VBOX_E_INVALID_OBJECT_STATE,
5114 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5115 mUserData->s.strName.c_str(), cSnapshots);
5116
5117 // This list collects the medium objects from all medium attachments
5118 // which we will detach from the machine and its snapshots, in a specific
5119 // order which allows for closing all media without getting "media in use"
5120 // errors, simply by going through the list from the front to the back:
5121 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5122 // and must be closed before the parent media from the snapshots, or closing the parents
5123 // will fail because they still have children);
5124 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5125 // the root ("first") snapshot of the machine.
5126 MediaList llMedia;
5127
5128 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5129 && mMediaData->mAttachments.size()
5130 )
5131 {
5132 // we have media attachments: detach them all and add the Medium objects to our list
5133 if (aCleanupMode != CleanupMode_UnregisterOnly)
5134 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5135 else
5136 return setError(VBOX_E_INVALID_OBJECT_STATE,
5137 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5138 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5139 }
5140
5141 if (cSnapshots)
5142 {
5143 // add the media from the medium attachments of the snapshots to llMedia
5144 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5145 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5146 // into the children first
5147
5148 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5149 MachineState_T oldState = mData->mMachineState;
5150 mData->mMachineState = MachineState_DeletingSnapshot;
5151
5152 // make a copy of the first snapshot so the refcount does not drop to 0
5153 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5154 // because of the AutoCaller voodoo)
5155 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5156
5157 // GO!
5158 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5159
5160 mData->mMachineState = oldState;
5161 }
5162
5163 if (FAILED(rc))
5164 {
5165 i_rollbackMedia();
5166 return rc;
5167 }
5168
5169 // commit all the media changes made above
5170 i_commitMedia();
5171
5172 mData->mRegistered = false;
5173
5174 // machine lock no longer needed
5175 alock.release();
5176
5177 // return media to caller
5178 size_t i = 0;
5179 aMedia.resize(llMedia.size());
5180 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5181 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5182
5183 mParent->i_unregisterMachine(this, id);
5184 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5185
5186 return S_OK;
5187}
5188
5189/**
5190 * Task record for deleting a machine config.
5191 */
5192struct Machine::DeleteConfigTask
5193 : public Machine::Task
5194{
5195 DeleteConfigTask(Machine *m,
5196 Progress *p,
5197 const Utf8Str &t,
5198 const RTCList<ComPtr<IMedium> > &llMediums,
5199 const StringsList &llFilesToDelete)
5200 : Task(m, p, t),
5201 m_llMediums(llMediums),
5202 m_llFilesToDelete(llFilesToDelete)
5203 {}
5204
5205 void handler()
5206 {
5207 m_pMachine->i_deleteConfigHandler(*this);
5208 }
5209
5210 RTCList<ComPtr<IMedium> > m_llMediums;
5211 StringsList m_llFilesToDelete;
5212};
5213
5214/**
5215 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5216 * SessionMachine::taskHandler().
5217 *
5218 * @note Locks this object for writing.
5219 *
5220 * @param task
5221 * @return
5222 */
5223void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5224{
5225 LogFlowThisFuncEnter();
5226
5227 AutoCaller autoCaller(this);
5228 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5229 if (FAILED(autoCaller.rc()))
5230 {
5231 /* we might have been uninitialized because the session was accidentally
5232 * closed by the client, so don't assert */
5233 HRESULT rc = setError(E_FAIL,
5234 tr("The session has been accidentally closed"));
5235 task.m_pProgress->i_notifyComplete(rc);
5236 LogFlowThisFuncLeave();
5237 return;
5238 }
5239
5240 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5241
5242 HRESULT rc = S_OK;
5243
5244 try
5245 {
5246 ULONG uLogHistoryCount = 3;
5247 ComPtr<ISystemProperties> systemProperties;
5248 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5249 if (FAILED(rc)) throw rc;
5250
5251 if (!systemProperties.isNull())
5252 {
5253 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5254 if (FAILED(rc)) throw rc;
5255 }
5256
5257 MachineState_T oldState = mData->mMachineState;
5258 i_setMachineState(MachineState_SettingUp);
5259 alock.release();
5260 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5261 {
5262 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5263 {
5264 AutoCaller mac(pMedium);
5265 if (FAILED(mac.rc())) throw mac.rc();
5266 Utf8Str strLocation = pMedium->i_getLocationFull();
5267 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5268 if (FAILED(rc)) throw rc;
5269 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5270 }
5271 if (pMedium->i_isMediumFormatFile())
5272 {
5273 ComPtr<IProgress> pProgress2;
5274 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5275 if (FAILED(rc)) throw rc;
5276 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5277 if (FAILED(rc)) throw rc;
5278 /* Check the result of the asynchronous process. */
5279 LONG iRc;
5280 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5281 if (FAILED(rc)) throw rc;
5282 /* If the thread of the progress object has an error, then
5283 * retrieve the error info from there, or it'll be lost. */
5284 if (FAILED(iRc))
5285 throw setError(ProgressErrorInfo(pProgress2));
5286 }
5287
5288 /* Close the medium, deliberately without checking the return
5289 * code, and without leaving any trace in the error info, as
5290 * a failure here is a very minor issue, which shouldn't happen
5291 * as above we even managed to delete the medium. */
5292 {
5293 ErrorInfoKeeper eik;
5294 pMedium->Close();
5295 }
5296 }
5297 i_setMachineState(oldState);
5298 alock.acquire();
5299
5300 // delete the files pushed on the task list by Machine::Delete()
5301 // (this includes saved states of the machine and snapshots and
5302 // medium storage files from the IMedium list passed in, and the
5303 // machine XML file)
5304 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5305 while (it != task.m_llFilesToDelete.end())
5306 {
5307 const Utf8Str &strFile = *it;
5308 LogFunc(("Deleting file %s\n", strFile.c_str()));
5309 int vrc = RTFileDelete(strFile.c_str());
5310 if (RT_FAILURE(vrc))
5311 throw setError(VBOX_E_IPRT_ERROR,
5312 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5313
5314 ++it;
5315 if (it == task.m_llFilesToDelete.end())
5316 {
5317 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5318 if (FAILED(rc)) throw rc;
5319 break;
5320 }
5321
5322 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5323 if (FAILED(rc)) throw rc;
5324 }
5325
5326 /* delete the settings only when the file actually exists */
5327 if (mData->pMachineConfigFile->fileExists())
5328 {
5329 /* Delete any backup or uncommitted XML files. Ignore failures.
5330 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5331 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5332 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5333 RTFileDelete(otherXml.c_str());
5334 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5335 RTFileDelete(otherXml.c_str());
5336
5337 /* delete the Logs folder, nothing important should be left
5338 * there (we don't check for errors because the user might have
5339 * some private files there that we don't want to delete) */
5340 Utf8Str logFolder;
5341 getLogFolder(logFolder);
5342 Assert(logFolder.length());
5343 if (RTDirExists(logFolder.c_str()))
5344 {
5345 /* Delete all VBox.log[.N] files from the Logs folder
5346 * (this must be in sync with the rotation logic in
5347 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5348 * files that may have been created by the GUI. */
5349 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5350 logFolder.c_str(), RTPATH_DELIMITER);
5351 RTFileDelete(log.c_str());
5352 log = Utf8StrFmt("%s%cVBox.png",
5353 logFolder.c_str(), RTPATH_DELIMITER);
5354 RTFileDelete(log.c_str());
5355 for (int i = uLogHistoryCount; i > 0; i--)
5356 {
5357 log = Utf8StrFmt("%s%cVBox.log.%d",
5358 logFolder.c_str(), RTPATH_DELIMITER, i);
5359 RTFileDelete(log.c_str());
5360 log = Utf8StrFmt("%s%cVBox.png.%d",
5361 logFolder.c_str(), RTPATH_DELIMITER, i);
5362 RTFileDelete(log.c_str());
5363 }
5364#if defined(RT_OS_WINDOWS)
5365 log = Utf8StrFmt("%s%cVBoxStartup.log",
5366 logFolder.c_str(), RTPATH_DELIMITER);
5367 RTFileDelete(log.c_str());
5368#endif
5369
5370 RTDirRemove(logFolder.c_str());
5371 }
5372
5373 /* delete the Snapshots folder, nothing important should be left
5374 * there (we don't check for errors because the user might have
5375 * some private files there that we don't want to delete) */
5376 Utf8Str strFullSnapshotFolder;
5377 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5378 Assert(!strFullSnapshotFolder.isEmpty());
5379 if (RTDirExists(strFullSnapshotFolder.c_str()))
5380 RTDirRemove(strFullSnapshotFolder.c_str());
5381
5382 // delete the directory that contains the settings file, but only
5383 // if it matches the VM name
5384 Utf8Str settingsDir;
5385 if (i_isInOwnDir(&settingsDir))
5386 RTDirRemove(settingsDir.c_str());
5387 }
5388
5389 alock.release();
5390
5391 mParent->i_saveModifiedRegistries();
5392 }
5393 catch (HRESULT aRC) { rc = aRC; }
5394
5395 task.m_pProgress->i_notifyComplete(rc);
5396
5397 LogFlowThisFuncLeave();
5398}
5399
5400HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5401{
5402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5403
5404 HRESULT rc = i_checkStateDependency(MutableStateDep);
5405 if (FAILED(rc)) return rc;
5406
5407 if (mData->mRegistered)
5408 return setError(VBOX_E_INVALID_VM_STATE,
5409 tr("Cannot delete settings of a registered machine"));
5410
5411 // collect files to delete
5412 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5413 if (mData->pMachineConfigFile->fileExists())
5414 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5415
5416 RTCList<ComPtr<IMedium> > llMediums;
5417 for (size_t i = 0; i < aMedia.size(); ++i)
5418 {
5419 IMedium *pIMedium(aMedia[i]);
5420 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5421 if (pMedium.isNull())
5422 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5423 SafeArray<BSTR> ids;
5424 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5425 if (FAILED(rc)) return rc;
5426 /* At this point the medium should not have any back references
5427 * anymore. If it has it is attached to another VM and *must* not
5428 * deleted. */
5429 if (ids.size() < 1)
5430 llMediums.append(pMedium);
5431 }
5432
5433 ComObjPtr<Progress> pProgress;
5434 pProgress.createObject();
5435 rc = pProgress->init(i_getVirtualBox(),
5436 static_cast<IMachine*>(this) /* aInitiator */,
5437 Bstr(tr("Deleting files")).raw(),
5438 true /* fCancellable */,
5439 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5440 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5441 if (FAILED(rc))
5442 return rc;
5443
5444 /* create and start the task on a separate thread (note that it will not
5445 * start working until we release alock) */
5446 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5447 rc = pTask->createThread();
5448 if (FAILED(rc))
5449 return rc;
5450
5451 pProgress.queryInterfaceTo(aProgress.asOutParam());
5452
5453 LogFlowFuncLeave();
5454
5455 return S_OK;
5456}
5457
5458HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5459{
5460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5461
5462 ComObjPtr<Snapshot> pSnapshot;
5463 HRESULT rc;
5464
5465 if (aNameOrId.isEmpty())
5466 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5467 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5468 else
5469 {
5470 Guid uuid(aNameOrId);
5471 if (uuid.isValid())
5472 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5473 else
5474 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5475 }
5476 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5477
5478 return rc;
5479}
5480
5481HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5482{
5483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5484
5485 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5486 if (FAILED(rc)) return rc;
5487
5488 ComObjPtr<SharedFolder> sharedFolder;
5489 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5490 if (SUCCEEDED(rc))
5491 return setError(VBOX_E_OBJECT_IN_USE,
5492 tr("Shared folder named '%s' already exists"),
5493 aName.c_str());
5494
5495 sharedFolder.createObject();
5496 rc = sharedFolder->init(i_getMachine(),
5497 aName,
5498 aHostPath,
5499 !!aWritable,
5500 !!aAutomount,
5501 true /* fFailOnError */);
5502 if (FAILED(rc)) return rc;
5503
5504 i_setModified(IsModified_SharedFolders);
5505 mHWData.backup();
5506 mHWData->mSharedFolders.push_back(sharedFolder);
5507
5508 /* inform the direct session if any */
5509 alock.release();
5510 i_onSharedFolderChange();
5511
5512 return S_OK;
5513}
5514
5515HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5516{
5517 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5518
5519 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5520 if (FAILED(rc)) return rc;
5521
5522 ComObjPtr<SharedFolder> sharedFolder;
5523 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5524 if (FAILED(rc)) return rc;
5525
5526 i_setModified(IsModified_SharedFolders);
5527 mHWData.backup();
5528 mHWData->mSharedFolders.remove(sharedFolder);
5529
5530 /* inform the direct session if any */
5531 alock.release();
5532 i_onSharedFolderChange();
5533
5534 return S_OK;
5535}
5536
5537HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5538{
5539 /* start with No */
5540 *aCanShow = FALSE;
5541
5542 ComPtr<IInternalSessionControl> directControl;
5543 {
5544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5545
5546 if (mData->mSession.mState != SessionState_Locked)
5547 return setError(VBOX_E_INVALID_VM_STATE,
5548 tr("Machine is not locked for session (session state: %s)"),
5549 Global::stringifySessionState(mData->mSession.mState));
5550
5551 if (mData->mSession.mLockType == LockType_VM)
5552 directControl = mData->mSession.mDirectControl;
5553 }
5554
5555 /* ignore calls made after #OnSessionEnd() is called */
5556 if (!directControl)
5557 return S_OK;
5558
5559 LONG64 dummy;
5560 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5561}
5562
5563HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5564{
5565 ComPtr<IInternalSessionControl> directControl;
5566 {
5567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5568
5569 if (mData->mSession.mState != SessionState_Locked)
5570 return setError(E_FAIL,
5571 tr("Machine is not locked for session (session state: %s)"),
5572 Global::stringifySessionState(mData->mSession.mState));
5573
5574 if (mData->mSession.mLockType == LockType_VM)
5575 directControl = mData->mSession.mDirectControl;
5576 }
5577
5578 /* ignore calls made after #OnSessionEnd() is called */
5579 if (!directControl)
5580 return S_OK;
5581
5582 BOOL dummy;
5583 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5584}
5585
5586#ifdef VBOX_WITH_GUEST_PROPS
5587/**
5588 * Look up a guest property in VBoxSVC's internal structures.
5589 */
5590HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5591 com::Utf8Str &aValue,
5592 LONG64 *aTimestamp,
5593 com::Utf8Str &aFlags) const
5594{
5595 using namespace guestProp;
5596
5597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5598 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5599
5600 if (it != mHWData->mGuestProperties.end())
5601 {
5602 char szFlags[MAX_FLAGS_LEN + 1];
5603 aValue = it->second.strValue;
5604 *aTimestamp = it->second.mTimestamp;
5605 writeFlags(it->second.mFlags, szFlags);
5606 aFlags = Utf8Str(szFlags);
5607 }
5608
5609 return S_OK;
5610}
5611
5612/**
5613 * Query the VM that a guest property belongs to for the property.
5614 * @returns E_ACCESSDENIED if the VM process is not available or not
5615 * currently handling queries and the lookup should then be done in
5616 * VBoxSVC.
5617 */
5618HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5619 com::Utf8Str &aValue,
5620 LONG64 *aTimestamp,
5621 com::Utf8Str &aFlags) const
5622{
5623 HRESULT rc = S_OK;
5624 BSTR bValue = NULL;
5625 BSTR bFlags = NULL;
5626
5627 ComPtr<IInternalSessionControl> directControl;
5628 {
5629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5630 if (mData->mSession.mLockType == LockType_VM)
5631 directControl = mData->mSession.mDirectControl;
5632 }
5633
5634 /* ignore calls made after #OnSessionEnd() is called */
5635 if (!directControl)
5636 rc = E_ACCESSDENIED;
5637 else
5638 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5639 0 /* accessMode */,
5640 &bValue, aTimestamp, &bFlags);
5641
5642 aValue = bValue;
5643 aFlags = bFlags;
5644
5645 return rc;
5646}
5647#endif // VBOX_WITH_GUEST_PROPS
5648
5649HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5650 com::Utf8Str &aValue,
5651 LONG64 *aTimestamp,
5652 com::Utf8Str &aFlags)
5653{
5654#ifndef VBOX_WITH_GUEST_PROPS
5655 ReturnComNotImplemented();
5656#else // VBOX_WITH_GUEST_PROPS
5657
5658 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5659
5660 if (rc == E_ACCESSDENIED)
5661 /* The VM is not running or the service is not (yet) accessible */
5662 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5663 return rc;
5664#endif // VBOX_WITH_GUEST_PROPS
5665}
5666
5667HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5668{
5669 LONG64 dummyTimestamp;
5670 com::Utf8Str dummyFlags;
5671 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5672 return rc;
5673
5674}
5675HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5676{
5677 com::Utf8Str dummyFlags;
5678 com::Utf8Str dummyValue;
5679 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5680 return rc;
5681}
5682
5683#ifdef VBOX_WITH_GUEST_PROPS
5684/**
5685 * Set a guest property in VBoxSVC's internal structures.
5686 */
5687HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5688 const com::Utf8Str &aFlags, bool fDelete)
5689{
5690 using namespace guestProp;
5691
5692 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5693 HRESULT rc = S_OK;
5694
5695 rc = i_checkStateDependency(MutableOrSavedStateDep);
5696 if (FAILED(rc)) return rc;
5697
5698 try
5699 {
5700 uint32_t fFlags = NILFLAG;
5701 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5702 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5703
5704 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5705 if (it == mHWData->mGuestProperties.end())
5706 {
5707 if (!fDelete)
5708 {
5709 i_setModified(IsModified_MachineData);
5710 mHWData.backupEx();
5711
5712 RTTIMESPEC time;
5713 HWData::GuestProperty prop;
5714 prop.strValue = Bstr(aValue).raw();
5715 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5716 prop.mFlags = fFlags;
5717 mHWData->mGuestProperties[aName] = prop;
5718 }
5719 }
5720 else
5721 {
5722 if (it->second.mFlags & (RDONLYHOST))
5723 {
5724 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5725 }
5726 else
5727 {
5728 i_setModified(IsModified_MachineData);
5729 mHWData.backupEx();
5730
5731 /* The backupEx() operation invalidates our iterator,
5732 * so get a new one. */
5733 it = mHWData->mGuestProperties.find(aName);
5734 Assert(it != mHWData->mGuestProperties.end());
5735
5736 if (!fDelete)
5737 {
5738 RTTIMESPEC time;
5739 it->second.strValue = aValue;
5740 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5741 it->second.mFlags = fFlags;
5742 }
5743 else
5744 mHWData->mGuestProperties.erase(it);
5745 }
5746 }
5747
5748 if ( SUCCEEDED(rc)
5749 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5750 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5751 RTSTR_MAX,
5752 aName.c_str(),
5753 RTSTR_MAX,
5754 NULL)
5755 )
5756 )
5757 {
5758 alock.release();
5759
5760 mParent->i_onGuestPropertyChange(mData->mUuid,
5761 Bstr(aName).raw(),
5762 Bstr(aValue).raw(),
5763 Bstr(aFlags).raw());
5764 }
5765 }
5766 catch (std::bad_alloc &)
5767 {
5768 rc = E_OUTOFMEMORY;
5769 }
5770
5771 return rc;
5772}
5773
5774/**
5775 * Set a property on the VM that that property belongs to.
5776 * @returns E_ACCESSDENIED if the VM process is not available or not
5777 * currently handling queries and the setting should then be done in
5778 * VBoxSVC.
5779 */
5780HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5781 const com::Utf8Str &aFlags, bool fDelete)
5782{
5783 HRESULT rc;
5784
5785 try
5786 {
5787 ComPtr<IInternalSessionControl> directControl;
5788 {
5789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5790 if (mData->mSession.mLockType == LockType_VM)
5791 directControl = mData->mSession.mDirectControl;
5792 }
5793
5794 BSTR dummy = NULL; /* will not be changed (setter) */
5795 LONG64 dummy64;
5796 if (!directControl)
5797 rc = E_ACCESSDENIED;
5798 else
5799 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5800 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5801 fDelete? 2: 1 /* accessMode */,
5802 &dummy, &dummy64, &dummy);
5803 }
5804 catch (std::bad_alloc &)
5805 {
5806 rc = E_OUTOFMEMORY;
5807 }
5808
5809 return rc;
5810}
5811#endif // VBOX_WITH_GUEST_PROPS
5812
5813HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5814 const com::Utf8Str &aFlags)
5815{
5816#ifndef VBOX_WITH_GUEST_PROPS
5817 ReturnComNotImplemented();
5818#else // VBOX_WITH_GUEST_PROPS
5819 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5820 if (rc == E_ACCESSDENIED)
5821 /* The VM is not running or the service is not (yet) accessible */
5822 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5823 return rc;
5824#endif // VBOX_WITH_GUEST_PROPS
5825}
5826
5827HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5828{
5829 return setGuestProperty(aProperty, aValue, "");
5830}
5831
5832HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5833{
5834#ifndef VBOX_WITH_GUEST_PROPS
5835 ReturnComNotImplemented();
5836#else // VBOX_WITH_GUEST_PROPS
5837 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5838 if (rc == E_ACCESSDENIED)
5839 /* The VM is not running or the service is not (yet) accessible */
5840 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5841 return rc;
5842#endif // VBOX_WITH_GUEST_PROPS
5843}
5844
5845#ifdef VBOX_WITH_GUEST_PROPS
5846/**
5847 * Enumerate the guest properties in VBoxSVC's internal structures.
5848 */
5849HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5850 std::vector<com::Utf8Str> &aNames,
5851 std::vector<com::Utf8Str> &aValues,
5852 std::vector<LONG64> &aTimestamps,
5853 std::vector<com::Utf8Str> &aFlags)
5854{
5855 using namespace guestProp;
5856
5857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5858 Utf8Str strPatterns(aPatterns);
5859
5860 HWData::GuestPropertyMap propMap;
5861
5862 /*
5863 * Look for matching patterns and build up a list.
5864 */
5865 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5866 while (it != mHWData->mGuestProperties.end())
5867 {
5868 if ( strPatterns.isEmpty()
5869 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5870 RTSTR_MAX,
5871 it->first.c_str(),
5872 RTSTR_MAX,
5873 NULL)
5874 )
5875 propMap.insert(*it);
5876 it++;
5877 }
5878
5879 alock.release();
5880
5881 /*
5882 * And build up the arrays for returning the property information.
5883 */
5884 size_t cEntries = propMap.size();
5885
5886 aNames.resize(cEntries);
5887 aValues.resize(cEntries);
5888 aTimestamps.resize(cEntries);
5889 aFlags.resize(cEntries);
5890
5891 char szFlags[MAX_FLAGS_LEN + 1];
5892 size_t i= 0;
5893 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5894 {
5895 aNames[i] = it->first;
5896 aValues[i] = it->second.strValue;
5897 aTimestamps[i] = it->second.mTimestamp;
5898 writeFlags(it->second.mFlags, szFlags);
5899 aFlags[i] = Utf8Str(szFlags);
5900 }
5901
5902 return S_OK;
5903}
5904
5905/**
5906 * Enumerate the properties managed by a VM.
5907 * @returns E_ACCESSDENIED if the VM process is not available or not
5908 * currently handling queries and the setting should then be done in
5909 * VBoxSVC.
5910 */
5911HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5912 std::vector<com::Utf8Str> &aNames,
5913 std::vector<com::Utf8Str> &aValues,
5914 std::vector<LONG64> &aTimestamps,
5915 std::vector<com::Utf8Str> &aFlags)
5916{
5917 HRESULT rc;
5918 ComPtr<IInternalSessionControl> directControl;
5919 {
5920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5921 if (mData->mSession.mLockType == LockType_VM)
5922 directControl = mData->mSession.mDirectControl;
5923 }
5924
5925 com::SafeArray<BSTR> bNames;
5926 com::SafeArray<BSTR> bValues;
5927 com::SafeArray<LONG64> bTimestamps;
5928 com::SafeArray<BSTR> bFlags;
5929
5930 if (!directControl)
5931 rc = E_ACCESSDENIED;
5932 else
5933 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5934 ComSafeArrayAsOutParam(bNames),
5935 ComSafeArrayAsOutParam(bValues),
5936 ComSafeArrayAsOutParam(bTimestamps),
5937 ComSafeArrayAsOutParam(bFlags));
5938 size_t i;
5939 aNames.resize(bNames.size());
5940 for (i = 0; i < bNames.size(); ++i)
5941 aNames[i] = Utf8Str(bNames[i]);
5942 aValues.resize(bValues.size());
5943 for (i = 0; i < bValues.size(); ++i)
5944 aValues[i] = Utf8Str(bValues[i]);
5945 aTimestamps.resize(bTimestamps.size());
5946 for (i = 0; i < bTimestamps.size(); ++i)
5947 aTimestamps[i] = bTimestamps[i];
5948 aFlags.resize(bFlags.size());
5949 for (i = 0; i < bFlags.size(); ++i)
5950 aFlags[i] = Utf8Str(bFlags[i]);
5951
5952 return rc;
5953}
5954#endif // VBOX_WITH_GUEST_PROPS
5955HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5956 std::vector<com::Utf8Str> &aNames,
5957 std::vector<com::Utf8Str> &aValues,
5958 std::vector<LONG64> &aTimestamps,
5959 std::vector<com::Utf8Str> &aFlags)
5960{
5961#ifndef VBOX_WITH_GUEST_PROPS
5962 ReturnComNotImplemented();
5963#else // VBOX_WITH_GUEST_PROPS
5964
5965 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5966
5967 if (rc == E_ACCESSDENIED)
5968 /* The VM is not running or the service is not (yet) accessible */
5969 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5970 return rc;
5971#endif // VBOX_WITH_GUEST_PROPS
5972}
5973
5974HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5975 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5976{
5977 MediaData::AttachmentList atts;
5978
5979 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5980 if (FAILED(rc)) return rc;
5981
5982 size_t i = 0;
5983 aMediumAttachments.resize(atts.size());
5984 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5985 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5986
5987 return S_OK;
5988}
5989
5990HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5991 LONG aControllerPort,
5992 LONG aDevice,
5993 ComPtr<IMediumAttachment> &aAttachment)
5994{
5995 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5996 aName.c_str(), aControllerPort, aDevice));
5997
5998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5999
6000 aAttachment = NULL;
6001
6002 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
6003 Bstr(aName).raw(),
6004 aControllerPort,
6005 aDevice);
6006 if (pAttach.isNull())
6007 return setError(VBOX_E_OBJECT_NOT_FOUND,
6008 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6009 aDevice, aControllerPort, aName.c_str());
6010
6011 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6012
6013 return S_OK;
6014}
6015
6016
6017HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6018 StorageBus_T aConnectionType,
6019 ComPtr<IStorageController> &aController)
6020{
6021 if ( (aConnectionType <= StorageBus_Null)
6022 || (aConnectionType > StorageBus_USB))
6023 return setError(E_INVALIDARG,
6024 tr("Invalid connection type: %d"),
6025 aConnectionType);
6026
6027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6028
6029 HRESULT rc = i_checkStateDependency(MutableStateDep);
6030 if (FAILED(rc)) return rc;
6031
6032 /* try to find one with the name first. */
6033 ComObjPtr<StorageController> ctrl;
6034
6035 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6036 if (SUCCEEDED(rc))
6037 return setError(VBOX_E_OBJECT_IN_USE,
6038 tr("Storage controller named '%s' already exists"),
6039 aName.c_str());
6040
6041 ctrl.createObject();
6042
6043 /* get a new instance number for the storage controller */
6044 ULONG ulInstance = 0;
6045 bool fBootable = true;
6046 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6047 it != mStorageControllers->end();
6048 ++it)
6049 {
6050 if ((*it)->i_getStorageBus() == aConnectionType)
6051 {
6052 ULONG ulCurInst = (*it)->i_getInstance();
6053
6054 if (ulCurInst >= ulInstance)
6055 ulInstance = ulCurInst + 1;
6056
6057 /* Only one controller of each type can be marked as bootable. */
6058 if ((*it)->i_getBootable())
6059 fBootable = false;
6060 }
6061 }
6062
6063 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6064 if (FAILED(rc)) return rc;
6065
6066 i_setModified(IsModified_Storage);
6067 mStorageControllers.backup();
6068 mStorageControllers->push_back(ctrl);
6069
6070 ctrl.queryInterfaceTo(aController.asOutParam());
6071
6072 /* inform the direct session if any */
6073 alock.release();
6074 i_onStorageControllerChange();
6075
6076 return S_OK;
6077}
6078
6079HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6080 ComPtr<IStorageController> &aStorageController)
6081{
6082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6083
6084 ComObjPtr<StorageController> ctrl;
6085
6086 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6087 if (SUCCEEDED(rc))
6088 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6089
6090 return rc;
6091}
6092
6093HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6094 ComPtr<IStorageController> &aStorageController)
6095{
6096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6097
6098 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6099 it != mStorageControllers->end();
6100 ++it)
6101 {
6102 if ((*it)->i_getInstance() == aInstance)
6103 {
6104 (*it).queryInterfaceTo(aStorageController.asOutParam());
6105 return S_OK;
6106 }
6107 }
6108
6109 return setError(VBOX_E_OBJECT_NOT_FOUND,
6110 tr("Could not find a storage controller with instance number '%lu'"),
6111 aInstance);
6112}
6113
6114HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6115{
6116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6117
6118 HRESULT rc = i_checkStateDependency(MutableStateDep);
6119 if (FAILED(rc)) return rc;
6120
6121 ComObjPtr<StorageController> ctrl;
6122
6123 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6124 if (SUCCEEDED(rc))
6125 {
6126 /* Ensure that only one controller of each type is marked as bootable. */
6127 if (aBootable == TRUE)
6128 {
6129 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6130 it != mStorageControllers->end();
6131 ++it)
6132 {
6133 ComObjPtr<StorageController> aCtrl = (*it);
6134
6135 if ( (aCtrl->i_getName() != aName)
6136 && aCtrl->i_getBootable() == TRUE
6137 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6138 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6139 {
6140 aCtrl->i_setBootable(FALSE);
6141 break;
6142 }
6143 }
6144 }
6145
6146 if (SUCCEEDED(rc))
6147 {
6148 ctrl->i_setBootable(aBootable);
6149 i_setModified(IsModified_Storage);
6150 }
6151 }
6152
6153 if (SUCCEEDED(rc))
6154 {
6155 /* inform the direct session if any */
6156 alock.release();
6157 i_onStorageControllerChange();
6158 }
6159
6160 return rc;
6161}
6162
6163HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6164{
6165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6166
6167 HRESULT rc = i_checkStateDependency(MutableStateDep);
6168 if (FAILED(rc)) return rc;
6169
6170 ComObjPtr<StorageController> ctrl;
6171 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6172 if (FAILED(rc)) return rc;
6173
6174 {
6175 /* find all attached devices to the appropriate storage controller and detach them all */
6176 // make a temporary list because detachDevice invalidates iterators into
6177 // mMediaData->mAttachments
6178 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6179
6180 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6181 it != llAttachments2.end();
6182 ++it)
6183 {
6184 MediumAttachment *pAttachTemp = *it;
6185
6186 AutoCaller localAutoCaller(pAttachTemp);
6187 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6188
6189 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6190
6191 if (pAttachTemp->i_getControllerName() == aName)
6192 {
6193 rc = i_detachDevice(pAttachTemp, alock, NULL);
6194 if (FAILED(rc)) return rc;
6195 }
6196 }
6197 }
6198
6199 /* We can remove it now. */
6200 i_setModified(IsModified_Storage);
6201 mStorageControllers.backup();
6202
6203 ctrl->i_unshare();
6204
6205 mStorageControllers->remove(ctrl);
6206
6207 /* inform the direct session if any */
6208 alock.release();
6209 i_onStorageControllerChange();
6210
6211 return S_OK;
6212}
6213
6214HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6215 ComPtr<IUSBController> &aController)
6216{
6217 if ( (aType <= USBControllerType_Null)
6218 || (aType >= USBControllerType_Last))
6219 return setError(E_INVALIDARG,
6220 tr("Invalid USB controller type: %d"),
6221 aType);
6222
6223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6224
6225 HRESULT rc = i_checkStateDependency(MutableStateDep);
6226 if (FAILED(rc)) return rc;
6227
6228 /* try to find one with the same type first. */
6229 ComObjPtr<USBController> ctrl;
6230
6231 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6232 if (SUCCEEDED(rc))
6233 return setError(VBOX_E_OBJECT_IN_USE,
6234 tr("USB controller named '%s' already exists"),
6235 aName.c_str());
6236
6237 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6238 ULONG maxInstances;
6239 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6240 if (FAILED(rc))
6241 return rc;
6242
6243 ULONG cInstances = i_getUSBControllerCountByType(aType);
6244 if (cInstances >= maxInstances)
6245 return setError(E_INVALIDARG,
6246 tr("Too many USB controllers of this type"));
6247
6248 ctrl.createObject();
6249
6250 rc = ctrl->init(this, aName, aType);
6251 if (FAILED(rc)) return rc;
6252
6253 i_setModified(IsModified_USB);
6254 mUSBControllers.backup();
6255 mUSBControllers->push_back(ctrl);
6256
6257 ctrl.queryInterfaceTo(aController.asOutParam());
6258
6259 /* inform the direct session if any */
6260 alock.release();
6261 i_onUSBControllerChange();
6262
6263 return S_OK;
6264}
6265
6266HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6267{
6268 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6269
6270 ComObjPtr<USBController> ctrl;
6271
6272 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6273 if (SUCCEEDED(rc))
6274 ctrl.queryInterfaceTo(aController.asOutParam());
6275
6276 return rc;
6277}
6278
6279HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6280 ULONG *aControllers)
6281{
6282 if ( (aType <= USBControllerType_Null)
6283 || (aType >= USBControllerType_Last))
6284 return setError(E_INVALIDARG,
6285 tr("Invalid USB controller type: %d"),
6286 aType);
6287
6288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6289
6290 ComObjPtr<USBController> ctrl;
6291
6292 *aControllers = i_getUSBControllerCountByType(aType);
6293
6294 return S_OK;
6295}
6296
6297HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6298{
6299
6300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6301
6302 HRESULT rc = i_checkStateDependency(MutableStateDep);
6303 if (FAILED(rc)) return rc;
6304
6305 ComObjPtr<USBController> ctrl;
6306 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6307 if (FAILED(rc)) return rc;
6308
6309 i_setModified(IsModified_USB);
6310 mUSBControllers.backup();
6311
6312 ctrl->i_unshare();
6313
6314 mUSBControllers->remove(ctrl);
6315
6316 /* inform the direct session if any */
6317 alock.release();
6318 i_onUSBControllerChange();
6319
6320 return S_OK;
6321}
6322
6323HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6324 ULONG *aOriginX,
6325 ULONG *aOriginY,
6326 ULONG *aWidth,
6327 ULONG *aHeight,
6328 BOOL *aEnabled)
6329{
6330 uint32_t u32OriginX= 0;
6331 uint32_t u32OriginY= 0;
6332 uint32_t u32Width = 0;
6333 uint32_t u32Height = 0;
6334 uint16_t u16Flags = 0;
6335
6336 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6337 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6338 if (RT_FAILURE(vrc))
6339 {
6340#ifdef RT_OS_WINDOWS
6341 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6342 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6343 * So just assign fEnable to TRUE again.
6344 * The right fix would be to change GUI API wrappers to make sure that parameters
6345 * are changed only if API succeeds.
6346 */
6347 *aEnabled = TRUE;
6348#endif
6349 return setError(VBOX_E_IPRT_ERROR,
6350 tr("Saved guest size is not available (%Rrc)"),
6351 vrc);
6352 }
6353
6354 *aOriginX = u32OriginX;
6355 *aOriginY = u32OriginY;
6356 *aWidth = u32Width;
6357 *aHeight = u32Height;
6358 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6359
6360 return S_OK;
6361}
6362
6363HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6364{
6365 if (aScreenId != 0)
6366 return E_NOTIMPL;
6367
6368 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6369
6370 uint8_t *pu8Data = NULL;
6371 uint32_t cbData = 0;
6372 uint32_t u32Width = 0;
6373 uint32_t u32Height = 0;
6374
6375 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6376
6377 if (RT_FAILURE(vrc))
6378 return setError(VBOX_E_IPRT_ERROR,
6379 tr("Saved screenshot data is not available (%Rrc)"),
6380 vrc);
6381
6382 *aSize = cbData;
6383 *aWidth = u32Width;
6384 *aHeight = u32Height;
6385
6386 freeSavedDisplayScreenshot(pu8Data);
6387
6388 return S_OK;
6389}
6390
6391HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6392 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6393{
6394 if (aScreenId != 0)
6395 return E_NOTIMPL;
6396
6397 if ( aBitmapFormat != BitmapFormat_BGR0
6398 && aBitmapFormat != BitmapFormat_BGRA
6399 && aBitmapFormat != BitmapFormat_RGBA
6400 && aBitmapFormat != BitmapFormat_PNG)
6401 return setError(E_NOTIMPL,
6402 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6403
6404 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6405
6406 uint8_t *pu8Data = NULL;
6407 uint32_t cbData = 0;
6408 uint32_t u32Width = 0;
6409 uint32_t u32Height = 0;
6410
6411 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6412
6413 if (RT_FAILURE(vrc))
6414 return setError(VBOX_E_IPRT_ERROR,
6415 tr("Saved thumbnail data is not available (%Rrc)"),
6416 vrc);
6417
6418 HRESULT hr = S_OK;
6419
6420 *aWidth = u32Width;
6421 *aHeight = u32Height;
6422
6423 if (cbData > 0)
6424 {
6425 /* Convert pixels to the format expected by the API caller. */
6426 if (aBitmapFormat == BitmapFormat_BGR0)
6427 {
6428 /* [0] B, [1] G, [2] R, [3] 0. */
6429 aData.resize(cbData);
6430 memcpy(&aData.front(), pu8Data, cbData);
6431 }
6432 else if (aBitmapFormat == BitmapFormat_BGRA)
6433 {
6434 /* [0] B, [1] G, [2] R, [3] A. */
6435 aData.resize(cbData);
6436 for (uint32_t i = 0; i < cbData; i += 4)
6437 {
6438 aData[i] = pu8Data[i];
6439 aData[i + 1] = pu8Data[i + 1];
6440 aData[i + 2] = pu8Data[i + 2];
6441 aData[i + 3] = 0xff;
6442 }
6443 }
6444 else if (aBitmapFormat == BitmapFormat_RGBA)
6445 {
6446 /* [0] R, [1] G, [2] B, [3] A. */
6447 aData.resize(cbData);
6448 for (uint32_t i = 0; i < cbData; i += 4)
6449 {
6450 aData[i] = pu8Data[i + 2];
6451 aData[i + 1] = pu8Data[i + 1];
6452 aData[i + 2] = pu8Data[i];
6453 aData[i + 3] = 0xff;
6454 }
6455 }
6456 else if (aBitmapFormat == BitmapFormat_PNG)
6457 {
6458 uint8_t *pu8PNG = NULL;
6459 uint32_t cbPNG = 0;
6460 uint32_t cxPNG = 0;
6461 uint32_t cyPNG = 0;
6462
6463 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6464
6465 if (RT_SUCCESS(vrc))
6466 {
6467 aData.resize(cbPNG);
6468 if (cbPNG)
6469 memcpy(&aData.front(), pu8PNG, cbPNG);
6470 }
6471 else
6472 hr = setError(VBOX_E_IPRT_ERROR,
6473 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6474 vrc);
6475
6476 RTMemFree(pu8PNG);
6477 }
6478 }
6479
6480 freeSavedDisplayScreenshot(pu8Data);
6481
6482 return hr;
6483}
6484
6485HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6486{
6487 if (aScreenId != 0)
6488 return E_NOTIMPL;
6489
6490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6491
6492 uint8_t *pu8Data = NULL;
6493 uint32_t cbData = 0;
6494 uint32_t u32Width = 0;
6495 uint32_t u32Height = 0;
6496
6497 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6498
6499 if (RT_FAILURE(vrc))
6500 return setError(VBOX_E_IPRT_ERROR,
6501 tr("Saved screenshot data is not available (%Rrc)"),
6502 vrc);
6503
6504 *aSize = cbData;
6505 *aWidth = u32Width;
6506 *aHeight = u32Height;
6507
6508 freeSavedDisplayScreenshot(pu8Data);
6509
6510 return S_OK;
6511}
6512
6513HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6514{
6515 if (aScreenId != 0)
6516 return E_NOTIMPL;
6517
6518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6519
6520 uint8_t *pu8Data = NULL;
6521 uint32_t cbData = 0;
6522 uint32_t u32Width = 0;
6523 uint32_t u32Height = 0;
6524
6525 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6526
6527 if (RT_FAILURE(vrc))
6528 return setError(VBOX_E_IPRT_ERROR,
6529 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6530 vrc);
6531
6532 *aWidth = u32Width;
6533 *aHeight = u32Height;
6534
6535 aData.resize(cbData);
6536 if (cbData)
6537 memcpy(&aData.front(), pu8Data, cbData);
6538
6539 freeSavedDisplayScreenshot(pu8Data);
6540
6541 return S_OK;
6542}
6543
6544HRESULT Machine::hotPlugCPU(ULONG aCpu)
6545{
6546 HRESULT rc = S_OK;
6547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6548
6549 if (!mHWData->mCPUHotPlugEnabled)
6550 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6551
6552 if (aCpu >= mHWData->mCPUCount)
6553 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6554
6555 if (mHWData->mCPUAttached[aCpu])
6556 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6557
6558 alock.release();
6559 rc = i_onCPUChange(aCpu, false);
6560 alock.acquire();
6561 if (FAILED(rc)) return rc;
6562
6563 i_setModified(IsModified_MachineData);
6564 mHWData.backup();
6565 mHWData->mCPUAttached[aCpu] = true;
6566
6567 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6568 if (Global::IsOnline(mData->mMachineState))
6569 i_saveSettings(NULL);
6570
6571 return S_OK;
6572}
6573
6574HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6575{
6576 HRESULT rc = S_OK;
6577
6578 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6579
6580 if (!mHWData->mCPUHotPlugEnabled)
6581 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6582
6583 if (aCpu >= SchemaDefs::MaxCPUCount)
6584 return setError(E_INVALIDARG,
6585 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6586 SchemaDefs::MaxCPUCount);
6587
6588 if (!mHWData->mCPUAttached[aCpu])
6589 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6590
6591 /* CPU 0 can't be detached */
6592 if (aCpu == 0)
6593 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6594
6595 alock.release();
6596 rc = i_onCPUChange(aCpu, true);
6597 alock.acquire();
6598 if (FAILED(rc)) return rc;
6599
6600 i_setModified(IsModified_MachineData);
6601 mHWData.backup();
6602 mHWData->mCPUAttached[aCpu] = false;
6603
6604 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6605 if (Global::IsOnline(mData->mMachineState))
6606 i_saveSettings(NULL);
6607
6608 return S_OK;
6609}
6610
6611HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6612{
6613 *aAttached = false;
6614
6615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6616
6617 /* If hotplug is enabled the CPU is always enabled. */
6618 if (!mHWData->mCPUHotPlugEnabled)
6619 {
6620 if (aCpu < mHWData->mCPUCount)
6621 *aAttached = true;
6622 }
6623 else
6624 {
6625 if (aCpu < SchemaDefs::MaxCPUCount)
6626 *aAttached = mHWData->mCPUAttached[aCpu];
6627 }
6628
6629 return S_OK;
6630}
6631
6632HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6633{
6634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6635
6636 Utf8Str log = i_queryLogFilename(aIdx);
6637 if (!RTFileExists(log.c_str()))
6638 log.setNull();
6639 aFilename = log;
6640
6641 return S_OK;
6642}
6643
6644HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6645{
6646 if (aSize < 0)
6647 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6648
6649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6650
6651 HRESULT rc = S_OK;
6652 Utf8Str log = i_queryLogFilename(aIdx);
6653
6654 /* do not unnecessarily hold the lock while doing something which does
6655 * not need the lock and potentially takes a long time. */
6656 alock.release();
6657
6658 /* Limit the chunk size to 32K for now, as that gives better performance
6659 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6660 * One byte expands to approx. 25 bytes of breathtaking XML. */
6661 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6662 aData.resize(cbData);
6663
6664 RTFILE LogFile;
6665 int vrc = RTFileOpen(&LogFile, log.c_str(),
6666 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6667 if (RT_SUCCESS(vrc))
6668 {
6669 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6670 if (RT_SUCCESS(vrc))
6671 aData.resize(cbData);
6672 else
6673 rc = setError(VBOX_E_IPRT_ERROR,
6674 tr("Could not read log file '%s' (%Rrc)"),
6675 log.c_str(), vrc);
6676 RTFileClose(LogFile);
6677 }
6678 else
6679 rc = setError(VBOX_E_IPRT_ERROR,
6680 tr("Could not open log file '%s' (%Rrc)"),
6681 log.c_str(), vrc);
6682
6683 if (FAILED(rc))
6684 aData.resize(0);
6685
6686 return rc;
6687}
6688
6689
6690/**
6691 * Currently this method doesn't attach device to the running VM,
6692 * just makes sure it's plugged on next VM start.
6693 */
6694HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6695{
6696 // lock scope
6697 {
6698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6699
6700 HRESULT rc = i_checkStateDependency(MutableStateDep);
6701 if (FAILED(rc)) return rc;
6702
6703 ChipsetType_T aChipset = ChipsetType_PIIX3;
6704 COMGETTER(ChipsetType)(&aChipset);
6705
6706 if (aChipset != ChipsetType_ICH9)
6707 {
6708 return setError(E_INVALIDARG,
6709 tr("Host PCI attachment only supported with ICH9 chipset"));
6710 }
6711
6712 // check if device with this host PCI address already attached
6713 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6714 it != mHWData->mPCIDeviceAssignments.end();
6715 ++it)
6716 {
6717 LONG iHostAddress = -1;
6718 ComPtr<PCIDeviceAttachment> pAttach;
6719 pAttach = *it;
6720 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6721 if (iHostAddress == aHostAddress)
6722 return setError(E_INVALIDARG,
6723 tr("Device with host PCI address already attached to this VM"));
6724 }
6725
6726 ComObjPtr<PCIDeviceAttachment> pda;
6727 char name[32];
6728
6729 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6730 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6731 Bstr bname(name);
6732 pda.createObject();
6733 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6734 i_setModified(IsModified_MachineData);
6735 mHWData.backup();
6736 mHWData->mPCIDeviceAssignments.push_back(pda);
6737 }
6738
6739 return S_OK;
6740}
6741
6742/**
6743 * Currently this method doesn't detach device from the running VM,
6744 * just makes sure it's not plugged on next VM start.
6745 */
6746HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6747{
6748 ComObjPtr<PCIDeviceAttachment> pAttach;
6749 bool fRemoved = false;
6750 HRESULT rc;
6751
6752 // lock scope
6753 {
6754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6755
6756 rc = i_checkStateDependency(MutableStateDep);
6757 if (FAILED(rc)) return rc;
6758
6759 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6760 it != mHWData->mPCIDeviceAssignments.end();
6761 ++it)
6762 {
6763 LONG iHostAddress = -1;
6764 pAttach = *it;
6765 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6766 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6767 {
6768 i_setModified(IsModified_MachineData);
6769 mHWData.backup();
6770 mHWData->mPCIDeviceAssignments.remove(pAttach);
6771 fRemoved = true;
6772 break;
6773 }
6774 }
6775 }
6776
6777
6778 /* Fire event outside of the lock */
6779 if (fRemoved)
6780 {
6781 Assert(!pAttach.isNull());
6782 ComPtr<IEventSource> es;
6783 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6784 Assert(SUCCEEDED(rc));
6785 Bstr mid;
6786 rc = this->COMGETTER(Id)(mid.asOutParam());
6787 Assert(SUCCEEDED(rc));
6788 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6789 }
6790
6791 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6792 tr("No host PCI device %08x attached"),
6793 aHostAddress
6794 );
6795}
6796
6797HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6798{
6799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6800
6801 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6802
6803 size_t i = 0;
6804 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6805 it != mHWData->mPCIDeviceAssignments.end();
6806 ++i, ++it)
6807 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6808
6809 return S_OK;
6810}
6811
6812HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6813{
6814 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6815
6816 return S_OK;
6817}
6818
6819HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6820{
6821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6822
6823 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6824
6825 return S_OK;
6826}
6827
6828HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6829{
6830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6831 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6832 if (SUCCEEDED(hrc))
6833 {
6834 hrc = mHWData.backupEx();
6835 if (SUCCEEDED(hrc))
6836 {
6837 i_setModified(IsModified_MachineData);
6838 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6839 }
6840 }
6841 return hrc;
6842}
6843
6844HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6845{
6846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6847 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6848 return S_OK;
6849}
6850
6851HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6852{
6853 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6854 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6855 if (SUCCEEDED(hrc))
6856 {
6857 hrc = mHWData.backupEx();
6858 if (SUCCEEDED(hrc))
6859 {
6860 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6861 if (SUCCEEDED(hrc))
6862 i_setModified(IsModified_MachineData);
6863 }
6864 }
6865 return hrc;
6866}
6867
6868HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6869{
6870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6871
6872 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6873
6874 return S_OK;
6875}
6876
6877HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6878{
6879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6880 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6881 if (SUCCEEDED(hrc))
6882 {
6883 hrc = mHWData.backupEx();
6884 if (SUCCEEDED(hrc))
6885 {
6886 i_setModified(IsModified_MachineData);
6887 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6888 }
6889 }
6890 return hrc;
6891}
6892
6893HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6894{
6895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6896
6897 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6898
6899 return S_OK;
6900}
6901
6902HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6903{
6904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6905
6906 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6907 if ( SUCCEEDED(hrc)
6908 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6909 {
6910 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6911 int vrc;
6912
6913 if (aAutostartEnabled)
6914 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6915 else
6916 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6917
6918 if (RT_SUCCESS(vrc))
6919 {
6920 hrc = mHWData.backupEx();
6921 if (SUCCEEDED(hrc))
6922 {
6923 i_setModified(IsModified_MachineData);
6924 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6925 }
6926 }
6927 else if (vrc == VERR_NOT_SUPPORTED)
6928 hrc = setError(VBOX_E_NOT_SUPPORTED,
6929 tr("The VM autostart feature is not supported on this platform"));
6930 else if (vrc == VERR_PATH_NOT_FOUND)
6931 hrc = setError(E_FAIL,
6932 tr("The path to the autostart database is not set"));
6933 else
6934 hrc = setError(E_UNEXPECTED,
6935 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6936 aAutostartEnabled ? "Adding" : "Removing",
6937 mUserData->s.strName.c_str(), vrc);
6938 }
6939 return hrc;
6940}
6941
6942HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6943{
6944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6945
6946 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6947
6948 return S_OK;
6949}
6950
6951HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6952{
6953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6954 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6955 if (SUCCEEDED(hrc))
6956 {
6957 hrc = mHWData.backupEx();
6958 if (SUCCEEDED(hrc))
6959 {
6960 i_setModified(IsModified_MachineData);
6961 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6962 }
6963 }
6964 return hrc;
6965}
6966
6967HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6968{
6969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6970
6971 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6972
6973 return S_OK;
6974}
6975
6976HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6977{
6978 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6979 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6980 if ( SUCCEEDED(hrc)
6981 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6982 {
6983 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6984 int vrc;
6985
6986 if (aAutostopType != AutostopType_Disabled)
6987 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6988 else
6989 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6990
6991 if (RT_SUCCESS(vrc))
6992 {
6993 hrc = mHWData.backupEx();
6994 if (SUCCEEDED(hrc))
6995 {
6996 i_setModified(IsModified_MachineData);
6997 mHWData->mAutostart.enmAutostopType = aAutostopType;
6998 }
6999 }
7000 else if (vrc == VERR_NOT_SUPPORTED)
7001 hrc = setError(VBOX_E_NOT_SUPPORTED,
7002 tr("The VM autostop feature is not supported on this platform"));
7003 else if (vrc == VERR_PATH_NOT_FOUND)
7004 hrc = setError(E_FAIL,
7005 tr("The path to the autostart database is not set"));
7006 else
7007 hrc = setError(E_UNEXPECTED,
7008 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7009 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7010 mUserData->s.strName.c_str(), vrc);
7011 }
7012 return hrc;
7013}
7014
7015HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7016{
7017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7018
7019 aDefaultFrontend = mHWData->mDefaultFrontend;
7020
7021 return S_OK;
7022}
7023
7024HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7025{
7026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7027 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7028 if (SUCCEEDED(hrc))
7029 {
7030 hrc = mHWData.backupEx();
7031 if (SUCCEEDED(hrc))
7032 {
7033 i_setModified(IsModified_MachineData);
7034 mHWData->mDefaultFrontend = aDefaultFrontend;
7035 }
7036 }
7037 return hrc;
7038}
7039
7040HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7041{
7042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7043 size_t cbIcon = mUserData->mIcon.size();
7044 aIcon.resize(cbIcon);
7045 if (cbIcon)
7046 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7047 return S_OK;
7048}
7049
7050HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7051{
7052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7053 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7054 if (SUCCEEDED(hrc))
7055 {
7056 i_setModified(IsModified_MachineData);
7057 mUserData.backup();
7058 size_t cbIcon = aIcon.size();
7059 mUserData->mIcon.resize(cbIcon);
7060 if (cbIcon)
7061 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7062 }
7063 return hrc;
7064}
7065
7066HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7067{
7068#ifdef VBOX_WITH_USB
7069 *aUSBProxyAvailable = true;
7070#else
7071 *aUSBProxyAvailable = false;
7072#endif
7073 return S_OK;
7074}
7075
7076HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7077 ComPtr<IProgress> &aProgress)
7078{
7079 ComObjPtr<Progress> pP;
7080 Progress *ppP = pP;
7081 IProgress *iP = static_cast<IProgress *>(ppP);
7082 IProgress **pProgress = &iP;
7083
7084 IMachine *pTarget = aTarget;
7085
7086 /* Convert the options. */
7087 RTCList<CloneOptions_T> optList;
7088 if (aOptions.size())
7089 for (size_t i = 0; i < aOptions.size(); ++i)
7090 optList.append(aOptions[i]);
7091
7092 if (optList.contains(CloneOptions_Link))
7093 {
7094 if (!i_isSnapshotMachine())
7095 return setError(E_INVALIDARG,
7096 tr("Linked clone can only be created from a snapshot"));
7097 if (aMode != CloneMode_MachineState)
7098 return setError(E_INVALIDARG,
7099 tr("Linked clone can only be created for a single machine state"));
7100 }
7101 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7102
7103 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7104
7105 HRESULT rc = pWorker->start(pProgress);
7106
7107 pP = static_cast<Progress *>(*pProgress);
7108 pP.queryInterfaceTo(aProgress.asOutParam());
7109
7110 return rc;
7111
7112}
7113
7114HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7115{
7116 NOREF(aProgress);
7117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7118
7119 // This check should always fail.
7120 HRESULT rc = i_checkStateDependency(MutableStateDep);
7121 if (FAILED(rc)) return rc;
7122
7123 AssertFailedReturn(E_NOTIMPL);
7124}
7125
7126HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7127{
7128 NOREF(aSavedStateFile);
7129 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7130
7131 // This check should always fail.
7132 HRESULT rc = i_checkStateDependency(MutableStateDep);
7133 if (FAILED(rc)) return rc;
7134
7135 AssertFailedReturn(E_NOTIMPL);
7136}
7137
7138HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7139{
7140 NOREF(aFRemoveFile);
7141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7142
7143 // This check should always fail.
7144 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7145 if (FAILED(rc)) return rc;
7146
7147 AssertFailedReturn(E_NOTIMPL);
7148}
7149
7150// public methods for internal purposes
7151/////////////////////////////////////////////////////////////////////////////
7152
7153/**
7154 * Adds the given IsModified_* flag to the dirty flags of the machine.
7155 * This must be called either during i_loadSettings or under the machine write lock.
7156 * @param fl
7157 */
7158void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7159{
7160 mData->flModifications |= fl;
7161 if (fAllowStateModification && i_isStateModificationAllowed())
7162 mData->mCurrentStateModified = true;
7163}
7164
7165/**
7166 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7167 * care of the write locking.
7168 *
7169 * @param fModifications The flag to add.
7170 */
7171void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7172{
7173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7174 i_setModified(fModification, fAllowStateModification);
7175}
7176
7177/**
7178 * Saves the registry entry of this machine to the given configuration node.
7179 *
7180 * @param aEntryNode Node to save the registry entry to.
7181 *
7182 * @note locks this object for reading.
7183 */
7184HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7185{
7186 AutoLimitedCaller autoCaller(this);
7187 AssertComRCReturnRC(autoCaller.rc());
7188
7189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7190
7191 data.uuid = mData->mUuid;
7192 data.strSettingsFile = mData->m_strConfigFile;
7193
7194 return S_OK;
7195}
7196
7197/**
7198 * Calculates the absolute path of the given path taking the directory of the
7199 * machine settings file as the current directory.
7200 *
7201 * @param aPath Path to calculate the absolute path for.
7202 * @param aResult Where to put the result (used only on success, can be the
7203 * same Utf8Str instance as passed in @a aPath).
7204 * @return IPRT result.
7205 *
7206 * @note Locks this object for reading.
7207 */
7208int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7209{
7210 AutoCaller autoCaller(this);
7211 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7212
7213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7214
7215 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7216
7217 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7218
7219 strSettingsDir.stripFilename();
7220 char folder[RTPATH_MAX];
7221 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7222 if (RT_SUCCESS(vrc))
7223 aResult = folder;
7224
7225 return vrc;
7226}
7227
7228/**
7229 * Copies strSource to strTarget, making it relative to the machine folder
7230 * if it is a subdirectory thereof, or simply copying it otherwise.
7231 *
7232 * @param strSource Path to evaluate and copy.
7233 * @param strTarget Buffer to receive target path.
7234 *
7235 * @note Locks this object for reading.
7236 */
7237void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7238 Utf8Str &strTarget)
7239{
7240 AutoCaller autoCaller(this);
7241 AssertComRCReturn(autoCaller.rc(), (void)0);
7242
7243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7244
7245 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7246 // use strTarget as a temporary buffer to hold the machine settings dir
7247 strTarget = mData->m_strConfigFileFull;
7248 strTarget.stripFilename();
7249 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7250 {
7251 // is relative: then append what's left
7252 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7253 // for empty paths (only possible for subdirs) use "." to avoid
7254 // triggering default settings for not present config attributes.
7255 if (strTarget.isEmpty())
7256 strTarget = ".";
7257 }
7258 else
7259 // is not relative: then overwrite
7260 strTarget = strSource;
7261}
7262
7263/**
7264 * Returns the full path to the machine's log folder in the
7265 * \a aLogFolder argument.
7266 */
7267void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7268{
7269 AutoCaller autoCaller(this);
7270 AssertComRCReturnVoid(autoCaller.rc());
7271
7272 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7273
7274 char szTmp[RTPATH_MAX];
7275 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7276 if (RT_SUCCESS(vrc))
7277 {
7278 if (szTmp[0] && !mUserData.isNull())
7279 {
7280 char szTmp2[RTPATH_MAX];
7281 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7282 if (RT_SUCCESS(vrc))
7283 aLogFolder = BstrFmt("%s%c%s",
7284 szTmp2,
7285 RTPATH_DELIMITER,
7286 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7287 }
7288 else
7289 vrc = VERR_PATH_IS_RELATIVE;
7290 }
7291
7292 if (RT_FAILURE(vrc))
7293 {
7294 // fallback if VBOX_USER_LOGHOME is not set or invalid
7295 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7296 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7297 aLogFolder.append(RTPATH_DELIMITER);
7298 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7299 }
7300}
7301
7302/**
7303 * Returns the full path to the machine's log file for an given index.
7304 */
7305Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7306 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7307{
7308 Utf8Str logFolder;
7309 getLogFolder(logFolder);
7310 Assert(logFolder.length());
7311 Utf8Str log;
7312 if (idx == 0)
7313 log = Utf8StrFmt("%s%cVBox.log",
7314 logFolder.c_str(), RTPATH_DELIMITER);
7315 else
7316 log = Utf8StrFmt("%s%cVBox.log.%d",
7317 logFolder.c_str(), RTPATH_DELIMITER, idx);
7318 return log;
7319}
7320
7321/**
7322 * Returns the full path to the machine's (hardened) startup log file.
7323 */
7324Utf8Str Machine::i_getStartupLogFilename(void)
7325{
7326 Utf8Str strFilename;
7327 getLogFolder(strFilename);
7328 Assert(strFilename.length());
7329 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7330 return strFilename;
7331}
7332
7333
7334/**
7335 * Composes a unique saved state filename based on the current system time. The filename is
7336 * granular to the second so this will work so long as no more than one snapshot is taken on
7337 * a machine per second.
7338 *
7339 * Before version 4.1, we used this formula for saved state files:
7340 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7341 * which no longer works because saved state files can now be shared between the saved state of the
7342 * "saved" machine and an online snapshot, and the following would cause problems:
7343 * 1) save machine
7344 * 2) create online snapshot from that machine state --> reusing saved state file
7345 * 3) save machine again --> filename would be reused, breaking the online snapshot
7346 *
7347 * So instead we now use a timestamp.
7348 *
7349 * @param str
7350 */
7351
7352void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7353{
7354 AutoCaller autoCaller(this);
7355 AssertComRCReturnVoid(autoCaller.rc());
7356
7357 {
7358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7359 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7360 }
7361
7362 RTTIMESPEC ts;
7363 RTTimeNow(&ts);
7364 RTTIME time;
7365 RTTimeExplode(&time, &ts);
7366
7367 strStateFilePath += RTPATH_DELIMITER;
7368 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7369 time.i32Year, time.u8Month, time.u8MonthDay,
7370 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7371}
7372
7373/**
7374 * Returns the full path to the default video capture file.
7375 */
7376void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7377{
7378 AutoCaller autoCaller(this);
7379 AssertComRCReturnVoid(autoCaller.rc());
7380
7381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7382
7383 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7384 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7385 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7386}
7387
7388/**
7389 * Returns whether at least one USB controller is present for the VM.
7390 */
7391bool Machine::i_isUSBControllerPresent()
7392{
7393 AutoCaller autoCaller(this);
7394 AssertComRCReturn(autoCaller.rc(), false);
7395
7396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7397
7398 return (mUSBControllers->size() > 0);
7399}
7400
7401/**
7402 * @note Locks this object for writing, calls the client process
7403 * (inside the lock).
7404 */
7405HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7406 const Utf8Str &strFrontend,
7407 const Utf8Str &strEnvironment,
7408 ProgressProxy *aProgress)
7409{
7410 LogFlowThisFuncEnter();
7411
7412 AssertReturn(aControl, E_FAIL);
7413 AssertReturn(aProgress, E_FAIL);
7414 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7415
7416 AutoCaller autoCaller(this);
7417 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7418
7419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7420
7421 if (!mData->mRegistered)
7422 return setError(E_UNEXPECTED,
7423 tr("The machine '%s' is not registered"),
7424 mUserData->s.strName.c_str());
7425
7426 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7427
7428 /* The process started when launching a VM with separate UI/VM processes is always
7429 * the UI process, i.e. needs special handling as it won't claim the session. */
7430 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7431
7432 if (fSeparate)
7433 {
7434 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mType.compare("headless", Utf8Str::CaseInsensitive))
7435 return setError(VBOX_E_INVALID_OBJECT_STATE,
7436 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7437 mUserData->s.strName.c_str());
7438 }
7439 else
7440 {
7441 if ( mData->mSession.mState == SessionState_Locked
7442 || mData->mSession.mState == SessionState_Spawning
7443 || mData->mSession.mState == SessionState_Unlocking)
7444 return setError(VBOX_E_INVALID_OBJECT_STATE,
7445 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7446 mUserData->s.strName.c_str());
7447
7448 /* may not be busy */
7449 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7450 }
7451
7452 /* get the path to the executable */
7453 char szPath[RTPATH_MAX];
7454 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7455 size_t cchBufLeft = strlen(szPath);
7456 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7457 szPath[cchBufLeft] = 0;
7458 char *pszNamePart = szPath + cchBufLeft;
7459 cchBufLeft = sizeof(szPath) - cchBufLeft;
7460
7461 int vrc = VINF_SUCCESS;
7462 RTPROCESS pid = NIL_RTPROCESS;
7463
7464 RTENV env = RTENV_DEFAULT;
7465
7466 if (!strEnvironment.isEmpty())
7467 {
7468 char *newEnvStr = NULL;
7469
7470 do
7471 {
7472 /* clone the current environment */
7473 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7474 AssertRCBreakStmt(vrc2, vrc = vrc2);
7475
7476 newEnvStr = RTStrDup(strEnvironment.c_str());
7477 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7478
7479 /* put new variables to the environment
7480 * (ignore empty variable names here since RTEnv API
7481 * intentionally doesn't do that) */
7482 char *var = newEnvStr;
7483 for (char *p = newEnvStr; *p; ++p)
7484 {
7485 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7486 {
7487 *p = '\0';
7488 if (*var)
7489 {
7490 char *val = strchr(var, '=');
7491 if (val)
7492 {
7493 *val++ = '\0';
7494 vrc2 = RTEnvSetEx(env, var, val);
7495 }
7496 else
7497 vrc2 = RTEnvUnsetEx(env, var);
7498 if (RT_FAILURE(vrc2))
7499 break;
7500 }
7501 var = p + 1;
7502 }
7503 }
7504 if (RT_SUCCESS(vrc2) && *var)
7505 vrc2 = RTEnvPutEx(env, var);
7506
7507 AssertRCBreakStmt(vrc2, vrc = vrc2);
7508 }
7509 while (0);
7510
7511 if (newEnvStr != NULL)
7512 RTStrFree(newEnvStr);
7513 }
7514
7515 /* Hardened startup logging */
7516#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7517 Utf8Str strSupStartLogArg("--sup-startup-log=");
7518 {
7519 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7520 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7521 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7522 {
7523 Utf8Str strStartupLogDir = strStartupLogFile;
7524 strStartupLogDir.stripFilename();
7525 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7526 file without stripping the file. */
7527 }
7528 strSupStartLogArg.append(strStartupLogFile);
7529 }
7530 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7531#else
7532 const char *pszSupStartupLogArg = NULL;
7533#endif
7534
7535
7536#ifdef VBOX_WITH_QTGUI
7537 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7538 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7539 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7540 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7541 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7542 {
7543# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7544 /* Modify the base path so that we don't need to use ".." below. */
7545 RTPathStripTrailingSlash(szPath);
7546 RTPathStripFilename(szPath);
7547 cchBufLeft = strlen(szPath);
7548 pszNamePart = szPath + cchBufLeft;
7549 cchBufLeft = sizeof(szPath) - cchBufLeft;
7550
7551# define OSX_APP_NAME "VirtualBoxVM"
7552# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7553
7554 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7555 if ( strAppOverride.contains(".")
7556 || strAppOverride.contains("/")
7557 || strAppOverride.contains("\\")
7558 || strAppOverride.contains(":"))
7559 strAppOverride.setNull();
7560 Utf8Str strAppPath;
7561 if (!strAppOverride.isEmpty())
7562 {
7563 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7564 Utf8Str strFullPath(szPath);
7565 strFullPath.append(strAppPath);
7566 /* there is a race, but people using this deserve the failure */
7567 if (!RTFileExists(strFullPath.c_str()))
7568 strAppOverride.setNull();
7569 }
7570 if (strAppOverride.isEmpty())
7571 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7572 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7573 strcpy(pszNamePart, strAppPath.c_str());
7574# else
7575 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7576 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7577 strcpy(pszNamePart, s_szVirtualBox_exe);
7578# endif
7579
7580 Utf8Str idStr = mData->mUuid.toString();
7581 const char *apszArgs[] =
7582 {
7583 szPath,
7584 "--comment", mUserData->s.strName.c_str(),
7585 "--startvm", idStr.c_str(),
7586 "--no-startvm-errormsgbox",
7587 NULL, /* For "--separate". */
7588 NULL, /* For "--sup-startup-log". */
7589 NULL
7590 };
7591 unsigned iArg = 6;
7592 if (fSeparate)
7593 apszArgs[iArg++] = "--separate";
7594 apszArgs[iArg++] = pszSupStartupLogArg;
7595
7596 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7597 }
7598#else /* !VBOX_WITH_QTGUI */
7599 if (0)
7600 ;
7601#endif /* VBOX_WITH_QTGUI */
7602
7603 else
7604
7605#ifdef VBOX_WITH_VBOXSDL
7606 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7607 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7608 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7609 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7610 {
7611 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7612 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7613 strcpy(pszNamePart, s_szVBoxSDL_exe);
7614
7615 Utf8Str idStr = mData->mUuid.toString();
7616 const char *apszArgs[] =
7617 {
7618 szPath,
7619 "--comment", mUserData->s.strName.c_str(),
7620 "--startvm", idStr.c_str(),
7621 NULL, /* For "--separate". */
7622 NULL, /* For "--sup-startup-log". */
7623 NULL
7624 };
7625 unsigned iArg = 5;
7626 if (fSeparate)
7627 apszArgs[iArg++] = "--separate";
7628 apszArgs[iArg++] = pszSupStartupLogArg;
7629
7630 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7631 }
7632#else /* !VBOX_WITH_VBOXSDL */
7633 if (0)
7634 ;
7635#endif /* !VBOX_WITH_VBOXSDL */
7636
7637 else
7638
7639#ifdef VBOX_WITH_HEADLESS
7640 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7641 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7642 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7643 )
7644 {
7645 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7646 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7647 * and a VM works even if the server has not been installed.
7648 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7649 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7650 * differently in 4.0 and 3.x.
7651 */
7652 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7653 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7654 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7655
7656 Utf8Str idStr = mData->mUuid.toString();
7657 const char *apszArgs[] =
7658 {
7659 szPath,
7660 "--comment", mUserData->s.strName.c_str(),
7661 "--startvm", idStr.c_str(),
7662 "--vrde", "config",
7663 NULL, /* For "--capture". */
7664 NULL, /* For "--sup-startup-log". */
7665 NULL
7666 };
7667 unsigned iArg = 7;
7668 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7669 apszArgs[iArg++] = "--capture";
7670 apszArgs[iArg++] = pszSupStartupLogArg;
7671
7672# ifdef RT_OS_WINDOWS
7673 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7674# else
7675 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7676# endif
7677 }
7678#else /* !VBOX_WITH_HEADLESS */
7679 if (0)
7680 ;
7681#endif /* !VBOX_WITH_HEADLESS */
7682 else
7683 {
7684 RTEnvDestroy(env);
7685 return setError(E_INVALIDARG,
7686 tr("Invalid frontend name: '%s'"),
7687 strFrontend.c_str());
7688 }
7689
7690 RTEnvDestroy(env);
7691
7692 if (RT_FAILURE(vrc))
7693 return setError(VBOX_E_IPRT_ERROR,
7694 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7695 mUserData->s.strName.c_str(), vrc);
7696
7697 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7698
7699 if (!fSeparate)
7700 {
7701 /*
7702 * Note that we don't release the lock here before calling the client,
7703 * because it doesn't need to call us back if called with a NULL argument.
7704 * Releasing the lock here is dangerous because we didn't prepare the
7705 * launch data yet, but the client we've just started may happen to be
7706 * too fast and call LockMachine() that will fail (because of PID, etc.),
7707 * so that the Machine will never get out of the Spawning session state.
7708 */
7709
7710 /* inform the session that it will be a remote one */
7711 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7712#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7713 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7714#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7715 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7716#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7717 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7718
7719 if (FAILED(rc))
7720 {
7721 /* restore the session state */
7722 mData->mSession.mState = SessionState_Unlocked;
7723 alock.release();
7724 mParent->i_addProcessToReap(pid);
7725 /* The failure may occur w/o any error info (from RPC), so provide one */
7726 return setError(VBOX_E_VM_ERROR,
7727 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7728 }
7729
7730 /* attach launch data to the machine */
7731 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7732 mData->mSession.mRemoteControls.push_back(aControl);
7733 mData->mSession.mProgress = aProgress;
7734 mData->mSession.mPID = pid;
7735 mData->mSession.mState = SessionState_Spawning;
7736 mData->mSession.mType = strFrontend;
7737 }
7738 else
7739 {
7740 /* For separate UI process we declare the launch as completed instantly, as the
7741 * actual headless VM start may or may not come. No point in remembering anything
7742 * yet, as what matters for us is when the headless VM gets started. */
7743 aProgress->i_notifyComplete(S_OK);
7744 }
7745
7746 alock.release();
7747 mParent->i_addProcessToReap(pid);
7748
7749 LogFlowThisFuncLeave();
7750 return S_OK;
7751}
7752
7753/**
7754 * Returns @c true if the given session machine instance has an open direct
7755 * session (and optionally also for direct sessions which are closing) and
7756 * returns the session control machine instance if so.
7757 *
7758 * Note that when the method returns @c false, the arguments remain unchanged.
7759 *
7760 * @param aMachine Session machine object.
7761 * @param aControl Direct session control object (optional).
7762 * @param aAllowClosing If true then additionally a session which is currently
7763 * being closed will also be allowed.
7764 *
7765 * @note locks this object for reading.
7766 */
7767bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7768 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7769 bool aAllowClosing /*= false*/)
7770{
7771 AutoLimitedCaller autoCaller(this);
7772 AssertComRCReturn(autoCaller.rc(), false);
7773
7774 /* just return false for inaccessible machines */
7775 if (getObjectState().getState() != ObjectState::Ready)
7776 return false;
7777
7778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7779
7780 if ( ( mData->mSession.mState == SessionState_Locked
7781 && mData->mSession.mLockType == LockType_VM)
7782 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7783 )
7784 {
7785 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7786
7787 aMachine = mData->mSession.mMachine;
7788
7789 if (aControl != NULL)
7790 *aControl = mData->mSession.mDirectControl;
7791
7792 return true;
7793 }
7794
7795 return false;
7796}
7797
7798/**
7799 * Returns @c true if the given machine has an spawning direct session.
7800 *
7801 * @note locks this object for reading.
7802 */
7803bool Machine::i_isSessionSpawning()
7804{
7805 AutoLimitedCaller autoCaller(this);
7806 AssertComRCReturn(autoCaller.rc(), false);
7807
7808 /* just return false for inaccessible machines */
7809 if (getObjectState().getState() != ObjectState::Ready)
7810 return false;
7811
7812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7813
7814 if (mData->mSession.mState == SessionState_Spawning)
7815 return true;
7816
7817 return false;
7818}
7819
7820/**
7821 * Called from the client watcher thread to check for unexpected client process
7822 * death during Session_Spawning state (e.g. before it successfully opened a
7823 * direct session).
7824 *
7825 * On Win32 and on OS/2, this method is called only when we've got the
7826 * direct client's process termination notification, so it always returns @c
7827 * true.
7828 *
7829 * On other platforms, this method returns @c true if the client process is
7830 * terminated and @c false if it's still alive.
7831 *
7832 * @note Locks this object for writing.
7833 */
7834bool Machine::i_checkForSpawnFailure()
7835{
7836 AutoCaller autoCaller(this);
7837 if (!autoCaller.isOk())
7838 {
7839 /* nothing to do */
7840 LogFlowThisFunc(("Already uninitialized!\n"));
7841 return true;
7842 }
7843
7844 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7845
7846 if (mData->mSession.mState != SessionState_Spawning)
7847 {
7848 /* nothing to do */
7849 LogFlowThisFunc(("Not spawning any more!\n"));
7850 return true;
7851 }
7852
7853 HRESULT rc = S_OK;
7854
7855 /* PID not yet initialized, skip check. */
7856 if (mData->mSession.mPID == NIL_RTPROCESS)
7857 return false;
7858
7859 RTPROCSTATUS status;
7860 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7861
7862 if (vrc != VERR_PROCESS_RUNNING)
7863 {
7864 Utf8Str strExtraInfo;
7865
7866#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7867 /* If the startup logfile exists and is of non-zero length, tell the
7868 user to look there for more details to encourage them to attach it
7869 when reporting startup issues. */
7870 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7871 uint64_t cbStartupLogFile = 0;
7872 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7873 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7874 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7875#endif
7876
7877 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7878 rc = setError(E_FAIL,
7879 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7880 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7881 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7882 rc = setError(E_FAIL,
7883 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7884 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7885 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7886 rc = setError(E_FAIL,
7887 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7888 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7889 else
7890 rc = setError(E_FAIL,
7891 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7892 i_getName().c_str(), vrc, strExtraInfo.c_str());
7893 }
7894
7895 if (FAILED(rc))
7896 {
7897 /* Close the remote session, remove the remote control from the list
7898 * and reset session state to Closed (@note keep the code in sync with
7899 * the relevant part in LockMachine()). */
7900
7901 Assert(mData->mSession.mRemoteControls.size() == 1);
7902 if (mData->mSession.mRemoteControls.size() == 1)
7903 {
7904 ErrorInfoKeeper eik;
7905 mData->mSession.mRemoteControls.front()->Uninitialize();
7906 }
7907
7908 mData->mSession.mRemoteControls.clear();
7909 mData->mSession.mState = SessionState_Unlocked;
7910
7911 /* finalize the progress after setting the state */
7912 if (!mData->mSession.mProgress.isNull())
7913 {
7914 mData->mSession.mProgress->notifyComplete(rc);
7915 mData->mSession.mProgress.setNull();
7916 }
7917
7918 mData->mSession.mPID = NIL_RTPROCESS;
7919
7920 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7921 return true;
7922 }
7923
7924 return false;
7925}
7926
7927/**
7928 * Checks whether the machine can be registered. If so, commits and saves
7929 * all settings.
7930 *
7931 * @note Must be called from mParent's write lock. Locks this object and
7932 * children for writing.
7933 */
7934HRESULT Machine::i_prepareRegister()
7935{
7936 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7937
7938 AutoLimitedCaller autoCaller(this);
7939 AssertComRCReturnRC(autoCaller.rc());
7940
7941 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7942
7943 /* wait for state dependents to drop to zero */
7944 i_ensureNoStateDependencies();
7945
7946 if (!mData->mAccessible)
7947 return setError(VBOX_E_INVALID_OBJECT_STATE,
7948 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7949 mUserData->s.strName.c_str(),
7950 mData->mUuid.toString().c_str());
7951
7952 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7953
7954 if (mData->mRegistered)
7955 return setError(VBOX_E_INVALID_OBJECT_STATE,
7956 tr("The machine '%s' with UUID {%s} is already registered"),
7957 mUserData->s.strName.c_str(),
7958 mData->mUuid.toString().c_str());
7959
7960 HRESULT rc = S_OK;
7961
7962 // Ensure the settings are saved. If we are going to be registered and
7963 // no config file exists yet, create it by calling i_saveSettings() too.
7964 if ( (mData->flModifications)
7965 || (!mData->pMachineConfigFile->fileExists())
7966 )
7967 {
7968 rc = i_saveSettings(NULL);
7969 // no need to check whether VirtualBox.xml needs saving too since
7970 // we can't have a machine XML file rename pending
7971 if (FAILED(rc)) return rc;
7972 }
7973
7974 /* more config checking goes here */
7975
7976 if (SUCCEEDED(rc))
7977 {
7978 /* we may have had implicit modifications we want to fix on success */
7979 i_commit();
7980
7981 mData->mRegistered = true;
7982 }
7983 else
7984 {
7985 /* we may have had implicit modifications we want to cancel on failure*/
7986 i_rollback(false /* aNotify */);
7987 }
7988
7989 return rc;
7990}
7991
7992/**
7993 * Increases the number of objects dependent on the machine state or on the
7994 * registered state. Guarantees that these two states will not change at least
7995 * until #releaseStateDependency() is called.
7996 *
7997 * Depending on the @a aDepType value, additional state checks may be made.
7998 * These checks will set extended error info on failure. See
7999 * #checkStateDependency() for more info.
8000 *
8001 * If this method returns a failure, the dependency is not added and the caller
8002 * is not allowed to rely on any particular machine state or registration state
8003 * value and may return the failed result code to the upper level.
8004 *
8005 * @param aDepType Dependency type to add.
8006 * @param aState Current machine state (NULL if not interested).
8007 * @param aRegistered Current registered state (NULL if not interested).
8008 *
8009 * @note Locks this object for writing.
8010 */
8011HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8012 MachineState_T *aState /* = NULL */,
8013 BOOL *aRegistered /* = NULL */)
8014{
8015 AutoCaller autoCaller(this);
8016 AssertComRCReturnRC(autoCaller.rc());
8017
8018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8019
8020 HRESULT rc = i_checkStateDependency(aDepType);
8021 if (FAILED(rc)) return rc;
8022
8023 {
8024 if (mData->mMachineStateChangePending != 0)
8025 {
8026 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8027 * drop to zero so don't add more. It may make sense to wait a bit
8028 * and retry before reporting an error (since the pending state
8029 * transition should be really quick) but let's just assert for
8030 * now to see if it ever happens on practice. */
8031
8032 AssertFailed();
8033
8034 return setError(E_ACCESSDENIED,
8035 tr("Machine state change is in progress. Please retry the operation later."));
8036 }
8037
8038 ++mData->mMachineStateDeps;
8039 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8040 }
8041
8042 if (aState)
8043 *aState = mData->mMachineState;
8044 if (aRegistered)
8045 *aRegistered = mData->mRegistered;
8046
8047 return S_OK;
8048}
8049
8050/**
8051 * Decreases the number of objects dependent on the machine state.
8052 * Must always complete the #addStateDependency() call after the state
8053 * dependency is no more necessary.
8054 */
8055void Machine::i_releaseStateDependency()
8056{
8057 AutoCaller autoCaller(this);
8058 AssertComRCReturnVoid(autoCaller.rc());
8059
8060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8061
8062 /* releaseStateDependency() w/o addStateDependency()? */
8063 AssertReturnVoid(mData->mMachineStateDeps != 0);
8064 -- mData->mMachineStateDeps;
8065
8066 if (mData->mMachineStateDeps == 0)
8067 {
8068 /* inform i_ensureNoStateDependencies() that there are no more deps */
8069 if (mData->mMachineStateChangePending != 0)
8070 {
8071 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8072 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8073 }
8074 }
8075}
8076
8077Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8078{
8079 /* start with nothing found */
8080 Utf8Str strResult("");
8081
8082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8083
8084 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8085 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8086 // found:
8087 strResult = it->second; // source is a Utf8Str
8088
8089 return strResult;
8090}
8091
8092// protected methods
8093/////////////////////////////////////////////////////////////////////////////
8094
8095/**
8096 * Performs machine state checks based on the @a aDepType value. If a check
8097 * fails, this method will set extended error info, otherwise it will return
8098 * S_OK. It is supposed, that on failure, the caller will immediately return
8099 * the return value of this method to the upper level.
8100 *
8101 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8102 *
8103 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8104 * current state of this machine object allows to change settings of the
8105 * machine (i.e. the machine is not registered, or registered but not running
8106 * and not saved). It is useful to call this method from Machine setters
8107 * before performing any change.
8108 *
8109 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8110 * as for MutableStateDep except that if the machine is saved, S_OK is also
8111 * returned. This is useful in setters which allow changing machine
8112 * properties when it is in the saved state.
8113 *
8114 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8115 * if the current state of this machine object allows to change runtime
8116 * changeable settings of the machine (i.e. the machine is not registered, or
8117 * registered but either running or not running and not saved). It is useful
8118 * to call this method from Machine setters before performing any changes to
8119 * runtime changeable settings.
8120 *
8121 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8122 * the same as for MutableOrRunningStateDep except that if the machine is
8123 * saved, S_OK is also returned. This is useful in setters which allow
8124 * changing runtime and saved state changeable machine properties.
8125 *
8126 * @param aDepType Dependency type to check.
8127 *
8128 * @note Non Machine based classes should use #addStateDependency() and
8129 * #releaseStateDependency() methods or the smart AutoStateDependency
8130 * template.
8131 *
8132 * @note This method must be called from under this object's read or write
8133 * lock.
8134 */
8135HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8136{
8137 switch (aDepType)
8138 {
8139 case AnyStateDep:
8140 {
8141 break;
8142 }
8143 case MutableStateDep:
8144 {
8145 if ( mData->mRegistered
8146 && ( !i_isSessionMachine()
8147 || ( mData->mMachineState != MachineState_Aborted
8148 && mData->mMachineState != MachineState_Teleported
8149 && mData->mMachineState != MachineState_PoweredOff
8150 )
8151 )
8152 )
8153 return setError(VBOX_E_INVALID_VM_STATE,
8154 tr("The machine is not mutable (state is %s)"),
8155 Global::stringifyMachineState(mData->mMachineState));
8156 break;
8157 }
8158 case MutableOrSavedStateDep:
8159 {
8160 if ( mData->mRegistered
8161 && ( !i_isSessionMachine()
8162 || ( mData->mMachineState != MachineState_Aborted
8163 && mData->mMachineState != MachineState_Teleported
8164 && mData->mMachineState != MachineState_Saved
8165 && mData->mMachineState != MachineState_PoweredOff
8166 )
8167 )
8168 )
8169 return setError(VBOX_E_INVALID_VM_STATE,
8170 tr("The machine is not mutable (state is %s)"),
8171 Global::stringifyMachineState(mData->mMachineState));
8172 break;
8173 }
8174 case MutableOrRunningStateDep:
8175 {
8176 if ( mData->mRegistered
8177 && ( !i_isSessionMachine()
8178 || ( mData->mMachineState != MachineState_Aborted
8179 && mData->mMachineState != MachineState_Teleported
8180 && mData->mMachineState != MachineState_PoweredOff
8181 && !Global::IsOnline(mData->mMachineState)
8182 )
8183 )
8184 )
8185 return setError(VBOX_E_INVALID_VM_STATE,
8186 tr("The machine is not mutable (state is %s)"),
8187 Global::stringifyMachineState(mData->mMachineState));
8188 break;
8189 }
8190 case MutableOrSavedOrRunningStateDep:
8191 {
8192 if ( mData->mRegistered
8193 && ( !i_isSessionMachine()
8194 || ( mData->mMachineState != MachineState_Aborted
8195 && mData->mMachineState != MachineState_Teleported
8196 && mData->mMachineState != MachineState_Saved
8197 && mData->mMachineState != MachineState_PoweredOff
8198 && !Global::IsOnline(mData->mMachineState)
8199 )
8200 )
8201 )
8202 return setError(VBOX_E_INVALID_VM_STATE,
8203 tr("The machine is not mutable (state is %s)"),
8204 Global::stringifyMachineState(mData->mMachineState));
8205 break;
8206 }
8207 }
8208
8209 return S_OK;
8210}
8211
8212/**
8213 * Helper to initialize all associated child objects and allocate data
8214 * structures.
8215 *
8216 * This method must be called as a part of the object's initialization procedure
8217 * (usually done in the #init() method).
8218 *
8219 * @note Must be called only from #init() or from #registeredInit().
8220 */
8221HRESULT Machine::initDataAndChildObjects()
8222{
8223 AutoCaller autoCaller(this);
8224 AssertComRCReturnRC(autoCaller.rc());
8225 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8226 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8227
8228 AssertReturn(!mData->mAccessible, E_FAIL);
8229
8230 /* allocate data structures */
8231 mSSData.allocate();
8232 mUserData.allocate();
8233 mHWData.allocate();
8234 mMediaData.allocate();
8235 mStorageControllers.allocate();
8236 mUSBControllers.allocate();
8237
8238 /* initialize mOSTypeId */
8239 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8240
8241 /* create associated BIOS settings object */
8242 unconst(mBIOSSettings).createObject();
8243 mBIOSSettings->init(this);
8244
8245 /* create an associated VRDE object (default is disabled) */
8246 unconst(mVRDEServer).createObject();
8247 mVRDEServer->init(this);
8248
8249 /* create associated serial port objects */
8250 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8251 {
8252 unconst(mSerialPorts[slot]).createObject();
8253 mSerialPorts[slot]->init(this, slot);
8254 }
8255
8256 /* create associated parallel port objects */
8257 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8258 {
8259 unconst(mParallelPorts[slot]).createObject();
8260 mParallelPorts[slot]->init(this, slot);
8261 }
8262
8263 /* create the audio adapter object (always present, default is disabled) */
8264 unconst(mAudioAdapter).createObject();
8265 mAudioAdapter->init(this);
8266
8267 /* create the USB device filters object (always present) */
8268 unconst(mUSBDeviceFilters).createObject();
8269 mUSBDeviceFilters->init(this);
8270
8271 /* create associated network adapter objects */
8272 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8273 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8274 {
8275 unconst(mNetworkAdapters[slot]).createObject();
8276 mNetworkAdapters[slot]->init(this, slot);
8277 }
8278
8279 /* create the bandwidth control */
8280 unconst(mBandwidthControl).createObject();
8281 mBandwidthControl->init(this);
8282
8283 return S_OK;
8284}
8285
8286/**
8287 * Helper to uninitialize all associated child objects and to free all data
8288 * structures.
8289 *
8290 * This method must be called as a part of the object's uninitialization
8291 * procedure (usually done in the #uninit() method).
8292 *
8293 * @note Must be called only from #uninit() or from #registeredInit().
8294 */
8295void Machine::uninitDataAndChildObjects()
8296{
8297 AutoCaller autoCaller(this);
8298 AssertComRCReturnVoid(autoCaller.rc());
8299 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8300 || getObjectState().getState() == ObjectState::Limited);
8301
8302 /* tell all our other child objects we've been uninitialized */
8303 if (mBandwidthControl)
8304 {
8305 mBandwidthControl->uninit();
8306 unconst(mBandwidthControl).setNull();
8307 }
8308
8309 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8310 {
8311 if (mNetworkAdapters[slot])
8312 {
8313 mNetworkAdapters[slot]->uninit();
8314 unconst(mNetworkAdapters[slot]).setNull();
8315 }
8316 }
8317
8318 if (mUSBDeviceFilters)
8319 {
8320 mUSBDeviceFilters->uninit();
8321 unconst(mUSBDeviceFilters).setNull();
8322 }
8323
8324 if (mAudioAdapter)
8325 {
8326 mAudioAdapter->uninit();
8327 unconst(mAudioAdapter).setNull();
8328 }
8329
8330 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8331 {
8332 if (mParallelPorts[slot])
8333 {
8334 mParallelPorts[slot]->uninit();
8335 unconst(mParallelPorts[slot]).setNull();
8336 }
8337 }
8338
8339 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8340 {
8341 if (mSerialPorts[slot])
8342 {
8343 mSerialPorts[slot]->uninit();
8344 unconst(mSerialPorts[slot]).setNull();
8345 }
8346 }
8347
8348 if (mVRDEServer)
8349 {
8350 mVRDEServer->uninit();
8351 unconst(mVRDEServer).setNull();
8352 }
8353
8354 if (mBIOSSettings)
8355 {
8356 mBIOSSettings->uninit();
8357 unconst(mBIOSSettings).setNull();
8358 }
8359
8360 /* Deassociate media (only when a real Machine or a SnapshotMachine
8361 * instance is uninitialized; SessionMachine instances refer to real
8362 * Machine media). This is necessary for a clean re-initialization of
8363 * the VM after successfully re-checking the accessibility state. Note
8364 * that in case of normal Machine or SnapshotMachine uninitialization (as
8365 * a result of unregistering or deleting the snapshot), outdated media
8366 * attachments will already be uninitialized and deleted, so this
8367 * code will not affect them. */
8368 if ( !!mMediaData
8369 && (!i_isSessionMachine())
8370 )
8371 {
8372 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8373 it != mMediaData->mAttachments.end();
8374 ++it)
8375 {
8376 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8377 if (pMedium.isNull())
8378 continue;
8379 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8380 AssertComRC(rc);
8381 }
8382 }
8383
8384 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8385 {
8386 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8387 if (mData->mFirstSnapshot)
8388 {
8389 // snapshots tree is protected by machine write lock; strictly
8390 // this isn't necessary here since we're deleting the entire
8391 // machine, but otherwise we assert in Snapshot::uninit()
8392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8393 mData->mFirstSnapshot->uninit();
8394 mData->mFirstSnapshot.setNull();
8395 }
8396
8397 mData->mCurrentSnapshot.setNull();
8398 }
8399
8400 /* free data structures (the essential mData structure is not freed here
8401 * since it may be still in use) */
8402 mMediaData.free();
8403 mStorageControllers.free();
8404 mUSBControllers.free();
8405 mHWData.free();
8406 mUserData.free();
8407 mSSData.free();
8408}
8409
8410/**
8411 * Returns a pointer to the Machine object for this machine that acts like a
8412 * parent for complex machine data objects such as shared folders, etc.
8413 *
8414 * For primary Machine objects and for SnapshotMachine objects, returns this
8415 * object's pointer itself. For SessionMachine objects, returns the peer
8416 * (primary) machine pointer.
8417 */
8418Machine* Machine::i_getMachine()
8419{
8420 if (i_isSessionMachine())
8421 return (Machine*)mPeer;
8422 return this;
8423}
8424
8425/**
8426 * Makes sure that there are no machine state dependents. If necessary, waits
8427 * for the number of dependents to drop to zero.
8428 *
8429 * Make sure this method is called from under this object's write lock to
8430 * guarantee that no new dependents may be added when this method returns
8431 * control to the caller.
8432 *
8433 * @note Locks this object for writing. The lock will be released while waiting
8434 * (if necessary).
8435 *
8436 * @warning To be used only in methods that change the machine state!
8437 */
8438void Machine::i_ensureNoStateDependencies()
8439{
8440 AssertReturnVoid(isWriteLockOnCurrentThread());
8441
8442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8443
8444 /* Wait for all state dependents if necessary */
8445 if (mData->mMachineStateDeps != 0)
8446 {
8447 /* lazy semaphore creation */
8448 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8449 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8450
8451 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8452 mData->mMachineStateDeps));
8453
8454 ++mData->mMachineStateChangePending;
8455
8456 /* reset the semaphore before waiting, the last dependent will signal
8457 * it */
8458 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8459
8460 alock.release();
8461
8462 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8463
8464 alock.acquire();
8465
8466 -- mData->mMachineStateChangePending;
8467 }
8468}
8469
8470/**
8471 * Changes the machine state and informs callbacks.
8472 *
8473 * This method is not intended to fail so it either returns S_OK or asserts (and
8474 * returns a failure).
8475 *
8476 * @note Locks this object for writing.
8477 */
8478HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8479{
8480 LogFlowThisFuncEnter();
8481 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8482 Assert(aMachineState != MachineState_Null);
8483
8484 AutoCaller autoCaller(this);
8485 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8486
8487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8488
8489 /* wait for state dependents to drop to zero */
8490 i_ensureNoStateDependencies();
8491
8492 MachineState_T const enmOldState = mData->mMachineState;
8493 if (enmOldState != aMachineState)
8494 {
8495 mData->mMachineState = aMachineState;
8496 RTTimeNow(&mData->mLastStateChange);
8497
8498#ifdef VBOX_WITH_DTRACE_R3_MAIN
8499 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8500#endif
8501 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8502 }
8503
8504 LogFlowThisFuncLeave();
8505 return S_OK;
8506}
8507
8508/**
8509 * Searches for a shared folder with the given logical name
8510 * in the collection of shared folders.
8511 *
8512 * @param aName logical name of the shared folder
8513 * @param aSharedFolder where to return the found object
8514 * @param aSetError whether to set the error info if the folder is
8515 * not found
8516 * @return
8517 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8518 *
8519 * @note
8520 * must be called from under the object's lock!
8521 */
8522HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8523 ComObjPtr<SharedFolder> &aSharedFolder,
8524 bool aSetError /* = false */)
8525{
8526 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8527 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8528 it != mHWData->mSharedFolders.end();
8529 ++it)
8530 {
8531 SharedFolder *pSF = *it;
8532 AutoCaller autoCaller(pSF);
8533 if (pSF->i_getName() == aName)
8534 {
8535 aSharedFolder = pSF;
8536 rc = S_OK;
8537 break;
8538 }
8539 }
8540
8541 if (aSetError && FAILED(rc))
8542 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8543
8544 return rc;
8545}
8546
8547/**
8548 * Initializes all machine instance data from the given settings structures
8549 * from XML. The exception is the machine UUID which needs special handling
8550 * depending on the caller's use case, so the caller needs to set that herself.
8551 *
8552 * This gets called in several contexts during machine initialization:
8553 *
8554 * -- When machine XML exists on disk already and needs to be loaded into memory,
8555 * for example, from registeredInit() to load all registered machines on
8556 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8557 * attached to the machine should be part of some media registry already.
8558 *
8559 * -- During OVF import, when a machine config has been constructed from an
8560 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8561 * ensure that the media listed as attachments in the config (which have
8562 * been imported from the OVF) receive the correct registry ID.
8563 *
8564 * -- During VM cloning.
8565 *
8566 * @param config Machine settings from XML.
8567 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8568 * for each attached medium in the config.
8569 * @return
8570 */
8571HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8572 const Guid *puuidRegistry)
8573{
8574 // copy name, description, OS type, teleporter, UTC etc.
8575 mUserData->s = config.machineUserData;
8576
8577 // Decode the Icon overide data from config userdata and set onto Machine.
8578 #define DECODE_STR_MAX _1M
8579 const char* pszStr = config.machineUserData.ovIcon.c_str();
8580 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8581 if (cbOut > DECODE_STR_MAX)
8582 return setError(E_FAIL,
8583 tr("Icon Data too long.'%d' > '%d'"),
8584 cbOut,
8585 DECODE_STR_MAX);
8586 mUserData->mIcon.resize(cbOut);
8587 int vrc = VINF_SUCCESS;
8588 if (cbOut)
8589 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8590 if (RT_FAILURE(vrc))
8591 {
8592 mUserData->mIcon.resize(0);
8593 return setError(E_FAIL,
8594 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8595 pszStr,
8596 vrc);
8597 }
8598
8599 // look up the object by Id to check it is valid
8600 ComPtr<IGuestOSType> guestOSType;
8601 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8602 guestOSType.asOutParam());
8603 if (FAILED(rc)) return rc;
8604
8605 // stateFile (optional)
8606 if (config.strStateFile.isEmpty())
8607 mSSData->strStateFilePath.setNull();
8608 else
8609 {
8610 Utf8Str stateFilePathFull(config.strStateFile);
8611 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8612 if (RT_FAILURE(vrc))
8613 return setError(E_FAIL,
8614 tr("Invalid saved state file path '%s' (%Rrc)"),
8615 config.strStateFile.c_str(),
8616 vrc);
8617 mSSData->strStateFilePath = stateFilePathFull;
8618 }
8619
8620 // snapshot folder needs special processing so set it again
8621 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8622 if (FAILED(rc)) return rc;
8623
8624 /* Copy the extra data items (Not in any case config is already the same as
8625 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8626 * make sure the extra data map is copied). */
8627 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8628
8629 /* currentStateModified (optional, default is true) */
8630 mData->mCurrentStateModified = config.fCurrentStateModified;
8631
8632 mData->mLastStateChange = config.timeLastStateChange;
8633
8634 /*
8635 * note: all mUserData members must be assigned prior this point because
8636 * we need to commit changes in order to let mUserData be shared by all
8637 * snapshot machine instances.
8638 */
8639 mUserData.commitCopy();
8640
8641 // machine registry, if present (must be loaded before snapshots)
8642 if (config.canHaveOwnMediaRegistry())
8643 {
8644 // determine machine folder
8645 Utf8Str strMachineFolder = i_getSettingsFileFull();
8646 strMachineFolder.stripFilename();
8647 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8648 config.mediaRegistry,
8649 strMachineFolder);
8650 if (FAILED(rc)) return rc;
8651 }
8652
8653 /* Snapshot node (optional) */
8654 size_t cRootSnapshots;
8655 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8656 {
8657 // there must be only one root snapshot
8658 Assert(cRootSnapshots == 1);
8659
8660 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8661
8662 rc = i_loadSnapshot(snap,
8663 config.uuidCurrentSnapshot,
8664 NULL); // no parent == first snapshot
8665 if (FAILED(rc)) return rc;
8666 }
8667
8668 // hardware data
8669 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8670 if (FAILED(rc)) return rc;
8671
8672 // load storage controllers
8673 rc = i_loadStorageControllers(config.storageMachine,
8674 puuidRegistry,
8675 NULL /* puuidSnapshot */);
8676 if (FAILED(rc)) return rc;
8677
8678 /*
8679 * NOTE: the assignment below must be the last thing to do,
8680 * otherwise it will be not possible to change the settings
8681 * somewhere in the code above because all setters will be
8682 * blocked by i_checkStateDependency(MutableStateDep).
8683 */
8684
8685 /* set the machine state to Aborted or Saved when appropriate */
8686 if (config.fAborted)
8687 {
8688 mSSData->strStateFilePath.setNull();
8689
8690 /* no need to use i_setMachineState() during init() */
8691 mData->mMachineState = MachineState_Aborted;
8692 }
8693 else if (!mSSData->strStateFilePath.isEmpty())
8694 {
8695 /* no need to use i_setMachineState() during init() */
8696 mData->mMachineState = MachineState_Saved;
8697 }
8698
8699 // after loading settings, we are no longer different from the XML on disk
8700 mData->flModifications = 0;
8701
8702 return S_OK;
8703}
8704
8705/**
8706 * Recursively loads all snapshots starting from the given.
8707 *
8708 * @param aNode <Snapshot> node.
8709 * @param aCurSnapshotId Current snapshot ID from the settings file.
8710 * @param aParentSnapshot Parent snapshot.
8711 */
8712HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8713 const Guid &aCurSnapshotId,
8714 Snapshot *aParentSnapshot)
8715{
8716 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8717 AssertReturn(!i_isSessionMachine(), E_FAIL);
8718
8719 HRESULT rc = S_OK;
8720
8721 Utf8Str strStateFile;
8722 if (!data.strStateFile.isEmpty())
8723 {
8724 /* optional */
8725 strStateFile = data.strStateFile;
8726 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8727 if (RT_FAILURE(vrc))
8728 return setError(E_FAIL,
8729 tr("Invalid saved state file path '%s' (%Rrc)"),
8730 strStateFile.c_str(),
8731 vrc);
8732 }
8733
8734 /* create a snapshot machine object */
8735 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8736 pSnapshotMachine.createObject();
8737 rc = pSnapshotMachine->initFromSettings(this,
8738 data.hardware,
8739 &data.debugging,
8740 &data.autostart,
8741 data.storage,
8742 data.uuid.ref(),
8743 strStateFile);
8744 if (FAILED(rc)) return rc;
8745
8746 /* create a snapshot object */
8747 ComObjPtr<Snapshot> pSnapshot;
8748 pSnapshot.createObject();
8749 /* initialize the snapshot */
8750 rc = pSnapshot->init(mParent, // VirtualBox object
8751 data.uuid,
8752 data.strName,
8753 data.strDescription,
8754 data.timestamp,
8755 pSnapshotMachine,
8756 aParentSnapshot);
8757 if (FAILED(rc)) return rc;
8758
8759 /* memorize the first snapshot if necessary */
8760 if (!mData->mFirstSnapshot)
8761 mData->mFirstSnapshot = pSnapshot;
8762
8763 /* memorize the current snapshot when appropriate */
8764 if ( !mData->mCurrentSnapshot
8765 && pSnapshot->i_getId() == aCurSnapshotId
8766 )
8767 mData->mCurrentSnapshot = pSnapshot;
8768
8769 // now create the children
8770 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8771 it != data.llChildSnapshots.end();
8772 ++it)
8773 {
8774 const settings::Snapshot &childData = *it;
8775 // recurse
8776 rc = i_loadSnapshot(childData,
8777 aCurSnapshotId,
8778 pSnapshot); // parent = the one we created above
8779 if (FAILED(rc)) return rc;
8780 }
8781
8782 return rc;
8783}
8784
8785/**
8786 * Loads settings into mHWData.
8787 *
8788 * @param data Reference to the hardware settings.
8789 * @param pDbg Pointer to the debugging settings.
8790 * @param pAutostart Pointer to the autostart settings.
8791 */
8792HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8793 const settings::Autostart *pAutostart)
8794{
8795 AssertReturn(!i_isSessionMachine(), E_FAIL);
8796
8797 HRESULT rc = S_OK;
8798
8799 try
8800 {
8801 /* The hardware version attribute (optional). */
8802 mHWData->mHWVersion = data.strVersion;
8803 mHWData->mHardwareUUID = data.uuid;
8804
8805 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8806 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8807 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8808 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8809 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8810 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8811 mHWData->mPAEEnabled = data.fPAE;
8812 mHWData->mLongMode = data.enmLongMode;
8813 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8814 mHWData->mCPUCount = data.cCPUs;
8815 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8816 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8817 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8818
8819 // cpu
8820 if (mHWData->mCPUHotPlugEnabled)
8821 {
8822 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8823 it != data.llCpus.end();
8824 ++it)
8825 {
8826 const settings::Cpu &cpu = *it;
8827
8828 mHWData->mCPUAttached[cpu.ulId] = true;
8829 }
8830 }
8831
8832 // cpuid leafs
8833 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8834 it != data.llCpuIdLeafs.end();
8835 ++it)
8836 {
8837 const settings::CpuIdLeaf &leaf = *it;
8838
8839 switch (leaf.ulId)
8840 {
8841 case 0x0:
8842 case 0x1:
8843 case 0x2:
8844 case 0x3:
8845 case 0x4:
8846 case 0x5:
8847 case 0x6:
8848 case 0x7:
8849 case 0x8:
8850 case 0x9:
8851 case 0xA:
8852 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8853 break;
8854
8855 case 0x80000000:
8856 case 0x80000001:
8857 case 0x80000002:
8858 case 0x80000003:
8859 case 0x80000004:
8860 case 0x80000005:
8861 case 0x80000006:
8862 case 0x80000007:
8863 case 0x80000008:
8864 case 0x80000009:
8865 case 0x8000000A:
8866 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8867 break;
8868
8869 default:
8870 /* just ignore */
8871 break;
8872 }
8873 }
8874
8875 mHWData->mMemorySize = data.ulMemorySizeMB;
8876 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8877
8878 // boot order
8879 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8880 {
8881 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8882 if (it == data.mapBootOrder.end())
8883 mHWData->mBootOrder[i] = DeviceType_Null;
8884 else
8885 mHWData->mBootOrder[i] = it->second;
8886 }
8887
8888 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8889 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8890 mHWData->mMonitorCount = data.cMonitors;
8891 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8892 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8893 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8894 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8895 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8896 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8897 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8898 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8899 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8900 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8901 if (!data.strVideoCaptureFile.isEmpty())
8902 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8903 else
8904 mHWData->mVideoCaptureFile.setNull();
8905 mHWData->mFirmwareType = data.firmwareType;
8906 mHWData->mPointingHIDType = data.pointingHIDType;
8907 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8908 mHWData->mChipsetType = data.chipsetType;
8909 mHWData->mParavirtProvider = data.paravirtProvider;
8910 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8911 mHWData->mHPETEnabled = data.fHPETEnabled;
8912
8913 /* VRDEServer */
8914 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8915 if (FAILED(rc)) return rc;
8916
8917 /* BIOS */
8918 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8919 if (FAILED(rc)) return rc;
8920
8921 // Bandwidth control (must come before network adapters)
8922 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8923 if (FAILED(rc)) return rc;
8924
8925 /* Shared folders */
8926 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8927 it != data.usbSettings.llUSBControllers.end();
8928 ++it)
8929 {
8930 const settings::USBController &settingsCtrl = *it;
8931 ComObjPtr<USBController> newCtrl;
8932
8933 newCtrl.createObject();
8934 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8935 mUSBControllers->push_back(newCtrl);
8936 }
8937
8938 /* USB device filters */
8939 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8940 if (FAILED(rc)) return rc;
8941
8942 // network adapters
8943 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8944 size_t oldCount = mNetworkAdapters.size();
8945 if (newCount > oldCount)
8946 {
8947 mNetworkAdapters.resize(newCount);
8948 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8949 {
8950 unconst(mNetworkAdapters[slot]).createObject();
8951 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8952 }
8953 }
8954 else if (newCount < oldCount)
8955 mNetworkAdapters.resize(newCount);
8956 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8957 it != data.llNetworkAdapters.end();
8958 ++it)
8959 {
8960 const settings::NetworkAdapter &nic = *it;
8961
8962 /* slot unicity is guaranteed by XML Schema */
8963 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8964 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8965 if (FAILED(rc)) return rc;
8966 }
8967
8968 // serial ports
8969 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8970 it != data.llSerialPorts.end();
8971 ++it)
8972 {
8973 const settings::SerialPort &s = *it;
8974
8975 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8976 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8977 if (FAILED(rc)) return rc;
8978 }
8979
8980 // parallel ports (optional)
8981 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8982 it != data.llParallelPorts.end();
8983 ++it)
8984 {
8985 const settings::ParallelPort &p = *it;
8986
8987 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8988 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8989 if (FAILED(rc)) return rc;
8990 }
8991
8992 /* AudioAdapter */
8993 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8994 if (FAILED(rc)) return rc;
8995
8996 /* Shared folders */
8997 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8998 it != data.llSharedFolders.end();
8999 ++it)
9000 {
9001 const settings::SharedFolder &sf = *it;
9002
9003 ComObjPtr<SharedFolder> sharedFolder;
9004 /* Check for double entries. Not allowed! */
9005 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9006 if (SUCCEEDED(rc))
9007 return setError(VBOX_E_OBJECT_IN_USE,
9008 tr("Shared folder named '%s' already exists"),
9009 sf.strName.c_str());
9010
9011 /* Create the new shared folder. Don't break on error. This will be
9012 * reported when the machine starts. */
9013 sharedFolder.createObject();
9014 rc = sharedFolder->init(i_getMachine(),
9015 sf.strName,
9016 sf.strHostPath,
9017 RT_BOOL(sf.fWritable),
9018 RT_BOOL(sf.fAutoMount),
9019 false /* fFailOnError */);
9020 if (FAILED(rc)) return rc;
9021 mHWData->mSharedFolders.push_back(sharedFolder);
9022 }
9023
9024 // Clipboard
9025 mHWData->mClipboardMode = data.clipboardMode;
9026
9027 // drag'n'drop
9028 mHWData->mDnDMode = data.dndMode;
9029
9030 // guest settings
9031 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9032
9033 // IO settings
9034 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9035 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9036
9037 // Host PCI devices
9038 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9039 it != data.pciAttachments.end();
9040 ++it)
9041 {
9042 const settings::HostPCIDeviceAttachment &hpda = *it;
9043 ComObjPtr<PCIDeviceAttachment> pda;
9044
9045 pda.createObject();
9046 pda->i_loadSettings(this, hpda);
9047 mHWData->mPCIDeviceAssignments.push_back(pda);
9048 }
9049
9050 /*
9051 * (The following isn't really real hardware, but it lives in HWData
9052 * for reasons of convenience.)
9053 */
9054
9055#ifdef VBOX_WITH_GUEST_PROPS
9056 /* Guest properties (optional) */
9057 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9058 it != data.llGuestProperties.end();
9059 ++it)
9060 {
9061 const settings::GuestProperty &prop = *it;
9062 uint32_t fFlags = guestProp::NILFLAG;
9063 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9064 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9065 mHWData->mGuestProperties[prop.strName] = property;
9066 }
9067
9068 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9069#endif /* VBOX_WITH_GUEST_PROPS defined */
9070
9071 rc = i_loadDebugging(pDbg);
9072 if (FAILED(rc))
9073 return rc;
9074
9075 mHWData->mAutostart = *pAutostart;
9076
9077 /* default frontend */
9078 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9079 }
9080 catch(std::bad_alloc &)
9081 {
9082 return E_OUTOFMEMORY;
9083 }
9084
9085 AssertComRC(rc);
9086 return rc;
9087}
9088
9089/**
9090 * Called from Machine::loadHardware() to load the debugging settings of the
9091 * machine.
9092 *
9093 * @param pDbg Pointer to the settings.
9094 */
9095HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9096{
9097 mHWData->mDebugging = *pDbg;
9098 /* no more processing currently required, this will probably change. */
9099 return S_OK;
9100}
9101
9102/**
9103 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9104 *
9105 * @param data
9106 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9107 * @param puuidSnapshot
9108 * @return
9109 */
9110HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9111 const Guid *puuidRegistry,
9112 const Guid *puuidSnapshot)
9113{
9114 AssertReturn(!i_isSessionMachine(), E_FAIL);
9115
9116 HRESULT rc = S_OK;
9117
9118 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9119 it != data.llStorageControllers.end();
9120 ++it)
9121 {
9122 const settings::StorageController &ctlData = *it;
9123
9124 ComObjPtr<StorageController> pCtl;
9125 /* Try to find one with the name first. */
9126 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9127 if (SUCCEEDED(rc))
9128 return setError(VBOX_E_OBJECT_IN_USE,
9129 tr("Storage controller named '%s' already exists"),
9130 ctlData.strName.c_str());
9131
9132 pCtl.createObject();
9133 rc = pCtl->init(this,
9134 ctlData.strName,
9135 ctlData.storageBus,
9136 ctlData.ulInstance,
9137 ctlData.fBootable);
9138 if (FAILED(rc)) return rc;
9139
9140 mStorageControllers->push_back(pCtl);
9141
9142 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9143 if (FAILED(rc)) return rc;
9144
9145 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9146 if (FAILED(rc)) return rc;
9147
9148 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9149 if (FAILED(rc)) return rc;
9150
9151 /* Set IDE emulation settings (only for AHCI controller). */
9152 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9153 {
9154 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9155 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9156 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9157 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9158 )
9159 return rc;
9160 }
9161
9162 /* Load the attached devices now. */
9163 rc = i_loadStorageDevices(pCtl,
9164 ctlData,
9165 puuidRegistry,
9166 puuidSnapshot);
9167 if (FAILED(rc)) return rc;
9168 }
9169
9170 return S_OK;
9171}
9172
9173/**
9174 * Called from i_loadStorageControllers for a controller's devices.
9175 *
9176 * @param aStorageController
9177 * @param data
9178 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9179 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9180 * @return
9181 */
9182HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9183 const settings::StorageController &data,
9184 const Guid *puuidRegistry,
9185 const Guid *puuidSnapshot)
9186{
9187 HRESULT rc = S_OK;
9188
9189 /* paranoia: detect duplicate attachments */
9190 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9191 it != data.llAttachedDevices.end();
9192 ++it)
9193 {
9194 const settings::AttachedDevice &ad = *it;
9195
9196 for (settings::AttachedDevicesList::const_iterator it2 = it;
9197 it2 != data.llAttachedDevices.end();
9198 ++it2)
9199 {
9200 if (it == it2)
9201 continue;
9202
9203 const settings::AttachedDevice &ad2 = *it2;
9204
9205 if ( ad.lPort == ad2.lPort
9206 && ad.lDevice == ad2.lDevice)
9207 {
9208 return setError(E_FAIL,
9209 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9210 aStorageController->i_getName().c_str(),
9211 ad.lPort,
9212 ad.lDevice,
9213 mUserData->s.strName.c_str());
9214 }
9215 }
9216 }
9217
9218 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9219 it != data.llAttachedDevices.end();
9220 ++it)
9221 {
9222 const settings::AttachedDevice &dev = *it;
9223 ComObjPtr<Medium> medium;
9224
9225 switch (dev.deviceType)
9226 {
9227 case DeviceType_Floppy:
9228 case DeviceType_DVD:
9229 if (dev.strHostDriveSrc.isNotEmpty())
9230 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9231 false /* fRefresh */, medium);
9232 else
9233 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9234 dev.uuid,
9235 false /* fRefresh */,
9236 false /* aSetError */,
9237 medium);
9238 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9239 // This is not an error. The host drive or UUID might have vanished, so just go
9240 // ahead without this removeable medium attachment
9241 rc = S_OK;
9242 break;
9243
9244 case DeviceType_HardDisk:
9245 {
9246 /* find a hard disk by UUID */
9247 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9248 if (FAILED(rc))
9249 {
9250 if (i_isSnapshotMachine())
9251 {
9252 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9253 // so the user knows that the bad disk is in a snapshot somewhere
9254 com::ErrorInfo info;
9255 return setError(E_FAIL,
9256 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9257 puuidSnapshot->raw(),
9258 info.getText().raw());
9259 }
9260 else
9261 return rc;
9262 }
9263
9264 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9265
9266 if (medium->i_getType() == MediumType_Immutable)
9267 {
9268 if (i_isSnapshotMachine())
9269 return setError(E_FAIL,
9270 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9271 "of the virtual machine '%s' ('%s')"),
9272 medium->i_getLocationFull().c_str(),
9273 dev.uuid.raw(),
9274 puuidSnapshot->raw(),
9275 mUserData->s.strName.c_str(),
9276 mData->m_strConfigFileFull.c_str());
9277
9278 return setError(E_FAIL,
9279 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9280 medium->i_getLocationFull().c_str(),
9281 dev.uuid.raw(),
9282 mUserData->s.strName.c_str(),
9283 mData->m_strConfigFileFull.c_str());
9284 }
9285
9286 if (medium->i_getType() == MediumType_MultiAttach)
9287 {
9288 if (i_isSnapshotMachine())
9289 return setError(E_FAIL,
9290 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9291 "of the virtual machine '%s' ('%s')"),
9292 medium->i_getLocationFull().c_str(),
9293 dev.uuid.raw(),
9294 puuidSnapshot->raw(),
9295 mUserData->s.strName.c_str(),
9296 mData->m_strConfigFileFull.c_str());
9297
9298 return setError(E_FAIL,
9299 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9300 medium->i_getLocationFull().c_str(),
9301 dev.uuid.raw(),
9302 mUserData->s.strName.c_str(),
9303 mData->m_strConfigFileFull.c_str());
9304 }
9305
9306 if ( !i_isSnapshotMachine()
9307 && medium->i_getChildren().size() != 0
9308 )
9309 return setError(E_FAIL,
9310 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9311 "because it has %d differencing child hard disks"),
9312 medium->i_getLocationFull().c_str(),
9313 dev.uuid.raw(),
9314 mUserData->s.strName.c_str(),
9315 mData->m_strConfigFileFull.c_str(),
9316 medium->i_getChildren().size());
9317
9318 if (i_findAttachment(mMediaData->mAttachments,
9319 medium))
9320 return setError(E_FAIL,
9321 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9322 medium->i_getLocationFull().c_str(),
9323 dev.uuid.raw(),
9324 mUserData->s.strName.c_str(),
9325 mData->m_strConfigFileFull.c_str());
9326
9327 break;
9328 }
9329
9330 default:
9331 return setError(E_FAIL,
9332 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9333 medium->i_getLocationFull().c_str(),
9334 mUserData->s.strName.c_str(),
9335 mData->m_strConfigFileFull.c_str());
9336 }
9337
9338 if (FAILED(rc))
9339 break;
9340
9341 /* Bandwidth groups are loaded at this point. */
9342 ComObjPtr<BandwidthGroup> pBwGroup;
9343
9344 if (!dev.strBwGroup.isEmpty())
9345 {
9346 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9347 if (FAILED(rc))
9348 return setError(E_FAIL,
9349 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9350 medium->i_getLocationFull().c_str(),
9351 dev.strBwGroup.c_str(),
9352 mUserData->s.strName.c_str(),
9353 mData->m_strConfigFileFull.c_str());
9354 pBwGroup->i_reference();
9355 }
9356
9357 const Bstr controllerName = aStorageController->i_getName();
9358 ComObjPtr<MediumAttachment> pAttachment;
9359 pAttachment.createObject();
9360 rc = pAttachment->init(this,
9361 medium,
9362 controllerName,
9363 dev.lPort,
9364 dev.lDevice,
9365 dev.deviceType,
9366 false,
9367 dev.fPassThrough,
9368 dev.fTempEject,
9369 dev.fNonRotational,
9370 dev.fDiscard,
9371 dev.fHotPluggable,
9372 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9373 if (FAILED(rc)) break;
9374
9375 /* associate the medium with this machine and snapshot */
9376 if (!medium.isNull())
9377 {
9378 AutoCaller medCaller(medium);
9379 if (FAILED(medCaller.rc())) return medCaller.rc();
9380 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9381
9382 if (i_isSnapshotMachine())
9383 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9384 else
9385 rc = medium->i_addBackReference(mData->mUuid);
9386 /* If the medium->addBackReference fails it sets an appropriate
9387 * error message, so no need to do any guesswork here. */
9388
9389 if (puuidRegistry)
9390 // caller wants registry ID to be set on all attached media (OVF import case)
9391 medium->i_addRegistry(*puuidRegistry);
9392 }
9393
9394 if (FAILED(rc))
9395 break;
9396
9397 /* back up mMediaData to let registeredInit() properly rollback on failure
9398 * (= limited accessibility) */
9399 i_setModified(IsModified_Storage);
9400 mMediaData.backup();
9401 mMediaData->mAttachments.push_back(pAttachment);
9402 }
9403
9404 return rc;
9405}
9406
9407/**
9408 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9409 *
9410 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9411 * @param aSnapshot where to return the found snapshot
9412 * @param aSetError true to set extended error info on failure
9413 */
9414HRESULT Machine::i_findSnapshotById(const Guid &aId,
9415 ComObjPtr<Snapshot> &aSnapshot,
9416 bool aSetError /* = false */)
9417{
9418 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9419
9420 if (!mData->mFirstSnapshot)
9421 {
9422 if (aSetError)
9423 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9424 return E_FAIL;
9425 }
9426
9427 if (aId.isZero())
9428 aSnapshot = mData->mFirstSnapshot;
9429 else
9430 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9431
9432 if (!aSnapshot)
9433 {
9434 if (aSetError)
9435 return setError(E_FAIL,
9436 tr("Could not find a snapshot with UUID {%s}"),
9437 aId.toString().c_str());
9438 return E_FAIL;
9439 }
9440
9441 return S_OK;
9442}
9443
9444/**
9445 * Returns the snapshot with the given name or fails of no such snapshot.
9446 *
9447 * @param aName snapshot name to find
9448 * @param aSnapshot where to return the found snapshot
9449 * @param aSetError true to set extended error info on failure
9450 */
9451HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9452 ComObjPtr<Snapshot> &aSnapshot,
9453 bool aSetError /* = false */)
9454{
9455 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9456
9457 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9458
9459 if (!mData->mFirstSnapshot)
9460 {
9461 if (aSetError)
9462 return setError(VBOX_E_OBJECT_NOT_FOUND,
9463 tr("This machine does not have any snapshots"));
9464 return VBOX_E_OBJECT_NOT_FOUND;
9465 }
9466
9467 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9468
9469 if (!aSnapshot)
9470 {
9471 if (aSetError)
9472 return setError(VBOX_E_OBJECT_NOT_FOUND,
9473 tr("Could not find a snapshot named '%s'"), strName.c_str());
9474 return VBOX_E_OBJECT_NOT_FOUND;
9475 }
9476
9477 return S_OK;
9478}
9479
9480/**
9481 * Returns a storage controller object with the given name.
9482 *
9483 * @param aName storage controller name to find
9484 * @param aStorageController where to return the found storage controller
9485 * @param aSetError true to set extended error info on failure
9486 */
9487HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9488 ComObjPtr<StorageController> &aStorageController,
9489 bool aSetError /* = false */)
9490{
9491 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9492
9493 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9494 it != mStorageControllers->end();
9495 ++it)
9496 {
9497 if ((*it)->i_getName() == aName)
9498 {
9499 aStorageController = (*it);
9500 return S_OK;
9501 }
9502 }
9503
9504 if (aSetError)
9505 return setError(VBOX_E_OBJECT_NOT_FOUND,
9506 tr("Could not find a storage controller named '%s'"),
9507 aName.c_str());
9508 return VBOX_E_OBJECT_NOT_FOUND;
9509}
9510
9511/**
9512 * Returns a USB controller object with the given name.
9513 *
9514 * @param aName USB controller name to find
9515 * @param aUSBController where to return the found USB controller
9516 * @param aSetError true to set extended error info on failure
9517 */
9518HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9519 ComObjPtr<USBController> &aUSBController,
9520 bool aSetError /* = false */)
9521{
9522 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9523
9524 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9525 it != mUSBControllers->end();
9526 ++it)
9527 {
9528 if ((*it)->i_getName() == aName)
9529 {
9530 aUSBController = (*it);
9531 return S_OK;
9532 }
9533 }
9534
9535 if (aSetError)
9536 return setError(VBOX_E_OBJECT_NOT_FOUND,
9537 tr("Could not find a storage controller named '%s'"),
9538 aName.c_str());
9539 return VBOX_E_OBJECT_NOT_FOUND;
9540}
9541
9542/**
9543 * Returns the number of USB controller instance of the given type.
9544 *
9545 * @param enmType USB controller type.
9546 */
9547ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9548{
9549 ULONG cCtrls = 0;
9550
9551 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9552 it != mUSBControllers->end();
9553 ++it)
9554 {
9555 if ((*it)->i_getControllerType() == enmType)
9556 cCtrls++;
9557 }
9558
9559 return cCtrls;
9560}
9561
9562HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9563 MediaData::AttachmentList &atts)
9564{
9565 AutoCaller autoCaller(this);
9566 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9567
9568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9569
9570 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9571 it != mMediaData->mAttachments.end();
9572 ++it)
9573 {
9574 const ComObjPtr<MediumAttachment> &pAtt = *it;
9575 // should never happen, but deal with NULL pointers in the list.
9576 AssertStmt(!pAtt.isNull(), continue);
9577
9578 // getControllerName() needs caller+read lock
9579 AutoCaller autoAttCaller(pAtt);
9580 if (FAILED(autoAttCaller.rc()))
9581 {
9582 atts.clear();
9583 return autoAttCaller.rc();
9584 }
9585 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9586
9587 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9588 atts.push_back(pAtt);
9589 }
9590
9591 return S_OK;
9592}
9593
9594
9595/**
9596 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9597 * file if the machine name was changed and about creating a new settings file
9598 * if this is a new machine.
9599 *
9600 * @note Must be never called directly but only from #saveSettings().
9601 */
9602HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9603{
9604 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9605
9606 HRESULT rc = S_OK;
9607
9608 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9609
9610 /// @todo need to handle primary group change, too
9611
9612 /* attempt to rename the settings file if machine name is changed */
9613 if ( mUserData->s.fNameSync
9614 && mUserData.isBackedUp()
9615 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9616 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9617 )
9618 {
9619 bool dirRenamed = false;
9620 bool fileRenamed = false;
9621
9622 Utf8Str configFile, newConfigFile;
9623 Utf8Str configFilePrev, newConfigFilePrev;
9624 Utf8Str configDir, newConfigDir;
9625
9626 do
9627 {
9628 int vrc = VINF_SUCCESS;
9629
9630 Utf8Str name = mUserData.backedUpData()->s.strName;
9631 Utf8Str newName = mUserData->s.strName;
9632 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9633 if (group == "/")
9634 group.setNull();
9635 Utf8Str newGroup = mUserData->s.llGroups.front();
9636 if (newGroup == "/")
9637 newGroup.setNull();
9638
9639 configFile = mData->m_strConfigFileFull;
9640
9641 /* first, rename the directory if it matches the group and machine name */
9642 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9643 group.c_str(), RTPATH_DELIMITER, name.c_str());
9644 /** @todo hack, make somehow use of ComposeMachineFilename */
9645 if (mUserData->s.fDirectoryIncludesUUID)
9646 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9647 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9648 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9649 /** @todo hack, make somehow use of ComposeMachineFilename */
9650 if (mUserData->s.fDirectoryIncludesUUID)
9651 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9652 configDir = configFile;
9653 configDir.stripFilename();
9654 newConfigDir = configDir;
9655 if ( configDir.length() >= groupPlusName.length()
9656 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9657 groupPlusName.c_str()))
9658 {
9659 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9660 Utf8Str newConfigBaseDir(newConfigDir);
9661 newConfigDir.append(newGroupPlusName);
9662 /* consistency: use \ if appropriate on the platform */
9663 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9664 /* new dir and old dir cannot be equal here because of 'if'
9665 * above and because name != newName */
9666 Assert(configDir != newConfigDir);
9667 if (!fSettingsFileIsNew)
9668 {
9669 /* perform real rename only if the machine is not new */
9670 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9671 if ( vrc == VERR_FILE_NOT_FOUND
9672 || vrc == VERR_PATH_NOT_FOUND)
9673 {
9674 /* create the parent directory, then retry renaming */
9675 Utf8Str parent(newConfigDir);
9676 parent.stripFilename();
9677 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9678 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9679 }
9680 if (RT_FAILURE(vrc))
9681 {
9682 rc = setError(E_FAIL,
9683 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9684 configDir.c_str(),
9685 newConfigDir.c_str(),
9686 vrc);
9687 break;
9688 }
9689 /* delete subdirectories which are no longer needed */
9690 Utf8Str dir(configDir);
9691 dir.stripFilename();
9692 while (dir != newConfigBaseDir && dir != ".")
9693 {
9694 vrc = RTDirRemove(dir.c_str());
9695 if (RT_FAILURE(vrc))
9696 break;
9697 dir.stripFilename();
9698 }
9699 dirRenamed = true;
9700 }
9701 }
9702
9703 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9704 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9705
9706 /* then try to rename the settings file itself */
9707 if (newConfigFile != configFile)
9708 {
9709 /* get the path to old settings file in renamed directory */
9710 configFile = Utf8StrFmt("%s%c%s",
9711 newConfigDir.c_str(),
9712 RTPATH_DELIMITER,
9713 RTPathFilename(configFile.c_str()));
9714 if (!fSettingsFileIsNew)
9715 {
9716 /* perform real rename only if the machine is not new */
9717 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9718 if (RT_FAILURE(vrc))
9719 {
9720 rc = setError(E_FAIL,
9721 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9722 configFile.c_str(),
9723 newConfigFile.c_str(),
9724 vrc);
9725 break;
9726 }
9727 fileRenamed = true;
9728 configFilePrev = configFile;
9729 configFilePrev += "-prev";
9730 newConfigFilePrev = newConfigFile;
9731 newConfigFilePrev += "-prev";
9732 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9733 }
9734 }
9735
9736 // update m_strConfigFileFull amd mConfigFile
9737 mData->m_strConfigFileFull = newConfigFile;
9738 // compute the relative path too
9739 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9740
9741 // store the old and new so that VirtualBox::i_saveSettings() can update
9742 // the media registry
9743 if ( mData->mRegistered
9744 && (configDir != newConfigDir || configFile != newConfigFile))
9745 {
9746 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9747
9748 if (pfNeedsGlobalSaveSettings)
9749 *pfNeedsGlobalSaveSettings = true;
9750 }
9751
9752 // in the saved state file path, replace the old directory with the new directory
9753 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9754 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9755
9756 // and do the same thing for the saved state file paths of all the online snapshots
9757 if (mData->mFirstSnapshot)
9758 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9759 newConfigDir.c_str());
9760 }
9761 while (0);
9762
9763 if (FAILED(rc))
9764 {
9765 /* silently try to rename everything back */
9766 if (fileRenamed)
9767 {
9768 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9769 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9770 }
9771 if (dirRenamed)
9772 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9773 }
9774
9775 if (FAILED(rc)) return rc;
9776 }
9777
9778 if (fSettingsFileIsNew)
9779 {
9780 /* create a virgin config file */
9781 int vrc = VINF_SUCCESS;
9782
9783 /* ensure the settings directory exists */
9784 Utf8Str path(mData->m_strConfigFileFull);
9785 path.stripFilename();
9786 if (!RTDirExists(path.c_str()))
9787 {
9788 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9789 if (RT_FAILURE(vrc))
9790 {
9791 return setError(E_FAIL,
9792 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9793 path.c_str(),
9794 vrc);
9795 }
9796 }
9797
9798 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9799 path = Utf8Str(mData->m_strConfigFileFull);
9800 RTFILE f = NIL_RTFILE;
9801 vrc = RTFileOpen(&f, path.c_str(),
9802 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9803 if (RT_FAILURE(vrc))
9804 return setError(E_FAIL,
9805 tr("Could not create the settings file '%s' (%Rrc)"),
9806 path.c_str(),
9807 vrc);
9808 RTFileClose(f);
9809 }
9810
9811 return rc;
9812}
9813
9814/**
9815 * Saves and commits machine data, user data and hardware data.
9816 *
9817 * Note that on failure, the data remains uncommitted.
9818 *
9819 * @a aFlags may combine the following flags:
9820 *
9821 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9822 * Used when saving settings after an operation that makes them 100%
9823 * correspond to the settings from the current snapshot.
9824 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9825 * #isReallyModified() returns false. This is necessary for cases when we
9826 * change machine data directly, not through the backup()/commit() mechanism.
9827 * - SaveS_Force: settings will be saved without doing a deep compare of the
9828 * settings structures. This is used when this is called because snapshots
9829 * have changed to avoid the overhead of the deep compare.
9830 *
9831 * @note Must be called from under this object's write lock. Locks children for
9832 * writing.
9833 *
9834 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9835 * initialized to false and that will be set to true by this function if
9836 * the caller must invoke VirtualBox::i_saveSettings() because the global
9837 * settings have changed. This will happen if a machine rename has been
9838 * saved and the global machine and media registries will therefore need
9839 * updating.
9840 */
9841HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9842 int aFlags /*= 0*/)
9843{
9844 LogFlowThisFuncEnter();
9845
9846 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9847
9848 /* make sure child objects are unable to modify the settings while we are
9849 * saving them */
9850 i_ensureNoStateDependencies();
9851
9852 AssertReturn(!i_isSnapshotMachine(),
9853 E_FAIL);
9854
9855 HRESULT rc = S_OK;
9856 bool fNeedsWrite = false;
9857
9858 /* First, prepare to save settings. It will care about renaming the
9859 * settings directory and file if the machine name was changed and about
9860 * creating a new settings file if this is a new machine. */
9861 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9862 if (FAILED(rc)) return rc;
9863
9864 // keep a pointer to the current settings structures
9865 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9866 settings::MachineConfigFile *pNewConfig = NULL;
9867
9868 try
9869 {
9870 // make a fresh one to have everyone write stuff into
9871 pNewConfig = new settings::MachineConfigFile(NULL);
9872 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9873
9874 // now go and copy all the settings data from COM to the settings structures
9875 // (this calles i_saveSettings() on all the COM objects in the machine)
9876 i_copyMachineDataToSettings(*pNewConfig);
9877
9878 if (aFlags & SaveS_ResetCurStateModified)
9879 {
9880 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9881 mData->mCurrentStateModified = FALSE;
9882 fNeedsWrite = true; // always, no need to compare
9883 }
9884 else if (aFlags & SaveS_Force)
9885 {
9886 fNeedsWrite = true; // always, no need to compare
9887 }
9888 else
9889 {
9890 if (!mData->mCurrentStateModified)
9891 {
9892 // do a deep compare of the settings that we just saved with the settings
9893 // previously stored in the config file; this invokes MachineConfigFile::operator==
9894 // which does a deep compare of all the settings, which is expensive but less expensive
9895 // than writing out XML in vain
9896 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9897
9898 // could still be modified if any settings changed
9899 mData->mCurrentStateModified = fAnySettingsChanged;
9900
9901 fNeedsWrite = fAnySettingsChanged;
9902 }
9903 else
9904 fNeedsWrite = true;
9905 }
9906
9907 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9908
9909 if (fNeedsWrite)
9910 // now spit it all out!
9911 pNewConfig->write(mData->m_strConfigFileFull);
9912
9913 mData->pMachineConfigFile = pNewConfig;
9914 delete pOldConfig;
9915 i_commit();
9916
9917 // after saving settings, we are no longer different from the XML on disk
9918 mData->flModifications = 0;
9919 }
9920 catch (HRESULT err)
9921 {
9922 // we assume that error info is set by the thrower
9923 rc = err;
9924
9925 // restore old config
9926 delete pNewConfig;
9927 mData->pMachineConfigFile = pOldConfig;
9928 }
9929 catch (...)
9930 {
9931 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9932 }
9933
9934 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9935 {
9936 /* Fire the data change event, even on failure (since we've already
9937 * committed all data). This is done only for SessionMachines because
9938 * mutable Machine instances are always not registered (i.e. private
9939 * to the client process that creates them) and thus don't need to
9940 * inform callbacks. */
9941 if (i_isSessionMachine())
9942 mParent->i_onMachineDataChange(mData->mUuid);
9943 }
9944
9945 LogFlowThisFunc(("rc=%08X\n", rc));
9946 LogFlowThisFuncLeave();
9947 return rc;
9948}
9949
9950/**
9951 * Implementation for saving the machine settings into the given
9952 * settings::MachineConfigFile instance. This copies machine extradata
9953 * from the previous machine config file in the instance data, if any.
9954 *
9955 * This gets called from two locations:
9956 *
9957 * -- Machine::i_saveSettings(), during the regular XML writing;
9958 *
9959 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9960 * exported to OVF and we write the VirtualBox proprietary XML
9961 * into a <vbox:Machine> tag.
9962 *
9963 * This routine fills all the fields in there, including snapshots, *except*
9964 * for the following:
9965 *
9966 * -- fCurrentStateModified. There is some special logic associated with that.
9967 *
9968 * The caller can then call MachineConfigFile::write() or do something else
9969 * with it.
9970 *
9971 * Caller must hold the machine lock!
9972 *
9973 * This throws XML errors and HRESULT, so the caller must have a catch block!
9974 */
9975void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9976{
9977 // deep copy extradata
9978 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9979
9980 config.uuid = mData->mUuid;
9981
9982 // copy name, description, OS type, teleport, UTC etc.
9983 config.machineUserData = mUserData->s;
9984
9985 // Encode the Icon Override data from Machine and store on config userdata.
9986 std::vector<BYTE> iconByte;
9987 getIcon(iconByte);
9988 ssize_t cbData = iconByte.size();
9989 if (cbData > 0)
9990 {
9991 ssize_t cchOut = RTBase64EncodedLength(cbData);
9992 Utf8Str strIconData;
9993 strIconData.reserve(cchOut+1);
9994 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9995 strIconData.mutableRaw(), strIconData.capacity(),
9996 NULL);
9997 if (RT_FAILURE(vrc))
9998 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9999 strIconData.jolt();
10000 config.machineUserData.ovIcon = strIconData;
10001 }
10002 else
10003 config.machineUserData.ovIcon.setNull();
10004
10005 if ( mData->mMachineState == MachineState_Saved
10006 || mData->mMachineState == MachineState_Restoring
10007 // when doing certain snapshot operations we may or may not have
10008 // a saved state in the current state, so keep everything as is
10009 || ( ( mData->mMachineState == MachineState_Snapshotting
10010 || mData->mMachineState == MachineState_DeletingSnapshot)
10011 && (!mSSData->strStateFilePath.isEmpty())
10012 )
10013 )
10014 {
10015 Assert(!mSSData->strStateFilePath.isEmpty());
10016 /* try to make the file name relative to the settings file dir */
10017 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10018 }
10019 else
10020 {
10021 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10022 config.strStateFile.setNull();
10023 }
10024
10025 if (mData->mCurrentSnapshot)
10026 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10027 else
10028 config.uuidCurrentSnapshot.clear();
10029
10030 config.timeLastStateChange = mData->mLastStateChange;
10031 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10032 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10033
10034 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10035 if (FAILED(rc)) throw rc;
10036
10037 rc = i_saveStorageControllers(config.storageMachine);
10038 if (FAILED(rc)) throw rc;
10039
10040 // save machine's media registry if this is VirtualBox 4.0 or later
10041 if (config.canHaveOwnMediaRegistry())
10042 {
10043 // determine machine folder
10044 Utf8Str strMachineFolder = i_getSettingsFileFull();
10045 strMachineFolder.stripFilename();
10046 mParent->i_saveMediaRegistry(config.mediaRegistry,
10047 i_getId(), // only media with registry ID == machine UUID
10048 strMachineFolder);
10049 // this throws HRESULT
10050 }
10051
10052 // save snapshots
10053 rc = i_saveAllSnapshots(config);
10054 if (FAILED(rc)) throw rc;
10055}
10056
10057/**
10058 * Saves all snapshots of the machine into the given machine config file. Called
10059 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10060 * @param config
10061 * @return
10062 */
10063HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10064{
10065 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10066
10067 HRESULT rc = S_OK;
10068
10069 try
10070 {
10071 config.llFirstSnapshot.clear();
10072
10073 if (mData->mFirstSnapshot)
10074 {
10075 // the settings use a list for "the first snapshot"
10076 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
10077
10078 // get reference to the snapshot on the list and work on that
10079 // element straight in the list to avoid excessive copying later
10080 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10081 if (FAILED(rc)) throw rc;
10082 }
10083
10084// if (mType == IsSessionMachine)
10085// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10086
10087 }
10088 catch (HRESULT err)
10089 {
10090 /* we assume that error info is set by the thrower */
10091 rc = err;
10092 }
10093 catch (...)
10094 {
10095 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10096 }
10097
10098 return rc;
10099}
10100
10101/**
10102 * Saves the VM hardware configuration. It is assumed that the
10103 * given node is empty.
10104 *
10105 * @param data Reference to the settings object for the hardware config.
10106 * @param pDbg Pointer to the settings object for the debugging config
10107 * which happens to live in mHWData.
10108 * @param pAutostart Pointer to the settings object for the autostart config
10109 * which happens to live in mHWData.
10110 */
10111HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10112 settings::Autostart *pAutostart)
10113{
10114 HRESULT rc = S_OK;
10115
10116 try
10117 {
10118 /* The hardware version attribute (optional).
10119 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10120 if ( mHWData->mHWVersion == "1"
10121 && mSSData->strStateFilePath.isEmpty()
10122 )
10123 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10124 other point needs to be found where this can be done. */
10125
10126 data.strVersion = mHWData->mHWVersion;
10127 data.uuid = mHWData->mHardwareUUID;
10128
10129 // CPU
10130 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10131 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10132 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10133 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10134 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10135 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10136 data.fPAE = !!mHWData->mPAEEnabled;
10137 data.enmLongMode = mHWData->mLongMode;
10138 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10139 data.cCPUs = mHWData->mCPUCount;
10140 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10141 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10142 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10143
10144 data.llCpus.clear();
10145 if (data.fCpuHotPlug)
10146 {
10147 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10148 {
10149 if (mHWData->mCPUAttached[idx])
10150 {
10151 settings::Cpu cpu;
10152 cpu.ulId = idx;
10153 data.llCpus.push_back(cpu);
10154 }
10155 }
10156 }
10157
10158 /* Standard and Extended CPUID leafs. */
10159 data.llCpuIdLeafs.clear();
10160 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10161 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10162 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10163 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10164 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10165 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10166
10167 // memory
10168 data.ulMemorySizeMB = mHWData->mMemorySize;
10169 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10170
10171 // firmware
10172 data.firmwareType = mHWData->mFirmwareType;
10173
10174 // HID
10175 data.pointingHIDType = mHWData->mPointingHIDType;
10176 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10177
10178 // chipset
10179 data.chipsetType = mHWData->mChipsetType;
10180
10181 // paravirt
10182 data.paravirtProvider = mHWData->mParavirtProvider;
10183
10184
10185 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10186
10187 // HPET
10188 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10189
10190 // boot order
10191 data.mapBootOrder.clear();
10192 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10193 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10194
10195 // display
10196 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10197 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10198 data.cMonitors = mHWData->mMonitorCount;
10199 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10200 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10201 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10202 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10203 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10204 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10205 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10206 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10207 {
10208 if (mHWData->maVideoCaptureScreens[i])
10209 ASMBitSet(&data.u64VideoCaptureScreens, i);
10210 else
10211 ASMBitClear(&data.u64VideoCaptureScreens, i);
10212 }
10213 /* store relative video capture file if possible */
10214 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10215
10216 /* VRDEServer settings (optional) */
10217 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10218 if (FAILED(rc)) throw rc;
10219
10220 /* BIOS (required) */
10221 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10222 if (FAILED(rc)) throw rc;
10223
10224 /* USB Controller (required) */
10225 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10226 {
10227 ComObjPtr<USBController> ctrl = *it;
10228 settings::USBController settingsCtrl;
10229
10230 settingsCtrl.strName = ctrl->i_getName();
10231 settingsCtrl.enmType = ctrl->i_getControllerType();
10232
10233 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10234 }
10235
10236 /* USB device filters (required) */
10237 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10238 if (FAILED(rc)) throw rc;
10239
10240 /* Network adapters (required) */
10241 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10242 data.llNetworkAdapters.clear();
10243 /* Write out only the nominal number of network adapters for this
10244 * chipset type. Since Machine::commit() hasn't been called there
10245 * may be extra NIC settings in the vector. */
10246 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10247 {
10248 settings::NetworkAdapter nic;
10249 nic.ulSlot = (uint32_t)slot;
10250 /* paranoia check... must not be NULL, but must not crash either. */
10251 if (mNetworkAdapters[slot])
10252 {
10253 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10254 if (FAILED(rc)) throw rc;
10255
10256 data.llNetworkAdapters.push_back(nic);
10257 }
10258 }
10259
10260 /* Serial ports */
10261 data.llSerialPorts.clear();
10262 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10263 {
10264 settings::SerialPort s;
10265 s.ulSlot = slot;
10266 rc = mSerialPorts[slot]->i_saveSettings(s);
10267 if (FAILED(rc)) return rc;
10268
10269 data.llSerialPorts.push_back(s);
10270 }
10271
10272 /* Parallel ports */
10273 data.llParallelPorts.clear();
10274 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10275 {
10276 settings::ParallelPort p;
10277 p.ulSlot = slot;
10278 rc = mParallelPorts[slot]->i_saveSettings(p);
10279 if (FAILED(rc)) return rc;
10280
10281 data.llParallelPorts.push_back(p);
10282 }
10283
10284 /* Audio adapter */
10285 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10286 if (FAILED(rc)) return rc;
10287
10288 /* Shared folders */
10289 data.llSharedFolders.clear();
10290 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10291 it != mHWData->mSharedFolders.end();
10292 ++it)
10293 {
10294 SharedFolder *pSF = *it;
10295 AutoCaller sfCaller(pSF);
10296 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10297 settings::SharedFolder sf;
10298 sf.strName = pSF->i_getName();
10299 sf.strHostPath = pSF->i_getHostPath();
10300 sf.fWritable = !!pSF->i_isWritable();
10301 sf.fAutoMount = !!pSF->i_isAutoMounted();
10302
10303 data.llSharedFolders.push_back(sf);
10304 }
10305
10306 // clipboard
10307 data.clipboardMode = mHWData->mClipboardMode;
10308
10309 // drag'n'drop
10310 data.dndMode = mHWData->mDnDMode;
10311
10312 /* Guest */
10313 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10314
10315 // IO settings
10316 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10317 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10318
10319 /* BandwidthControl (required) */
10320 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10321 if (FAILED(rc)) throw rc;
10322
10323 /* Host PCI devices */
10324 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10325 it != mHWData->mPCIDeviceAssignments.end();
10326 ++it)
10327 {
10328 ComObjPtr<PCIDeviceAttachment> pda = *it;
10329 settings::HostPCIDeviceAttachment hpda;
10330
10331 rc = pda->i_saveSettings(hpda);
10332 if (FAILED(rc)) throw rc;
10333
10334 data.pciAttachments.push_back(hpda);
10335 }
10336
10337
10338 // guest properties
10339 data.llGuestProperties.clear();
10340#ifdef VBOX_WITH_GUEST_PROPS
10341 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10342 it != mHWData->mGuestProperties.end();
10343 ++it)
10344 {
10345 HWData::GuestProperty property = it->second;
10346
10347 /* Remove transient guest properties at shutdown unless we
10348 * are saving state */
10349 if ( ( mData->mMachineState == MachineState_PoweredOff
10350 || mData->mMachineState == MachineState_Aborted
10351 || mData->mMachineState == MachineState_Teleported)
10352 && ( property.mFlags & guestProp::TRANSIENT
10353 || property.mFlags & guestProp::TRANSRESET))
10354 continue;
10355 settings::GuestProperty prop;
10356 prop.strName = it->first;
10357 prop.strValue = property.strValue;
10358 prop.timestamp = property.mTimestamp;
10359 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10360 guestProp::writeFlags(property.mFlags, szFlags);
10361 prop.strFlags = szFlags;
10362
10363 data.llGuestProperties.push_back(prop);
10364 }
10365
10366 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10367 /* I presume this doesn't require a backup(). */
10368 mData->mGuestPropertiesModified = FALSE;
10369#endif /* VBOX_WITH_GUEST_PROPS defined */
10370
10371 *pDbg = mHWData->mDebugging;
10372 *pAutostart = mHWData->mAutostart;
10373
10374 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10375 }
10376 catch(std::bad_alloc &)
10377 {
10378 return E_OUTOFMEMORY;
10379 }
10380
10381 AssertComRC(rc);
10382 return rc;
10383}
10384
10385/**
10386 * Saves the storage controller configuration.
10387 *
10388 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10389 */
10390HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10391{
10392 data.llStorageControllers.clear();
10393
10394 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10395 it != mStorageControllers->end();
10396 ++it)
10397 {
10398 HRESULT rc;
10399 ComObjPtr<StorageController> pCtl = *it;
10400
10401 settings::StorageController ctl;
10402 ctl.strName = pCtl->i_getName();
10403 ctl.controllerType = pCtl->i_getControllerType();
10404 ctl.storageBus = pCtl->i_getStorageBus();
10405 ctl.ulInstance = pCtl->i_getInstance();
10406 ctl.fBootable = pCtl->i_getBootable();
10407
10408 /* Save the port count. */
10409 ULONG portCount;
10410 rc = pCtl->COMGETTER(PortCount)(&portCount);
10411 ComAssertComRCRet(rc, rc);
10412 ctl.ulPortCount = portCount;
10413
10414 /* Save fUseHostIOCache */
10415 BOOL fUseHostIOCache;
10416 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10417 ComAssertComRCRet(rc, rc);
10418 ctl.fUseHostIOCache = !!fUseHostIOCache;
10419
10420 /* Save IDE emulation settings. */
10421 if (ctl.controllerType == StorageControllerType_IntelAhci)
10422 {
10423 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10424 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10425 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10426 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10427 )
10428 ComAssertComRCRet(rc, rc);
10429 }
10430
10431 /* save the devices now. */
10432 rc = i_saveStorageDevices(pCtl, ctl);
10433 ComAssertComRCRet(rc, rc);
10434
10435 data.llStorageControllers.push_back(ctl);
10436 }
10437
10438 return S_OK;
10439}
10440
10441/**
10442 * Saves the hard disk configuration.
10443 */
10444HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10445 settings::StorageController &data)
10446{
10447 MediaData::AttachmentList atts;
10448
10449 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10450 if (FAILED(rc)) return rc;
10451
10452 data.llAttachedDevices.clear();
10453 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10454 it != atts.end();
10455 ++it)
10456 {
10457 settings::AttachedDevice dev;
10458 IMediumAttachment *iA = *it;
10459 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10460 Medium *pMedium = pAttach->i_getMedium();
10461
10462 dev.deviceType = pAttach->i_getType();
10463 dev.lPort = pAttach->i_getPort();
10464 dev.lDevice = pAttach->i_getDevice();
10465 dev.fPassThrough = pAttach->i_getPassthrough();
10466 dev.fHotPluggable = pAttach->i_getHotPluggable();
10467 if (pMedium)
10468 {
10469 if (pMedium->i_isHostDrive())
10470 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10471 else
10472 dev.uuid = pMedium->i_getId();
10473 dev.fTempEject = pAttach->i_getTempEject();
10474 dev.fNonRotational = pAttach->i_getNonRotational();
10475 dev.fDiscard = pAttach->i_getDiscard();
10476 }
10477
10478 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10479
10480 data.llAttachedDevices.push_back(dev);
10481 }
10482
10483 return S_OK;
10484}
10485
10486/**
10487 * Saves machine state settings as defined by aFlags
10488 * (SaveSTS_* values).
10489 *
10490 * @param aFlags Combination of SaveSTS_* flags.
10491 *
10492 * @note Locks objects for writing.
10493 */
10494HRESULT Machine::i_saveStateSettings(int aFlags)
10495{
10496 if (aFlags == 0)
10497 return S_OK;
10498
10499 AutoCaller autoCaller(this);
10500 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10501
10502 /* This object's write lock is also necessary to serialize file access
10503 * (prevent concurrent reads and writes) */
10504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10505
10506 HRESULT rc = S_OK;
10507
10508 Assert(mData->pMachineConfigFile);
10509
10510 try
10511 {
10512 if (aFlags & SaveSTS_CurStateModified)
10513 mData->pMachineConfigFile->fCurrentStateModified = true;
10514
10515 if (aFlags & SaveSTS_StateFilePath)
10516 {
10517 if (!mSSData->strStateFilePath.isEmpty())
10518 /* try to make the file name relative to the settings file dir */
10519 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10520 else
10521 mData->pMachineConfigFile->strStateFile.setNull();
10522 }
10523
10524 if (aFlags & SaveSTS_StateTimeStamp)
10525 {
10526 Assert( mData->mMachineState != MachineState_Aborted
10527 || mSSData->strStateFilePath.isEmpty());
10528
10529 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10530
10531 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10532//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10533 }
10534
10535 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10536 }
10537 catch (...)
10538 {
10539 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10540 }
10541
10542 return rc;
10543}
10544
10545/**
10546 * Ensures that the given medium is added to a media registry. If this machine
10547 * was created with 4.0 or later, then the machine registry is used. Otherwise
10548 * the global VirtualBox media registry is used.
10549 *
10550 * Caller must NOT hold machine lock, media tree or any medium locks!
10551 *
10552 * @param pMedium
10553 */
10554void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10555{
10556 /* Paranoia checks: do not hold machine or media tree locks. */
10557 AssertReturnVoid(!isWriteLockOnCurrentThread());
10558 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10559
10560 ComObjPtr<Medium> pBase;
10561 {
10562 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10563 pBase = pMedium->i_getBase();
10564 }
10565
10566 /* Paranoia checks: do not hold medium locks. */
10567 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10568 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10569
10570 // decide which medium registry to use now that the medium is attached:
10571 Guid uuid;
10572 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10573 // machine XML is VirtualBox 4.0 or higher:
10574 uuid = i_getId(); // machine UUID
10575 else
10576 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10577
10578 if (pMedium->i_addRegistry(uuid))
10579 mParent->i_markRegistryModified(uuid);
10580
10581 /* For more complex hard disk structures it can happen that the base
10582 * medium isn't yet associated with any medium registry. Do that now. */
10583 if (pMedium != pBase)
10584 {
10585 /* Tree lock needed by Medium::addRegistry when recursing. */
10586 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10587 if (pBase->i_addRegistryRecursive(uuid))
10588 {
10589 treeLock.release();
10590 mParent->i_markRegistryModified(uuid);
10591 }
10592 }
10593}
10594
10595/**
10596 * Creates differencing hard disks for all normal hard disks attached to this
10597 * machine and a new set of attachments to refer to created disks.
10598 *
10599 * Used when taking a snapshot or when deleting the current state. Gets called
10600 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10601 *
10602 * This method assumes that mMediaData contains the original hard disk attachments
10603 * it needs to create diffs for. On success, these attachments will be replaced
10604 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10605 * called to delete created diffs which will also rollback mMediaData and restore
10606 * whatever was backed up before calling this method.
10607 *
10608 * Attachments with non-normal hard disks are left as is.
10609 *
10610 * If @a aOnline is @c false then the original hard disks that require implicit
10611 * diffs will be locked for reading. Otherwise it is assumed that they are
10612 * already locked for writing (when the VM was started). Note that in the latter
10613 * case it is responsibility of the caller to lock the newly created diffs for
10614 * writing if this method succeeds.
10615 *
10616 * @param aProgress Progress object to run (must contain at least as
10617 * many operations left as the number of hard disks
10618 * attached).
10619 * @param aOnline Whether the VM was online prior to this operation.
10620 *
10621 * @note The progress object is not marked as completed, neither on success nor
10622 * on failure. This is a responsibility of the caller.
10623 *
10624 * @note Locks this object and the media tree for writing.
10625 */
10626HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10627 ULONG aWeight,
10628 bool aOnline)
10629{
10630 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10631
10632 AutoCaller autoCaller(this);
10633 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10634
10635 AutoMultiWriteLock2 alock(this->lockHandle(),
10636 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10637
10638 /* must be in a protective state because we release the lock below */
10639 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10640 || mData->mMachineState == MachineState_OnlineSnapshotting
10641 || mData->mMachineState == MachineState_LiveSnapshotting
10642 || mData->mMachineState == MachineState_RestoringSnapshot
10643 || mData->mMachineState == MachineState_DeletingSnapshot
10644 , E_FAIL);
10645
10646 HRESULT rc = S_OK;
10647
10648 // use appropriate locked media map (online or offline)
10649 MediumLockListMap lockedMediaOffline;
10650 MediumLockListMap *lockedMediaMap;
10651 if (aOnline)
10652 lockedMediaMap = &mData->mSession.mLockedMedia;
10653 else
10654 lockedMediaMap = &lockedMediaOffline;
10655
10656 try
10657 {
10658 if (!aOnline)
10659 {
10660 /* lock all attached hard disks early to detect "in use"
10661 * situations before creating actual diffs */
10662 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10663 it != mMediaData->mAttachments.end();
10664 ++it)
10665 {
10666 MediumAttachment* pAtt = *it;
10667 if (pAtt->i_getType() == DeviceType_HardDisk)
10668 {
10669 Medium* pMedium = pAtt->i_getMedium();
10670 Assert(pMedium);
10671
10672 MediumLockList *pMediumLockList(new MediumLockList());
10673 alock.release();
10674 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10675 false /* fMediumLockWrite */,
10676 false /* fMediumLockWriteAll */,
10677 NULL,
10678 *pMediumLockList);
10679 alock.acquire();
10680 if (FAILED(rc))
10681 {
10682 delete pMediumLockList;
10683 throw rc;
10684 }
10685 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10686 if (FAILED(rc))
10687 {
10688 throw setError(rc,
10689 tr("Collecting locking information for all attached media failed"));
10690 }
10691 }
10692 }
10693
10694 /* Now lock all media. If this fails, nothing is locked. */
10695 alock.release();
10696 rc = lockedMediaMap->Lock();
10697 alock.acquire();
10698 if (FAILED(rc))
10699 {
10700 throw setError(rc,
10701 tr("Locking of attached media failed"));
10702 }
10703 }
10704
10705 /* remember the current list (note that we don't use backup() since
10706 * mMediaData may be already backed up) */
10707 MediaData::AttachmentList atts = mMediaData->mAttachments;
10708
10709 /* start from scratch */
10710 mMediaData->mAttachments.clear();
10711
10712 /* go through remembered attachments and create diffs for normal hard
10713 * disks and attach them */
10714 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10715 it != atts.end();
10716 ++it)
10717 {
10718 MediumAttachment* pAtt = *it;
10719
10720 DeviceType_T devType = pAtt->i_getType();
10721 Medium* pMedium = pAtt->i_getMedium();
10722
10723 if ( devType != DeviceType_HardDisk
10724 || pMedium == NULL
10725 || pMedium->i_getType() != MediumType_Normal)
10726 {
10727 /* copy the attachment as is */
10728
10729 /** @todo the progress object created in SessionMachine::TakeSnaphot
10730 * only expects operations for hard disks. Later other
10731 * device types need to show up in the progress as well. */
10732 if (devType == DeviceType_HardDisk)
10733 {
10734 if (pMedium == NULL)
10735 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10736 aWeight); // weight
10737 else
10738 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10739 pMedium->i_getBase()->i_getName().c_str()).raw(),
10740 aWeight); // weight
10741 }
10742
10743 mMediaData->mAttachments.push_back(pAtt);
10744 continue;
10745 }
10746
10747 /* need a diff */
10748 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10749 pMedium->i_getBase()->i_getName().c_str()).raw(),
10750 aWeight); // weight
10751
10752 Utf8Str strFullSnapshotFolder;
10753 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10754
10755 ComObjPtr<Medium> diff;
10756 diff.createObject();
10757 // store the diff in the same registry as the parent
10758 // (this cannot fail here because we can't create implicit diffs for
10759 // unregistered images)
10760 Guid uuidRegistryParent;
10761 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10762 Assert(fInRegistry); NOREF(fInRegistry);
10763 rc = diff->init(mParent,
10764 pMedium->i_getPreferredDiffFormat(),
10765 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10766 uuidRegistryParent,
10767 DeviceType_HardDisk);
10768 if (FAILED(rc)) throw rc;
10769
10770 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10771 * the push_back? Looks like we're going to release medium with the
10772 * wrong kind of lock (general issue with if we fail anywhere at all)
10773 * and an orphaned VDI in the snapshots folder. */
10774
10775 /* update the appropriate lock list */
10776 MediumLockList *pMediumLockList;
10777 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10778 AssertComRCThrowRC(rc);
10779 if (aOnline)
10780 {
10781 alock.release();
10782 /* The currently attached medium will be read-only, change
10783 * the lock type to read. */
10784 rc = pMediumLockList->Update(pMedium, false);
10785 alock.acquire();
10786 AssertComRCThrowRC(rc);
10787 }
10788
10789 /* release the locks before the potentially lengthy operation */
10790 alock.release();
10791 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10792 pMediumLockList,
10793 NULL /* aProgress */,
10794 true /* aWait */);
10795 alock.acquire();
10796 if (FAILED(rc)) throw rc;
10797
10798 /* actual lock list update is done in Medium::commitMedia */
10799
10800 rc = diff->i_addBackReference(mData->mUuid);
10801 AssertComRCThrowRC(rc);
10802
10803 /* add a new attachment */
10804 ComObjPtr<MediumAttachment> attachment;
10805 attachment.createObject();
10806 rc = attachment->init(this,
10807 diff,
10808 pAtt->i_getControllerName(),
10809 pAtt->i_getPort(),
10810 pAtt->i_getDevice(),
10811 DeviceType_HardDisk,
10812 true /* aImplicit */,
10813 false /* aPassthrough */,
10814 false /* aTempEject */,
10815 pAtt->i_getNonRotational(),
10816 pAtt->i_getDiscard(),
10817 pAtt->i_getHotPluggable(),
10818 pAtt->i_getBandwidthGroup());
10819 if (FAILED(rc)) throw rc;
10820
10821 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10822 AssertComRCThrowRC(rc);
10823 mMediaData->mAttachments.push_back(attachment);
10824 }
10825 }
10826 catch (HRESULT aRC) { rc = aRC; }
10827
10828 /* unlock all hard disks we locked when there is no VM */
10829 if (!aOnline)
10830 {
10831 ErrorInfoKeeper eik;
10832
10833 HRESULT rc1 = lockedMediaMap->Clear();
10834 AssertComRC(rc1);
10835 }
10836
10837 return rc;
10838}
10839
10840/**
10841 * Deletes implicit differencing hard disks created either by
10842 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10843 *
10844 * Note that to delete hard disks created by #AttachDevice() this method is
10845 * called from #fixupMedia() when the changes are rolled back.
10846 *
10847 * @note Locks this object and the media tree for writing.
10848 */
10849HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10850{
10851 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10852
10853 AutoCaller autoCaller(this);
10854 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10855
10856 AutoMultiWriteLock2 alock(this->lockHandle(),
10857 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10858
10859 /* We absolutely must have backed up state. */
10860 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10861
10862 /* Check if there are any implicitly created diff images. */
10863 bool fImplicitDiffs = false;
10864 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10865 it != mMediaData->mAttachments.end();
10866 ++it)
10867 {
10868 const ComObjPtr<MediumAttachment> &pAtt = *it;
10869 if (pAtt->i_isImplicit())
10870 {
10871 fImplicitDiffs = true;
10872 break;
10873 }
10874 }
10875 /* If there is nothing to do, leave early. This saves lots of image locking
10876 * effort. It also avoids a MachineStateChanged event without real reason.
10877 * This is important e.g. when loading a VM config, because there should be
10878 * no events. Otherwise API clients can become thoroughly confused for
10879 * inaccessible VMs (the code for loading VM configs uses this method for
10880 * cleanup if the config makes no sense), as they take such events as an
10881 * indication that the VM is alive, and they would force the VM config to
10882 * be reread, leading to an endless loop. */
10883 if (!fImplicitDiffs)
10884 return S_OK;
10885
10886 HRESULT rc = S_OK;
10887 MachineState_T oldState = mData->mMachineState;
10888
10889 /* will release the lock before the potentially lengthy operation,
10890 * so protect with the special state (unless already protected) */
10891 if ( oldState != MachineState_Snapshotting
10892 && oldState != MachineState_OnlineSnapshotting
10893 && oldState != MachineState_LiveSnapshotting
10894 && oldState != MachineState_RestoringSnapshot
10895 && oldState != MachineState_DeletingSnapshot
10896 && oldState != MachineState_DeletingSnapshotOnline
10897 && oldState != MachineState_DeletingSnapshotPaused
10898 )
10899 i_setMachineState(MachineState_SettingUp);
10900
10901 // use appropriate locked media map (online or offline)
10902 MediumLockListMap lockedMediaOffline;
10903 MediumLockListMap *lockedMediaMap;
10904 if (aOnline)
10905 lockedMediaMap = &mData->mSession.mLockedMedia;
10906 else
10907 lockedMediaMap = &lockedMediaOffline;
10908
10909 try
10910 {
10911 if (!aOnline)
10912 {
10913 /* lock all attached hard disks early to detect "in use"
10914 * situations before deleting actual diffs */
10915 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10916 it != mMediaData->mAttachments.end();
10917 ++it)
10918 {
10919 MediumAttachment* pAtt = *it;
10920 if (pAtt->i_getType() == DeviceType_HardDisk)
10921 {
10922 Medium* pMedium = pAtt->i_getMedium();
10923 Assert(pMedium);
10924
10925 MediumLockList *pMediumLockList(new MediumLockList());
10926 alock.release();
10927 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10928 false /* fMediumLockWrite */,
10929 false /* fMediumLockWriteAll */,
10930 NULL,
10931 *pMediumLockList);
10932 alock.acquire();
10933
10934 if (FAILED(rc))
10935 {
10936 delete pMediumLockList;
10937 throw rc;
10938 }
10939
10940 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10941 if (FAILED(rc))
10942 throw rc;
10943 }
10944 }
10945
10946 if (FAILED(rc))
10947 throw rc;
10948 } // end of offline
10949
10950 /* Lock lists are now up to date and include implicitly created media */
10951
10952 /* Go through remembered attachments and delete all implicitly created
10953 * diffs and fix up the attachment information */
10954 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10955 MediaData::AttachmentList implicitAtts;
10956 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10957 it != mMediaData->mAttachments.end();
10958 ++it)
10959 {
10960 ComObjPtr<MediumAttachment> pAtt = *it;
10961 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10962 if (pMedium.isNull())
10963 continue;
10964
10965 // Implicit attachments go on the list for deletion and back references are removed.
10966 if (pAtt->i_isImplicit())
10967 {
10968 /* Deassociate and mark for deletion */
10969 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10970 rc = pMedium->i_removeBackReference(mData->mUuid);
10971 if (FAILED(rc))
10972 throw rc;
10973 implicitAtts.push_back(pAtt);
10974 continue;
10975 }
10976
10977 /* Was this medium attached before? */
10978 if (!i_findAttachment(oldAtts, pMedium))
10979 {
10980 /* no: de-associate */
10981 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10982 rc = pMedium->i_removeBackReference(mData->mUuid);
10983 if (FAILED(rc))
10984 throw rc;
10985 continue;
10986 }
10987 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10988 }
10989
10990 /* If there are implicit attachments to delete, throw away the lock
10991 * map contents (which will unlock all media) since the medium
10992 * attachments will be rolled back. Below we need to completely
10993 * recreate the lock map anyway since it is infinitely complex to
10994 * do this incrementally (would need reconstructing each attachment
10995 * change, which would be extremely hairy). */
10996 if (implicitAtts.size() != 0)
10997 {
10998 ErrorInfoKeeper eik;
10999
11000 HRESULT rc1 = lockedMediaMap->Clear();
11001 AssertComRC(rc1);
11002 }
11003
11004 /* rollback hard disk changes */
11005 mMediaData.rollback();
11006
11007 MultiResult mrc(S_OK);
11008
11009 // Delete unused implicit diffs.
11010 if (implicitAtts.size() != 0)
11011 {
11012 alock.release();
11013
11014 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11015 {
11016 // Remove medium associated with this attachment.
11017 ComObjPtr<MediumAttachment> pAtt = *it;
11018 Assert(pAtt);
11019 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11020 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11021 Assert(pMedium);
11022
11023 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11024 // continue on delete failure, just collect error messages
11025 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11026 pMedium->i_getLocationFull().c_str() ));
11027 mrc = rc;
11028 }
11029 // Clear the list of deleted implicit attachments now, while not
11030 // holding the lock, as it will ultimately trigger Medium::uninit()
11031 // calls which assume that the media tree lock isn't held.
11032 implicitAtts.clear();
11033
11034 alock.acquire();
11035
11036 /* if there is a VM recreate media lock map as mentioned above,
11037 * otherwise it is a waste of time and we leave things unlocked */
11038 if (aOnline)
11039 {
11040 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11041 /* must never be NULL, but better safe than sorry */
11042 if (!pMachine.isNull())
11043 {
11044 alock.release();
11045 rc = mData->mSession.mMachine->i_lockMedia();
11046 alock.acquire();
11047 if (FAILED(rc))
11048 throw rc;
11049 }
11050 }
11051 }
11052 }
11053 catch (HRESULT aRC) {rc = aRC;}
11054
11055 if (mData->mMachineState == MachineState_SettingUp)
11056 i_setMachineState(oldState);
11057
11058 /* unlock all hard disks we locked when there is no VM */
11059 if (!aOnline)
11060 {
11061 ErrorInfoKeeper eik;
11062
11063 HRESULT rc1 = lockedMediaMap->Clear();
11064 AssertComRC(rc1);
11065 }
11066
11067 return rc;
11068}
11069
11070
11071/**
11072 * Looks through the given list of media attachments for one with the given parameters
11073 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11074 * can be searched as well if needed.
11075 *
11076 * @param list
11077 * @param aControllerName
11078 * @param aControllerPort
11079 * @param aDevice
11080 * @return
11081 */
11082MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11083 IN_BSTR aControllerName,
11084 LONG aControllerPort,
11085 LONG aDevice)
11086{
11087 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11088 {
11089 MediumAttachment *pAttach = *it;
11090 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11091 return pAttach;
11092 }
11093
11094 return NULL;
11095}
11096
11097/**
11098 * Looks through the given list of media attachments for one with the given parameters
11099 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11100 * can be searched as well if needed.
11101 *
11102 * @param list
11103 * @param aControllerName
11104 * @param aControllerPort
11105 * @param aDevice
11106 * @return
11107 */
11108MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11109 ComObjPtr<Medium> pMedium)
11110{
11111 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11112 {
11113 MediumAttachment *pAttach = *it;
11114 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11115 if (pMediumThis == pMedium)
11116 return pAttach;
11117 }
11118
11119 return NULL;
11120}
11121
11122/**
11123 * Looks through the given list of media attachments for one with the given parameters
11124 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11125 * can be searched as well if needed.
11126 *
11127 * @param list
11128 * @param aControllerName
11129 * @param aControllerPort
11130 * @param aDevice
11131 * @return
11132 */
11133MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11134 Guid &id)
11135{
11136 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11137 {
11138 MediumAttachment *pAttach = *it;
11139 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11140 if (pMediumThis->i_getId() == id)
11141 return pAttach;
11142 }
11143
11144 return NULL;
11145}
11146
11147/**
11148 * Main implementation for Machine::DetachDevice. This also gets called
11149 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11150 *
11151 * @param pAttach Medium attachment to detach.
11152 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11153 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11154 * SnapshotMachine, and this must be its snapshot.
11155 * @return
11156 */
11157HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11158 AutoWriteLock &writeLock,
11159 Snapshot *pSnapshot)
11160{
11161 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11162 DeviceType_T mediumType = pAttach->i_getType();
11163
11164 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11165
11166 if (pAttach->i_isImplicit())
11167 {
11168 /* attempt to implicitly delete the implicitly created diff */
11169
11170 /// @todo move the implicit flag from MediumAttachment to Medium
11171 /// and forbid any hard disk operation when it is implicit. Or maybe
11172 /// a special media state for it to make it even more simple.
11173
11174 Assert(mMediaData.isBackedUp());
11175
11176 /* will release the lock before the potentially lengthy operation, so
11177 * protect with the special state */
11178 MachineState_T oldState = mData->mMachineState;
11179 i_setMachineState(MachineState_SettingUp);
11180
11181 writeLock.release();
11182
11183 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11184 true /*aWait*/);
11185
11186 writeLock.acquire();
11187
11188 i_setMachineState(oldState);
11189
11190 if (FAILED(rc)) return rc;
11191 }
11192
11193 i_setModified(IsModified_Storage);
11194 mMediaData.backup();
11195 mMediaData->mAttachments.remove(pAttach);
11196
11197 if (!oldmedium.isNull())
11198 {
11199 // if this is from a snapshot, do not defer detachment to commitMedia()
11200 if (pSnapshot)
11201 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11202 // else if non-hard disk media, do not defer detachment to commitMedia() either
11203 else if (mediumType != DeviceType_HardDisk)
11204 oldmedium->i_removeBackReference(mData->mUuid);
11205 }
11206
11207 return S_OK;
11208}
11209
11210/**
11211 * Goes thru all media of the given list and
11212 *
11213 * 1) calls i_detachDevice() on each of them for this machine and
11214 * 2) adds all Medium objects found in the process to the given list,
11215 * depending on cleanupMode.
11216 *
11217 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11218 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11219 * media to the list.
11220 *
11221 * This gets called from Machine::Unregister, both for the actual Machine and
11222 * the SnapshotMachine objects that might be found in the snapshots.
11223 *
11224 * Requires caller and locking. The machine lock must be passed in because it
11225 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11226 *
11227 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11228 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11229 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11230 * Full, then all media get added;
11231 * otherwise no media get added.
11232 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11233 * @return
11234 */
11235HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11236 Snapshot *pSnapshot,
11237 CleanupMode_T cleanupMode,
11238 MediaList &llMedia)
11239{
11240 Assert(isWriteLockOnCurrentThread());
11241
11242 HRESULT rc;
11243
11244 // make a temporary list because i_detachDevice invalidates iterators into
11245 // mMediaData->mAttachments
11246 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11247
11248 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11249 {
11250 ComObjPtr<MediumAttachment> &pAttach = *it;
11251 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11252
11253 if (!pMedium.isNull())
11254 {
11255 AutoCaller mac(pMedium);
11256 if (FAILED(mac.rc())) return mac.rc();
11257 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11258 DeviceType_T devType = pMedium->i_getDeviceType();
11259 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11260 && devType == DeviceType_HardDisk)
11261 || (cleanupMode == CleanupMode_Full)
11262 )
11263 {
11264 llMedia.push_back(pMedium);
11265 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11266 /* Not allowed to keep this lock as below we need the parent
11267 * medium lock, and the lock order is parent to child. */
11268 lock.release();
11269 /*
11270 * Search for medias which are not attached to any machine, but
11271 * in the chain to an attached disk. Mediums are only consided
11272 * if they are:
11273 * - have only one child
11274 * - no references to any machines
11275 * - are of normal medium type
11276 */
11277 while (!pParent.isNull())
11278 {
11279 AutoCaller mac1(pParent);
11280 if (FAILED(mac1.rc())) return mac1.rc();
11281 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11282 if (pParent->i_getChildren().size() == 1)
11283 {
11284 if ( pParent->i_getMachineBackRefCount() == 0
11285 && pParent->i_getType() == MediumType_Normal
11286 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11287 llMedia.push_back(pParent);
11288 }
11289 else
11290 break;
11291 pParent = pParent->i_getParent();
11292 }
11293 }
11294 }
11295
11296 // real machine: then we need to use the proper method
11297 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11298
11299 if (FAILED(rc))
11300 return rc;
11301 }
11302
11303 return S_OK;
11304}
11305
11306/**
11307 * Perform deferred hard disk detachments.
11308 *
11309 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11310 * backed up).
11311 *
11312 * If @a aOnline is @c true then this method will also unlock the old hard disks
11313 * for which the new implicit diffs were created and will lock these new diffs for
11314 * writing.
11315 *
11316 * @param aOnline Whether the VM was online prior to this operation.
11317 *
11318 * @note Locks this object for writing!
11319 */
11320void Machine::i_commitMedia(bool aOnline /*= false*/)
11321{
11322 AutoCaller autoCaller(this);
11323 AssertComRCReturnVoid(autoCaller.rc());
11324
11325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11326
11327 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11328
11329 HRESULT rc = S_OK;
11330
11331 /* no attach/detach operations -- nothing to do */
11332 if (!mMediaData.isBackedUp())
11333 return;
11334
11335 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11336 bool fMediaNeedsLocking = false;
11337
11338 /* enumerate new attachments */
11339 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11340 it != mMediaData->mAttachments.end();
11341 ++it)
11342 {
11343 MediumAttachment *pAttach = *it;
11344
11345 pAttach->i_commit();
11346
11347 Medium* pMedium = pAttach->i_getMedium();
11348 bool fImplicit = pAttach->i_isImplicit();
11349
11350 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11351 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11352 fImplicit));
11353
11354 /** @todo convert all this Machine-based voodoo to MediumAttachment
11355 * based commit logic. */
11356 if (fImplicit)
11357 {
11358 /* convert implicit attachment to normal */
11359 pAttach->i_setImplicit(false);
11360
11361 if ( aOnline
11362 && pMedium
11363 && pAttach->i_getType() == DeviceType_HardDisk
11364 )
11365 {
11366 /* update the appropriate lock list */
11367 MediumLockList *pMediumLockList;
11368 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11369 AssertComRC(rc);
11370 if (pMediumLockList)
11371 {
11372 /* unlock if there's a need to change the locking */
11373 if (!fMediaNeedsLocking)
11374 {
11375 rc = mData->mSession.mLockedMedia.Unlock();
11376 AssertComRC(rc);
11377 fMediaNeedsLocking = true;
11378 }
11379 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11380 AssertComRC(rc);
11381 rc = pMediumLockList->Append(pMedium, true);
11382 AssertComRC(rc);
11383 }
11384 }
11385
11386 continue;
11387 }
11388
11389 if (pMedium)
11390 {
11391 /* was this medium attached before? */
11392 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11393 {
11394 MediumAttachment *pOldAttach = *oldIt;
11395 if (pOldAttach->i_getMedium() == pMedium)
11396 {
11397 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11398
11399 /* yes: remove from old to avoid de-association */
11400 oldAtts.erase(oldIt);
11401 break;
11402 }
11403 }
11404 }
11405 }
11406
11407 /* enumerate remaining old attachments and de-associate from the
11408 * current machine state */
11409 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11410 {
11411 MediumAttachment *pAttach = *it;
11412 Medium* pMedium = pAttach->i_getMedium();
11413
11414 /* Detach only hard disks, since DVD/floppy media is detached
11415 * instantly in MountMedium. */
11416 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11417 {
11418 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11419
11420 /* now de-associate from the current machine state */
11421 rc = pMedium->i_removeBackReference(mData->mUuid);
11422 AssertComRC(rc);
11423
11424 if (aOnline)
11425 {
11426 /* unlock since medium is not used anymore */
11427 MediumLockList *pMediumLockList;
11428 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11429 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11430 {
11431 /* this happens for online snapshots, there the attachment
11432 * is changing, but only to a diff image created under
11433 * the old one, so there is no separate lock list */
11434 Assert(!pMediumLockList);
11435 }
11436 else
11437 {
11438 AssertComRC(rc);
11439 if (pMediumLockList)
11440 {
11441 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11442 AssertComRC(rc);
11443 }
11444 }
11445 }
11446 }
11447 }
11448
11449 /* take media locks again so that the locking state is consistent */
11450 if (fMediaNeedsLocking)
11451 {
11452 Assert(aOnline);
11453 rc = mData->mSession.mLockedMedia.Lock();
11454 AssertComRC(rc);
11455 }
11456
11457 /* commit the hard disk changes */
11458 mMediaData.commit();
11459
11460 if (i_isSessionMachine())
11461 {
11462 /*
11463 * Update the parent machine to point to the new owner.
11464 * This is necessary because the stored parent will point to the
11465 * session machine otherwise and cause crashes or errors later
11466 * when the session machine gets invalid.
11467 */
11468 /** @todo Change the MediumAttachment class to behave like any other
11469 * class in this regard by creating peer MediumAttachment
11470 * objects for session machines and share the data with the peer
11471 * machine.
11472 */
11473 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11474 it != mMediaData->mAttachments.end();
11475 ++it)
11476 (*it)->i_updateParentMachine(mPeer);
11477
11478 /* attach new data to the primary machine and reshare it */
11479 mPeer->mMediaData.attach(mMediaData);
11480 }
11481
11482 return;
11483}
11484
11485/**
11486 * Perform deferred deletion of implicitly created diffs.
11487 *
11488 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11489 * backed up).
11490 *
11491 * @note Locks this object for writing!
11492 */
11493void Machine::i_rollbackMedia()
11494{
11495 AutoCaller autoCaller(this);
11496 AssertComRCReturnVoid(autoCaller.rc());
11497
11498 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11499 LogFlowThisFunc(("Entering rollbackMedia\n"));
11500
11501 HRESULT rc = S_OK;
11502
11503 /* no attach/detach operations -- nothing to do */
11504 if (!mMediaData.isBackedUp())
11505 return;
11506
11507 /* enumerate new attachments */
11508 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11509 it != mMediaData->mAttachments.end();
11510 ++it)
11511 {
11512 MediumAttachment *pAttach = *it;
11513 /* Fix up the backrefs for DVD/floppy media. */
11514 if (pAttach->i_getType() != DeviceType_HardDisk)
11515 {
11516 Medium* pMedium = pAttach->i_getMedium();
11517 if (pMedium)
11518 {
11519 rc = pMedium->i_removeBackReference(mData->mUuid);
11520 AssertComRC(rc);
11521 }
11522 }
11523
11524 (*it)->i_rollback();
11525
11526 pAttach = *it;
11527 /* Fix up the backrefs for DVD/floppy media. */
11528 if (pAttach->i_getType() != DeviceType_HardDisk)
11529 {
11530 Medium* pMedium = pAttach->i_getMedium();
11531 if (pMedium)
11532 {
11533 rc = pMedium->i_addBackReference(mData->mUuid);
11534 AssertComRC(rc);
11535 }
11536 }
11537 }
11538
11539 /** @todo convert all this Machine-based voodoo to MediumAttachment
11540 * based rollback logic. */
11541 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11542
11543 return;
11544}
11545
11546/**
11547 * Returns true if the settings file is located in the directory named exactly
11548 * as the machine; this means, among other things, that the machine directory
11549 * should be auto-renamed.
11550 *
11551 * @param aSettingsDir if not NULL, the full machine settings file directory
11552 * name will be assigned there.
11553 *
11554 * @note Doesn't lock anything.
11555 * @note Not thread safe (must be called from this object's lock).
11556 */
11557bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11558{
11559 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11560 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11561 if (aSettingsDir)
11562 *aSettingsDir = strMachineDirName;
11563 strMachineDirName.stripPath(); // vmname
11564 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11565 strConfigFileOnly.stripPath() // vmname.vbox
11566 .stripSuffix(); // vmname
11567 /** @todo hack, make somehow use of ComposeMachineFilename */
11568 if (mUserData->s.fDirectoryIncludesUUID)
11569 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11570
11571 AssertReturn(!strMachineDirName.isEmpty(), false);
11572 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11573
11574 return strMachineDirName == strConfigFileOnly;
11575}
11576
11577/**
11578 * Discards all changes to machine settings.
11579 *
11580 * @param aNotify Whether to notify the direct session about changes or not.
11581 *
11582 * @note Locks objects for writing!
11583 */
11584void Machine::i_rollback(bool aNotify)
11585{
11586 AutoCaller autoCaller(this);
11587 AssertComRCReturn(autoCaller.rc(), (void)0);
11588
11589 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11590
11591 if (!mStorageControllers.isNull())
11592 {
11593 if (mStorageControllers.isBackedUp())
11594 {
11595 /* unitialize all new devices (absent in the backed up list). */
11596 StorageControllerList::const_iterator it = mStorageControllers->begin();
11597 StorageControllerList *backedList = mStorageControllers.backedUpData();
11598 while (it != mStorageControllers->end())
11599 {
11600 if ( std::find(backedList->begin(), backedList->end(), *it)
11601 == backedList->end()
11602 )
11603 {
11604 (*it)->uninit();
11605 }
11606 ++it;
11607 }
11608
11609 /* restore the list */
11610 mStorageControllers.rollback();
11611 }
11612
11613 /* rollback any changes to devices after restoring the list */
11614 if (mData->flModifications & IsModified_Storage)
11615 {
11616 StorageControllerList::const_iterator it = mStorageControllers->begin();
11617 while (it != mStorageControllers->end())
11618 {
11619 (*it)->i_rollback();
11620 ++it;
11621 }
11622 }
11623 }
11624
11625 if (!mUSBControllers.isNull())
11626 {
11627 if (mUSBControllers.isBackedUp())
11628 {
11629 /* unitialize all new devices (absent in the backed up list). */
11630 USBControllerList::const_iterator it = mUSBControllers->begin();
11631 USBControllerList *backedList = mUSBControllers.backedUpData();
11632 while (it != mUSBControllers->end())
11633 {
11634 if ( std::find(backedList->begin(), backedList->end(), *it)
11635 == backedList->end()
11636 )
11637 {
11638 (*it)->uninit();
11639 }
11640 ++it;
11641 }
11642
11643 /* restore the list */
11644 mUSBControllers.rollback();
11645 }
11646
11647 /* rollback any changes to devices after restoring the list */
11648 if (mData->flModifications & IsModified_USB)
11649 {
11650 USBControllerList::const_iterator it = mUSBControllers->begin();
11651 while (it != mUSBControllers->end())
11652 {
11653 (*it)->i_rollback();
11654 ++it;
11655 }
11656 }
11657 }
11658
11659 mUserData.rollback();
11660
11661 mHWData.rollback();
11662
11663 if (mData->flModifications & IsModified_Storage)
11664 i_rollbackMedia();
11665
11666 if (mBIOSSettings)
11667 mBIOSSettings->i_rollback();
11668
11669 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11670 mVRDEServer->i_rollback();
11671
11672 if (mAudioAdapter)
11673 mAudioAdapter->i_rollback();
11674
11675 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11676 mUSBDeviceFilters->i_rollback();
11677
11678 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11679 mBandwidthControl->i_rollback();
11680
11681 if (!mHWData.isNull())
11682 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11683 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11684 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11685 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11686
11687 if (mData->flModifications & IsModified_NetworkAdapters)
11688 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11689 if ( mNetworkAdapters[slot]
11690 && mNetworkAdapters[slot]->i_isModified())
11691 {
11692 mNetworkAdapters[slot]->i_rollback();
11693 networkAdapters[slot] = mNetworkAdapters[slot];
11694 }
11695
11696 if (mData->flModifications & IsModified_SerialPorts)
11697 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11698 if ( mSerialPorts[slot]
11699 && mSerialPorts[slot]->i_isModified())
11700 {
11701 mSerialPorts[slot]->i_rollback();
11702 serialPorts[slot] = mSerialPorts[slot];
11703 }
11704
11705 if (mData->flModifications & IsModified_ParallelPorts)
11706 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11707 if ( mParallelPorts[slot]
11708 && mParallelPorts[slot]->i_isModified())
11709 {
11710 mParallelPorts[slot]->i_rollback();
11711 parallelPorts[slot] = mParallelPorts[slot];
11712 }
11713
11714 if (aNotify)
11715 {
11716 /* inform the direct session about changes */
11717
11718 ComObjPtr<Machine> that = this;
11719 uint32_t flModifications = mData->flModifications;
11720 alock.release();
11721
11722 if (flModifications & IsModified_SharedFolders)
11723 that->i_onSharedFolderChange();
11724
11725 if (flModifications & IsModified_VRDEServer)
11726 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11727 if (flModifications & IsModified_USB)
11728 that->i_onUSBControllerChange();
11729
11730 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11731 if (networkAdapters[slot])
11732 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11733 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11734 if (serialPorts[slot])
11735 that->i_onSerialPortChange(serialPorts[slot]);
11736 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11737 if (parallelPorts[slot])
11738 that->i_onParallelPortChange(parallelPorts[slot]);
11739
11740 if (flModifications & IsModified_Storage)
11741 that->i_onStorageControllerChange();
11742
11743#if 0
11744 if (flModifications & IsModified_BandwidthControl)
11745 that->onBandwidthControlChange();
11746#endif
11747 }
11748}
11749
11750/**
11751 * Commits all the changes to machine settings.
11752 *
11753 * Note that this operation is supposed to never fail.
11754 *
11755 * @note Locks this object and children for writing.
11756 */
11757void Machine::i_commit()
11758{
11759 AutoCaller autoCaller(this);
11760 AssertComRCReturnVoid(autoCaller.rc());
11761
11762 AutoCaller peerCaller(mPeer);
11763 AssertComRCReturnVoid(peerCaller.rc());
11764
11765 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11766
11767 /*
11768 * use safe commit to ensure Snapshot machines (that share mUserData)
11769 * will still refer to a valid memory location
11770 */
11771 mUserData.commitCopy();
11772
11773 mHWData.commit();
11774
11775 if (mMediaData.isBackedUp())
11776 i_commitMedia(Global::IsOnline(mData->mMachineState));
11777
11778 mBIOSSettings->i_commit();
11779 mVRDEServer->i_commit();
11780 mAudioAdapter->i_commit();
11781 mUSBDeviceFilters->i_commit();
11782 mBandwidthControl->i_commit();
11783
11784 /* Since mNetworkAdapters is a list which might have been changed (resized)
11785 * without using the Backupable<> template we need to handle the copying
11786 * of the list entries manually, including the creation of peers for the
11787 * new objects. */
11788 bool commitNetworkAdapters = false;
11789 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11790 if (mPeer)
11791 {
11792 /* commit everything, even the ones which will go away */
11793 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11794 mNetworkAdapters[slot]->i_commit();
11795 /* copy over the new entries, creating a peer and uninit the original */
11796 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11797 for (size_t slot = 0; slot < newSize; slot++)
11798 {
11799 /* look if this adapter has a peer device */
11800 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11801 if (!peer)
11802 {
11803 /* no peer means the adapter is a newly created one;
11804 * create a peer owning data this data share it with */
11805 peer.createObject();
11806 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11807 }
11808 mPeer->mNetworkAdapters[slot] = peer;
11809 }
11810 /* uninit any no longer needed network adapters */
11811 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11812 mNetworkAdapters[slot]->uninit();
11813 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11814 {
11815 if (mPeer->mNetworkAdapters[slot])
11816 mPeer->mNetworkAdapters[slot]->uninit();
11817 }
11818 /* Keep the original network adapter count until this point, so that
11819 * discarding a chipset type change will not lose settings. */
11820 mNetworkAdapters.resize(newSize);
11821 mPeer->mNetworkAdapters.resize(newSize);
11822 }
11823 else
11824 {
11825 /* we have no peer (our parent is the newly created machine);
11826 * just commit changes to the network adapters */
11827 commitNetworkAdapters = true;
11828 }
11829 if (commitNetworkAdapters)
11830 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11831 mNetworkAdapters[slot]->i_commit();
11832
11833 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11834 mSerialPorts[slot]->i_commit();
11835 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11836 mParallelPorts[slot]->i_commit();
11837
11838 bool commitStorageControllers = false;
11839
11840 if (mStorageControllers.isBackedUp())
11841 {
11842 mStorageControllers.commit();
11843
11844 if (mPeer)
11845 {
11846 /* Commit all changes to new controllers (this will reshare data with
11847 * peers for those who have peers) */
11848 StorageControllerList *newList = new StorageControllerList();
11849 StorageControllerList::const_iterator it = mStorageControllers->begin();
11850 while (it != mStorageControllers->end())
11851 {
11852 (*it)->i_commit();
11853
11854 /* look if this controller has a peer device */
11855 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11856 if (!peer)
11857 {
11858 /* no peer means the device is a newly created one;
11859 * create a peer owning data this device share it with */
11860 peer.createObject();
11861 peer->init(mPeer, *it, true /* aReshare */);
11862 }
11863 else
11864 {
11865 /* remove peer from the old list */
11866 mPeer->mStorageControllers->remove(peer);
11867 }
11868 /* and add it to the new list */
11869 newList->push_back(peer);
11870
11871 ++it;
11872 }
11873
11874 /* uninit old peer's controllers that are left */
11875 it = mPeer->mStorageControllers->begin();
11876 while (it != mPeer->mStorageControllers->end())
11877 {
11878 (*it)->uninit();
11879 ++it;
11880 }
11881
11882 /* attach new list of controllers to our peer */
11883 mPeer->mStorageControllers.attach(newList);
11884 }
11885 else
11886 {
11887 /* we have no peer (our parent is the newly created machine);
11888 * just commit changes to devices */
11889 commitStorageControllers = true;
11890 }
11891 }
11892 else
11893 {
11894 /* the list of controllers itself is not changed,
11895 * just commit changes to controllers themselves */
11896 commitStorageControllers = true;
11897 }
11898
11899 if (commitStorageControllers)
11900 {
11901 StorageControllerList::const_iterator it = mStorageControllers->begin();
11902 while (it != mStorageControllers->end())
11903 {
11904 (*it)->i_commit();
11905 ++it;
11906 }
11907 }
11908
11909 bool commitUSBControllers = false;
11910
11911 if (mUSBControllers.isBackedUp())
11912 {
11913 mUSBControllers.commit();
11914
11915 if (mPeer)
11916 {
11917 /* Commit all changes to new controllers (this will reshare data with
11918 * peers for those who have peers) */
11919 USBControllerList *newList = new USBControllerList();
11920 USBControllerList::const_iterator it = mUSBControllers->begin();
11921 while (it != mUSBControllers->end())
11922 {
11923 (*it)->i_commit();
11924
11925 /* look if this controller has a peer device */
11926 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11927 if (!peer)
11928 {
11929 /* no peer means the device is a newly created one;
11930 * create a peer owning data this device share it with */
11931 peer.createObject();
11932 peer->init(mPeer, *it, true /* aReshare */);
11933 }
11934 else
11935 {
11936 /* remove peer from the old list */
11937 mPeer->mUSBControllers->remove(peer);
11938 }
11939 /* and add it to the new list */
11940 newList->push_back(peer);
11941
11942 ++it;
11943 }
11944
11945 /* uninit old peer's controllers that are left */
11946 it = mPeer->mUSBControllers->begin();
11947 while (it != mPeer->mUSBControllers->end())
11948 {
11949 (*it)->uninit();
11950 ++it;
11951 }
11952
11953 /* attach new list of controllers to our peer */
11954 mPeer->mUSBControllers.attach(newList);
11955 }
11956 else
11957 {
11958 /* we have no peer (our parent is the newly created machine);
11959 * just commit changes to devices */
11960 commitUSBControllers = true;
11961 }
11962 }
11963 else
11964 {
11965 /* the list of controllers itself is not changed,
11966 * just commit changes to controllers themselves */
11967 commitUSBControllers = true;
11968 }
11969
11970 if (commitUSBControllers)
11971 {
11972 USBControllerList::const_iterator it = mUSBControllers->begin();
11973 while (it != mUSBControllers->end())
11974 {
11975 (*it)->i_commit();
11976 ++it;
11977 }
11978 }
11979
11980 if (i_isSessionMachine())
11981 {
11982 /* attach new data to the primary machine and reshare it */
11983 mPeer->mUserData.attach(mUserData);
11984 mPeer->mHWData.attach(mHWData);
11985 /* mMediaData is reshared by fixupMedia */
11986 // mPeer->mMediaData.attach(mMediaData);
11987 Assert(mPeer->mMediaData.data() == mMediaData.data());
11988 }
11989}
11990
11991/**
11992 * Copies all the hardware data from the given machine.
11993 *
11994 * Currently, only called when the VM is being restored from a snapshot. In
11995 * particular, this implies that the VM is not running during this method's
11996 * call.
11997 *
11998 * @note This method must be called from under this object's lock.
11999 *
12000 * @note This method doesn't call #commit(), so all data remains backed up and
12001 * unsaved.
12002 */
12003void Machine::i_copyFrom(Machine *aThat)
12004{
12005 AssertReturnVoid(!i_isSnapshotMachine());
12006 AssertReturnVoid(aThat->i_isSnapshotMachine());
12007
12008 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12009
12010 mHWData.assignCopy(aThat->mHWData);
12011
12012 // create copies of all shared folders (mHWData after attaching a copy
12013 // contains just references to original objects)
12014 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12015 it != mHWData->mSharedFolders.end();
12016 ++it)
12017 {
12018 ComObjPtr<SharedFolder> folder;
12019 folder.createObject();
12020 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12021 AssertComRC(rc);
12022 *it = folder;
12023 }
12024
12025 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12026 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12027 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12028 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12029 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12030
12031 /* create private copies of all controllers */
12032 mStorageControllers.backup();
12033 mStorageControllers->clear();
12034 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12035 it != aThat->mStorageControllers->end();
12036 ++it)
12037 {
12038 ComObjPtr<StorageController> ctrl;
12039 ctrl.createObject();
12040 ctrl->initCopy(this, *it);
12041 mStorageControllers->push_back(ctrl);
12042 }
12043
12044 /* create private copies of all USB controllers */
12045 mUSBControllers.backup();
12046 mUSBControllers->clear();
12047 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12048 it != aThat->mUSBControllers->end();
12049 ++it)
12050 {
12051 ComObjPtr<USBController> ctrl;
12052 ctrl.createObject();
12053 ctrl->initCopy(this, *it);
12054 mUSBControllers->push_back(ctrl);
12055 }
12056
12057 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12058 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12059 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12060 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12061 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12062 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12063 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12064}
12065
12066/**
12067 * Returns whether the given storage controller is hotplug capable.
12068 *
12069 * @returns true if the controller supports hotplugging
12070 * false otherwise.
12071 * @param enmCtrlType The controller type to check for.
12072 */
12073bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12074{
12075 ComPtr<ISystemProperties> systemProperties;
12076 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12077 if (FAILED(rc))
12078 return false;
12079
12080 BOOL aHotplugCapable = FALSE;
12081 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12082
12083 return RT_BOOL(aHotplugCapable);
12084}
12085
12086#ifdef VBOX_WITH_RESOURCE_USAGE_API
12087
12088void Machine::i_getDiskList(MediaList &list)
12089{
12090 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12091 it != mMediaData->mAttachments.end();
12092 ++it)
12093 {
12094 MediumAttachment* pAttach = *it;
12095 /* just in case */
12096 AssertStmt(pAttach, continue);
12097
12098 AutoCaller localAutoCallerA(pAttach);
12099 if (FAILED(localAutoCallerA.rc())) continue;
12100
12101 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12102
12103 if (pAttach->i_getType() == DeviceType_HardDisk)
12104 list.push_back(pAttach->i_getMedium());
12105 }
12106}
12107
12108void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12109{
12110 AssertReturnVoid(isWriteLockOnCurrentThread());
12111 AssertPtrReturnVoid(aCollector);
12112
12113 pm::CollectorHAL *hal = aCollector->getHAL();
12114 /* Create sub metrics */
12115 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12116 "Percentage of processor time spent in user mode by the VM process.");
12117 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12118 "Percentage of processor time spent in kernel mode by the VM process.");
12119 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12120 "Size of resident portion of VM process in memory.");
12121 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12122 "Actual size of all VM disks combined.");
12123 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12124 "Network receive rate.");
12125 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12126 "Network transmit rate.");
12127 /* Create and register base metrics */
12128 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12129 cpuLoadUser, cpuLoadKernel);
12130 aCollector->registerBaseMetric(cpuLoad);
12131 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12132 ramUsageUsed);
12133 aCollector->registerBaseMetric(ramUsage);
12134 MediaList disks;
12135 i_getDiskList(disks);
12136 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12137 diskUsageUsed);
12138 aCollector->registerBaseMetric(diskUsage);
12139
12140 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12141 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12142 new pm::AggregateAvg()));
12143 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12144 new pm::AggregateMin()));
12145 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12146 new pm::AggregateMax()));
12147 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12148 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12149 new pm::AggregateAvg()));
12150 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12151 new pm::AggregateMin()));
12152 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12153 new pm::AggregateMax()));
12154
12155 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12156 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12157 new pm::AggregateAvg()));
12158 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12159 new pm::AggregateMin()));
12160 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12161 new pm::AggregateMax()));
12162
12163 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12164 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12165 new pm::AggregateAvg()));
12166 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12167 new pm::AggregateMin()));
12168 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12169 new pm::AggregateMax()));
12170
12171
12172 /* Guest metrics collector */
12173 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12174 aCollector->registerGuest(mCollectorGuest);
12175 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12176 this, __PRETTY_FUNCTION__, mCollectorGuest));
12177
12178 /* Create sub metrics */
12179 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12180 "Percentage of processor time spent in user mode as seen by the guest.");
12181 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12182 "Percentage of processor time spent in kernel mode as seen by the guest.");
12183 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12184 "Percentage of processor time spent idling as seen by the guest.");
12185
12186 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12187 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12188 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12189 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12190 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12191 pm::SubMetric *guestMemCache = new pm::SubMetric(
12192 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12193
12194 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12195 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12196
12197 /* Create and register base metrics */
12198 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12199 machineNetRx, machineNetTx);
12200 aCollector->registerBaseMetric(machineNetRate);
12201
12202 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12203 guestLoadUser, guestLoadKernel, guestLoadIdle);
12204 aCollector->registerBaseMetric(guestCpuLoad);
12205
12206 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12207 guestMemTotal, guestMemFree,
12208 guestMemBalloon, guestMemShared,
12209 guestMemCache, guestPagedTotal);
12210 aCollector->registerBaseMetric(guestCpuMem);
12211
12212 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12213 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12214 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12215 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12216
12217 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12218 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12219 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12220 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12221
12222 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12223 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12224 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12225 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12226
12227 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12228 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12229 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12230 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12231
12232 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12233 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12234 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12235 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12236
12237 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12238 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12239 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12240 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12241
12242 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12243 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12244 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12245 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12246
12247 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12248 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12249 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12250 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12251
12252 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12253 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12254 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12255 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12256
12257 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12258 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12259 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12260 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12261
12262 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12263 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12264 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12265 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12266}
12267
12268void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12269{
12270 AssertReturnVoid(isWriteLockOnCurrentThread());
12271
12272 if (aCollector)
12273 {
12274 aCollector->unregisterMetricsFor(aMachine);
12275 aCollector->unregisterBaseMetricsFor(aMachine);
12276 }
12277}
12278
12279#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12280
12281
12282////////////////////////////////////////////////////////////////////////////////
12283
12284DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12285
12286HRESULT SessionMachine::FinalConstruct()
12287{
12288 LogFlowThisFunc(("\n"));
12289
12290 mClientToken = NULL;
12291
12292 return BaseFinalConstruct();
12293}
12294
12295void SessionMachine::FinalRelease()
12296{
12297 LogFlowThisFunc(("\n"));
12298
12299 Assert(!mClientToken);
12300 /* paranoia, should not hang around any more */
12301 if (mClientToken)
12302 {
12303 delete mClientToken;
12304 mClientToken = NULL;
12305 }
12306
12307 uninit(Uninit::Unexpected);
12308
12309 BaseFinalRelease();
12310}
12311
12312/**
12313 * @note Must be called only by Machine::LockMachine() from its own write lock.
12314 */
12315HRESULT SessionMachine::init(Machine *aMachine)
12316{
12317 LogFlowThisFuncEnter();
12318 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12319
12320 AssertReturn(aMachine, E_INVALIDARG);
12321
12322 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12323
12324 /* Enclose the state transition NotReady->InInit->Ready */
12325 AutoInitSpan autoInitSpan(this);
12326 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12327
12328 HRESULT rc = S_OK;
12329
12330 /* create the machine client token */
12331 try
12332 {
12333 mClientToken = new ClientToken(aMachine, this);
12334 if (!mClientToken->isReady())
12335 {
12336 delete mClientToken;
12337 mClientToken = NULL;
12338 rc = E_FAIL;
12339 }
12340 }
12341 catch (std::bad_alloc &)
12342 {
12343 rc = E_OUTOFMEMORY;
12344 }
12345 if (FAILED(rc))
12346 return rc;
12347
12348 /* memorize the peer Machine */
12349 unconst(mPeer) = aMachine;
12350 /* share the parent pointer */
12351 unconst(mParent) = aMachine->mParent;
12352
12353 /* take the pointers to data to share */
12354 mData.share(aMachine->mData);
12355 mSSData.share(aMachine->mSSData);
12356
12357 mUserData.share(aMachine->mUserData);
12358 mHWData.share(aMachine->mHWData);
12359 mMediaData.share(aMachine->mMediaData);
12360
12361 mStorageControllers.allocate();
12362 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12363 it != aMachine->mStorageControllers->end();
12364 ++it)
12365 {
12366 ComObjPtr<StorageController> ctl;
12367 ctl.createObject();
12368 ctl->init(this, *it);
12369 mStorageControllers->push_back(ctl);
12370 }
12371
12372 mUSBControllers.allocate();
12373 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12374 it != aMachine->mUSBControllers->end();
12375 ++it)
12376 {
12377 ComObjPtr<USBController> ctl;
12378 ctl.createObject();
12379 ctl->init(this, *it);
12380 mUSBControllers->push_back(ctl);
12381 }
12382
12383 unconst(mBIOSSettings).createObject();
12384 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12385 /* create another VRDEServer object that will be mutable */
12386 unconst(mVRDEServer).createObject();
12387 mVRDEServer->init(this, aMachine->mVRDEServer);
12388 /* create another audio adapter object that will be mutable */
12389 unconst(mAudioAdapter).createObject();
12390 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12391 /* create a list of serial ports that will be mutable */
12392 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12393 {
12394 unconst(mSerialPorts[slot]).createObject();
12395 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12396 }
12397 /* create a list of parallel ports that will be mutable */
12398 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12399 {
12400 unconst(mParallelPorts[slot]).createObject();
12401 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12402 }
12403
12404 /* create another USB device filters object that will be mutable */
12405 unconst(mUSBDeviceFilters).createObject();
12406 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12407
12408 /* create a list of network adapters that will be mutable */
12409 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12410 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12411 {
12412 unconst(mNetworkAdapters[slot]).createObject();
12413 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12414 }
12415
12416 /* create another bandwidth control object that will be mutable */
12417 unconst(mBandwidthControl).createObject();
12418 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12419
12420 /* default is to delete saved state on Saved -> PoweredOff transition */
12421 mRemoveSavedState = true;
12422
12423 /* Confirm a successful initialization when it's the case */
12424 autoInitSpan.setSucceeded();
12425
12426 miNATNetworksStarted = 0;
12427
12428 LogFlowThisFuncLeave();
12429 return rc;
12430}
12431
12432/**
12433 * Uninitializes this session object. If the reason is other than
12434 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12435 * or the client watcher code.
12436 *
12437 * @param aReason uninitialization reason
12438 *
12439 * @note Locks mParent + this object for writing.
12440 */
12441void SessionMachine::uninit(Uninit::Reason aReason)
12442{
12443 LogFlowThisFuncEnter();
12444 LogFlowThisFunc(("reason=%d\n", aReason));
12445
12446 /*
12447 * Strongly reference ourselves to prevent this object deletion after
12448 * mData->mSession.mMachine.setNull() below (which can release the last
12449 * reference and call the destructor). Important: this must be done before
12450 * accessing any members (and before AutoUninitSpan that does it as well).
12451 * This self reference will be released as the very last step on return.
12452 */
12453 ComObjPtr<SessionMachine> selfRef = this;
12454
12455 /* Enclose the state transition Ready->InUninit->NotReady */
12456 AutoUninitSpan autoUninitSpan(this);
12457 if (autoUninitSpan.uninitDone())
12458 {
12459 LogFlowThisFunc(("Already uninitialized\n"));
12460 LogFlowThisFuncLeave();
12461 return;
12462 }
12463
12464 if (autoUninitSpan.initFailed())
12465 {
12466 /* We've been called by init() because it's failed. It's not really
12467 * necessary (nor it's safe) to perform the regular uninit sequence
12468 * below, the following is enough.
12469 */
12470 LogFlowThisFunc(("Initialization failed.\n"));
12471 /* destroy the machine client token */
12472 if (mClientToken)
12473 {
12474 delete mClientToken;
12475 mClientToken = NULL;
12476 }
12477 uninitDataAndChildObjects();
12478 mData.free();
12479 unconst(mParent) = NULL;
12480 unconst(mPeer) = NULL;
12481 LogFlowThisFuncLeave();
12482 return;
12483 }
12484
12485 MachineState_T lastState;
12486 {
12487 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12488 lastState = mData->mMachineState;
12489 }
12490 NOREF(lastState);
12491
12492#ifdef VBOX_WITH_USB
12493 // release all captured USB devices, but do this before requesting the locks below
12494 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12495 {
12496 /* Console::captureUSBDevices() is called in the VM process only after
12497 * setting the machine state to Starting or Restoring.
12498 * Console::detachAllUSBDevices() will be called upon successful
12499 * termination. So, we need to release USB devices only if there was
12500 * an abnormal termination of a running VM.
12501 *
12502 * This is identical to SessionMachine::DetachAllUSBDevices except
12503 * for the aAbnormal argument. */
12504 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12505 AssertComRC(rc);
12506 NOREF(rc);
12507
12508 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12509 if (service)
12510 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12511 }
12512#endif /* VBOX_WITH_USB */
12513
12514 // we need to lock this object in uninit() because the lock is shared
12515 // with mPeer (as well as data we modify below). mParent lock is needed
12516 // by several calls to it, and USB needs host lock.
12517 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12518
12519#ifdef VBOX_WITH_RESOURCE_USAGE_API
12520 /*
12521 * It is safe to call Machine::i_unregisterMetrics() here because
12522 * PerformanceCollector::samplerCallback no longer accesses guest methods
12523 * holding the lock.
12524 */
12525 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12526 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12527 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12528 this, __PRETTY_FUNCTION__, mCollectorGuest));
12529 if (mCollectorGuest)
12530 {
12531 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12532 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12533 mCollectorGuest = NULL;
12534 }
12535#endif
12536
12537 if (aReason == Uninit::Abnormal)
12538 {
12539 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12540 Global::IsOnlineOrTransient(lastState)));
12541
12542 /* reset the state to Aborted */
12543 if (mData->mMachineState != MachineState_Aborted)
12544 i_setMachineState(MachineState_Aborted);
12545 }
12546
12547 // any machine settings modified?
12548 if (mData->flModifications)
12549 {
12550 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12551 i_rollback(false /* aNotify */);
12552 }
12553
12554 mData->mSession.mPID = NIL_RTPROCESS;
12555
12556 if (aReason == Uninit::Unexpected)
12557 {
12558 /* Uninitialization didn't come from #checkForDeath(), so tell the
12559 * client watcher thread to update the set of machines that have open
12560 * sessions. */
12561 mParent->i_updateClientWatcher();
12562 }
12563
12564 /* uninitialize all remote controls */
12565 if (mData->mSession.mRemoteControls.size())
12566 {
12567 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12568 mData->mSession.mRemoteControls.size()));
12569
12570 Data::Session::RemoteControlList::iterator it =
12571 mData->mSession.mRemoteControls.begin();
12572 while (it != mData->mSession.mRemoteControls.end())
12573 {
12574 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12575 HRESULT rc = (*it)->Uninitialize();
12576 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12577 if (FAILED(rc))
12578 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12579 ++it;
12580 }
12581 mData->mSession.mRemoteControls.clear();
12582 }
12583
12584 /* Remove all references to the NAT network service. The service will stop
12585 * if all references (also from other VMs) are removed. */
12586 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12587 {
12588 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12589 {
12590 NetworkAttachmentType_T type;
12591 HRESULT hrc;
12592
12593 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12594 if ( SUCCEEDED(hrc)
12595 && type == NetworkAttachmentType_NATNetwork)
12596 {
12597 Bstr name;
12598 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12599 if (SUCCEEDED(hrc))
12600 {
12601 multilock.release();
12602 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12603 mUserData->s.strName.c_str(), name.raw()));
12604 mParent->i_natNetworkRefDec(name.raw());
12605 multilock.acquire();
12606 }
12607 }
12608 }
12609 }
12610
12611 /*
12612 * An expected uninitialization can come only from #checkForDeath().
12613 * Otherwise it means that something's gone really wrong (for example,
12614 * the Session implementation has released the VirtualBox reference
12615 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12616 * etc). However, it's also possible, that the client releases the IPC
12617 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12618 * but the VirtualBox release event comes first to the server process.
12619 * This case is practically possible, so we should not assert on an
12620 * unexpected uninit, just log a warning.
12621 */
12622
12623 if ((aReason == Uninit::Unexpected))
12624 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12625
12626 if (aReason != Uninit::Normal)
12627 {
12628 mData->mSession.mDirectControl.setNull();
12629 }
12630 else
12631 {
12632 /* this must be null here (see #OnSessionEnd()) */
12633 Assert(mData->mSession.mDirectControl.isNull());
12634 Assert(mData->mSession.mState == SessionState_Unlocking);
12635 Assert(!mData->mSession.mProgress.isNull());
12636 }
12637 if (mData->mSession.mProgress)
12638 {
12639 if (aReason == Uninit::Normal)
12640 mData->mSession.mProgress->i_notifyComplete(S_OK);
12641 else
12642 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12643 COM_IIDOF(ISession),
12644 getComponentName(),
12645 tr("The VM session was aborted"));
12646 mData->mSession.mProgress.setNull();
12647 }
12648
12649 /* remove the association between the peer machine and this session machine */
12650 Assert( (SessionMachine*)mData->mSession.mMachine == this
12651 || aReason == Uninit::Unexpected);
12652
12653 /* reset the rest of session data */
12654 mData->mSession.mLockType = LockType_Null;
12655 mData->mSession.mMachine.setNull();
12656 mData->mSession.mState = SessionState_Unlocked;
12657 mData->mSession.mType.setNull();
12658
12659 /* destroy the machine client token before leaving the exclusive lock */
12660 if (mClientToken)
12661 {
12662 delete mClientToken;
12663 mClientToken = NULL;
12664 }
12665
12666 /* fire an event */
12667 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12668
12669 uninitDataAndChildObjects();
12670
12671 /* free the essential data structure last */
12672 mData.free();
12673
12674 /* release the exclusive lock before setting the below two to NULL */
12675 multilock.release();
12676
12677 unconst(mParent) = NULL;
12678 unconst(mPeer) = NULL;
12679
12680 LogFlowThisFuncLeave();
12681}
12682
12683// util::Lockable interface
12684////////////////////////////////////////////////////////////////////////////////
12685
12686/**
12687 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12688 * with the primary Machine instance (mPeer).
12689 */
12690RWLockHandle *SessionMachine::lockHandle() const
12691{
12692 AssertReturn(mPeer != NULL, NULL);
12693 return mPeer->lockHandle();
12694}
12695
12696// IInternalMachineControl methods
12697////////////////////////////////////////////////////////////////////////////////
12698
12699/**
12700 * Passes collected guest statistics to performance collector object
12701 */
12702HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12703 ULONG aCpuKernel, ULONG aCpuIdle,
12704 ULONG aMemTotal, ULONG aMemFree,
12705 ULONG aMemBalloon, ULONG aMemShared,
12706 ULONG aMemCache, ULONG aPageTotal,
12707 ULONG aAllocVMM, ULONG aFreeVMM,
12708 ULONG aBalloonedVMM, ULONG aSharedVMM,
12709 ULONG aVmNetRx, ULONG aVmNetTx)
12710{
12711#ifdef VBOX_WITH_RESOURCE_USAGE_API
12712 if (mCollectorGuest)
12713 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12714 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12715 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12716 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12717
12718 return S_OK;
12719#else
12720 NOREF(aValidStats);
12721 NOREF(aCpuUser);
12722 NOREF(aCpuKernel);
12723 NOREF(aCpuIdle);
12724 NOREF(aMemTotal);
12725 NOREF(aMemFree);
12726 NOREF(aMemBalloon);
12727 NOREF(aMemShared);
12728 NOREF(aMemCache);
12729 NOREF(aPageTotal);
12730 NOREF(aAllocVMM);
12731 NOREF(aFreeVMM);
12732 NOREF(aBalloonedVMM);
12733 NOREF(aSharedVMM);
12734 NOREF(aVmNetRx);
12735 NOREF(aVmNetTx);
12736 return E_NOTIMPL;
12737#endif
12738}
12739
12740////////////////////////////////////////////////////////////////////////////////
12741//
12742// SessionMachine task records
12743//
12744////////////////////////////////////////////////////////////////////////////////
12745
12746/**
12747 * Task record for saving the machine state.
12748 */
12749struct SessionMachine::SaveStateTask
12750 : public Machine::Task
12751{
12752 SaveStateTask(SessionMachine *m,
12753 Progress *p,
12754 const Utf8Str &t,
12755 Reason_T enmReason,
12756 const Utf8Str &strStateFilePath)
12757 : Task(m, p, t),
12758 m_enmReason(enmReason),
12759 m_strStateFilePath(strStateFilePath)
12760 {}
12761
12762 void handler()
12763 {
12764 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12765 }
12766
12767 Reason_T m_enmReason;
12768 Utf8Str m_strStateFilePath;
12769};
12770
12771/**
12772 * Task thread implementation for SessionMachine::SaveState(), called from
12773 * SessionMachine::taskHandler().
12774 *
12775 * @note Locks this object for writing.
12776 *
12777 * @param task
12778 * @return
12779 */
12780void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12781{
12782 LogFlowThisFuncEnter();
12783
12784 AutoCaller autoCaller(this);
12785 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12786 if (FAILED(autoCaller.rc()))
12787 {
12788 /* we might have been uninitialized because the session was accidentally
12789 * closed by the client, so don't assert */
12790 HRESULT rc = setError(E_FAIL,
12791 tr("The session has been accidentally closed"));
12792 task.m_pProgress->i_notifyComplete(rc);
12793 LogFlowThisFuncLeave();
12794 return;
12795 }
12796
12797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12798
12799 HRESULT rc = S_OK;
12800
12801 try
12802 {
12803 ComPtr<IInternalSessionControl> directControl;
12804 if (mData->mSession.mLockType == LockType_VM)
12805 directControl = mData->mSession.mDirectControl;
12806 if (directControl.isNull())
12807 throw setError(VBOX_E_INVALID_VM_STATE,
12808 tr("Trying to save state without a running VM"));
12809 alock.release();
12810 BOOL fSuspendedBySave;
12811 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12812 Assert(!fSuspendedBySave);
12813 alock.acquire();
12814
12815 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12816 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12817 throw E_FAIL);
12818
12819 if (SUCCEEDED(rc))
12820 {
12821 mSSData->strStateFilePath = task.m_strStateFilePath;
12822
12823 /* save all VM settings */
12824 rc = i_saveSettings(NULL);
12825 // no need to check whether VirtualBox.xml needs saving also since
12826 // we can't have a name change pending at this point
12827 }
12828 else
12829 {
12830 // On failure, set the state to the state we had at the beginning.
12831 i_setMachineState(task.m_machineStateBackup);
12832 i_updateMachineStateOnClient();
12833
12834 // Delete the saved state file (might have been already created).
12835 // No need to check whether this is shared with a snapshot here
12836 // because we certainly created a fresh saved state file here.
12837 RTFileDelete(task.m_strStateFilePath.c_str());
12838 }
12839 }
12840 catch (HRESULT aRC) { rc = aRC; }
12841
12842 task.m_pProgress->i_notifyComplete(rc);
12843
12844 LogFlowThisFuncLeave();
12845}
12846
12847/**
12848 * @note Locks this object for writing.
12849 */
12850HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12851{
12852 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12853}
12854
12855HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12856{
12857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12858
12859 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12860 if (FAILED(rc)) return rc;
12861
12862 if ( mData->mMachineState != MachineState_Running
12863 && mData->mMachineState != MachineState_Paused
12864 )
12865 return setError(VBOX_E_INVALID_VM_STATE,
12866 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12867 Global::stringifyMachineState(mData->mMachineState));
12868
12869 ComObjPtr<Progress> pProgress;
12870 pProgress.createObject();
12871 rc = pProgress->init(i_getVirtualBox(),
12872 static_cast<IMachine *>(this) /* aInitiator */,
12873 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12874 FALSE /* aCancelable */);
12875 if (FAILED(rc))
12876 return rc;
12877
12878 Utf8Str strStateFilePath;
12879 i_composeSavedStateFilename(strStateFilePath);
12880
12881 /* create and start the task on a separate thread (note that it will not
12882 * start working until we release alock) */
12883 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12884 rc = pTask->createThread();
12885 if (FAILED(rc))
12886 return rc;
12887
12888 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12889 i_setMachineState(MachineState_Saving);
12890 i_updateMachineStateOnClient();
12891
12892 pProgress.queryInterfaceTo(aProgress.asOutParam());
12893
12894 return S_OK;
12895}
12896
12897/**
12898 * @note Locks this object for writing.
12899 */
12900HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12901{
12902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12903
12904 HRESULT rc = i_checkStateDependency(MutableStateDep);
12905 if (FAILED(rc)) return rc;
12906
12907 if ( mData->mMachineState != MachineState_PoweredOff
12908 && mData->mMachineState != MachineState_Teleported
12909 && mData->mMachineState != MachineState_Aborted
12910 )
12911 return setError(VBOX_E_INVALID_VM_STATE,
12912 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12913 Global::stringifyMachineState(mData->mMachineState));
12914
12915 com::Utf8Str stateFilePathFull;
12916 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12917 if (RT_FAILURE(vrc))
12918 return setError(VBOX_E_FILE_ERROR,
12919 tr("Invalid saved state file path '%s' (%Rrc)"),
12920 aSavedStateFile.c_str(),
12921 vrc);
12922
12923 mSSData->strStateFilePath = stateFilePathFull;
12924
12925 /* The below i_setMachineState() will detect the state transition and will
12926 * update the settings file */
12927
12928 return i_setMachineState(MachineState_Saved);
12929}
12930
12931/**
12932 * @note Locks this object for writing.
12933 */
12934HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12935{
12936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12937
12938 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12939 if (FAILED(rc)) return rc;
12940
12941 if (mData->mMachineState != MachineState_Saved)
12942 return setError(VBOX_E_INVALID_VM_STATE,
12943 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12944 Global::stringifyMachineState(mData->mMachineState));
12945
12946 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12947
12948 /*
12949 * Saved -> PoweredOff transition will be detected in the SessionMachine
12950 * and properly handled.
12951 */
12952 rc = i_setMachineState(MachineState_PoweredOff);
12953 return rc;
12954}
12955
12956
12957/**
12958 * @note Locks the same as #i_setMachineState() does.
12959 */
12960HRESULT SessionMachine::updateState(MachineState_T aState)
12961{
12962 return i_setMachineState(aState);
12963}
12964
12965/**
12966 * @note Locks this object for writing.
12967 */
12968HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12969{
12970 IProgress* pProgress(aProgress);
12971
12972 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12973
12974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12975
12976 if (mData->mSession.mState != SessionState_Locked)
12977 return VBOX_E_INVALID_OBJECT_STATE;
12978
12979 if (!mData->mSession.mProgress.isNull())
12980 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12981
12982 /* If we didn't reference the NAT network service yet, add a reference to
12983 * force a start */
12984 if (miNATNetworksStarted < 1)
12985 {
12986 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12987 {
12988 NetworkAttachmentType_T type;
12989 HRESULT hrc;
12990 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12991 if ( SUCCEEDED(hrc)
12992 && type == NetworkAttachmentType_NATNetwork)
12993 {
12994 Bstr name;
12995 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12996 if (SUCCEEDED(hrc))
12997 {
12998 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12999 mUserData->s.strName.c_str(), name.raw()));
13000 mPeer->lockHandle()->unlockWrite();
13001 mParent->i_natNetworkRefInc(name.raw());
13002#ifdef RT_LOCK_STRICT
13003 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13004#else
13005 mPeer->lockHandle()->lockWrite();
13006#endif
13007 }
13008 }
13009 }
13010 miNATNetworksStarted++;
13011 }
13012
13013 LogFlowThisFunc(("returns S_OK.\n"));
13014 return S_OK;
13015}
13016
13017/**
13018 * @note Locks this object for writing.
13019 */
13020HRESULT SessionMachine::endPowerUp(LONG aResult)
13021{
13022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13023
13024 if (mData->mSession.mState != SessionState_Locked)
13025 return VBOX_E_INVALID_OBJECT_STATE;
13026
13027 /* Finalize the LaunchVMProcess progress object. */
13028 if (mData->mSession.mProgress)
13029 {
13030 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13031 mData->mSession.mProgress.setNull();
13032 }
13033
13034 if (SUCCEEDED((HRESULT)aResult))
13035 {
13036#ifdef VBOX_WITH_RESOURCE_USAGE_API
13037 /* The VM has been powered up successfully, so it makes sense
13038 * now to offer the performance metrics for a running machine
13039 * object. Doing it earlier wouldn't be safe. */
13040 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13041 mData->mSession.mPID);
13042#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13043 }
13044
13045 return S_OK;
13046}
13047
13048/**
13049 * @note Locks this object for writing.
13050 */
13051HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13052{
13053 LogFlowThisFuncEnter();
13054
13055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13056
13057 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13058 E_FAIL);
13059
13060 /* create a progress object to track operation completion */
13061 ComObjPtr<Progress> pProgress;
13062 pProgress.createObject();
13063 pProgress->init(i_getVirtualBox(),
13064 static_cast<IMachine *>(this) /* aInitiator */,
13065 Bstr(tr("Stopping the virtual machine")).raw(),
13066 FALSE /* aCancelable */);
13067
13068 /* fill in the console task data */
13069 mConsoleTaskData.mLastState = mData->mMachineState;
13070 mConsoleTaskData.mProgress = pProgress;
13071
13072 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13073 i_setMachineState(MachineState_Stopping);
13074
13075 pProgress.queryInterfaceTo(aProgress.asOutParam());
13076
13077 return S_OK;
13078}
13079
13080/**
13081 * @note Locks this object for writing.
13082 */
13083HRESULT SessionMachine::endPoweringDown(LONG aResult,
13084 const com::Utf8Str &aErrMsg)
13085{
13086 LogFlowThisFuncEnter();
13087
13088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13089
13090 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13091 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13092 && mConsoleTaskData.mLastState != MachineState_Null,
13093 E_FAIL);
13094
13095 /*
13096 * On failure, set the state to the state we had when BeginPoweringDown()
13097 * was called (this is expected by Console::PowerDown() and the associated
13098 * task). On success the VM process already changed the state to
13099 * MachineState_PoweredOff, so no need to do anything.
13100 */
13101 if (FAILED(aResult))
13102 i_setMachineState(mConsoleTaskData.mLastState);
13103
13104 /* notify the progress object about operation completion */
13105 Assert(mConsoleTaskData.mProgress);
13106 if (SUCCEEDED(aResult))
13107 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13108 else
13109 {
13110 if (aErrMsg.length())
13111 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13112 COM_IIDOF(ISession),
13113 getComponentName(),
13114 aErrMsg.c_str());
13115 else
13116 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13117 }
13118
13119 /* clear out the temporary saved state data */
13120 mConsoleTaskData.mLastState = MachineState_Null;
13121 mConsoleTaskData.mProgress.setNull();
13122
13123 LogFlowThisFuncLeave();
13124 return S_OK;
13125}
13126
13127
13128/**
13129 * Goes through the USB filters of the given machine to see if the given
13130 * device matches any filter or not.
13131 *
13132 * @note Locks the same as USBController::hasMatchingFilter() does.
13133 */
13134HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13135 BOOL *aMatched,
13136 ULONG *aMaskedInterfaces)
13137{
13138 LogFlowThisFunc(("\n"));
13139
13140#ifdef VBOX_WITH_USB
13141 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13142#else
13143 NOREF(aDevice);
13144 NOREF(aMaskedInterfaces);
13145 *aMatched = FALSE;
13146#endif
13147
13148 return S_OK;
13149}
13150
13151/**
13152 * @note Locks the same as Host::captureUSBDevice() does.
13153 */
13154HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13155{
13156 LogFlowThisFunc(("\n"));
13157
13158#ifdef VBOX_WITH_USB
13159 /* if captureDeviceForVM() fails, it must have set extended error info */
13160 clearError();
13161 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13162 if (FAILED(rc)) return rc;
13163
13164 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13165 AssertReturn(service, E_FAIL);
13166 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13167#else
13168 NOREF(aId);
13169 return E_NOTIMPL;
13170#endif
13171}
13172
13173/**
13174 * @note Locks the same as Host::detachUSBDevice() does.
13175 */
13176HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13177 BOOL aDone)
13178{
13179 LogFlowThisFunc(("\n"));
13180
13181#ifdef VBOX_WITH_USB
13182 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13183 AssertReturn(service, E_FAIL);
13184 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13185#else
13186 NOREF(aId);
13187 NOREF(aDone);
13188 return E_NOTIMPL;
13189#endif
13190}
13191
13192/**
13193 * Inserts all machine filters to the USB proxy service and then calls
13194 * Host::autoCaptureUSBDevices().
13195 *
13196 * Called by Console from the VM process upon VM startup.
13197 *
13198 * @note Locks what called methods lock.
13199 */
13200HRESULT SessionMachine::autoCaptureUSBDevices()
13201{
13202 LogFlowThisFunc(("\n"));
13203
13204#ifdef VBOX_WITH_USB
13205 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13206 AssertComRC(rc);
13207 NOREF(rc);
13208
13209 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13210 AssertReturn(service, E_FAIL);
13211 return service->autoCaptureDevicesForVM(this);
13212#else
13213 return S_OK;
13214#endif
13215}
13216
13217/**
13218 * Removes all machine filters from the USB proxy service and then calls
13219 * Host::detachAllUSBDevices().
13220 *
13221 * Called by Console from the VM process upon normal VM termination or by
13222 * SessionMachine::uninit() upon abnormal VM termination (from under the
13223 * Machine/SessionMachine lock).
13224 *
13225 * @note Locks what called methods lock.
13226 */
13227HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13228{
13229 LogFlowThisFunc(("\n"));
13230
13231#ifdef VBOX_WITH_USB
13232 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13233 AssertComRC(rc);
13234 NOREF(rc);
13235
13236 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13237 AssertReturn(service, E_FAIL);
13238 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13239#else
13240 NOREF(aDone);
13241 return S_OK;
13242#endif
13243}
13244
13245/**
13246 * @note Locks this object for writing.
13247 */
13248HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13249 ComPtr<IProgress> &aProgress)
13250{
13251 LogFlowThisFuncEnter();
13252
13253 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13254 /*
13255 * We don't assert below because it might happen that a non-direct session
13256 * informs us it is closed right after we've been uninitialized -- it's ok.
13257 */
13258
13259 /* get IInternalSessionControl interface */
13260 ComPtr<IInternalSessionControl> control(aSession);
13261
13262 ComAssertRet(!control.isNull(), E_INVALIDARG);
13263
13264 /* Creating a Progress object requires the VirtualBox lock, and
13265 * thus locking it here is required by the lock order rules. */
13266 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13267
13268 if (control == mData->mSession.mDirectControl)
13269 {
13270 /* The direct session is being normally closed by the client process
13271 * ----------------------------------------------------------------- */
13272
13273 /* go to the closing state (essential for all open*Session() calls and
13274 * for #checkForDeath()) */
13275 Assert(mData->mSession.mState == SessionState_Locked);
13276 mData->mSession.mState = SessionState_Unlocking;
13277
13278 /* set direct control to NULL to release the remote instance */
13279 mData->mSession.mDirectControl.setNull();
13280 LogFlowThisFunc(("Direct control is set to NULL\n"));
13281
13282 if (mData->mSession.mProgress)
13283 {
13284 /* finalize the progress, someone might wait if a frontend
13285 * closes the session before powering on the VM. */
13286 mData->mSession.mProgress->notifyComplete(E_FAIL,
13287 COM_IIDOF(ISession),
13288 getComponentName(),
13289 tr("The VM session was closed before any attempt to power it on"));
13290 mData->mSession.mProgress.setNull();
13291 }
13292
13293 /* Create the progress object the client will use to wait until
13294 * #checkForDeath() is called to uninitialize this session object after
13295 * it releases the IPC semaphore.
13296 * Note! Because we're "reusing" mProgress here, this must be a proxy
13297 * object just like for LaunchVMProcess. */
13298 Assert(mData->mSession.mProgress.isNull());
13299 ComObjPtr<ProgressProxy> progress;
13300 progress.createObject();
13301 ComPtr<IUnknown> pPeer(mPeer);
13302 progress->init(mParent, pPeer,
13303 Bstr(tr("Closing session")).raw(),
13304 FALSE /* aCancelable */);
13305 progress.queryInterfaceTo(aProgress.asOutParam());
13306 mData->mSession.mProgress = progress;
13307 }
13308 else
13309 {
13310 /* the remote session is being normally closed */
13311 Data::Session::RemoteControlList::iterator it =
13312 mData->mSession.mRemoteControls.begin();
13313 while (it != mData->mSession.mRemoteControls.end())
13314 {
13315 if (control == *it)
13316 break;
13317 ++it;
13318 }
13319 BOOL found = it != mData->mSession.mRemoteControls.end();
13320 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13321 E_INVALIDARG);
13322 // This MUST be erase(it), not remove(*it) as the latter triggers a
13323 // very nasty use after free due to the place where the value "lives".
13324 mData->mSession.mRemoteControls.erase(it);
13325 }
13326
13327 /* signal the client watcher thread, because the client is going away */
13328 mParent->i_updateClientWatcher();
13329
13330 LogFlowThisFuncLeave();
13331 return S_OK;
13332}
13333
13334HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13335 std::vector<com::Utf8Str> &aValues,
13336 std::vector<LONG64> &aTimestamps,
13337 std::vector<com::Utf8Str> &aFlags)
13338{
13339 LogFlowThisFunc(("\n"));
13340
13341#ifdef VBOX_WITH_GUEST_PROPS
13342 using namespace guestProp;
13343
13344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13345
13346 size_t cEntries = mHWData->mGuestProperties.size();
13347 aNames.resize(cEntries);
13348 aValues.resize(cEntries);
13349 aTimestamps.resize(cEntries);
13350 aFlags.resize(cEntries);
13351
13352 size_t i = 0;
13353 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13354 it != mHWData->mGuestProperties.end();
13355 ++it, ++i)
13356 {
13357 char szFlags[MAX_FLAGS_LEN + 1];
13358 aNames[i] = it->first;
13359 aValues[i] = it->second.strValue;
13360 aTimestamps[i] = it->second.mTimestamp;
13361
13362 /* If it is NULL, keep it NULL. */
13363 if (it->second.mFlags)
13364 {
13365 writeFlags(it->second.mFlags, szFlags);
13366 aFlags[i] = szFlags;
13367 }
13368 else
13369 aFlags[i] = "";
13370 }
13371 return S_OK;
13372#else
13373 ReturnComNotImplemented();
13374#endif
13375}
13376
13377HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13378 const com::Utf8Str &aValue,
13379 LONG64 aTimestamp,
13380 const com::Utf8Str &aFlags,
13381 BOOL *aNotify)
13382{
13383 LogFlowThisFunc(("\n"));
13384
13385#ifdef VBOX_WITH_GUEST_PROPS
13386 using namespace guestProp;
13387
13388 *aNotify = FALSE;
13389
13390 try
13391 {
13392 /*
13393 * Convert input up front.
13394 */
13395 uint32_t fFlags = NILFLAG;
13396 if (aFlags.length())
13397 {
13398 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13399 AssertRCReturn(vrc, E_INVALIDARG);
13400 }
13401
13402 /*
13403 * Now grab the object lock, validate the state and do the update.
13404 */
13405
13406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13407
13408 switch (mData->mMachineState)
13409 {
13410 case MachineState_Paused:
13411 case MachineState_Running:
13412 case MachineState_Teleporting:
13413 case MachineState_TeleportingPausedVM:
13414 case MachineState_OnlineSnapshotting:
13415 case MachineState_LiveSnapshotting:
13416 case MachineState_DeletingSnapshotOnline:
13417 case MachineState_DeletingSnapshotPaused:
13418 case MachineState_Saving:
13419 case MachineState_Stopping:
13420 break;
13421
13422 default:
13423 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13424 VBOX_E_INVALID_VM_STATE);
13425 }
13426
13427 i_setModified(IsModified_MachineData);
13428 mHWData.backup();
13429
13430 bool fDelete = !aValue.length();
13431 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13432 if (it != mHWData->mGuestProperties.end())
13433 {
13434 if (!fDelete)
13435 {
13436 it->second.strValue = aValue;
13437 it->second.mTimestamp = aTimestamp;
13438 it->second.mFlags = fFlags;
13439 }
13440 else
13441 mHWData->mGuestProperties.erase(it);
13442
13443 mData->mGuestPropertiesModified = TRUE;
13444 }
13445 else if (!fDelete)
13446 {
13447 HWData::GuestProperty prop;
13448 prop.strValue = aValue;
13449 prop.mTimestamp = aTimestamp;
13450 prop.mFlags = fFlags;
13451
13452 mHWData->mGuestProperties[aName] = prop;
13453 mData->mGuestPropertiesModified = TRUE;
13454 }
13455
13456 /*
13457 * Send a callback notification if appropriate
13458 */
13459 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13460 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13461 RTSTR_MAX,
13462 aName.c_str(),
13463 RTSTR_MAX, NULL)
13464 )
13465 {
13466 alock.release();
13467
13468 mParent->i_onGuestPropertyChange(mData->mUuid,
13469 Bstr(aName).raw(),
13470 Bstr(aValue).raw(),
13471 Bstr(aFlags).raw());
13472 *aNotify = TRUE;
13473 }
13474 }
13475 catch (...)
13476 {
13477 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13478 }
13479 return S_OK;
13480#else
13481 ReturnComNotImplemented();
13482#endif
13483}
13484
13485
13486HRESULT SessionMachine::lockMedia()
13487{
13488 AutoMultiWriteLock2 alock(this->lockHandle(),
13489 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13490
13491 AssertReturn( mData->mMachineState == MachineState_Starting
13492 || mData->mMachineState == MachineState_Restoring
13493 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13494
13495 clearError();
13496 alock.release();
13497 return i_lockMedia();
13498}
13499
13500HRESULT SessionMachine::unlockMedia()
13501{
13502 HRESULT hrc = i_unlockMedia();
13503 return hrc;
13504}
13505
13506HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13507 ComPtr<IMediumAttachment> &aNewAttachment)
13508{
13509 // request the host lock first, since might be calling Host methods for getting host drives;
13510 // next, protect the media tree all the while we're in here, as well as our member variables
13511 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13512 this->lockHandle(),
13513 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13514
13515 IMediumAttachment *iAttach = aAttachment;
13516 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13517
13518 Bstr ctrlName;
13519 LONG lPort;
13520 LONG lDevice;
13521 bool fTempEject;
13522 {
13523 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13524
13525 /* Need to query the details first, as the IMediumAttachment reference
13526 * might be to the original settings, which we are going to change. */
13527 ctrlName = pAttach->i_getControllerName();
13528 lPort = pAttach->i_getPort();
13529 lDevice = pAttach->i_getDevice();
13530 fTempEject = pAttach->i_getTempEject();
13531 }
13532
13533 if (!fTempEject)
13534 {
13535 /* Remember previously mounted medium. The medium before taking the
13536 * backup is not necessarily the same thing. */
13537 ComObjPtr<Medium> oldmedium;
13538 oldmedium = pAttach->i_getMedium();
13539
13540 i_setModified(IsModified_Storage);
13541 mMediaData.backup();
13542
13543 // The backup operation makes the pAttach reference point to the
13544 // old settings. Re-get the correct reference.
13545 pAttach = i_findAttachment(mMediaData->mAttachments,
13546 ctrlName.raw(),
13547 lPort,
13548 lDevice);
13549
13550 {
13551 AutoCaller autoAttachCaller(this);
13552 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13553
13554 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13555 if (!oldmedium.isNull())
13556 oldmedium->i_removeBackReference(mData->mUuid);
13557
13558 pAttach->i_updateMedium(NULL);
13559 pAttach->i_updateEjected();
13560 }
13561
13562 i_setModified(IsModified_Storage);
13563 }
13564 else
13565 {
13566 {
13567 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13568 pAttach->i_updateEjected();
13569 }
13570 }
13571
13572 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13573
13574 return S_OK;
13575}
13576
13577// public methods only for internal purposes
13578/////////////////////////////////////////////////////////////////////////////
13579
13580#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13581/**
13582 * Called from the client watcher thread to check for expected or unexpected
13583 * death of the client process that has a direct session to this machine.
13584 *
13585 * On Win32 and on OS/2, this method is called only when we've got the
13586 * mutex (i.e. the client has either died or terminated normally) so it always
13587 * returns @c true (the client is terminated, the session machine is
13588 * uninitialized).
13589 *
13590 * On other platforms, the method returns @c true if the client process has
13591 * terminated normally or abnormally and the session machine was uninitialized,
13592 * and @c false if the client process is still alive.
13593 *
13594 * @note Locks this object for writing.
13595 */
13596bool SessionMachine::i_checkForDeath()
13597{
13598 Uninit::Reason reason;
13599 bool terminated = false;
13600
13601 /* Enclose autoCaller with a block because calling uninit() from under it
13602 * will deadlock. */
13603 {
13604 AutoCaller autoCaller(this);
13605 if (!autoCaller.isOk())
13606 {
13607 /* return true if not ready, to cause the client watcher to exclude
13608 * the corresponding session from watching */
13609 LogFlowThisFunc(("Already uninitialized!\n"));
13610 return true;
13611 }
13612
13613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13614
13615 /* Determine the reason of death: if the session state is Closing here,
13616 * everything is fine. Otherwise it means that the client did not call
13617 * OnSessionEnd() before it released the IPC semaphore. This may happen
13618 * either because the client process has abnormally terminated, or
13619 * because it simply forgot to call ISession::Close() before exiting. We
13620 * threat the latter also as an abnormal termination (see
13621 * Session::uninit() for details). */
13622 reason = mData->mSession.mState == SessionState_Unlocking ?
13623 Uninit::Normal :
13624 Uninit::Abnormal;
13625
13626 if (mClientToken)
13627 terminated = mClientToken->release();
13628 } /* AutoCaller block */
13629
13630 if (terminated)
13631 uninit(reason);
13632
13633 return terminated;
13634}
13635
13636void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13637{
13638 LogFlowThisFunc(("\n"));
13639
13640 strTokenId.setNull();
13641
13642 AutoCaller autoCaller(this);
13643 AssertComRCReturnVoid(autoCaller.rc());
13644
13645 Assert(mClientToken);
13646 if (mClientToken)
13647 mClientToken->getId(strTokenId);
13648}
13649#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13650IToken *SessionMachine::i_getToken()
13651{
13652 LogFlowThisFunc(("\n"));
13653
13654 AutoCaller autoCaller(this);
13655 AssertComRCReturn(autoCaller.rc(), NULL);
13656
13657 Assert(mClientToken);
13658 if (mClientToken)
13659 return mClientToken->getToken();
13660 else
13661 return NULL;
13662}
13663#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13664
13665Machine::ClientToken *SessionMachine::i_getClientToken()
13666{
13667 LogFlowThisFunc(("\n"));
13668
13669 AutoCaller autoCaller(this);
13670 AssertComRCReturn(autoCaller.rc(), NULL);
13671
13672 return mClientToken;
13673}
13674
13675
13676/**
13677 * @note Locks this object for reading.
13678 */
13679HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13680{
13681 LogFlowThisFunc(("\n"));
13682
13683 AutoCaller autoCaller(this);
13684 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13685
13686 ComPtr<IInternalSessionControl> directControl;
13687 {
13688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13689 if (mData->mSession.mLockType == LockType_VM)
13690 directControl = mData->mSession.mDirectControl;
13691 }
13692
13693 /* ignore notifications sent after #OnSessionEnd() is called */
13694 if (!directControl)
13695 return S_OK;
13696
13697 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13698}
13699
13700/**
13701 * @note Locks this object for reading.
13702 */
13703HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13704 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13705 IN_BSTR aGuestIp, LONG aGuestPort)
13706{
13707 LogFlowThisFunc(("\n"));
13708
13709 AutoCaller autoCaller(this);
13710 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13711
13712 ComPtr<IInternalSessionControl> directControl;
13713 {
13714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13715 if (mData->mSession.mLockType == LockType_VM)
13716 directControl = mData->mSession.mDirectControl;
13717 }
13718
13719 /* ignore notifications sent after #OnSessionEnd() is called */
13720 if (!directControl)
13721 return S_OK;
13722 /*
13723 * instead acting like callback we ask IVirtualBox deliver corresponding event
13724 */
13725
13726 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13727 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13728 return S_OK;
13729}
13730
13731/**
13732 * @note Locks this object for reading.
13733 */
13734HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13735{
13736 LogFlowThisFunc(("\n"));
13737
13738 AutoCaller autoCaller(this);
13739 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13740
13741 ComPtr<IInternalSessionControl> directControl;
13742 {
13743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13744 if (mData->mSession.mLockType == LockType_VM)
13745 directControl = mData->mSession.mDirectControl;
13746 }
13747
13748 /* ignore notifications sent after #OnSessionEnd() is called */
13749 if (!directControl)
13750 return S_OK;
13751
13752 return directControl->OnSerialPortChange(serialPort);
13753}
13754
13755/**
13756 * @note Locks this object for reading.
13757 */
13758HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13759{
13760 LogFlowThisFunc(("\n"));
13761
13762 AutoCaller autoCaller(this);
13763 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13764
13765 ComPtr<IInternalSessionControl> directControl;
13766 {
13767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13768 if (mData->mSession.mLockType == LockType_VM)
13769 directControl = mData->mSession.mDirectControl;
13770 }
13771
13772 /* ignore notifications sent after #OnSessionEnd() is called */
13773 if (!directControl)
13774 return S_OK;
13775
13776 return directControl->OnParallelPortChange(parallelPort);
13777}
13778
13779/**
13780 * @note Locks this object for reading.
13781 */
13782HRESULT SessionMachine::i_onStorageControllerChange()
13783{
13784 LogFlowThisFunc(("\n"));
13785
13786 AutoCaller autoCaller(this);
13787 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13788
13789 ComPtr<IInternalSessionControl> directControl;
13790 {
13791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13792 if (mData->mSession.mLockType == LockType_VM)
13793 directControl = mData->mSession.mDirectControl;
13794 }
13795
13796 /* ignore notifications sent after #OnSessionEnd() is called */
13797 if (!directControl)
13798 return S_OK;
13799
13800 return directControl->OnStorageControllerChange();
13801}
13802
13803/**
13804 * @note Locks this object for reading.
13805 */
13806HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13807{
13808 LogFlowThisFunc(("\n"));
13809
13810 AutoCaller autoCaller(this);
13811 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13812
13813 ComPtr<IInternalSessionControl> directControl;
13814 {
13815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13816 if (mData->mSession.mLockType == LockType_VM)
13817 directControl = mData->mSession.mDirectControl;
13818 }
13819
13820 /* ignore notifications sent after #OnSessionEnd() is called */
13821 if (!directControl)
13822 return S_OK;
13823
13824 return directControl->OnMediumChange(aAttachment, aForce);
13825}
13826
13827/**
13828 * @note Locks this object for reading.
13829 */
13830HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13831{
13832 LogFlowThisFunc(("\n"));
13833
13834 AutoCaller autoCaller(this);
13835 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13836
13837 ComPtr<IInternalSessionControl> directControl;
13838 {
13839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13840 if (mData->mSession.mLockType == LockType_VM)
13841 directControl = mData->mSession.mDirectControl;
13842 }
13843
13844 /* ignore notifications sent after #OnSessionEnd() is called */
13845 if (!directControl)
13846 return S_OK;
13847
13848 return directControl->OnCPUChange(aCPU, aRemove);
13849}
13850
13851HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
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->OnCPUExecutionCapChange(aExecutionCap);
13870}
13871
13872/**
13873 * @note Locks this object for reading.
13874 */
13875HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13876{
13877 LogFlowThisFunc(("\n"));
13878
13879 AutoCaller autoCaller(this);
13880 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13881
13882 ComPtr<IInternalSessionControl> directControl;
13883 {
13884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13885 if (mData->mSession.mLockType == LockType_VM)
13886 directControl = mData->mSession.mDirectControl;
13887 }
13888
13889 /* ignore notifications sent after #OnSessionEnd() is called */
13890 if (!directControl)
13891 return S_OK;
13892
13893 return directControl->OnVRDEServerChange(aRestart);
13894}
13895
13896/**
13897 * @note Locks this object for reading.
13898 */
13899HRESULT SessionMachine::i_onVideoCaptureChange()
13900{
13901 LogFlowThisFunc(("\n"));
13902
13903 AutoCaller autoCaller(this);
13904 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13905
13906 ComPtr<IInternalSessionControl> directControl;
13907 {
13908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13909 if (mData->mSession.mLockType == LockType_VM)
13910 directControl = mData->mSession.mDirectControl;
13911 }
13912
13913 /* ignore notifications sent after #OnSessionEnd() is called */
13914 if (!directControl)
13915 return S_OK;
13916
13917 return directControl->OnVideoCaptureChange();
13918}
13919
13920/**
13921 * @note Locks this object for reading.
13922 */
13923HRESULT SessionMachine::i_onUSBControllerChange()
13924{
13925 LogFlowThisFunc(("\n"));
13926
13927 AutoCaller autoCaller(this);
13928 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13929
13930 ComPtr<IInternalSessionControl> directControl;
13931 {
13932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13933 if (mData->mSession.mLockType == LockType_VM)
13934 directControl = mData->mSession.mDirectControl;
13935 }
13936
13937 /* ignore notifications sent after #OnSessionEnd() is called */
13938 if (!directControl)
13939 return S_OK;
13940
13941 return directControl->OnUSBControllerChange();
13942}
13943
13944/**
13945 * @note Locks this object for reading.
13946 */
13947HRESULT SessionMachine::i_onSharedFolderChange()
13948{
13949 LogFlowThisFunc(("\n"));
13950
13951 AutoCaller autoCaller(this);
13952 AssertComRCReturnRC(autoCaller.rc());
13953
13954 ComPtr<IInternalSessionControl> directControl;
13955 {
13956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13957 if (mData->mSession.mLockType == LockType_VM)
13958 directControl = mData->mSession.mDirectControl;
13959 }
13960
13961 /* ignore notifications sent after #OnSessionEnd() is called */
13962 if (!directControl)
13963 return S_OK;
13964
13965 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13966}
13967
13968/**
13969 * @note Locks this object for reading.
13970 */
13971HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13972{
13973 LogFlowThisFunc(("\n"));
13974
13975 AutoCaller autoCaller(this);
13976 AssertComRCReturnRC(autoCaller.rc());
13977
13978 ComPtr<IInternalSessionControl> directControl;
13979 {
13980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13981 if (mData->mSession.mLockType == LockType_VM)
13982 directControl = mData->mSession.mDirectControl;
13983 }
13984
13985 /* ignore notifications sent after #OnSessionEnd() is called */
13986 if (!directControl)
13987 return S_OK;
13988
13989 return directControl->OnClipboardModeChange(aClipboardMode);
13990}
13991
13992/**
13993 * @note Locks this object for reading.
13994 */
13995HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13996{
13997 LogFlowThisFunc(("\n"));
13998
13999 AutoCaller autoCaller(this);
14000 AssertComRCReturnRC(autoCaller.rc());
14001
14002 ComPtr<IInternalSessionControl> directControl;
14003 {
14004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14005 if (mData->mSession.mLockType == LockType_VM)
14006 directControl = mData->mSession.mDirectControl;
14007 }
14008
14009 /* ignore notifications sent after #OnSessionEnd() is called */
14010 if (!directControl)
14011 return S_OK;
14012
14013 return directControl->OnDnDModeChange(aDnDMode);
14014}
14015
14016/**
14017 * @note Locks this object for reading.
14018 */
14019HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14020{
14021 LogFlowThisFunc(("\n"));
14022
14023 AutoCaller autoCaller(this);
14024 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14025
14026 ComPtr<IInternalSessionControl> directControl;
14027 {
14028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14029 if (mData->mSession.mLockType == LockType_VM)
14030 directControl = mData->mSession.mDirectControl;
14031 }
14032
14033 /* ignore notifications sent after #OnSessionEnd() is called */
14034 if (!directControl)
14035 return S_OK;
14036
14037 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14038}
14039
14040/**
14041 * @note Locks this object for reading.
14042 */
14043HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14044{
14045 LogFlowThisFunc(("\n"));
14046
14047 AutoCaller autoCaller(this);
14048 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14049
14050 ComPtr<IInternalSessionControl> directControl;
14051 {
14052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14053 if (mData->mSession.mLockType == LockType_VM)
14054 directControl = mData->mSession.mDirectControl;
14055 }
14056
14057 /* ignore notifications sent after #OnSessionEnd() is called */
14058 if (!directControl)
14059 return S_OK;
14060
14061 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14062}
14063
14064/**
14065 * Returns @c true if this machine's USB controller reports it has a matching
14066 * filter for the given USB device and @c false otherwise.
14067 *
14068 * @note locks this object for reading.
14069 */
14070bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14071{
14072 AutoCaller autoCaller(this);
14073 /* silently return if not ready -- this method may be called after the
14074 * direct machine session has been called */
14075 if (!autoCaller.isOk())
14076 return false;
14077
14078#ifdef VBOX_WITH_USB
14079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14080
14081 switch (mData->mMachineState)
14082 {
14083 case MachineState_Starting:
14084 case MachineState_Restoring:
14085 case MachineState_TeleportingIn:
14086 case MachineState_Paused:
14087 case MachineState_Running:
14088 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14089 * elsewhere... */
14090 alock.release();
14091 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14092 default: break;
14093 }
14094#else
14095 NOREF(aDevice);
14096 NOREF(aMaskedIfs);
14097#endif
14098 return false;
14099}
14100
14101/**
14102 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14103 */
14104HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14105 IVirtualBoxErrorInfo *aError,
14106 ULONG aMaskedIfs,
14107 const com::Utf8Str &aCaptureFilename)
14108{
14109 LogFlowThisFunc(("\n"));
14110
14111 AutoCaller autoCaller(this);
14112
14113 /* This notification may happen after the machine object has been
14114 * uninitialized (the session was closed), so don't assert. */
14115 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14116
14117 ComPtr<IInternalSessionControl> directControl;
14118 {
14119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14120 if (mData->mSession.mLockType == LockType_VM)
14121 directControl = mData->mSession.mDirectControl;
14122 }
14123
14124 /* fail on notifications sent after #OnSessionEnd() is called, it is
14125 * expected by the caller */
14126 if (!directControl)
14127 return E_FAIL;
14128
14129 /* No locks should be held at this point. */
14130 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14131 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14132
14133 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14134}
14135
14136/**
14137 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14138 */
14139HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14140 IVirtualBoxErrorInfo *aError)
14141{
14142 LogFlowThisFunc(("\n"));
14143
14144 AutoCaller autoCaller(this);
14145
14146 /* This notification may happen after the machine object has been
14147 * uninitialized (the session was closed), so don't assert. */
14148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14149
14150 ComPtr<IInternalSessionControl> directControl;
14151 {
14152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14153 if (mData->mSession.mLockType == LockType_VM)
14154 directControl = mData->mSession.mDirectControl;
14155 }
14156
14157 /* fail on notifications sent after #OnSessionEnd() is called, it is
14158 * expected by the caller */
14159 if (!directControl)
14160 return E_FAIL;
14161
14162 /* No locks should be held at this point. */
14163 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14164 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14165
14166 return directControl->OnUSBDeviceDetach(aId, aError);
14167}
14168
14169// protected methods
14170/////////////////////////////////////////////////////////////////////////////
14171
14172/**
14173 * Deletes the given file if it is no longer in use by either the current machine state
14174 * (if the machine is "saved") or any of the machine's snapshots.
14175 *
14176 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14177 * but is different for each SnapshotMachine. When calling this, the order of calling this
14178 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14179 * is therefore critical. I know, it's all rather messy.
14180 *
14181 * @param strStateFile
14182 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14183 * the test for whether the saved state file is in use.
14184 */
14185void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14186 Snapshot *pSnapshotToIgnore)
14187{
14188 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14189 if ( (strStateFile.isNotEmpty())
14190 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14191 )
14192 // ... and it must also not be shared with other snapshots
14193 if ( !mData->mFirstSnapshot
14194 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14195 // this checks the SnapshotMachine's state file paths
14196 )
14197 RTFileDelete(strStateFile.c_str());
14198}
14199
14200/**
14201 * Locks the attached media.
14202 *
14203 * All attached hard disks are locked for writing and DVD/floppy are locked for
14204 * reading. Parents of attached hard disks (if any) are locked for reading.
14205 *
14206 * This method also performs accessibility check of all media it locks: if some
14207 * media is inaccessible, the method will return a failure and a bunch of
14208 * extended error info objects per each inaccessible medium.
14209 *
14210 * Note that this method is atomic: if it returns a success, all media are
14211 * locked as described above; on failure no media is locked at all (all
14212 * succeeded individual locks will be undone).
14213 *
14214 * The caller is responsible for doing the necessary state sanity checks.
14215 *
14216 * The locks made by this method must be undone by calling #unlockMedia() when
14217 * no more needed.
14218 */
14219HRESULT SessionMachine::i_lockMedia()
14220{
14221 AutoCaller autoCaller(this);
14222 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14223
14224 AutoMultiWriteLock2 alock(this->lockHandle(),
14225 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14226
14227 /* bail out if trying to lock things with already set up locking */
14228 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14229
14230 MultiResult mrc(S_OK);
14231
14232 /* Collect locking information for all medium objects attached to the VM. */
14233 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14234 it != mMediaData->mAttachments.end();
14235 ++it)
14236 {
14237 MediumAttachment* pAtt = *it;
14238 DeviceType_T devType = pAtt->i_getType();
14239 Medium *pMedium = pAtt->i_getMedium();
14240
14241 MediumLockList *pMediumLockList(new MediumLockList());
14242 // There can be attachments without a medium (floppy/dvd), and thus
14243 // it's impossible to create a medium lock list. It still makes sense
14244 // to have the empty medium lock list in the map in case a medium is
14245 // attached later.
14246 if (pMedium != NULL)
14247 {
14248 MediumType_T mediumType = pMedium->i_getType();
14249 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14250 || mediumType == MediumType_Shareable;
14251 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14252
14253 alock.release();
14254 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14255 !fIsReadOnlyLock /* fMediumLockWrite */,
14256 false /* fMediumLockWriteAll */,
14257 NULL,
14258 *pMediumLockList);
14259 alock.acquire();
14260 if (FAILED(mrc))
14261 {
14262 delete pMediumLockList;
14263 mData->mSession.mLockedMedia.Clear();
14264 break;
14265 }
14266 }
14267
14268 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14269 if (FAILED(rc))
14270 {
14271 mData->mSession.mLockedMedia.Clear();
14272 mrc = setError(rc,
14273 tr("Collecting locking information for all attached media failed"));
14274 break;
14275 }
14276 }
14277
14278 if (SUCCEEDED(mrc))
14279 {
14280 /* Now lock all media. If this fails, nothing is locked. */
14281 alock.release();
14282 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14283 alock.acquire();
14284 if (FAILED(rc))
14285 {
14286 mrc = setError(rc,
14287 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14288 }
14289 }
14290
14291 return mrc;
14292}
14293
14294/**
14295 * Undoes the locks made by by #lockMedia().
14296 */
14297HRESULT SessionMachine::i_unlockMedia()
14298{
14299 AutoCaller autoCaller(this);
14300 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14301
14302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14303
14304 /* we may be holding important error info on the current thread;
14305 * preserve it */
14306 ErrorInfoKeeper eik;
14307
14308 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14309 AssertComRC(rc);
14310 return rc;
14311}
14312
14313/**
14314 * Helper to change the machine state (reimplementation).
14315 *
14316 * @note Locks this object for writing.
14317 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14318 * it can cause crashes in random places due to unexpectedly committing
14319 * the current settings. The caller is responsible for that. The call
14320 * to saveStateSettings is fine, because this method does not commit.
14321 */
14322HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14323{
14324 LogFlowThisFuncEnter();
14325 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14326
14327 AutoCaller autoCaller(this);
14328 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14329
14330 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14331
14332 MachineState_T oldMachineState = mData->mMachineState;
14333
14334 AssertMsgReturn(oldMachineState != aMachineState,
14335 ("oldMachineState=%s, aMachineState=%s\n",
14336 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14337 E_FAIL);
14338
14339 HRESULT rc = S_OK;
14340
14341 int stsFlags = 0;
14342 bool deleteSavedState = false;
14343
14344 /* detect some state transitions */
14345
14346 if ( ( oldMachineState == MachineState_Saved
14347 && aMachineState == MachineState_Restoring)
14348 || ( ( oldMachineState == MachineState_PoweredOff
14349 || oldMachineState == MachineState_Teleported
14350 || oldMachineState == MachineState_Aborted
14351 )
14352 && ( aMachineState == MachineState_TeleportingIn
14353 || aMachineState == MachineState_Starting
14354 )
14355 )
14356 )
14357 {
14358 /* The EMT thread is about to start */
14359
14360 /* Nothing to do here for now... */
14361
14362 /// @todo NEWMEDIA don't let mDVDDrive and other children
14363 /// change anything when in the Starting/Restoring state
14364 }
14365 else if ( ( oldMachineState == MachineState_Running
14366 || oldMachineState == MachineState_Paused
14367 || oldMachineState == MachineState_Teleporting
14368 || oldMachineState == MachineState_OnlineSnapshotting
14369 || oldMachineState == MachineState_LiveSnapshotting
14370 || oldMachineState == MachineState_Stuck
14371 || oldMachineState == MachineState_Starting
14372 || oldMachineState == MachineState_Stopping
14373 || oldMachineState == MachineState_Saving
14374 || oldMachineState == MachineState_Restoring
14375 || oldMachineState == MachineState_TeleportingPausedVM
14376 || oldMachineState == MachineState_TeleportingIn
14377 )
14378 && ( aMachineState == MachineState_PoweredOff
14379 || aMachineState == MachineState_Saved
14380 || aMachineState == MachineState_Teleported
14381 || aMachineState == MachineState_Aborted
14382 )
14383 )
14384 {
14385 /* The EMT thread has just stopped, unlock attached media. Note that as
14386 * opposed to locking that is done from Console, we do unlocking here
14387 * because the VM process may have aborted before having a chance to
14388 * properly unlock all media it locked. */
14389
14390 unlockMedia();
14391 }
14392
14393 if (oldMachineState == MachineState_Restoring)
14394 {
14395 if (aMachineState != MachineState_Saved)
14396 {
14397 /*
14398 * delete the saved state file once the machine has finished
14399 * restoring from it (note that Console sets the state from
14400 * Restoring to Saved if the VM couldn't restore successfully,
14401 * to give the user an ability to fix an error and retry --
14402 * we keep the saved state file in this case)
14403 */
14404 deleteSavedState = true;
14405 }
14406 }
14407 else if ( oldMachineState == MachineState_Saved
14408 && ( aMachineState == MachineState_PoweredOff
14409 || aMachineState == MachineState_Aborted
14410 || aMachineState == MachineState_Teleported
14411 )
14412 )
14413 {
14414 /*
14415 * delete the saved state after SessionMachine::ForgetSavedState() is called
14416 * or if the VM process (owning a direct VM session) crashed while the
14417 * VM was Saved
14418 */
14419
14420 /// @todo (dmik)
14421 // Not sure that deleting the saved state file just because of the
14422 // client death before it attempted to restore the VM is a good
14423 // thing. But when it crashes we need to go to the Aborted state
14424 // which cannot have the saved state file associated... The only
14425 // way to fix this is to make the Aborted condition not a VM state
14426 // but a bool flag: i.e., when a crash occurs, set it to true and
14427 // change the state to PoweredOff or Saved depending on the
14428 // saved state presence.
14429
14430 deleteSavedState = true;
14431 mData->mCurrentStateModified = TRUE;
14432 stsFlags |= SaveSTS_CurStateModified;
14433 }
14434
14435 if ( aMachineState == MachineState_Starting
14436 || aMachineState == MachineState_Restoring
14437 || aMachineState == MachineState_TeleportingIn
14438 )
14439 {
14440 /* set the current state modified flag to indicate that the current
14441 * state is no more identical to the state in the
14442 * current snapshot */
14443 if (!mData->mCurrentSnapshot.isNull())
14444 {
14445 mData->mCurrentStateModified = TRUE;
14446 stsFlags |= SaveSTS_CurStateModified;
14447 }
14448 }
14449
14450 if (deleteSavedState)
14451 {
14452 if (mRemoveSavedState)
14453 {
14454 Assert(!mSSData->strStateFilePath.isEmpty());
14455
14456 // it is safe to delete the saved state file if ...
14457 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14458 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14459 // ... none of the snapshots share the saved state file
14460 )
14461 RTFileDelete(mSSData->strStateFilePath.c_str());
14462 }
14463
14464 mSSData->strStateFilePath.setNull();
14465 stsFlags |= SaveSTS_StateFilePath;
14466 }
14467
14468 /* redirect to the underlying peer machine */
14469 mPeer->i_setMachineState(aMachineState);
14470
14471 if ( aMachineState == MachineState_PoweredOff
14472 || aMachineState == MachineState_Teleported
14473 || aMachineState == MachineState_Aborted
14474 || aMachineState == MachineState_Saved)
14475 {
14476 /* the machine has stopped execution
14477 * (or the saved state file was adopted) */
14478 stsFlags |= SaveSTS_StateTimeStamp;
14479 }
14480
14481 if ( ( oldMachineState == MachineState_PoweredOff
14482 || oldMachineState == MachineState_Aborted
14483 || oldMachineState == MachineState_Teleported
14484 )
14485 && aMachineState == MachineState_Saved)
14486 {
14487 /* the saved state file was adopted */
14488 Assert(!mSSData->strStateFilePath.isEmpty());
14489 stsFlags |= SaveSTS_StateFilePath;
14490 }
14491
14492#ifdef VBOX_WITH_GUEST_PROPS
14493 if ( aMachineState == MachineState_PoweredOff
14494 || aMachineState == MachineState_Aborted
14495 || aMachineState == MachineState_Teleported)
14496 {
14497 /* Make sure any transient guest properties get removed from the
14498 * property store on shutdown. */
14499 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14500
14501 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14502 settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14503 while (it != llGuestProperties.end())
14504 {
14505 const settings::GuestProperty &prop = *it;
14506 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14507 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14508 {
14509 it = llGuestProperties.erase(it);
14510 fNeedsSaving = true;
14511 }
14512 else
14513 {
14514 ++it;
14515 }
14516 }
14517
14518 if (fNeedsSaving)
14519 {
14520 mData->mCurrentStateModified = TRUE;
14521 stsFlags |= SaveSTS_CurStateModified;
14522 }
14523 }
14524#endif /* VBOX_WITH_GUEST_PROPS */
14525
14526 rc = i_saveStateSettings(stsFlags);
14527
14528 if ( ( oldMachineState != MachineState_PoweredOff
14529 && oldMachineState != MachineState_Aborted
14530 && oldMachineState != MachineState_Teleported
14531 )
14532 && ( aMachineState == MachineState_PoweredOff
14533 || aMachineState == MachineState_Aborted
14534 || aMachineState == MachineState_Teleported
14535 )
14536 )
14537 {
14538 /* we've been shut down for any reason */
14539 /* no special action so far */
14540 }
14541
14542 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14543 LogFlowThisFuncLeave();
14544 return rc;
14545}
14546
14547/**
14548 * Sends the current machine state value to the VM process.
14549 *
14550 * @note Locks this object for reading, then calls a client process.
14551 */
14552HRESULT SessionMachine::i_updateMachineStateOnClient()
14553{
14554 AutoCaller autoCaller(this);
14555 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14556
14557 ComPtr<IInternalSessionControl> directControl;
14558 {
14559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14560 AssertReturn(!!mData, E_FAIL);
14561 if (mData->mSession.mLockType == LockType_VM)
14562 directControl = mData->mSession.mDirectControl;
14563
14564 /* directControl may be already set to NULL here in #OnSessionEnd()
14565 * called too early by the direct session process while there is still
14566 * some operation (like deleting the snapshot) in progress. The client
14567 * process in this case is waiting inside Session::close() for the
14568 * "end session" process object to complete, while #uninit() called by
14569 * #checkForDeath() on the Watcher thread is waiting for the pending
14570 * operation to complete. For now, we accept this inconsistent behavior
14571 * and simply do nothing here. */
14572
14573 if (mData->mSession.mState == SessionState_Unlocking)
14574 return S_OK;
14575 }
14576
14577 /* ignore notifications sent after #OnSessionEnd() is called */
14578 if (!directControl)
14579 return S_OK;
14580
14581 return directControl->UpdateMachineState(mData->mMachineState);
14582}
14583
14584
14585/**
14586 * Static Machine method that can get passed to RTThreadCreate to
14587 * have a thread started for a Task. See Machine::Task.
14588 */
14589/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14590{
14591 AssertReturn(pvUser, VERR_INVALID_POINTER);
14592
14593 Task *pTask = static_cast<Task *>(pvUser);
14594 pTask->handler();
14595 /** @todo r=klaus it would be safer to update the progress object here,
14596 * as it avoids possible races due to scoping issues/tricks in the handler */
14597 // it's our responsibility to delete the task
14598 delete pTask;
14599
14600 return 0;
14601}
14602
14603/*static*/
14604HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14605{
14606 va_list args;
14607 va_start(args, pcszMsg);
14608 HRESULT rc = setErrorInternal(aResultCode,
14609 getStaticClassIID(),
14610 getStaticComponentName(),
14611 Utf8Str(pcszMsg, args),
14612 false /* aWarning */,
14613 true /* aLogIt */);
14614 va_end(args);
14615 return rc;
14616}
14617
14618
14619HRESULT Machine::updateState(MachineState_T aState)
14620{
14621 NOREF(aState);
14622 ReturnComNotImplemented();
14623}
14624
14625HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14626{
14627 NOREF(aProgress);
14628 ReturnComNotImplemented();
14629}
14630
14631HRESULT Machine::endPowerUp(LONG aResult)
14632{
14633 NOREF(aResult);
14634 ReturnComNotImplemented();
14635}
14636
14637HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14638{
14639 NOREF(aProgress);
14640 ReturnComNotImplemented();
14641}
14642
14643HRESULT Machine::endPoweringDown(LONG aResult,
14644 const com::Utf8Str &aErrMsg)
14645{
14646 NOREF(aResult);
14647 NOREF(aErrMsg);
14648 ReturnComNotImplemented();
14649}
14650
14651HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14652 BOOL *aMatched,
14653 ULONG *aMaskedInterfaces)
14654{
14655 NOREF(aDevice);
14656 NOREF(aMatched);
14657 NOREF(aMaskedInterfaces);
14658 ReturnComNotImplemented();
14659
14660}
14661
14662HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14663{
14664 NOREF(aId); NOREF(aCaptureFilename);
14665 ReturnComNotImplemented();
14666}
14667
14668HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14669 BOOL aDone)
14670{
14671 NOREF(aId);
14672 NOREF(aDone);
14673 ReturnComNotImplemented();
14674}
14675
14676HRESULT Machine::autoCaptureUSBDevices()
14677{
14678 ReturnComNotImplemented();
14679}
14680
14681HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14682{
14683 NOREF(aDone);
14684 ReturnComNotImplemented();
14685}
14686
14687HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14688 ComPtr<IProgress> &aProgress)
14689{
14690 NOREF(aSession);
14691 NOREF(aProgress);
14692 ReturnComNotImplemented();
14693}
14694
14695HRESULT Machine::finishOnlineMergeMedium()
14696{
14697 ReturnComNotImplemented();
14698}
14699
14700HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14701 std::vector<com::Utf8Str> &aValues,
14702 std::vector<LONG64> &aTimestamps,
14703 std::vector<com::Utf8Str> &aFlags)
14704{
14705 NOREF(aNames);
14706 NOREF(aValues);
14707 NOREF(aTimestamps);
14708 NOREF(aFlags);
14709 ReturnComNotImplemented();
14710}
14711
14712HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14713 const com::Utf8Str &aValue,
14714 LONG64 aTimestamp,
14715 const com::Utf8Str &aFlags,
14716 BOOL *aNotify)
14717{
14718 NOREF(aName);
14719 NOREF(aValue);
14720 NOREF(aTimestamp);
14721 NOREF(aFlags);
14722 NOREF(aNotify);
14723 ReturnComNotImplemented();
14724}
14725
14726HRESULT Machine::lockMedia()
14727{
14728 ReturnComNotImplemented();
14729}
14730
14731HRESULT Machine::unlockMedia()
14732{
14733 ReturnComNotImplemented();
14734}
14735
14736HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14737 ComPtr<IMediumAttachment> &aNewAttachment)
14738{
14739 NOREF(aAttachment);
14740 NOREF(aNewAttachment);
14741 ReturnComNotImplemented();
14742}
14743
14744HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14745 ULONG aCpuUser,
14746 ULONG aCpuKernel,
14747 ULONG aCpuIdle,
14748 ULONG aMemTotal,
14749 ULONG aMemFree,
14750 ULONG aMemBalloon,
14751 ULONG aMemShared,
14752 ULONG aMemCache,
14753 ULONG aPagedTotal,
14754 ULONG aMemAllocTotal,
14755 ULONG aMemFreeTotal,
14756 ULONG aMemBalloonTotal,
14757 ULONG aMemSharedTotal,
14758 ULONG aVmNetRx,
14759 ULONG aVmNetTx)
14760{
14761 NOREF(aValidStats);
14762 NOREF(aCpuUser);
14763 NOREF(aCpuKernel);
14764 NOREF(aCpuIdle);
14765 NOREF(aMemTotal);
14766 NOREF(aMemFree);
14767 NOREF(aMemBalloon);
14768 NOREF(aMemShared);
14769 NOREF(aMemCache);
14770 NOREF(aPagedTotal);
14771 NOREF(aMemAllocTotal);
14772 NOREF(aMemFreeTotal);
14773 NOREF(aMemBalloonTotal);
14774 NOREF(aMemSharedTotal);
14775 NOREF(aVmNetRx);
14776 NOREF(aVmNetTx);
14777 ReturnComNotImplemented();
14778}
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