VirtualBox

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

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

Main/Machine: fix self-deadlock during inaccessible machine unregistration

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 509.5 KB
Line 
1/* $Id: MachineImpl.cpp 55807 2015-05-11 16:58:45Z 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.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47
48// generated header
49#include "VBoxEvents.h"
50
51#ifdef VBOX_WITH_USB
52# include "USBProxyService.h"
53#endif
54
55#include "AutoCaller.h"
56#include "HashedPw.h"
57#include "Performance.h"
58
59#include <iprt/asm.h>
60#include <iprt/path.h>
61#include <iprt/dir.h>
62#include <iprt/env.h>
63#include <iprt/lockvalidator.h>
64#include <iprt/process.h>
65#include <iprt/cpp/utils.h>
66#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
67#include <iprt/sha.h>
68#include <iprt/string.h>
69#include <iprt/base64.h>
70
71#include <VBox/com/array.h>
72#include <VBox/com/list.h>
73
74#include <VBox/err.h>
75#include <VBox/param.h>
76#include <VBox/settings.h>
77#include <VBox/vmm/ssm.h>
78
79#ifdef VBOX_WITH_GUEST_PROPS
80# include <VBox/HostServices/GuestPropertySvc.h>
81# include <VBox/com/array.h>
82#endif
83
84#include "VBox/com/MultiResult.h"
85
86#include <algorithm>
87
88#ifdef VBOX_WITH_DTRACE_R3_MAIN
89# include "dtrace/VBoxAPI.h"
90#endif
91
92#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
93# define HOSTSUFF_EXE ".exe"
94#else /* !RT_OS_WINDOWS */
95# define HOSTSUFF_EXE ""
96#endif /* !RT_OS_WINDOWS */
97
98// defines / prototypes
99/////////////////////////////////////////////////////////////////////////////
100
101/////////////////////////////////////////////////////////////////////////////
102// Machine::Data structure
103/////////////////////////////////////////////////////////////////////////////
104
105Machine::Data::Data()
106{
107 mRegistered = FALSE;
108 pMachineConfigFile = NULL;
109 /* Contains hints on what has changed when the user is using the VM (config
110 * changes, running the VM, ...). This is used to decide if a config needs
111 * to be written to disk. */
112 flModifications = 0;
113 /* VM modification usually also trigger setting the current state to
114 * "Modified". Although this is not always the case. An e.g. is the VM
115 * initialization phase or when snapshot related data is changed. The
116 * actually behavior is controlled by the following flag. */
117 m_fAllowStateModification = false;
118 mAccessible = FALSE;
119 /* mUuid is initialized in Machine::init() */
120
121 mMachineState = MachineState_PoweredOff;
122 RTTimeNow(&mLastStateChange);
123
124 mMachineStateDeps = 0;
125 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
126 mMachineStateChangePending = 0;
127
128 mCurrentStateModified = TRUE;
129 mGuestPropertiesModified = FALSE;
130
131 mSession.mPID = NIL_RTPROCESS;
132 mSession.mLockType = LockType_Null;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mTripleFaultReset = false;
196 mHPETEnabled = false;
197 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
198 mCpuIdPortabilityLevel = 0;
199
200 /* default boot order: floppy - DVD - HDD */
201 mBootOrder[0] = DeviceType_Floppy;
202 mBootOrder[1] = DeviceType_DVD;
203 mBootOrder[2] = DeviceType_HardDisk;
204 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
205 mBootOrder[i] = DeviceType_Null;
206
207 mClipboardMode = ClipboardMode_Disabled;
208 mDnDMode = DnDMode_Disabled;
209 mGuestPropertyNotificationPatterns = "";
210
211 mFirmwareType = FirmwareType_BIOS;
212 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
213 mPointingHIDType = PointingHIDType_PS2Mouse;
214 mChipsetType = ChipsetType_PIIX3;
215 mParavirtProvider = ParavirtProvider_Default;
216 mEmulatedUSBCardReaderEnabled = FALSE;
217
218 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
219 mCPUAttached[i] = false;
220
221 mIOCacheEnabled = true;
222 mIOCacheSize = 5; /* 5MB */
223}
224
225Machine::HWData::~HWData()
226{
227}
228
229/////////////////////////////////////////////////////////////////////////////
230// Machine::HDData structure
231/////////////////////////////////////////////////////////////////////////////
232
233Machine::MediaData::MediaData()
234{
235}
236
237Machine::MediaData::~MediaData()
238{
239}
240
241/////////////////////////////////////////////////////////////////////////////
242// Machine class
243/////////////////////////////////////////////////////////////////////////////
244
245// constructor / destructor
246/////////////////////////////////////////////////////////////////////////////
247
248Machine::Machine() :
249#ifdef VBOX_WITH_RESOURCE_USAGE_API
250 mCollectorGuest(NULL),
251#endif
252 mPeer(NULL),
253 mParent(NULL),
254 mSerialPorts(),
255 mParallelPorts(),
256 uRegistryNeedsSaving(0)
257{}
258
259Machine::~Machine()
260{}
261
262HRESULT Machine::FinalConstruct()
263{
264 LogFlowThisFunc(("\n"));
265 return BaseFinalConstruct();
266}
267
268void Machine::FinalRelease()
269{
270 LogFlowThisFunc(("\n"));
271 uninit();
272 BaseFinalRelease();
273}
274
275/**
276 * Initializes a new machine instance; this init() variant creates a new, empty machine.
277 * This gets called from VirtualBox::CreateMachine().
278 *
279 * @param aParent Associated parent object
280 * @param strConfigFile Local file system path to the VM settings file (can
281 * be relative to the VirtualBox config directory).
282 * @param strName name for the machine
283 * @param llGroups list of groups for the machine
284 * @param aOsType OS Type of this machine or NULL.
285 * @param aId UUID for the new machine.
286 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
287 *
288 * @return Success indicator. if not S_OK, the machine object is invalid
289 */
290HRESULT Machine::init(VirtualBox *aParent,
291 const Utf8Str &strConfigFile,
292 const Utf8Str &strName,
293 const StringsList &llGroups,
294 GuestOSType *aOsType,
295 const Guid &aId,
296 bool fForceOverwrite,
297 bool fDirectoryIncludesUUID)
298{
299 LogFlowThisFuncEnter();
300 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
301
302 /* Enclose the state transition NotReady->InInit->Ready */
303 AutoInitSpan autoInitSpan(this);
304 AssertReturn(autoInitSpan.isOk(), E_FAIL);
305
306 HRESULT rc = initImpl(aParent, strConfigFile);
307 if (FAILED(rc)) return rc;
308
309 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
310 if (FAILED(rc)) return rc;
311
312 if (SUCCEEDED(rc))
313 {
314 // create an empty machine config
315 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
316
317 rc = initDataAndChildObjects();
318 }
319
320 if (SUCCEEDED(rc))
321 {
322 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
323 mData->mAccessible = TRUE;
324
325 unconst(mData->mUuid) = aId;
326
327 mUserData->s.strName = strName;
328
329 mUserData->s.llGroups = llGroups;
330
331 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
332 // the "name sync" flag determines whether the machine directory gets renamed along
333 // with the machine file; say so if the settings file name is the same as the
334 // settings file parent directory (machine directory)
335 mUserData->s.fNameSync = i_isInOwnDir();
336
337 // initialize the default snapshots folder
338 rc = COMSETTER(SnapshotFolder)(NULL);
339 AssertComRC(rc);
340
341 if (aOsType)
342 {
343 /* Store OS type */
344 mUserData->s.strOsType = aOsType->i_id();
345
346 /* Apply BIOS defaults */
347 mBIOSSettings->i_applyDefaults(aOsType);
348
349 /* Apply network adapters defaults */
350 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
351 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
352
353 /* Apply serial port defaults */
354 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
355 mSerialPorts[slot]->i_applyDefaults(aOsType);
356
357 /* Let the OS type select 64-bit ness. */
358 mHWData->mLongMode = aOsType->i_is64Bit()
359 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
360 }
361
362 /* At this point the changing of the current state modification
363 * flag is allowed. */
364 i_allowStateModification();
365
366 /* commit all changes made during the initialization */
367 i_commit();
368 }
369
370 /* Confirm a successful initialization when it's the case */
371 if (SUCCEEDED(rc))
372 {
373 if (mData->mAccessible)
374 autoInitSpan.setSucceeded();
375 else
376 autoInitSpan.setLimited();
377 }
378
379 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
380 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
381 mData->mRegistered,
382 mData->mAccessible,
383 rc));
384
385 LogFlowThisFuncLeave();
386
387 return rc;
388}
389
390/**
391 * Initializes a new instance with data from machine XML (formerly Init_Registered).
392 * Gets called in two modes:
393 *
394 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
395 * UUID is specified and we mark the machine as "registered";
396 *
397 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
398 * and the machine remains unregistered until RegisterMachine() is called.
399 *
400 * @param aParent Associated parent object
401 * @param aConfigFile Local file system path to the VM settings file (can
402 * be relative to the VirtualBox config directory).
403 * @param aId UUID of the machine or NULL (see above).
404 *
405 * @return Success indicator. if not S_OK, the machine object is invalid
406 */
407HRESULT Machine::initFromSettings(VirtualBox *aParent,
408 const Utf8Str &strConfigFile,
409 const Guid *aId)
410{
411 LogFlowThisFuncEnter();
412 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
413
414 /* Enclose the state transition NotReady->InInit->Ready */
415 AutoInitSpan autoInitSpan(this);
416 AssertReturn(autoInitSpan.isOk(), E_FAIL);
417
418 HRESULT rc = initImpl(aParent, strConfigFile);
419 if (FAILED(rc)) return rc;
420
421 if (aId)
422 {
423 // loading a registered VM:
424 unconst(mData->mUuid) = *aId;
425 mData->mRegistered = TRUE;
426 // now load the settings from XML:
427 rc = i_registeredInit();
428 // this calls initDataAndChildObjects() and loadSettings()
429 }
430 else
431 {
432 // opening an unregistered VM (VirtualBox::OpenMachine()):
433 rc = initDataAndChildObjects();
434
435 if (SUCCEEDED(rc))
436 {
437 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
438 mData->mAccessible = TRUE;
439
440 try
441 {
442 // load and parse machine XML; this will throw on XML or logic errors
443 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
444
445 // reject VM UUID duplicates, they can happen if someone
446 // tries to register an already known VM config again
447 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
448 true /* fPermitInaccessible */,
449 false /* aDoSetError */,
450 NULL) != VBOX_E_OBJECT_NOT_FOUND)
451 {
452 throw setError(E_FAIL,
453 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
454 mData->m_strConfigFile.c_str());
455 }
456
457 // use UUID from machine config
458 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
459
460 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
461 NULL /* puuidRegistry */);
462 if (FAILED(rc)) throw rc;
463
464 /* At this point the changing of the current state modification
465 * flag is allowed. */
466 i_allowStateModification();
467
468 i_commit();
469 }
470 catch (HRESULT err)
471 {
472 /* we assume that error info is set by the thrower */
473 rc = err;
474 }
475 catch (...)
476 {
477 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
478 }
479 }
480 }
481
482 /* Confirm a successful initialization when it's the case */
483 if (SUCCEEDED(rc))
484 {
485 if (mData->mAccessible)
486 autoInitSpan.setSucceeded();
487 else
488 {
489 autoInitSpan.setLimited();
490
491 // uninit media from this machine's media registry, or else
492 // reloading the settings will fail
493 mParent->i_unregisterMachineMedia(i_getId());
494 }
495 }
496
497 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
498 "rc=%08X\n",
499 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
500 mData->mRegistered, mData->mAccessible, rc));
501
502 LogFlowThisFuncLeave();
503
504 return rc;
505}
506
507/**
508 * Initializes a new instance from a machine config that is already in memory
509 * (import OVF case). Since we are importing, the UUID in the machine
510 * config is ignored and we always generate a fresh one.
511 *
512 * @param strName Name for the new machine; this overrides what is specified in config and is used
513 * for the settings file as well.
514 * @param config Machine configuration loaded and parsed from XML.
515 *
516 * @return Success indicator. if not S_OK, the machine object is invalid
517 */
518HRESULT Machine::init(VirtualBox *aParent,
519 const Utf8Str &strName,
520 const settings::MachineConfigFile &config)
521{
522 LogFlowThisFuncEnter();
523
524 /* Enclose the state transition NotReady->InInit->Ready */
525 AutoInitSpan autoInitSpan(this);
526 AssertReturn(autoInitSpan.isOk(), E_FAIL);
527
528 Utf8Str strConfigFile;
529 aParent->i_getDefaultMachineFolder(strConfigFile);
530 strConfigFile.append(RTPATH_DELIMITER);
531 strConfigFile.append(strName);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(".vbox");
535
536 HRESULT rc = initImpl(aParent, strConfigFile);
537 if (FAILED(rc)) return rc;
538
539 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
540 if (FAILED(rc)) return rc;
541
542 rc = initDataAndChildObjects();
543
544 if (SUCCEEDED(rc))
545 {
546 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
547 mData->mAccessible = TRUE;
548
549 // create empty machine config for instance data
550 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
551
552 // generate fresh UUID, ignore machine config
553 unconst(mData->mUuid).create();
554
555 rc = i_loadMachineDataFromSettings(config,
556 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
557
558 // override VM name as well, it may be different
559 mUserData->s.strName = strName;
560
561 if (SUCCEEDED(rc))
562 {
563 /* At this point the changing of the current state modification
564 * flag is allowed. */
565 i_allowStateModification();
566
567 /* commit all changes made during the initialization */
568 i_commit();
569 }
570 }
571
572 /* Confirm a successful initialization when it's the case */
573 if (SUCCEEDED(rc))
574 {
575 if (mData->mAccessible)
576 autoInitSpan.setSucceeded();
577 else
578 {
579 /* Ignore all errors from unregistering, they would destroy
580- * the more interesting error information we already have,
581- * pinpointing the issue with the VM config. */
582 ErrorInfoKeeper eik;
583
584 autoInitSpan.setLimited();
585
586 // uninit media from this machine's media registry, or else
587 // reloading the settings will fail
588 mParent->i_unregisterMachineMedia(i_getId());
589 }
590 }
591
592 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
593 "rc=%08X\n",
594 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
595 mData->mRegistered, mData->mAccessible, rc));
596
597 LogFlowThisFuncLeave();
598
599 return rc;
600}
601
602/**
603 * Shared code between the various init() implementations.
604 * @param aParent
605 * @return
606 */
607HRESULT Machine::initImpl(VirtualBox *aParent,
608 const Utf8Str &strConfigFile)
609{
610 LogFlowThisFuncEnter();
611
612 AssertReturn(aParent, E_INVALIDARG);
613 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
614
615 HRESULT rc = S_OK;
616
617 /* share the parent weakly */
618 unconst(mParent) = aParent;
619
620 /* allocate the essential machine data structure (the rest will be
621 * allocated later by initDataAndChildObjects() */
622 mData.allocate();
623
624 /* memorize the config file name (as provided) */
625 mData->m_strConfigFile = strConfigFile;
626
627 /* get the full file name */
628 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
629 if (RT_FAILURE(vrc1))
630 return setError(VBOX_E_FILE_ERROR,
631 tr("Invalid machine settings file name '%s' (%Rrc)"),
632 strConfigFile.c_str(),
633 vrc1);
634
635 LogFlowThisFuncLeave();
636
637 return rc;
638}
639
640/**
641 * Tries to create a machine settings file in the path stored in the machine
642 * instance data. Used when a new machine is created to fail gracefully if
643 * the settings file could not be written (e.g. because machine dir is read-only).
644 * @return
645 */
646HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
647{
648 HRESULT rc = S_OK;
649
650 // when we create a new machine, we must be able to create the settings file
651 RTFILE f = NIL_RTFILE;
652 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
653 if ( RT_SUCCESS(vrc)
654 || vrc == VERR_SHARING_VIOLATION
655 )
656 {
657 if (RT_SUCCESS(vrc))
658 RTFileClose(f);
659 if (!fForceOverwrite)
660 rc = setError(VBOX_E_FILE_ERROR,
661 tr("Machine settings file '%s' already exists"),
662 mData->m_strConfigFileFull.c_str());
663 else
664 {
665 /* try to delete the config file, as otherwise the creation
666 * of a new settings file will fail. */
667 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
668 if (RT_FAILURE(vrc2))
669 rc = setError(VBOX_E_FILE_ERROR,
670 tr("Could not delete the existing settings file '%s' (%Rrc)"),
671 mData->m_strConfigFileFull.c_str(), vrc2);
672 }
673 }
674 else if ( vrc != VERR_FILE_NOT_FOUND
675 && vrc != VERR_PATH_NOT_FOUND
676 )
677 rc = setError(VBOX_E_FILE_ERROR,
678 tr("Invalid machine settings file name '%s' (%Rrc)"),
679 mData->m_strConfigFileFull.c_str(),
680 vrc);
681 return rc;
682}
683
684/**
685 * Initializes the registered machine by loading the settings file.
686 * This method is separated from #init() in order to make it possible to
687 * retry the operation after VirtualBox startup instead of refusing to
688 * startup the whole VirtualBox server in case if the settings file of some
689 * registered VM is invalid or inaccessible.
690 *
691 * @note Must be always called from this object's write lock
692 * (unless called from #init() that doesn't need any locking).
693 * @note Locks the mUSBController method for writing.
694 * @note Subclasses must not call this method.
695 */
696HRESULT Machine::i_registeredInit()
697{
698 AssertReturn(!i_isSessionMachine(), E_FAIL);
699 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
700 AssertReturn(mData->mUuid.isValid(), E_FAIL);
701 AssertReturn(!mData->mAccessible, E_FAIL);
702
703 HRESULT rc = initDataAndChildObjects();
704
705 if (SUCCEEDED(rc))
706 {
707 /* Temporarily reset the registered flag in order to let setters
708 * potentially called from loadSettings() succeed (isMutable() used in
709 * all setters will return FALSE for a Machine instance if mRegistered
710 * is TRUE). */
711 mData->mRegistered = FALSE;
712
713 try
714 {
715 // load and parse machine XML; this will throw on XML or logic errors
716 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
717
718 if (mData->mUuid != mData->pMachineConfigFile->uuid)
719 throw setError(E_FAIL,
720 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
721 mData->pMachineConfigFile->uuid.raw(),
722 mData->m_strConfigFileFull.c_str(),
723 mData->mUuid.toString().c_str(),
724 mParent->i_settingsFilePath().c_str());
725
726 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
727 NULL /* const Guid *puuidRegistry */);
728 if (FAILED(rc)) throw rc;
729 }
730 catch (HRESULT err)
731 {
732 /* we assume that error info is set by the thrower */
733 rc = err;
734 }
735 catch (...)
736 {
737 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
738 }
739
740 /* Restore the registered flag (even on failure) */
741 mData->mRegistered = TRUE;
742 }
743
744 if (SUCCEEDED(rc))
745 {
746 /* Set mAccessible to TRUE only if we successfully locked and loaded
747 * the settings file */
748 mData->mAccessible = TRUE;
749
750 /* commit all changes made during loading the settings file */
751 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
752 /// @todo r=klaus for some reason the settings loading logic backs up
753 // the settings, and therefore a commit is needed. Should probably be changed.
754 }
755 else
756 {
757 /* If the machine is registered, then, instead of returning a
758 * failure, we mark it as inaccessible and set the result to
759 * success to give it a try later */
760
761 /* fetch the current error info */
762 mData->mAccessError = com::ErrorInfo();
763 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
764 mData->mUuid.raw(),
765 mData->mAccessError.getText().raw()));
766
767 /* rollback all changes */
768 i_rollback(false /* aNotify */);
769
770 // uninit media from this machine's media registry, or else
771 // reloading the settings will fail
772 mParent->i_unregisterMachineMedia(i_getId());
773
774 /* uninitialize the common part to make sure all data is reset to
775 * default (null) values */
776 uninitDataAndChildObjects();
777
778 rc = S_OK;
779 }
780
781 return rc;
782}
783
784/**
785 * Uninitializes the instance.
786 * Called either from FinalRelease() or by the parent when it gets destroyed.
787 *
788 * @note The caller of this method must make sure that this object
789 * a) doesn't have active callers on the current thread and b) is not locked
790 * by the current thread; otherwise uninit() will hang either a) due to
791 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
792 * a dead-lock caused by this thread waiting for all callers on the other
793 * threads are done but preventing them from doing so by holding a lock.
794 */
795void Machine::uninit()
796{
797 LogFlowThisFuncEnter();
798
799 Assert(!isWriteLockOnCurrentThread());
800
801 Assert(!uRegistryNeedsSaving);
802 if (uRegistryNeedsSaving)
803 {
804 AutoCaller autoCaller(this);
805 if (SUCCEEDED(autoCaller.rc()))
806 {
807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
808 i_saveSettings(NULL, Machine::SaveS_Force);
809 }
810 }
811
812 /* Enclose the state transition Ready->InUninit->NotReady */
813 AutoUninitSpan autoUninitSpan(this);
814 if (autoUninitSpan.uninitDone())
815 return;
816
817 Assert(!i_isSnapshotMachine());
818 Assert(!i_isSessionMachine());
819 Assert(!!mData);
820
821 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
822 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
823
824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
825
826 if (!mData->mSession.mMachine.isNull())
827 {
828 /* Theoretically, this can only happen if the VirtualBox server has been
829 * terminated while there were clients running that owned open direct
830 * sessions. Since in this case we are definitely called by
831 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
832 * won't happen on the client watcher thread (because it does
833 * VirtualBox::addCaller() for the duration of the
834 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
835 * cannot happen until the VirtualBox caller is released). This is
836 * important, because SessionMachine::uninit() cannot correctly operate
837 * after we return from this method (it expects the Machine instance is
838 * still valid). We'll call it ourselves below.
839 */
840 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
841 (SessionMachine*)mData->mSession.mMachine));
842
843 if (Global::IsOnlineOrTransient(mData->mMachineState))
844 {
845 LogWarningThisFunc(("Setting state to Aborted!\n"));
846 /* set machine state using SessionMachine reimplementation */
847 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
848 }
849
850 /*
851 * Uninitialize SessionMachine using public uninit() to indicate
852 * an unexpected uninitialization.
853 */
854 mData->mSession.mMachine->uninit();
855 /* SessionMachine::uninit() must set mSession.mMachine to null */
856 Assert(mData->mSession.mMachine.isNull());
857 }
858
859 // uninit media from this machine's media registry, if they're still there
860 Guid uuidMachine(i_getId());
861
862 /* the lock is no more necessary (SessionMachine is uninitialized) */
863 alock.release();
864
865 /* XXX This will fail with
866 * "cannot be closed because it is still attached to 1 virtual machines"
867 * because at this point we did not call uninitDataAndChildObjects() yet
868 * and therefore also removeBackReference() for all these mediums was not called! */
869
870 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
871 mParent->i_unregisterMachineMedia(uuidMachine);
872
873 // has machine been modified?
874 if (mData->flModifications)
875 {
876 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
877 i_rollback(false /* aNotify */);
878 }
879
880 if (mData->mAccessible)
881 uninitDataAndChildObjects();
882
883 /* free the essential data structure last */
884 mData.free();
885
886 LogFlowThisFuncLeave();
887}
888
889// Wrapped IMachine properties
890/////////////////////////////////////////////////////////////////////////////
891HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
892{
893 /* mParent is constant during life time, no need to lock */
894 ComObjPtr<VirtualBox> pVirtualBox(mParent);
895 aParent = pVirtualBox;
896
897 return S_OK;
898}
899
900
901HRESULT Machine::getAccessible(BOOL *aAccessible)
902{
903 /* In some cases (medium registry related), it is necessary to be able to
904 * go through the list of all machines. Happens when an inaccessible VM
905 * has a sensible medium registry. */
906 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
908
909 HRESULT rc = S_OK;
910
911 if (!mData->mAccessible)
912 {
913 /* try to initialize the VM once more if not accessible */
914
915 AutoReinitSpan autoReinitSpan(this);
916 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
917
918#ifdef DEBUG
919 LogFlowThisFunc(("Dumping media backreferences\n"));
920 mParent->i_dumpAllBackRefs();
921#endif
922
923 if (mData->pMachineConfigFile)
924 {
925 // reset the XML file to force loadSettings() (called from registeredInit())
926 // to parse it again; the file might have changed
927 delete mData->pMachineConfigFile;
928 mData->pMachineConfigFile = NULL;
929 }
930
931 rc = i_registeredInit();
932
933 if (SUCCEEDED(rc) && mData->mAccessible)
934 {
935 autoReinitSpan.setSucceeded();
936
937 /* make sure interesting parties will notice the accessibility
938 * state change */
939 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
940 mParent->i_onMachineDataChange(mData->mUuid);
941 }
942 }
943
944 if (SUCCEEDED(rc))
945 *aAccessible = mData->mAccessible;
946
947 LogFlowThisFuncLeave();
948
949 return rc;
950}
951
952HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
953{
954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
955
956 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
957 {
958 /* return shortly */
959 aAccessError = NULL;
960 return S_OK;
961 }
962
963 HRESULT rc = S_OK;
964
965 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
966 rc = errorInfo.createObject();
967 if (SUCCEEDED(rc))
968 {
969 errorInfo->init(mData->mAccessError.getResultCode(),
970 mData->mAccessError.getInterfaceID().ref(),
971 Utf8Str(mData->mAccessError.getComponent()).c_str(),
972 Utf8Str(mData->mAccessError.getText()));
973 aAccessError = errorInfo;
974 }
975
976 return rc;
977}
978
979HRESULT Machine::getName(com::Utf8Str &aName)
980{
981 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
982
983 aName = mUserData->s.strName;
984
985 return S_OK;
986}
987
988HRESULT Machine::setName(const com::Utf8Str &aName)
989{
990 // prohibit setting a UUID only as the machine name, or else it can
991 // never be found by findMachine()
992 Guid test(aName);
993
994 if (test.isValid())
995 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
996
997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
998
999 HRESULT rc = i_checkStateDependency(MutableStateDep);
1000 if (FAILED(rc)) return rc;
1001
1002 i_setModified(IsModified_MachineData);
1003 mUserData.backup();
1004 mUserData->s.strName = aName;
1005
1006 return S_OK;
1007}
1008
1009HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1010{
1011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1012
1013 aDescription = mUserData->s.strDescription;
1014
1015 return S_OK;
1016}
1017
1018HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1019{
1020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1021
1022 // this can be done in principle in any state as it doesn't affect the VM
1023 // significantly, but play safe by not messing around while complex
1024 // activities are going on
1025 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1026 if (FAILED(rc)) return rc;
1027
1028 i_setModified(IsModified_MachineData);
1029 mUserData.backup();
1030 mUserData->s.strDescription = aDescription;
1031
1032 return S_OK;
1033}
1034
1035HRESULT Machine::getId(com::Guid &aId)
1036{
1037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1038
1039 aId = mData->mUuid;
1040
1041 return S_OK;
1042}
1043
1044HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1045{
1046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1047 aGroups.resize(mUserData->s.llGroups.size());
1048 size_t i = 0;
1049 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1050 it != mUserData->s.llGroups.end(); ++it, ++i)
1051 aGroups[i] = (*it);
1052
1053 return S_OK;
1054}
1055
1056HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1057{
1058 StringsList llGroups;
1059 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1060 if (FAILED(rc))
1061 return rc;
1062
1063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1064
1065 rc = i_checkStateDependency(MutableOrSavedStateDep);
1066 if (FAILED(rc)) return rc;
1067
1068 i_setModified(IsModified_MachineData);
1069 mUserData.backup();
1070 mUserData->s.llGroups = llGroups;
1071
1072 return S_OK;
1073}
1074
1075HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1076{
1077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1078
1079 aOSTypeId = mUserData->s.strOsType;
1080
1081 return S_OK;
1082}
1083
1084HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1085{
1086 /* look up the object by Id to check it is valid */
1087 ComPtr<IGuestOSType> guestOSType;
1088 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1089 if (FAILED(rc)) return rc;
1090
1091 /* when setting, always use the "etalon" value for consistency -- lookup
1092 * by ID is case-insensitive and the input value may have different case */
1093 Bstr osTypeId;
1094 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1095 if (FAILED(rc)) return rc;
1096
1097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1098
1099 rc = i_checkStateDependency(MutableStateDep);
1100 if (FAILED(rc)) return rc;
1101
1102 i_setModified(IsModified_MachineData);
1103 mUserData.backup();
1104 mUserData->s.strOsType = osTypeId;
1105
1106 return S_OK;
1107}
1108
1109HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1110{
1111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1112
1113 *aFirmwareType = mHWData->mFirmwareType;
1114
1115 return S_OK;
1116}
1117
1118HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1119{
1120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1121
1122 HRESULT rc = i_checkStateDependency(MutableStateDep);
1123 if (FAILED(rc)) return rc;
1124
1125 i_setModified(IsModified_MachineData);
1126 mHWData.backup();
1127 mHWData->mFirmwareType = aFirmwareType;
1128
1129 return S_OK;
1130}
1131
1132HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1133{
1134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1135
1136 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1137
1138 return S_OK;
1139}
1140
1141HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1142{
1143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1144
1145 HRESULT rc = i_checkStateDependency(MutableStateDep);
1146 if (FAILED(rc)) return rc;
1147
1148 i_setModified(IsModified_MachineData);
1149 mHWData.backup();
1150 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1151
1152 return S_OK;
1153}
1154
1155HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1156{
1157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1158
1159 *aPointingHIDType = mHWData->mPointingHIDType;
1160
1161 return S_OK;
1162}
1163
1164HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1165{
1166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1167
1168 HRESULT rc = i_checkStateDependency(MutableStateDep);
1169 if (FAILED(rc)) return rc;
1170
1171 i_setModified(IsModified_MachineData);
1172 mHWData.backup();
1173 mHWData->mPointingHIDType = aPointingHIDType;
1174
1175 return S_OK;
1176}
1177
1178HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1179{
1180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1181
1182 *aChipsetType = mHWData->mChipsetType;
1183
1184 return S_OK;
1185}
1186
1187HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1188{
1189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1190
1191 HRESULT rc = i_checkStateDependency(MutableStateDep);
1192 if (FAILED(rc)) return rc;
1193
1194 if (aChipsetType != mHWData->mChipsetType)
1195 {
1196 i_setModified(IsModified_MachineData);
1197 mHWData.backup();
1198 mHWData->mChipsetType = aChipsetType;
1199
1200 // Resize network adapter array, to be finalized on commit/rollback.
1201 // We must not throw away entries yet, otherwise settings are lost
1202 // without a way to roll back.
1203 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1204 size_t oldCount = mNetworkAdapters.size();
1205 if (newCount > oldCount)
1206 {
1207 mNetworkAdapters.resize(newCount);
1208 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1209 {
1210 unconst(mNetworkAdapters[slot]).createObject();
1211 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1212 }
1213 }
1214 }
1215
1216 return S_OK;
1217}
1218
1219HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1220{
1221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1222
1223 *aParavirtProvider = mHWData->mParavirtProvider;
1224
1225 return S_OK;
1226}
1227
1228HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1229{
1230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1231
1232 HRESULT rc = i_checkStateDependency(MutableStateDep);
1233 if (FAILED(rc)) return rc;
1234
1235 if (aParavirtProvider != mHWData->mParavirtProvider)
1236 {
1237 i_setModified(IsModified_MachineData);
1238 mHWData.backup();
1239 mHWData->mParavirtProvider = aParavirtProvider;
1240 }
1241
1242 return S_OK;
1243}
1244
1245HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1246{
1247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 *aParavirtProvider = mHWData->mParavirtProvider;
1250 switch (mHWData->mParavirtProvider)
1251 {
1252 case ParavirtProvider_None:
1253 case ParavirtProvider_HyperV:
1254 case ParavirtProvider_KVM:
1255 case ParavirtProvider_Minimal:
1256 break;
1257
1258 /* Resolve dynamic provider types to the effective types. */
1259 default:
1260 {
1261 ComPtr<IGuestOSType> ptrGuestOSType;
1262 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1263 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1264
1265 Bstr guestTypeFamilyId;
1266 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1267 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1268 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1269
1270 switch (mHWData->mParavirtProvider)
1271 {
1272 case ParavirtProvider_Legacy:
1273 {
1274 if (fOsXGuest)
1275 *aParavirtProvider = ParavirtProvider_Minimal;
1276 else
1277 *aParavirtProvider = ParavirtProvider_None;
1278 break;
1279 }
1280
1281 case ParavirtProvider_Default:
1282 {
1283 if (fOsXGuest)
1284 *aParavirtProvider = ParavirtProvider_Minimal;
1285 else if ( mUserData->s.strOsType == "Windows10"
1286 || mUserData->s.strOsType == "Windows10_64"
1287 || mUserData->s.strOsType == "Windows81"
1288 || mUserData->s.strOsType == "Windows81_64"
1289 || mUserData->s.strOsType == "Windows8"
1290 || mUserData->s.strOsType == "Windows8_64"
1291 || mUserData->s.strOsType == "Windows7"
1292 || mUserData->s.strOsType == "Windows7_64"
1293 || mUserData->s.strOsType == "WindowsVista"
1294 || mUserData->s.strOsType == "WindowsVista_64"
1295 || mUserData->s.strOsType == "Windows2012"
1296 || mUserData->s.strOsType == "Windows2012_64"
1297 || mUserData->s.strOsType == "Windows2008"
1298 || mUserData->s.strOsType == "Windows2008_64")
1299 {
1300 *aParavirtProvider = ParavirtProvider_HyperV;
1301 }
1302 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1303 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1304 || mUserData->s.strOsType == "Linux"
1305 || mUserData->s.strOsType == "Linux_64"
1306 || mUserData->s.strOsType == "ArchLinux"
1307 || mUserData->s.strOsType == "ArchLinux_64"
1308 || mUserData->s.strOsType == "Debian"
1309 || mUserData->s.strOsType == "Debian_64"
1310 || mUserData->s.strOsType == "Fedora"
1311 || mUserData->s.strOsType == "Fedora_64"
1312 || mUserData->s.strOsType == "Gentoo"
1313 || mUserData->s.strOsType == "Gentoo_64"
1314 || mUserData->s.strOsType == "Mandriva"
1315 || mUserData->s.strOsType == "Mandriva_64"
1316 || mUserData->s.strOsType == "OpenSUSE"
1317 || mUserData->s.strOsType == "OpenSUSE_64"
1318 || mUserData->s.strOsType == "Oracle"
1319 || mUserData->s.strOsType == "Oracle_64"
1320 || mUserData->s.strOsType == "RedHat"
1321 || mUserData->s.strOsType == "RedHat_64"
1322 || mUserData->s.strOsType == "Turbolinux"
1323 || mUserData->s.strOsType == "Turbolinux_64"
1324 || mUserData->s.strOsType == "Ubuntu"
1325 || mUserData->s.strOsType == "Ubuntu_64"
1326 || mUserData->s.strOsType == "Xandros"
1327 || mUserData->s.strOsType == "Xandros_64")
1328 {
1329 *aParavirtProvider = ParavirtProvider_KVM;
1330 }
1331 else
1332 *aParavirtProvider = ParavirtProvider_None;
1333 break;
1334 }
1335 }
1336 break;
1337 }
1338 }
1339
1340 Assert( *aParavirtProvider == ParavirtProvider_None
1341 || *aParavirtProvider == ParavirtProvider_Minimal
1342 || *aParavirtProvider == ParavirtProvider_HyperV
1343 || *aParavirtProvider == ParavirtProvider_KVM);
1344 return S_OK;
1345}
1346
1347HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1348{
1349 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1350
1351 aHardwareVersion = mHWData->mHWVersion;
1352
1353 return S_OK;
1354}
1355
1356HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1357{
1358 /* check known version */
1359 Utf8Str hwVersion = aHardwareVersion;
1360 if ( hwVersion.compare("1") != 0
1361 && hwVersion.compare("2") != 0)
1362 return setError(E_INVALIDARG,
1363 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1364
1365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1366
1367 HRESULT rc = i_checkStateDependency(MutableStateDep);
1368 if (FAILED(rc)) return rc;
1369
1370 i_setModified(IsModified_MachineData);
1371 mHWData.backup();
1372 mHWData->mHWVersion = aHardwareVersion;
1373
1374 return S_OK;
1375}
1376
1377HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1378{
1379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1380
1381 if (!mHWData->mHardwareUUID.isZero())
1382 aHardwareUUID = mHWData->mHardwareUUID;
1383 else
1384 aHardwareUUID = mData->mUuid;
1385
1386 return S_OK;
1387}
1388
1389HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1390{
1391 if (!aHardwareUUID.isValid())
1392 return E_INVALIDARG;
1393
1394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 HRESULT rc = i_checkStateDependency(MutableStateDep);
1397 if (FAILED(rc)) return rc;
1398
1399 i_setModified(IsModified_MachineData);
1400 mHWData.backup();
1401 if (aHardwareUUID == mData->mUuid)
1402 mHWData->mHardwareUUID.clear();
1403 else
1404 mHWData->mHardwareUUID = aHardwareUUID;
1405
1406 return S_OK;
1407}
1408
1409HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1410{
1411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1412
1413 *aMemorySize = mHWData->mMemorySize;
1414
1415 return S_OK;
1416}
1417
1418HRESULT Machine::setMemorySize(ULONG aMemorySize)
1419{
1420 /* check RAM limits */
1421 if ( aMemorySize < MM_RAM_MIN_IN_MB
1422 || aMemorySize > MM_RAM_MAX_IN_MB
1423 )
1424 return setError(E_INVALIDARG,
1425 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1426 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1427
1428 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1429
1430 HRESULT rc = i_checkStateDependency(MutableStateDep);
1431 if (FAILED(rc)) return rc;
1432
1433 i_setModified(IsModified_MachineData);
1434 mHWData.backup();
1435 mHWData->mMemorySize = aMemorySize;
1436
1437 return S_OK;
1438}
1439
1440HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1441{
1442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1443
1444 *aCPUCount = mHWData->mCPUCount;
1445
1446 return S_OK;
1447}
1448
1449HRESULT Machine::setCPUCount(ULONG aCPUCount)
1450{
1451 /* check CPU limits */
1452 if ( aCPUCount < SchemaDefs::MinCPUCount
1453 || aCPUCount > SchemaDefs::MaxCPUCount
1454 )
1455 return setError(E_INVALIDARG,
1456 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1457 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1458
1459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1460
1461 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1462 if (mHWData->mCPUHotPlugEnabled)
1463 {
1464 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1465 {
1466 if (mHWData->mCPUAttached[idx])
1467 return setError(E_INVALIDARG,
1468 tr("There is still a CPU attached to socket %lu."
1469 "Detach the CPU before removing the socket"),
1470 aCPUCount, idx+1);
1471 }
1472 }
1473
1474 HRESULT rc = i_checkStateDependency(MutableStateDep);
1475 if (FAILED(rc)) return rc;
1476
1477 i_setModified(IsModified_MachineData);
1478 mHWData.backup();
1479 mHWData->mCPUCount = aCPUCount;
1480
1481 return S_OK;
1482}
1483
1484HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1485{
1486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1487
1488 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1489
1490 return S_OK;
1491}
1492
1493HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1494{
1495 HRESULT rc = S_OK;
1496
1497 /* check throttle limits */
1498 if ( aCPUExecutionCap < 1
1499 || aCPUExecutionCap > 100
1500 )
1501 return setError(E_INVALIDARG,
1502 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1503 aCPUExecutionCap, 1, 100);
1504
1505 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1506
1507 alock.release();
1508 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1509 alock.acquire();
1510 if (FAILED(rc)) return rc;
1511
1512 i_setModified(IsModified_MachineData);
1513 mHWData.backup();
1514 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1515
1516 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1517 if (Global::IsOnline(mData->mMachineState))
1518 i_saveSettings(NULL);
1519
1520 return S_OK;
1521}
1522
1523HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1524{
1525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1526
1527 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1528
1529 return S_OK;
1530}
1531
1532HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1533{
1534 HRESULT rc = S_OK;
1535
1536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1537
1538 rc = i_checkStateDependency(MutableStateDep);
1539 if (FAILED(rc)) return rc;
1540
1541 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1542 {
1543 if (aCPUHotPlugEnabled)
1544 {
1545 i_setModified(IsModified_MachineData);
1546 mHWData.backup();
1547
1548 /* Add the amount of CPUs currently attached */
1549 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1550 mHWData->mCPUAttached[i] = true;
1551 }
1552 else
1553 {
1554 /*
1555 * We can disable hotplug only if the amount of maximum CPUs is equal
1556 * to the amount of attached CPUs
1557 */
1558 unsigned cCpusAttached = 0;
1559 unsigned iHighestId = 0;
1560
1561 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1562 {
1563 if (mHWData->mCPUAttached[i])
1564 {
1565 cCpusAttached++;
1566 iHighestId = i;
1567 }
1568 }
1569
1570 if ( (cCpusAttached != mHWData->mCPUCount)
1571 || (iHighestId >= mHWData->mCPUCount))
1572 return setError(E_INVALIDARG,
1573 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1574
1575 i_setModified(IsModified_MachineData);
1576 mHWData.backup();
1577 }
1578 }
1579
1580 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1581
1582 return rc;
1583}
1584
1585HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1586{
1587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1588
1589 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1590
1591 return S_OK;
1592}
1593
1594HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1595{
1596 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1597
1598 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1599 if (SUCCEEDED(hrc))
1600 {
1601 i_setModified(IsModified_MachineData);
1602 mHWData.backup();
1603 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1604 }
1605 return hrc;
1606}
1607
1608HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1609{
1610#ifdef VBOX_WITH_USB_CARDREADER
1611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1612
1613 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1614
1615 return S_OK;
1616#else
1617 NOREF(aEmulatedUSBCardReaderEnabled);
1618 return E_NOTIMPL;
1619#endif
1620}
1621
1622HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1623{
1624#ifdef VBOX_WITH_USB_CARDREADER
1625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1626
1627 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1628 if (FAILED(rc)) return rc;
1629
1630 i_setModified(IsModified_MachineData);
1631 mHWData.backup();
1632 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1633
1634 return S_OK;
1635#else
1636 NOREF(aEmulatedUSBCardReaderEnabled);
1637 return E_NOTIMPL;
1638#endif
1639}
1640
1641HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1642{
1643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1644
1645 *aHPETEnabled = mHWData->mHPETEnabled;
1646
1647 return S_OK;
1648}
1649
1650HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1651{
1652 HRESULT rc = S_OK;
1653
1654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1655
1656 rc = i_checkStateDependency(MutableStateDep);
1657 if (FAILED(rc)) return rc;
1658
1659 i_setModified(IsModified_MachineData);
1660 mHWData.backup();
1661
1662 mHWData->mHPETEnabled = aHPETEnabled;
1663
1664 return rc;
1665}
1666
1667HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1668{
1669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1670
1671 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1672 return S_OK;
1673}
1674
1675HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1676{
1677 HRESULT rc = S_OK;
1678
1679 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1680
1681 i_setModified(IsModified_MachineData);
1682 mHWData.backup();
1683 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1684
1685 alock.release();
1686 rc = i_onVideoCaptureChange();
1687 alock.acquire();
1688 if (FAILED(rc))
1689 {
1690 /*
1691 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1692 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1693 * determine if it should start or stop capturing. Therefore we need to manually
1694 * undo change.
1695 */
1696 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1697 return rc;
1698 }
1699
1700 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1701 if (Global::IsOnline(mData->mMachineState))
1702 i_saveSettings(NULL);
1703
1704 return rc;
1705}
1706
1707HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1708{
1709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1710 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1711 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1712 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1713 return S_OK;
1714}
1715
1716HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1717{
1718 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1719 bool fChanged = false;
1720
1721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1722
1723 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1724 {
1725 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1726 {
1727 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1728 fChanged = true;
1729 }
1730 }
1731 if (fChanged)
1732 {
1733 alock.release();
1734 HRESULT rc = i_onVideoCaptureChange();
1735 alock.acquire();
1736 if (FAILED(rc)) return rc;
1737 i_setModified(IsModified_MachineData);
1738
1739 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1740 if (Global::IsOnline(mData->mMachineState))
1741 i_saveSettings(NULL);
1742 }
1743
1744 return S_OK;
1745}
1746
1747HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1748{
1749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1750 if (mHWData->mVideoCaptureFile.isEmpty())
1751 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1752 else
1753 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1754 return S_OK;
1755}
1756
1757HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1758{
1759 Utf8Str strFile(aVideoCaptureFile);
1760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1761
1762 if ( Global::IsOnline(mData->mMachineState)
1763 && mHWData->mVideoCaptureEnabled)
1764 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1765
1766 if (!RTPathStartsWithRoot(strFile.c_str()))
1767 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1768
1769 if (!strFile.isEmpty())
1770 {
1771 Utf8Str defaultFile;
1772 i_getDefaultVideoCaptureFile(defaultFile);
1773 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1774 strFile.setNull();
1775 }
1776
1777 i_setModified(IsModified_MachineData);
1778 mHWData.backup();
1779 mHWData->mVideoCaptureFile = strFile;
1780
1781 return S_OK;
1782}
1783
1784HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1785{
1786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1787 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1788 return S_OK;
1789}
1790
1791HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1792{
1793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1794
1795 if ( Global::IsOnline(mData->mMachineState)
1796 && mHWData->mVideoCaptureEnabled)
1797 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1798
1799 i_setModified(IsModified_MachineData);
1800 mHWData.backup();
1801 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1802
1803 return S_OK;
1804}
1805
1806HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1807{
1808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1809 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1810 return S_OK;
1811}
1812
1813HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1814{
1815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1816
1817 if ( Global::IsOnline(mData->mMachineState)
1818 && mHWData->mVideoCaptureEnabled)
1819 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1820
1821 i_setModified(IsModified_MachineData);
1822 mHWData.backup();
1823 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1824
1825 return S_OK;
1826}
1827
1828HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1829{
1830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1831 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1832 return S_OK;
1833}
1834
1835HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1836{
1837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1838
1839 if ( Global::IsOnline(mData->mMachineState)
1840 && mHWData->mVideoCaptureEnabled)
1841 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1842
1843 i_setModified(IsModified_MachineData);
1844 mHWData.backup();
1845 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1846
1847 return S_OK;
1848}
1849
1850HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1851{
1852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1853 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1854 return S_OK;
1855}
1856
1857HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1858{
1859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1860
1861 if ( Global::IsOnline(mData->mMachineState)
1862 && mHWData->mVideoCaptureEnabled)
1863 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1864
1865 i_setModified(IsModified_MachineData);
1866 mHWData.backup();
1867 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1868
1869 return S_OK;
1870}
1871
1872HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1873{
1874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1875 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1876 return S_OK;
1877}
1878
1879HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1880{
1881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1882
1883 if ( Global::IsOnline(mData->mMachineState)
1884 && mHWData->mVideoCaptureEnabled)
1885 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1886
1887 i_setModified(IsModified_MachineData);
1888 mHWData.backup();
1889 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1890
1891 return S_OK;
1892}
1893
1894HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1895{
1896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1897 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1898 return S_OK;
1899}
1900
1901HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1902{
1903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1904
1905 if ( Global::IsOnline(mData->mMachineState)
1906 && mHWData->mVideoCaptureEnabled)
1907 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1908
1909 i_setModified(IsModified_MachineData);
1910 mHWData.backup();
1911 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1912
1913 return S_OK;
1914}
1915
1916HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1917{
1918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1919
1920 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1921 return S_OK;
1922}
1923
1924HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1925{
1926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1927
1928 if ( Global::IsOnline(mData->mMachineState)
1929 && mHWData->mVideoCaptureEnabled)
1930 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1931
1932 i_setModified(IsModified_MachineData);
1933 mHWData.backup();
1934 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1935
1936 return S_OK;
1937}
1938
1939HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1940{
1941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1942
1943 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1944
1945 return S_OK;
1946}
1947
1948HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1949{
1950 switch (aGraphicsControllerType)
1951 {
1952 case GraphicsControllerType_Null:
1953 case GraphicsControllerType_VBoxVGA:
1954#ifdef VBOX_WITH_VMSVGA
1955 case GraphicsControllerType_VMSVGA:
1956#endif
1957 break;
1958 default:
1959 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1960 }
1961
1962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1963
1964 HRESULT rc = i_checkStateDependency(MutableStateDep);
1965 if (FAILED(rc)) return rc;
1966
1967 i_setModified(IsModified_MachineData);
1968 mHWData.backup();
1969 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1970
1971 return S_OK;
1972}
1973
1974HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1975{
1976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1977
1978 *aVRAMSize = mHWData->mVRAMSize;
1979
1980 return S_OK;
1981}
1982
1983HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1984{
1985 /* check VRAM limits */
1986 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1987 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1988 return setError(E_INVALIDARG,
1989 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1990 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1991
1992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1993
1994 HRESULT rc = i_checkStateDependency(MutableStateDep);
1995 if (FAILED(rc)) return rc;
1996
1997 i_setModified(IsModified_MachineData);
1998 mHWData.backup();
1999 mHWData->mVRAMSize = aVRAMSize;
2000
2001 return S_OK;
2002}
2003
2004/** @todo this method should not be public */
2005HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2006{
2007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2008
2009 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2010
2011 return S_OK;
2012}
2013
2014/**
2015 * Set the memory balloon size.
2016 *
2017 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2018 * we have to make sure that we never call IGuest from here.
2019 */
2020HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2021{
2022 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2023#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2024 /* check limits */
2025 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2026 return setError(E_INVALIDARG,
2027 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2028 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2029
2030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2031
2032 i_setModified(IsModified_MachineData);
2033 mHWData.backup();
2034 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2035
2036 return S_OK;
2037#else
2038 NOREF(aMemoryBalloonSize);
2039 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2040#endif
2041}
2042
2043HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2044{
2045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2046
2047 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2048 return S_OK;
2049}
2050
2051HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2052{
2053#ifdef VBOX_WITH_PAGE_SHARING
2054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2055
2056 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2057 i_setModified(IsModified_MachineData);
2058 mHWData.backup();
2059 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2060 return S_OK;
2061#else
2062 NOREF(aPageFusionEnabled);
2063 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2064#endif
2065}
2066
2067HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2068{
2069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2070
2071 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2072
2073 return S_OK;
2074}
2075
2076HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2077{
2078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2079
2080 HRESULT rc = i_checkStateDependency(MutableStateDep);
2081 if (FAILED(rc)) return rc;
2082
2083 /** @todo check validity! */
2084
2085 i_setModified(IsModified_MachineData);
2086 mHWData.backup();
2087 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2088
2089 return S_OK;
2090}
2091
2092
2093HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2094{
2095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2096
2097 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2098
2099 return S_OK;
2100}
2101
2102HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2103{
2104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2105
2106 HRESULT rc = i_checkStateDependency(MutableStateDep);
2107 if (FAILED(rc)) return rc;
2108
2109 /** @todo check validity! */
2110 i_setModified(IsModified_MachineData);
2111 mHWData.backup();
2112 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2113
2114 return S_OK;
2115}
2116
2117HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2118{
2119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2120
2121 *aMonitorCount = mHWData->mMonitorCount;
2122
2123 return S_OK;
2124}
2125
2126HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2127{
2128 /* make sure monitor count is a sensible number */
2129 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2130 return setError(E_INVALIDARG,
2131 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2132 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2133
2134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2135
2136 HRESULT rc = i_checkStateDependency(MutableStateDep);
2137 if (FAILED(rc)) return rc;
2138
2139 i_setModified(IsModified_MachineData);
2140 mHWData.backup();
2141 mHWData->mMonitorCount = aMonitorCount;
2142
2143 return S_OK;
2144}
2145
2146HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2147{
2148 /* mBIOSSettings is constant during life time, no need to lock */
2149 aBIOSSettings = mBIOSSettings;
2150
2151 return S_OK;
2152}
2153
2154HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2155{
2156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2157
2158 switch (aProperty)
2159 {
2160 case CPUPropertyType_PAE:
2161 *aValue = mHWData->mPAEEnabled;
2162 break;
2163
2164 case CPUPropertyType_LongMode:
2165 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2166 *aValue = TRUE;
2167 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2168 *aValue = FALSE;
2169#if HC_ARCH_BITS == 64
2170 else
2171 *aValue = TRUE;
2172#else
2173 else
2174 {
2175 *aValue = FALSE;
2176
2177 ComPtr<IGuestOSType> ptrGuestOSType;
2178 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2179 if (SUCCEEDED(hrc2))
2180 {
2181 BOOL fIs64Bit = FALSE;
2182 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2183 if (SUCCEEDED(hrc2) && fIs64Bit)
2184 {
2185 ComObjPtr<Host> ptrHost = mParent->i_host();
2186 alock.release();
2187
2188 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2189 if (FAILED(hrc2))
2190 *aValue = FALSE;
2191 }
2192 }
2193 }
2194#endif
2195 break;
2196
2197 case CPUPropertyType_TripleFaultReset:
2198 *aValue = mHWData->mTripleFaultReset;
2199 break;
2200
2201 default:
2202 return E_INVALIDARG;
2203 }
2204 return S_OK;
2205}
2206
2207HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2208{
2209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2210
2211 HRESULT rc = i_checkStateDependency(MutableStateDep);
2212 if (FAILED(rc)) return rc;
2213
2214 switch (aProperty)
2215 {
2216 case CPUPropertyType_PAE:
2217 i_setModified(IsModified_MachineData);
2218 mHWData.backup();
2219 mHWData->mPAEEnabled = !!aValue;
2220 break;
2221
2222 case CPUPropertyType_LongMode:
2223 i_setModified(IsModified_MachineData);
2224 mHWData.backup();
2225 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2226 break;
2227
2228 case CPUPropertyType_TripleFaultReset:
2229 i_setModified(IsModified_MachineData);
2230 mHWData.backup();
2231 mHWData->mTripleFaultReset = !!aValue;
2232 break;
2233
2234 default:
2235 return E_INVALIDARG;
2236 }
2237 return S_OK;
2238}
2239
2240HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2241{
2242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2243
2244 switch(aId)
2245 {
2246 case 0x0:
2247 case 0x1:
2248 case 0x2:
2249 case 0x3:
2250 case 0x4:
2251 case 0x5:
2252 case 0x6:
2253 case 0x7:
2254 case 0x8:
2255 case 0x9:
2256 case 0xA:
2257 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2258 return E_INVALIDARG;
2259
2260 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2261 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2262 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2263 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2264 break;
2265
2266 case 0x80000000:
2267 case 0x80000001:
2268 case 0x80000002:
2269 case 0x80000003:
2270 case 0x80000004:
2271 case 0x80000005:
2272 case 0x80000006:
2273 case 0x80000007:
2274 case 0x80000008:
2275 case 0x80000009:
2276 case 0x8000000A:
2277 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2278 return E_INVALIDARG;
2279
2280 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2281 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2282 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2283 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2284 break;
2285
2286 default:
2287 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2288 }
2289 return S_OK;
2290}
2291
2292
2293HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2294{
2295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2296
2297 HRESULT rc = i_checkStateDependency(MutableStateDep);
2298 if (FAILED(rc)) return rc;
2299
2300 switch(aId)
2301 {
2302 case 0x0:
2303 case 0x1:
2304 case 0x2:
2305 case 0x3:
2306 case 0x4:
2307 case 0x5:
2308 case 0x6:
2309 case 0x7:
2310 case 0x8:
2311 case 0x9:
2312 case 0xA:
2313 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2314 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2315 i_setModified(IsModified_MachineData);
2316 mHWData.backup();
2317 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2318 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2319 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2320 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2321 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2322 break;
2323
2324 case 0x80000000:
2325 case 0x80000001:
2326 case 0x80000002:
2327 case 0x80000003:
2328 case 0x80000004:
2329 case 0x80000005:
2330 case 0x80000006:
2331 case 0x80000007:
2332 case 0x80000008:
2333 case 0x80000009:
2334 case 0x8000000A:
2335 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2336 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2337 i_setModified(IsModified_MachineData);
2338 mHWData.backup();
2339 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2340 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2341 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2342 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2343 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2344 break;
2345
2346 default:
2347 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2348 }
2349 return S_OK;
2350}
2351
2352HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2353{
2354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2355
2356 HRESULT rc = i_checkStateDependency(MutableStateDep);
2357 if (FAILED(rc)) return rc;
2358
2359 switch(aId)
2360 {
2361 case 0x0:
2362 case 0x1:
2363 case 0x2:
2364 case 0x3:
2365 case 0x4:
2366 case 0x5:
2367 case 0x6:
2368 case 0x7:
2369 case 0x8:
2370 case 0x9:
2371 case 0xA:
2372 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2373 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2374 i_setModified(IsModified_MachineData);
2375 mHWData.backup();
2376 /* Invalidate leaf. */
2377 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2378 break;
2379
2380 case 0x80000000:
2381 case 0x80000001:
2382 case 0x80000002:
2383 case 0x80000003:
2384 case 0x80000004:
2385 case 0x80000005:
2386 case 0x80000006:
2387 case 0x80000007:
2388 case 0x80000008:
2389 case 0x80000009:
2390 case 0x8000000A:
2391 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2392 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2393 i_setModified(IsModified_MachineData);
2394 mHWData.backup();
2395 /* Invalidate leaf. */
2396 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2397 break;
2398
2399 default:
2400 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2401 }
2402 return S_OK;
2403}
2404
2405HRESULT Machine::removeAllCPUIDLeaves()
2406{
2407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2408
2409 HRESULT rc = i_checkStateDependency(MutableStateDep);
2410 if (FAILED(rc)) return rc;
2411
2412 i_setModified(IsModified_MachineData);
2413 mHWData.backup();
2414
2415 /* Invalidate all standard leafs. */
2416 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2417 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2418
2419 /* Invalidate all extended leafs. */
2420 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2421 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2422
2423 return S_OK;
2424}
2425HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2426{
2427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2428
2429 switch(aProperty)
2430 {
2431 case HWVirtExPropertyType_Enabled:
2432 *aValue = mHWData->mHWVirtExEnabled;
2433 break;
2434
2435 case HWVirtExPropertyType_VPID:
2436 *aValue = mHWData->mHWVirtExVPIDEnabled;
2437 break;
2438
2439 case HWVirtExPropertyType_NestedPaging:
2440 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2441 break;
2442
2443 case HWVirtExPropertyType_UnrestrictedExecution:
2444 *aValue = mHWData->mHWVirtExUXEnabled;
2445 break;
2446
2447 case HWVirtExPropertyType_LargePages:
2448 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2449#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2450 *aValue = FALSE;
2451#endif
2452 break;
2453
2454 case HWVirtExPropertyType_Force:
2455 *aValue = mHWData->mHWVirtExForceEnabled;
2456 break;
2457
2458 default:
2459 return E_INVALIDARG;
2460 }
2461 return S_OK;
2462}
2463
2464HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2465{
2466 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2467
2468 HRESULT rc = i_checkStateDependency(MutableStateDep);
2469 if (FAILED(rc)) return rc;
2470
2471 switch(aProperty)
2472 {
2473 case HWVirtExPropertyType_Enabled:
2474 i_setModified(IsModified_MachineData);
2475 mHWData.backup();
2476 mHWData->mHWVirtExEnabled = !!aValue;
2477 break;
2478
2479 case HWVirtExPropertyType_VPID:
2480 i_setModified(IsModified_MachineData);
2481 mHWData.backup();
2482 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2483 break;
2484
2485 case HWVirtExPropertyType_NestedPaging:
2486 i_setModified(IsModified_MachineData);
2487 mHWData.backup();
2488 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2489 break;
2490
2491 case HWVirtExPropertyType_UnrestrictedExecution:
2492 i_setModified(IsModified_MachineData);
2493 mHWData.backup();
2494 mHWData->mHWVirtExUXEnabled = !!aValue;
2495 break;
2496
2497 case HWVirtExPropertyType_LargePages:
2498 i_setModified(IsModified_MachineData);
2499 mHWData.backup();
2500 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2501 break;
2502
2503 case HWVirtExPropertyType_Force:
2504 i_setModified(IsModified_MachineData);
2505 mHWData.backup();
2506 mHWData->mHWVirtExForceEnabled = !!aValue;
2507 break;
2508
2509 default:
2510 return E_INVALIDARG;
2511 }
2512
2513 return S_OK;
2514}
2515
2516HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2517{
2518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2519
2520 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2521
2522 return S_OK;
2523}
2524
2525HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2526{
2527 /* @todo (r=dmik):
2528 * 1. Allow to change the name of the snapshot folder containing snapshots
2529 * 2. Rename the folder on disk instead of just changing the property
2530 * value (to be smart and not to leave garbage). Note that it cannot be
2531 * done here because the change may be rolled back. Thus, the right
2532 * place is #saveSettings().
2533 */
2534
2535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2536
2537 HRESULT rc = i_checkStateDependency(MutableStateDep);
2538 if (FAILED(rc)) return rc;
2539
2540 if (!mData->mCurrentSnapshot.isNull())
2541 return setError(E_FAIL,
2542 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2543
2544 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2545
2546 if (strSnapshotFolder.isEmpty())
2547 strSnapshotFolder = "Snapshots";
2548 int vrc = i_calculateFullPath(strSnapshotFolder,
2549 strSnapshotFolder);
2550 if (RT_FAILURE(vrc))
2551 return setError(E_FAIL,
2552 tr("Invalid snapshot folder '%s' (%Rrc)"),
2553 strSnapshotFolder.c_str(), vrc);
2554
2555 i_setModified(IsModified_MachineData);
2556 mUserData.backup();
2557
2558 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2559
2560 return S_OK;
2561}
2562
2563HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2564{
2565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2566
2567 aMediumAttachments.resize(mMediaData->mAttachments.size());
2568 size_t i = 0;
2569 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2570 it != mMediaData->mAttachments.end(); ++it, ++i)
2571 aMediumAttachments[i] = *it;
2572
2573 return S_OK;
2574}
2575
2576HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2577{
2578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2579
2580 Assert(!!mVRDEServer);
2581
2582 aVRDEServer = mVRDEServer;
2583
2584 return S_OK;
2585}
2586
2587HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2588{
2589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2590
2591 aAudioAdapter = mAudioAdapter;
2592
2593 return S_OK;
2594}
2595
2596HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2597{
2598#ifdef VBOX_WITH_VUSB
2599 clearError();
2600 MultiResult rc(S_OK);
2601
2602# ifdef VBOX_WITH_USB
2603 rc = mParent->i_host()->i_checkUSBProxyService();
2604 if (FAILED(rc)) return rc;
2605# endif
2606
2607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2608
2609 USBControllerList data = *mUSBControllers.data();
2610 aUSBControllers.resize(data.size());
2611 size_t i = 0;
2612 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2613 aUSBControllers[i] = *it;
2614
2615 return S_OK;
2616#else
2617 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2618 * extended error info to indicate that USB is simply not available
2619 * (w/o treating it as a failure), for example, as in OSE */
2620 NOREF(aUSBControllers);
2621 ReturnComNotImplemented();
2622#endif /* VBOX_WITH_VUSB */
2623}
2624
2625HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2626{
2627#ifdef VBOX_WITH_VUSB
2628 clearError();
2629 MultiResult rc(S_OK);
2630
2631# ifdef VBOX_WITH_USB
2632 rc = mParent->i_host()->i_checkUSBProxyService();
2633 if (FAILED(rc)) return rc;
2634# endif
2635
2636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2637
2638 aUSBDeviceFilters = mUSBDeviceFilters;
2639 return rc;
2640#else
2641 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2642 * extended error info to indicate that USB is simply not available
2643 * (w/o treating it as a failure), for example, as in OSE */
2644 NOREF(aUSBDeviceFilters);
2645 ReturnComNotImplemented();
2646#endif /* VBOX_WITH_VUSB */
2647}
2648
2649HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2650{
2651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2652
2653 aSettingsFilePath = mData->m_strConfigFileFull;
2654
2655 return S_OK;
2656}
2657
2658HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2659{
2660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2661
2662 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2663 if (FAILED(rc)) return rc;
2664
2665 if (!mData->pMachineConfigFile->fileExists())
2666 // this is a new machine, and no config file exists yet:
2667 *aSettingsModified = TRUE;
2668 else
2669 *aSettingsModified = (mData->flModifications != 0);
2670
2671 return S_OK;
2672}
2673
2674HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2675{
2676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2677
2678 *aSessionState = mData->mSession.mState;
2679
2680 return S_OK;
2681}
2682
2683HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2684{
2685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2686
2687 aSessionName = mData->mSession.mName;
2688
2689 return S_OK;
2690}
2691
2692HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2693{
2694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2695
2696 *aSessionPID = mData->mSession.mPID;
2697
2698 return S_OK;
2699}
2700
2701HRESULT Machine::getState(MachineState_T *aState)
2702{
2703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2704
2705 *aState = mData->mMachineState;
2706 Assert(mData->mMachineState != MachineState_Null);
2707
2708 return S_OK;
2709}
2710
2711HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2712{
2713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2714
2715 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2716
2717 return S_OK;
2718}
2719
2720HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2721{
2722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2723
2724 aStateFilePath = mSSData->strStateFilePath;
2725
2726 return S_OK;
2727}
2728
2729HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2730{
2731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2732
2733 i_getLogFolder(aLogFolder);
2734
2735 return S_OK;
2736}
2737
2738HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2739{
2740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2741
2742 aCurrentSnapshot = mData->mCurrentSnapshot;
2743
2744 return S_OK;
2745}
2746
2747HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2748{
2749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2750
2751 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2752 ? 0
2753 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2754
2755 return S_OK;
2756}
2757
2758HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2759{
2760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2761
2762 /* Note: for machines with no snapshots, we always return FALSE
2763 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2764 * reasons :) */
2765
2766 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2767 ? FALSE
2768 : mData->mCurrentStateModified;
2769
2770 return S_OK;
2771}
2772
2773HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2774{
2775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2776
2777 aSharedFolders.resize(mHWData->mSharedFolders.size());
2778 size_t i = 0;
2779 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2780 it != mHWData->mSharedFolders.end(); ++i, ++it)
2781 aSharedFolders[i] = *it;
2782
2783 return S_OK;
2784}
2785
2786HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2787{
2788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 *aClipboardMode = mHWData->mClipboardMode;
2791
2792 return S_OK;
2793}
2794
2795HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2796{
2797 HRESULT rc = S_OK;
2798
2799 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2800
2801 alock.release();
2802 rc = i_onClipboardModeChange(aClipboardMode);
2803 alock.acquire();
2804 if (FAILED(rc)) return rc;
2805
2806 i_setModified(IsModified_MachineData);
2807 mHWData.backup();
2808 mHWData->mClipboardMode = aClipboardMode;
2809
2810 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2811 if (Global::IsOnline(mData->mMachineState))
2812 i_saveSettings(NULL);
2813
2814 return S_OK;
2815}
2816
2817HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2818{
2819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 *aDnDMode = mHWData->mDnDMode;
2822
2823 return S_OK;
2824}
2825
2826HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2827{
2828 HRESULT rc = S_OK;
2829
2830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2831
2832 alock.release();
2833 rc = i_onDnDModeChange(aDnDMode);
2834
2835 alock.acquire();
2836 if (FAILED(rc)) return rc;
2837
2838 i_setModified(IsModified_MachineData);
2839 mHWData.backup();
2840 mHWData->mDnDMode = aDnDMode;
2841
2842 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2843 if (Global::IsOnline(mData->mMachineState))
2844 i_saveSettings(NULL);
2845
2846 return S_OK;
2847}
2848
2849HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2850{
2851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2852
2853 try
2854 {
2855 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2856 }
2857 catch (...)
2858 {
2859 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2860 }
2861
2862 return S_OK;
2863}
2864
2865HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2866{
2867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2868
2869 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2870 if (FAILED(rc)) return rc;
2871
2872 i_setModified(IsModified_MachineData);
2873 mHWData.backup();
2874 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2875 return rc;
2876}
2877
2878HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2879{
2880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2881 StorageControllerList data = *mStorageControllers.data();
2882 size_t i = 0;
2883 aStorageControllers.resize(data.size());
2884 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2885 aStorageControllers[i] = *it;
2886 return S_OK;
2887}
2888
2889HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2890{
2891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2892
2893 *aEnabled = mUserData->s.fTeleporterEnabled;
2894
2895 return S_OK;
2896}
2897
2898HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2899{
2900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2901
2902 /* Only allow it to be set to true when PoweredOff or Aborted.
2903 (Clearing it is always permitted.) */
2904 if ( aTeleporterEnabled
2905 && mData->mRegistered
2906 && ( !i_isSessionMachine()
2907 || ( mData->mMachineState != MachineState_PoweredOff
2908 && mData->mMachineState != MachineState_Teleported
2909 && mData->mMachineState != MachineState_Aborted
2910 )
2911 )
2912 )
2913 return setError(VBOX_E_INVALID_VM_STATE,
2914 tr("The machine is not powered off (state is %s)"),
2915 Global::stringifyMachineState(mData->mMachineState));
2916
2917 i_setModified(IsModified_MachineData);
2918 mUserData.backup();
2919 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2920
2921 return S_OK;
2922}
2923
2924HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2925{
2926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2927
2928 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2929
2930 return S_OK;
2931}
2932
2933HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2934{
2935 if (aTeleporterPort >= _64K)
2936 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2937
2938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2939
2940 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2941 if (FAILED(rc)) return rc;
2942
2943 i_setModified(IsModified_MachineData);
2944 mUserData.backup();
2945 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2946
2947 return S_OK;
2948}
2949
2950HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2951{
2952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2953
2954 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2955
2956 return S_OK;
2957}
2958
2959HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2960{
2961 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2962
2963 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2964 if (FAILED(rc)) return rc;
2965
2966 i_setModified(IsModified_MachineData);
2967 mUserData.backup();
2968 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2969
2970 return S_OK;
2971}
2972
2973HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2974{
2975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2976 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2977
2978 return S_OK;
2979}
2980
2981HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2982{
2983 /*
2984 * Hash the password first.
2985 */
2986 com::Utf8Str aT = aTeleporterPassword;
2987
2988 if (!aT.isEmpty())
2989 {
2990 if (VBoxIsPasswordHashed(&aT))
2991 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2992 VBoxHashPassword(&aT);
2993 }
2994
2995 /*
2996 * Do the update.
2997 */
2998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2999 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3000 if (SUCCEEDED(hrc))
3001 {
3002 i_setModified(IsModified_MachineData);
3003 mUserData.backup();
3004 mUserData->s.strTeleporterPassword = aT;
3005 }
3006
3007 return hrc;
3008}
3009
3010HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3011{
3012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3013
3014 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3015 return S_OK;
3016}
3017
3018HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3019{
3020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3021
3022 /* @todo deal with running state change. */
3023 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3024 if (FAILED(rc)) return rc;
3025
3026 i_setModified(IsModified_MachineData);
3027 mUserData.backup();
3028 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3029 return S_OK;
3030}
3031
3032HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3033{
3034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3035
3036 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3037 return S_OK;
3038}
3039
3040HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3041{
3042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3043
3044 /* @todo deal with running state change. */
3045 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3046 if (FAILED(rc)) return rc;
3047
3048 i_setModified(IsModified_MachineData);
3049 mUserData.backup();
3050 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3051 return S_OK;
3052}
3053
3054HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3055{
3056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3057
3058 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3059 return S_OK;
3060}
3061
3062HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3063{
3064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3065
3066 /* @todo deal with running state change. */
3067 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3068 if (FAILED(rc)) return rc;
3069
3070 i_setModified(IsModified_MachineData);
3071 mUserData.backup();
3072 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3073 return S_OK;
3074}
3075
3076HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3077{
3078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3079
3080 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3081
3082 return S_OK;
3083}
3084
3085HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3086{
3087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3088
3089 /* @todo deal with running state change. */
3090 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3091 if (FAILED(rc)) return rc;
3092
3093 i_setModified(IsModified_MachineData);
3094 mUserData.backup();
3095 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3096
3097 return S_OK;
3098}
3099
3100HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3101{
3102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3103
3104 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3105 return S_OK;
3106}
3107
3108HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3109{
3110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3111
3112 /* @todo deal with running state change. */
3113 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3114 if (FAILED(rc)) return rc;
3115
3116 i_setModified(IsModified_MachineData);
3117 mUserData.backup();
3118 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3119 return S_OK;
3120}
3121
3122HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3123{
3124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3125
3126 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3127
3128 return S_OK;
3129}
3130
3131HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3132{
3133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3134
3135 /* Only allow it to be set to true when PoweredOff or Aborted.
3136 (Clearing it is always permitted.) */
3137 if ( aRTCUseUTC
3138 && mData->mRegistered
3139 && ( !i_isSessionMachine()
3140 || ( mData->mMachineState != MachineState_PoweredOff
3141 && mData->mMachineState != MachineState_Teleported
3142 && mData->mMachineState != MachineState_Aborted
3143 )
3144 )
3145 )
3146 return setError(VBOX_E_INVALID_VM_STATE,
3147 tr("The machine is not powered off (state is %s)"),
3148 Global::stringifyMachineState(mData->mMachineState));
3149
3150 i_setModified(IsModified_MachineData);
3151 mUserData.backup();
3152 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3153
3154 return S_OK;
3155}
3156
3157HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3158{
3159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3160
3161 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3162
3163 return S_OK;
3164}
3165
3166HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3167{
3168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3169
3170 HRESULT rc = i_checkStateDependency(MutableStateDep);
3171 if (FAILED(rc)) return rc;
3172
3173 i_setModified(IsModified_MachineData);
3174 mHWData.backup();
3175 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3176
3177 return S_OK;
3178}
3179
3180HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3181{
3182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3183
3184 *aIOCacheSize = mHWData->mIOCacheSize;
3185
3186 return S_OK;
3187}
3188
3189HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3190{
3191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3192
3193 HRESULT rc = i_checkStateDependency(MutableStateDep);
3194 if (FAILED(rc)) return rc;
3195
3196 i_setModified(IsModified_MachineData);
3197 mHWData.backup();
3198 mHWData->mIOCacheSize = aIOCacheSize;
3199
3200 return S_OK;
3201}
3202
3203
3204/**
3205 * @note Locks objects!
3206 */
3207HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3208 LockType_T aLockType)
3209{
3210 /* check the session state */
3211 SessionState_T state;
3212 HRESULT rc = aSession->COMGETTER(State)(&state);
3213 if (FAILED(rc)) return rc;
3214
3215 if (state != SessionState_Unlocked)
3216 return setError(VBOX_E_INVALID_OBJECT_STATE,
3217 tr("The given session is busy"));
3218
3219 // get the client's IInternalSessionControl interface
3220 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3221 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3222 E_INVALIDARG);
3223
3224 // session name (only used in some code paths)
3225 Utf8Str strSessionName;
3226
3227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3228
3229 if (!mData->mRegistered)
3230 return setError(E_UNEXPECTED,
3231 tr("The machine '%s' is not registered"),
3232 mUserData->s.strName.c_str());
3233
3234 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3235
3236 SessionState_T oldState = mData->mSession.mState;
3237 /* Hack: in case the session is closing and there is a progress object
3238 * which allows waiting for the session to be closed, take the opportunity
3239 * and do a limited wait (max. 1 second). This helps a lot when the system
3240 * is busy and thus session closing can take a little while. */
3241 if ( mData->mSession.mState == SessionState_Unlocking
3242 && mData->mSession.mProgress)
3243 {
3244 alock.release();
3245 mData->mSession.mProgress->WaitForCompletion(1000);
3246 alock.acquire();
3247 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3248 }
3249
3250 // try again now
3251 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3252 // (i.e. session machine exists)
3253 && (aLockType == LockType_Shared) // caller wants a shared link to the
3254 // existing session that holds the write lock:
3255 )
3256 {
3257 // OK, share the session... we are now dealing with three processes:
3258 // 1) VBoxSVC (where this code runs);
3259 // 2) process C: the caller's client process (who wants a shared session);
3260 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3261
3262 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3263 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3264 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3265 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3266 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3267
3268 /*
3269 * Release the lock before calling the client process. It's safe here
3270 * since the only thing to do after we get the lock again is to add
3271 * the remote control to the list (which doesn't directly influence
3272 * anything).
3273 */
3274 alock.release();
3275
3276 // get the console of the session holding the write lock (this is a remote call)
3277 ComPtr<IConsole> pConsoleW;
3278 if (mData->mSession.mLockType == LockType_VM)
3279 {
3280 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3281 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3282 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3283 if (FAILED(rc))
3284 // the failure may occur w/o any error info (from RPC), so provide one
3285 return setError(VBOX_E_VM_ERROR,
3286 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3287 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3288 }
3289
3290 // share the session machine and W's console with the caller's session
3291 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3292 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3293 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3294
3295 if (FAILED(rc))
3296 // the failure may occur w/o any error info (from RPC), so provide one
3297 return setError(VBOX_E_VM_ERROR,
3298 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3299 alock.acquire();
3300
3301 // need to revalidate the state after acquiring the lock again
3302 if (mData->mSession.mState != SessionState_Locked)
3303 {
3304 pSessionControl->Uninitialize();
3305 return setError(VBOX_E_INVALID_SESSION_STATE,
3306 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3307 mUserData->s.strName.c_str());
3308 }
3309
3310 // add the caller's session to the list
3311 mData->mSession.mRemoteControls.push_back(pSessionControl);
3312 }
3313 else if ( mData->mSession.mState == SessionState_Locked
3314 || mData->mSession.mState == SessionState_Unlocking
3315 )
3316 {
3317 // sharing not permitted, or machine still unlocking:
3318 return setError(VBOX_E_INVALID_OBJECT_STATE,
3319 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3320 mUserData->s.strName.c_str());
3321 }
3322 else
3323 {
3324 // machine is not locked: then write-lock the machine (create the session machine)
3325
3326 // must not be busy
3327 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3328
3329 // get the caller's session PID
3330 RTPROCESS pid = NIL_RTPROCESS;
3331 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3332 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3333 Assert(pid != NIL_RTPROCESS);
3334
3335 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3336
3337 if (fLaunchingVMProcess)
3338 {
3339 if (mData->mSession.mPID == NIL_RTPROCESS)
3340 {
3341 // two or more clients racing for a lock, the one which set the
3342 // session state to Spawning will win, the others will get an
3343 // error as we can't decide here if waiting a little would help
3344 // (only for shared locks this would avoid an error)
3345 return setError(VBOX_E_INVALID_OBJECT_STATE,
3346 tr("The machine '%s' already has a lock request pending"),
3347 mUserData->s.strName.c_str());
3348 }
3349
3350 // this machine is awaiting for a spawning session to be opened:
3351 // then the calling process must be the one that got started by
3352 // LaunchVMProcess()
3353
3354 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3355 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3356
3357#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3358 /* Hardened windows builds spawns three processes when a VM is
3359 launched, the 3rd one is the one that will end up here. */
3360 RTPROCESS ppid;
3361 int rc = RTProcQueryParent(pid, &ppid);
3362 if (RT_SUCCESS(rc))
3363 rc = RTProcQueryParent(ppid, &ppid);
3364 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3365 || rc == VERR_ACCESS_DENIED)
3366 {
3367 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3368 mData->mSession.mPID = pid;
3369 }
3370#endif
3371
3372 if (mData->mSession.mPID != pid)
3373 return setError(E_ACCESSDENIED,
3374 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3375 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3376 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3377 }
3378
3379 // create the mutable SessionMachine from the current machine
3380 ComObjPtr<SessionMachine> sessionMachine;
3381 sessionMachine.createObject();
3382 rc = sessionMachine->init(this);
3383 AssertComRC(rc);
3384
3385 /* NOTE: doing return from this function after this point but
3386 * before the end is forbidden since it may call SessionMachine::uninit()
3387 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3388 * lock while still holding the Machine lock in alock so that a deadlock
3389 * is possible due to the wrong lock order. */
3390
3391 if (SUCCEEDED(rc))
3392 {
3393 /*
3394 * Set the session state to Spawning to protect against subsequent
3395 * attempts to open a session and to unregister the machine after
3396 * we release the lock.
3397 */
3398 SessionState_T origState = mData->mSession.mState;
3399 mData->mSession.mState = SessionState_Spawning;
3400
3401#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3402 /* Get the client token ID to be passed to the client process */
3403 Utf8Str strTokenId;
3404 sessionMachine->i_getTokenId(strTokenId);
3405 Assert(!strTokenId.isEmpty());
3406#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3407 /* Get the client token to be passed to the client process */
3408 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3409 /* The token is now "owned" by pToken, fix refcount */
3410 if (!pToken.isNull())
3411 pToken->Release();
3412#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3413
3414 /*
3415 * Release the lock before calling the client process -- it will call
3416 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3417 * because the state is Spawning, so that LaunchVMProcess() and
3418 * LockMachine() calls will fail. This method, called before we
3419 * acquire the lock again, will fail because of the wrong PID.
3420 *
3421 * Note that mData->mSession.mRemoteControls accessed outside
3422 * the lock may not be modified when state is Spawning, so it's safe.
3423 */
3424 alock.release();
3425
3426 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3427#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3428 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3429#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3430 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3431 /* Now the token is owned by the client process. */
3432 pToken.setNull();
3433#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3434 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3435
3436 /* The failure may occur w/o any error info (from RPC), so provide one */
3437 if (FAILED(rc))
3438 setError(VBOX_E_VM_ERROR,
3439 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3440
3441 // get session name, either to remember or to compare against
3442 // the already known session name.
3443 {
3444 Bstr bstrSessionName;
3445 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3446 if (SUCCEEDED(rc2))
3447 strSessionName = bstrSessionName;
3448 }
3449
3450 if ( SUCCEEDED(rc)
3451 && fLaunchingVMProcess
3452 )
3453 {
3454 /* complete the remote session initialization */
3455
3456 /* get the console from the direct session */
3457 ComPtr<IConsole> console;
3458 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3459 ComAssertComRC(rc);
3460
3461 if (SUCCEEDED(rc) && !console)
3462 {
3463 ComAssert(!!console);
3464 rc = E_FAIL;
3465 }
3466
3467 /* assign machine & console to the remote session */
3468 if (SUCCEEDED(rc))
3469 {
3470 /*
3471 * after LaunchVMProcess(), the first and the only
3472 * entry in remoteControls is that remote session
3473 */
3474 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3475 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3476 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3477
3478 /* The failure may occur w/o any error info (from RPC), so provide one */
3479 if (FAILED(rc))
3480 setError(VBOX_E_VM_ERROR,
3481 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3482 }
3483
3484 if (FAILED(rc))
3485 pSessionControl->Uninitialize();
3486 }
3487
3488 /* acquire the lock again */
3489 alock.acquire();
3490
3491 /* Restore the session state */
3492 mData->mSession.mState = origState;
3493 }
3494
3495 // finalize spawning anyway (this is why we don't return on errors above)
3496 if (fLaunchingVMProcess)
3497 {
3498 Assert(mData->mSession.mName == strSessionName);
3499 /* Note that the progress object is finalized later */
3500 /** @todo Consider checking mData->mSession.mProgress for cancellation
3501 * around here. */
3502
3503 /* We don't reset mSession.mPID here because it is necessary for
3504 * SessionMachine::uninit() to reap the child process later. */
3505
3506 if (FAILED(rc))
3507 {
3508 /* Close the remote session, remove the remote control from the list
3509 * and reset session state to Closed (@note keep the code in sync
3510 * with the relevant part in checkForSpawnFailure()). */
3511
3512 Assert(mData->mSession.mRemoteControls.size() == 1);
3513 if (mData->mSession.mRemoteControls.size() == 1)
3514 {
3515 ErrorInfoKeeper eik;
3516 mData->mSession.mRemoteControls.front()->Uninitialize();
3517 }
3518
3519 mData->mSession.mRemoteControls.clear();
3520 mData->mSession.mState = SessionState_Unlocked;
3521 }
3522 }
3523 else
3524 {
3525 /* memorize PID of the directly opened session */
3526 if (SUCCEEDED(rc))
3527 mData->mSession.mPID = pid;
3528 }
3529
3530 if (SUCCEEDED(rc))
3531 {
3532 mData->mSession.mLockType = aLockType;
3533 /* memorize the direct session control and cache IUnknown for it */
3534 mData->mSession.mDirectControl = pSessionControl;
3535 mData->mSession.mState = SessionState_Locked;
3536 if (!fLaunchingVMProcess)
3537 mData->mSession.mName = strSessionName;
3538 /* associate the SessionMachine with this Machine */
3539 mData->mSession.mMachine = sessionMachine;
3540
3541 /* request an IUnknown pointer early from the remote party for later
3542 * identity checks (it will be internally cached within mDirectControl
3543 * at least on XPCOM) */
3544 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3545 NOREF(unk);
3546 }
3547
3548 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3549 * would break the lock order */
3550 alock.release();
3551
3552 /* uninitialize the created session machine on failure */
3553 if (FAILED(rc))
3554 sessionMachine->uninit();
3555 }
3556
3557 if (SUCCEEDED(rc))
3558 {
3559 /*
3560 * tell the client watcher thread to update the set of
3561 * machines that have open sessions
3562 */
3563 mParent->i_updateClientWatcher();
3564
3565 if (oldState != SessionState_Locked)
3566 /* fire an event */
3567 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3568 }
3569
3570 return rc;
3571}
3572
3573/**
3574 * @note Locks objects!
3575 */
3576HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3577 const com::Utf8Str &aName,
3578 const com::Utf8Str &aEnvironment,
3579 ComPtr<IProgress> &aProgress)
3580{
3581 Utf8Str strFrontend(aName);
3582 /* "emergencystop" doesn't need the session, so skip the checks/interface
3583 * retrieval. This code doesn't quite fit in here, but introducing a
3584 * special API method would be even more effort, and would require explicit
3585 * support by every API client. It's better to hide the feature a bit. */
3586 if (strFrontend != "emergencystop")
3587 CheckComArgNotNull(aSession);
3588
3589 HRESULT rc = S_OK;
3590 if (strFrontend.isEmpty())
3591 {
3592 Bstr bstrFrontend;
3593 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3594 if (FAILED(rc))
3595 return rc;
3596 strFrontend = bstrFrontend;
3597 if (strFrontend.isEmpty())
3598 {
3599 ComPtr<ISystemProperties> systemProperties;
3600 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3601 if (FAILED(rc))
3602 return rc;
3603 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3604 if (FAILED(rc))
3605 return rc;
3606 strFrontend = bstrFrontend;
3607 }
3608 /* paranoia - emergencystop is not a valid default */
3609 if (strFrontend == "emergencystop")
3610 strFrontend = Utf8Str::Empty;
3611 }
3612 /* default frontend: Qt GUI */
3613 if (strFrontend.isEmpty())
3614 strFrontend = "GUI/Qt";
3615
3616 if (strFrontend != "emergencystop")
3617 {
3618 /* check the session state */
3619 SessionState_T state;
3620 rc = aSession->COMGETTER(State)(&state);
3621 if (FAILED(rc))
3622 return rc;
3623
3624 if (state != SessionState_Unlocked)
3625 return setError(VBOX_E_INVALID_OBJECT_STATE,
3626 tr("The given session is busy"));
3627
3628 /* get the IInternalSessionControl interface */
3629 ComPtr<IInternalSessionControl> control(aSession);
3630 ComAssertMsgRet(!control.isNull(),
3631 ("No IInternalSessionControl interface"),
3632 E_INVALIDARG);
3633
3634 /* get the teleporter enable state for the progress object init. */
3635 BOOL fTeleporterEnabled;
3636 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3637 if (FAILED(rc))
3638 return rc;
3639
3640 /* create a progress object */
3641 ComObjPtr<ProgressProxy> progress;
3642 progress.createObject();
3643 rc = progress->init(mParent,
3644 static_cast<IMachine*>(this),
3645 Bstr(tr("Starting VM")).raw(),
3646 TRUE /* aCancelable */,
3647 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3648 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3649 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3650 2 /* uFirstOperationWeight */,
3651 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3652
3653 if (SUCCEEDED(rc))
3654 {
3655 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3656 if (SUCCEEDED(rc))
3657 {
3658 aProgress = progress;
3659
3660 /* signal the client watcher thread */
3661 mParent->i_updateClientWatcher();
3662
3663 /* fire an event */
3664 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3665 }
3666 }
3667 }
3668 else
3669 {
3670 /* no progress object - either instant success or failure */
3671 aProgress = NULL;
3672
3673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3674
3675 if (mData->mSession.mState != SessionState_Locked)
3676 return setError(VBOX_E_INVALID_OBJECT_STATE,
3677 tr("The machine '%s' is not locked by a session"),
3678 mUserData->s.strName.c_str());
3679
3680 /* must have a VM process associated - do not kill normal API clients
3681 * with an open session */
3682 if (!Global::IsOnline(mData->mMachineState))
3683 return setError(VBOX_E_INVALID_OBJECT_STATE,
3684 tr("The machine '%s' does not have a VM process"),
3685 mUserData->s.strName.c_str());
3686
3687 /* forcibly terminate the VM process */
3688 if (mData->mSession.mPID != NIL_RTPROCESS)
3689 RTProcTerminate(mData->mSession.mPID);
3690
3691 /* signal the client watcher thread, as most likely the client has
3692 * been terminated */
3693 mParent->i_updateClientWatcher();
3694 }
3695
3696 return rc;
3697}
3698
3699HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3700{
3701 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3702 return setError(E_INVALIDARG,
3703 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3704 aPosition, SchemaDefs::MaxBootPosition);
3705
3706 if (aDevice == DeviceType_USB)
3707 return setError(E_NOTIMPL,
3708 tr("Booting from USB device is currently not supported"));
3709
3710 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3711
3712 HRESULT rc = i_checkStateDependency(MutableStateDep);
3713 if (FAILED(rc)) return rc;
3714
3715 i_setModified(IsModified_MachineData);
3716 mHWData.backup();
3717 mHWData->mBootOrder[aPosition - 1] = aDevice;
3718
3719 return S_OK;
3720}
3721
3722HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3723{
3724 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3725 return setError(E_INVALIDARG,
3726 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3727 aPosition, SchemaDefs::MaxBootPosition);
3728
3729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3730
3731 *aDevice = mHWData->mBootOrder[aPosition - 1];
3732
3733 return S_OK;
3734}
3735
3736HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3737 LONG aControllerPort,
3738 LONG aDevice,
3739 DeviceType_T aType,
3740 const ComPtr<IMedium> &aMedium)
3741{
3742 IMedium *aM = aMedium;
3743 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3744 aName.c_str(), aControllerPort, aDevice, aType, aM));
3745
3746 // request the host lock first, since might be calling Host methods for getting host drives;
3747 // next, protect the media tree all the while we're in here, as well as our member variables
3748 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3749 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3750
3751 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3752 if (FAILED(rc)) return rc;
3753
3754 /// @todo NEWMEDIA implicit machine registration
3755 if (!mData->mRegistered)
3756 return setError(VBOX_E_INVALID_OBJECT_STATE,
3757 tr("Cannot attach storage devices to an unregistered machine"));
3758
3759 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3760
3761 /* Check for an existing controller. */
3762 ComObjPtr<StorageController> ctl;
3763 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3764 if (FAILED(rc)) return rc;
3765
3766 StorageControllerType_T ctrlType;
3767 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3768 if (FAILED(rc))
3769 return setError(E_FAIL,
3770 tr("Could not get type of controller '%s'"),
3771 aName.c_str());
3772
3773 bool fSilent = false;
3774 Utf8Str strReconfig;
3775
3776 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3777 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3778 if ( mData->mMachineState == MachineState_Paused
3779 && strReconfig == "1")
3780 fSilent = true;
3781
3782 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3783 bool fHotplug = false;
3784 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3785 fHotplug = true;
3786
3787 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3788 return setError(VBOX_E_INVALID_VM_STATE,
3789 tr("Controller '%s' does not support hotplugging"),
3790 aName.c_str());
3791
3792 // check that the port and device are not out of range
3793 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3794 if (FAILED(rc)) return rc;
3795
3796 /* check if the device slot is already busy */
3797 MediumAttachment *pAttachTemp;
3798 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3799 Bstr(aName).raw(),
3800 aControllerPort,
3801 aDevice)))
3802 {
3803 Medium *pMedium = pAttachTemp->i_getMedium();
3804 if (pMedium)
3805 {
3806 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3807 return setError(VBOX_E_OBJECT_IN_USE,
3808 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3809 pMedium->i_getLocationFull().c_str(),
3810 aControllerPort,
3811 aDevice,
3812 aName.c_str());
3813 }
3814 else
3815 return setError(VBOX_E_OBJECT_IN_USE,
3816 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3817 aControllerPort, aDevice, aName.c_str());
3818 }
3819
3820 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3821 if (aMedium && medium.isNull())
3822 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3823
3824 AutoCaller mediumCaller(medium);
3825 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3826
3827 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3828
3829 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3830 && !medium.isNull()
3831 )
3832 return setError(VBOX_E_OBJECT_IN_USE,
3833 tr("Medium '%s' is already attached to this virtual machine"),
3834 medium->i_getLocationFull().c_str());
3835
3836 if (!medium.isNull())
3837 {
3838 MediumType_T mtype = medium->i_getType();
3839 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3840 // For DVDs it's not written to the config file, so needs no global config
3841 // version bump. For floppies it's a new attribute "type", which is ignored
3842 // by older VirtualBox version, so needs no global config version bump either.
3843 // For hard disks this type is not accepted.
3844 if (mtype == MediumType_MultiAttach)
3845 {
3846 // This type is new with VirtualBox 4.0 and therefore requires settings
3847 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3848 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3849 // two reasons: The medium type is a property of the media registry tree, which
3850 // can reside in the global config file (for pre-4.0 media); we would therefore
3851 // possibly need to bump the global config version. We don't want to do that though
3852 // because that might make downgrading to pre-4.0 impossible.
3853 // As a result, we can only use these two new types if the medium is NOT in the
3854 // global registry:
3855 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3856 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3857 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3858 )
3859 return setError(VBOX_E_INVALID_OBJECT_STATE,
3860 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3861 "to machines that were created with VirtualBox 4.0 or later"),
3862 medium->i_getLocationFull().c_str());
3863 }
3864 }
3865
3866 bool fIndirect = false;
3867 if (!medium.isNull())
3868 fIndirect = medium->i_isReadOnly();
3869 bool associate = true;
3870
3871 do
3872 {
3873 if ( aType == DeviceType_HardDisk
3874 && mMediaData.isBackedUp())
3875 {
3876 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3877
3878 /* check if the medium was attached to the VM before we started
3879 * changing attachments in which case the attachment just needs to
3880 * be restored */
3881 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3882 {
3883 AssertReturn(!fIndirect, E_FAIL);
3884
3885 /* see if it's the same bus/channel/device */
3886 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3887 {
3888 /* the simplest case: restore the whole attachment
3889 * and return, nothing else to do */
3890 mMediaData->mAttachments.push_back(pAttachTemp);
3891
3892 /* Reattach the medium to the VM. */
3893 if (fHotplug || fSilent)
3894 {
3895 mediumLock.release();
3896 treeLock.release();
3897 alock.release();
3898
3899 MediumLockList *pMediumLockList(new MediumLockList());
3900
3901 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3902 true /* fMediumLockWrite */,
3903 false /* fMediumLockWriteAll */,
3904 NULL,
3905 *pMediumLockList);
3906 alock.acquire();
3907 if (FAILED(rc))
3908 delete pMediumLockList;
3909 else
3910 {
3911 mData->mSession.mLockedMedia.Unlock();
3912 alock.release();
3913 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3914 mData->mSession.mLockedMedia.Lock();
3915 alock.acquire();
3916 }
3917 alock.release();
3918
3919 if (SUCCEEDED(rc))
3920 {
3921 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3922 /* Remove lock list in case of error. */
3923 if (FAILED(rc))
3924 {
3925 mData->mSession.mLockedMedia.Unlock();
3926 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3927 mData->mSession.mLockedMedia.Lock();
3928 }
3929 }
3930 }
3931
3932 return S_OK;
3933 }
3934
3935 /* bus/channel/device differ; we need a new attachment object,
3936 * but don't try to associate it again */
3937 associate = false;
3938 break;
3939 }
3940 }
3941
3942 /* go further only if the attachment is to be indirect */
3943 if (!fIndirect)
3944 break;
3945
3946 /* perform the so called smart attachment logic for indirect
3947 * attachments. Note that smart attachment is only applicable to base
3948 * hard disks. */
3949
3950 if (medium->i_getParent().isNull())
3951 {
3952 /* first, investigate the backup copy of the current hard disk
3953 * attachments to make it possible to re-attach existing diffs to
3954 * another device slot w/o losing their contents */
3955 if (mMediaData.isBackedUp())
3956 {
3957 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3958
3959 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3960 uint32_t foundLevel = 0;
3961
3962 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3963 {
3964 uint32_t level = 0;
3965 MediumAttachment *pAttach = *it;
3966 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3967 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3968 if (pMedium.isNull())
3969 continue;
3970
3971 if (pMedium->i_getBase(&level) == medium)
3972 {
3973 /* skip the hard disk if its currently attached (we
3974 * cannot attach the same hard disk twice) */
3975 if (i_findAttachment(mMediaData->mAttachments,
3976 pMedium))
3977 continue;
3978
3979 /* matched device, channel and bus (i.e. attached to the
3980 * same place) will win and immediately stop the search;
3981 * otherwise the attachment that has the youngest
3982 * descendant of medium will be used
3983 */
3984 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3985 {
3986 /* the simplest case: restore the whole attachment
3987 * and return, nothing else to do */
3988 mMediaData->mAttachments.push_back(*it);
3989
3990 /* Reattach the medium to the VM. */
3991 if (fHotplug || fSilent)
3992 {
3993 mediumLock.release();
3994 treeLock.release();
3995 alock.release();
3996
3997 MediumLockList *pMediumLockList(new MediumLockList());
3998
3999 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4000 true /* fMediumLockWrite */,
4001 false /* fMediumLockWriteAll */,
4002 NULL,
4003 *pMediumLockList);
4004 alock.acquire();
4005 if (FAILED(rc))
4006 delete pMediumLockList;
4007 else
4008 {
4009 mData->mSession.mLockedMedia.Unlock();
4010 alock.release();
4011 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4012 mData->mSession.mLockedMedia.Lock();
4013 alock.acquire();
4014 }
4015 alock.release();
4016
4017 if (SUCCEEDED(rc))
4018 {
4019 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4020 /* Remove lock list in case of error. */
4021 if (FAILED(rc))
4022 {
4023 mData->mSession.mLockedMedia.Unlock();
4024 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4025 mData->mSession.mLockedMedia.Lock();
4026 }
4027 }
4028 }
4029
4030 return S_OK;
4031 }
4032 else if ( foundIt == oldAtts.end()
4033 || level > foundLevel /* prefer younger */
4034 )
4035 {
4036 foundIt = it;
4037 foundLevel = level;
4038 }
4039 }
4040 }
4041
4042 if (foundIt != oldAtts.end())
4043 {
4044 /* use the previously attached hard disk */
4045 medium = (*foundIt)->i_getMedium();
4046 mediumCaller.attach(medium);
4047 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4048 mediumLock.attach(medium);
4049 /* not implicit, doesn't require association with this VM */
4050 fIndirect = false;
4051 associate = false;
4052 /* go right to the MediumAttachment creation */
4053 break;
4054 }
4055 }
4056
4057 /* must give up the medium lock and medium tree lock as below we
4058 * go over snapshots, which needs a lock with higher lock order. */
4059 mediumLock.release();
4060 treeLock.release();
4061
4062 /* then, search through snapshots for the best diff in the given
4063 * hard disk's chain to base the new diff on */
4064
4065 ComObjPtr<Medium> base;
4066 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4067 while (snap)
4068 {
4069 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4070
4071 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4072
4073 MediumAttachment *pAttachFound = NULL;
4074 uint32_t foundLevel = 0;
4075
4076 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4077 {
4078 MediumAttachment *pAttach = *it;
4079 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4080 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4081 if (pMedium.isNull())
4082 continue;
4083
4084 uint32_t level = 0;
4085 if (pMedium->i_getBase(&level) == medium)
4086 {
4087 /* matched device, channel and bus (i.e. attached to the
4088 * same place) will win and immediately stop the search;
4089 * otherwise the attachment that has the youngest
4090 * descendant of medium will be used
4091 */
4092 if ( pAttach->i_getDevice() == aDevice
4093 && pAttach->i_getPort() == aControllerPort
4094 && pAttach->i_getControllerName() == aName
4095 )
4096 {
4097 pAttachFound = pAttach;
4098 break;
4099 }
4100 else if ( !pAttachFound
4101 || level > foundLevel /* prefer younger */
4102 )
4103 {
4104 pAttachFound = pAttach;
4105 foundLevel = level;
4106 }
4107 }
4108 }
4109
4110 if (pAttachFound)
4111 {
4112 base = pAttachFound->i_getMedium();
4113 break;
4114 }
4115
4116 snap = snap->i_getParent();
4117 }
4118
4119 /* re-lock medium tree and the medium, as we need it below */
4120 treeLock.acquire();
4121 mediumLock.acquire();
4122
4123 /* found a suitable diff, use it as a base */
4124 if (!base.isNull())
4125 {
4126 medium = base;
4127 mediumCaller.attach(medium);
4128 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4129 mediumLock.attach(medium);
4130 }
4131 }
4132
4133 Utf8Str strFullSnapshotFolder;
4134 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4135
4136 ComObjPtr<Medium> diff;
4137 diff.createObject();
4138 // store this diff in the same registry as the parent
4139 Guid uuidRegistryParent;
4140 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4141 {
4142 // parent image has no registry: this can happen if we're attaching a new immutable
4143 // image that has not yet been attached (medium then points to the base and we're
4144 // creating the diff image for the immutable, and the parent is not yet registered);
4145 // put the parent in the machine registry then
4146 mediumLock.release();
4147 treeLock.release();
4148 alock.release();
4149 i_addMediumToRegistry(medium);
4150 alock.acquire();
4151 treeLock.acquire();
4152 mediumLock.acquire();
4153 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4154 }
4155 rc = diff->init(mParent,
4156 medium->i_getPreferredDiffFormat(),
4157 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4158 uuidRegistryParent,
4159 DeviceType_HardDisk);
4160 if (FAILED(rc)) return rc;
4161
4162 /* Apply the normal locking logic to the entire chain. */
4163 MediumLockList *pMediumLockList(new MediumLockList());
4164 mediumLock.release();
4165 treeLock.release();
4166 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4167 true /* fMediumLockWrite */,
4168 false /* fMediumLockWriteAll */,
4169 medium,
4170 *pMediumLockList);
4171 treeLock.acquire();
4172 mediumLock.acquire();
4173 if (SUCCEEDED(rc))
4174 {
4175 mediumLock.release();
4176 treeLock.release();
4177 rc = pMediumLockList->Lock();
4178 treeLock.acquire();
4179 mediumLock.acquire();
4180 if (FAILED(rc))
4181 setError(rc,
4182 tr("Could not lock medium when creating diff '%s'"),
4183 diff->i_getLocationFull().c_str());
4184 else
4185 {
4186 /* will release the lock before the potentially lengthy
4187 * operation, so protect with the special state */
4188 MachineState_T oldState = mData->mMachineState;
4189 i_setMachineState(MachineState_SettingUp);
4190
4191 mediumLock.release();
4192 treeLock.release();
4193 alock.release();
4194
4195 rc = medium->i_createDiffStorage(diff,
4196 MediumVariant_Standard,
4197 pMediumLockList,
4198 NULL /* aProgress */,
4199 true /* aWait */);
4200
4201 alock.acquire();
4202 treeLock.acquire();
4203 mediumLock.acquire();
4204
4205 i_setMachineState(oldState);
4206 }
4207 }
4208
4209 /* Unlock the media and free the associated memory. */
4210 delete pMediumLockList;
4211
4212 if (FAILED(rc)) return rc;
4213
4214 /* use the created diff for the actual attachment */
4215 medium = diff;
4216 mediumCaller.attach(medium);
4217 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4218 mediumLock.attach(medium);
4219 }
4220 while (0);
4221
4222 ComObjPtr<MediumAttachment> attachment;
4223 attachment.createObject();
4224 rc = attachment->init(this,
4225 medium,
4226 aName,
4227 aControllerPort,
4228 aDevice,
4229 aType,
4230 fIndirect,
4231 false /* fPassthrough */,
4232 false /* fTempEject */,
4233 false /* fNonRotational */,
4234 false /* fDiscard */,
4235 fHotplug /* fHotPluggable */,
4236 Utf8Str::Empty);
4237 if (FAILED(rc)) return rc;
4238
4239 if (associate && !medium.isNull())
4240 {
4241 // as the last step, associate the medium to the VM
4242 rc = medium->i_addBackReference(mData->mUuid);
4243 // here we can fail because of Deleting, or being in process of creating a Diff
4244 if (FAILED(rc)) return rc;
4245
4246 mediumLock.release();
4247 treeLock.release();
4248 alock.release();
4249 i_addMediumToRegistry(medium);
4250 alock.acquire();
4251 treeLock.acquire();
4252 mediumLock.acquire();
4253 }
4254
4255 /* success: finally remember the attachment */
4256 i_setModified(IsModified_Storage);
4257 mMediaData.backup();
4258 mMediaData->mAttachments.push_back(attachment);
4259
4260 mediumLock.release();
4261 treeLock.release();
4262 alock.release();
4263
4264 if (fHotplug || fSilent)
4265 {
4266 if (!medium.isNull())
4267 {
4268 MediumLockList *pMediumLockList(new MediumLockList());
4269
4270 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4271 true /* fMediumLockWrite */,
4272 false /* fMediumLockWriteAll */,
4273 NULL,
4274 *pMediumLockList);
4275 alock.acquire();
4276 if (FAILED(rc))
4277 delete pMediumLockList;
4278 else
4279 {
4280 mData->mSession.mLockedMedia.Unlock();
4281 alock.release();
4282 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4283 mData->mSession.mLockedMedia.Lock();
4284 alock.acquire();
4285 }
4286 alock.release();
4287 }
4288
4289 if (SUCCEEDED(rc))
4290 {
4291 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4292 /* Remove lock list in case of error. */
4293 if (FAILED(rc))
4294 {
4295 mData->mSession.mLockedMedia.Unlock();
4296 mData->mSession.mLockedMedia.Remove(attachment);
4297 mData->mSession.mLockedMedia.Lock();
4298 }
4299 }
4300 }
4301
4302 /* Save modified registries, but skip this machine as it's the caller's
4303 * job to save its settings like all other settings changes. */
4304 mParent->i_unmarkRegistryModified(i_getId());
4305 mParent->i_saveModifiedRegistries();
4306
4307 return rc;
4308}
4309
4310HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4311 LONG aDevice)
4312{
4313 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4314 aName.c_str(), aControllerPort, aDevice));
4315
4316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4317
4318 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4319 if (FAILED(rc)) return rc;
4320
4321 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4322
4323 /* Check for an existing controller. */
4324 ComObjPtr<StorageController> ctl;
4325 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4326 if (FAILED(rc)) return rc;
4327
4328 StorageControllerType_T ctrlType;
4329 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4330 if (FAILED(rc))
4331 return setError(E_FAIL,
4332 tr("Could not get type of controller '%s'"),
4333 aName.c_str());
4334
4335 bool fSilent = false;
4336 Utf8Str strReconfig;
4337
4338 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4339 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4340 if ( mData->mMachineState == MachineState_Paused
4341 && strReconfig == "1")
4342 fSilent = true;
4343
4344 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4345 bool fHotplug = false;
4346 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4347 fHotplug = true;
4348
4349 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4350 return setError(VBOX_E_INVALID_VM_STATE,
4351 tr("Controller '%s' does not support hotplugging"),
4352 aName.c_str());
4353
4354 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4355 Bstr(aName).raw(),
4356 aControllerPort,
4357 aDevice);
4358 if (!pAttach)
4359 return setError(VBOX_E_OBJECT_NOT_FOUND,
4360 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4361 aDevice, aControllerPort, aName.c_str());
4362
4363 if (fHotplug && !pAttach->i_getHotPluggable())
4364 return setError(VBOX_E_NOT_SUPPORTED,
4365 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4366 aDevice, aControllerPort, aName.c_str());
4367
4368 /*
4369 * The VM has to detach the device before we delete any implicit diffs.
4370 * If this fails we can roll back without loosing data.
4371 */
4372 if (fHotplug || fSilent)
4373 {
4374 alock.release();
4375 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4376 alock.acquire();
4377 }
4378 if (FAILED(rc)) return rc;
4379
4380 /* If we are here everything went well and we can delete the implicit now. */
4381 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4382
4383 alock.release();
4384
4385 /* Save modified registries, but skip this machine as it's the caller's
4386 * job to save its settings like all other settings changes. */
4387 mParent->i_unmarkRegistryModified(i_getId());
4388 mParent->i_saveModifiedRegistries();
4389
4390 return rc;
4391}
4392
4393HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4394 LONG aDevice, BOOL aPassthrough)
4395{
4396 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4397 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4398
4399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4400
4401 HRESULT rc = i_checkStateDependency(MutableStateDep);
4402 if (FAILED(rc)) return rc;
4403
4404 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4405
4406 if (Global::IsOnlineOrTransient(mData->mMachineState))
4407 return setError(VBOX_E_INVALID_VM_STATE,
4408 tr("Invalid machine state: %s"),
4409 Global::stringifyMachineState(mData->mMachineState));
4410
4411 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4412 Bstr(aName).raw(),
4413 aControllerPort,
4414 aDevice);
4415 if (!pAttach)
4416 return setError(VBOX_E_OBJECT_NOT_FOUND,
4417 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4418 aDevice, aControllerPort, aName.c_str());
4419
4420
4421 i_setModified(IsModified_Storage);
4422 mMediaData.backup();
4423
4424 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4425
4426 if (pAttach->i_getType() != DeviceType_DVD)
4427 return setError(E_INVALIDARG,
4428 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4429 aDevice, aControllerPort, aName.c_str());
4430 pAttach->i_updatePassthrough(!!aPassthrough);
4431
4432 return S_OK;
4433}
4434
4435HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4436 LONG aDevice, BOOL aTemporaryEject)
4437{
4438
4439 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4440 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4441
4442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4443
4444 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4445 if (FAILED(rc)) return rc;
4446
4447 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4448 Bstr(aName).raw(),
4449 aControllerPort,
4450 aDevice);
4451 if (!pAttach)
4452 return setError(VBOX_E_OBJECT_NOT_FOUND,
4453 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4454 aDevice, aControllerPort, aName.c_str());
4455
4456
4457 i_setModified(IsModified_Storage);
4458 mMediaData.backup();
4459
4460 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4461
4462 if (pAttach->i_getType() != DeviceType_DVD)
4463 return setError(E_INVALIDARG,
4464 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4465 aDevice, aControllerPort, aName.c_str());
4466 pAttach->i_updateTempEject(!!aTemporaryEject);
4467
4468 return S_OK;
4469}
4470
4471HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4472 LONG aDevice, BOOL aNonRotational)
4473{
4474
4475 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4476 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4477
4478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4479
4480 HRESULT rc = i_checkStateDependency(MutableStateDep);
4481 if (FAILED(rc)) return rc;
4482
4483 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4484
4485 if (Global::IsOnlineOrTransient(mData->mMachineState))
4486 return setError(VBOX_E_INVALID_VM_STATE,
4487 tr("Invalid machine state: %s"),
4488 Global::stringifyMachineState(mData->mMachineState));
4489
4490 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4491 Bstr(aName).raw(),
4492 aControllerPort,
4493 aDevice);
4494 if (!pAttach)
4495 return setError(VBOX_E_OBJECT_NOT_FOUND,
4496 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4497 aDevice, aControllerPort, aName.c_str());
4498
4499
4500 i_setModified(IsModified_Storage);
4501 mMediaData.backup();
4502
4503 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4504
4505 if (pAttach->i_getType() != DeviceType_HardDisk)
4506 return setError(E_INVALIDARG,
4507 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4508 aDevice, aControllerPort, aName.c_str());
4509 pAttach->i_updateNonRotational(!!aNonRotational);
4510
4511 return S_OK;
4512}
4513
4514HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4515 LONG aDevice, BOOL aDiscard)
4516{
4517
4518 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4519 aName.c_str(), aControllerPort, aDevice, aDiscard));
4520
4521 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4522
4523 HRESULT rc = i_checkStateDependency(MutableStateDep);
4524 if (FAILED(rc)) return rc;
4525
4526 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4527
4528 if (Global::IsOnlineOrTransient(mData->mMachineState))
4529 return setError(VBOX_E_INVALID_VM_STATE,
4530 tr("Invalid machine state: %s"),
4531 Global::stringifyMachineState(mData->mMachineState));
4532
4533 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4534 Bstr(aName).raw(),
4535 aControllerPort,
4536 aDevice);
4537 if (!pAttach)
4538 return setError(VBOX_E_OBJECT_NOT_FOUND,
4539 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4540 aDevice, aControllerPort, aName.c_str());
4541
4542
4543 i_setModified(IsModified_Storage);
4544 mMediaData.backup();
4545
4546 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4547
4548 if (pAttach->i_getType() != DeviceType_HardDisk)
4549 return setError(E_INVALIDARG,
4550 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4551 aDevice, aControllerPort, aName.c_str());
4552 pAttach->i_updateDiscard(!!aDiscard);
4553
4554 return S_OK;
4555}
4556
4557HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4558 LONG aDevice, BOOL aHotPluggable)
4559{
4560 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4561 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4562
4563 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4564
4565 HRESULT rc = i_checkStateDependency(MutableStateDep);
4566 if (FAILED(rc)) return rc;
4567
4568 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4569
4570 if (Global::IsOnlineOrTransient(mData->mMachineState))
4571 return setError(VBOX_E_INVALID_VM_STATE,
4572 tr("Invalid machine state: %s"),
4573 Global::stringifyMachineState(mData->mMachineState));
4574
4575 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4576 Bstr(aName).raw(),
4577 aControllerPort,
4578 aDevice);
4579 if (!pAttach)
4580 return setError(VBOX_E_OBJECT_NOT_FOUND,
4581 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4582 aDevice, aControllerPort, aName.c_str());
4583
4584 /* Check for an existing controller. */
4585 ComObjPtr<StorageController> ctl;
4586 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4587 if (FAILED(rc)) return rc;
4588
4589 StorageControllerType_T ctrlType;
4590 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4591 if (FAILED(rc))
4592 return setError(E_FAIL,
4593 tr("Could not get type of controller '%s'"),
4594 aName.c_str());
4595
4596 if (!i_isControllerHotplugCapable(ctrlType))
4597 return setError(VBOX_E_NOT_SUPPORTED,
4598 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4599 aName.c_str());
4600
4601 i_setModified(IsModified_Storage);
4602 mMediaData.backup();
4603
4604 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4605
4606 if (pAttach->i_getType() == DeviceType_Floppy)
4607 return setError(E_INVALIDARG,
4608 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4609 aDevice, aControllerPort, aName.c_str());
4610 pAttach->i_updateHotPluggable(!!aHotPluggable);
4611
4612 return S_OK;
4613}
4614
4615HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4616 LONG aDevice)
4617{
4618 int rc = S_OK;
4619 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4620 aName.c_str(), aControllerPort, aDevice));
4621
4622 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4623
4624 return rc;
4625}
4626
4627HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4628 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4629{
4630 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4631 aName.c_str(), aControllerPort, aDevice));
4632
4633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4634
4635 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4636 if (FAILED(rc)) return rc;
4637
4638 if (Global::IsOnlineOrTransient(mData->mMachineState))
4639 return setError(VBOX_E_INVALID_VM_STATE,
4640 tr("Invalid machine state: %s"),
4641 Global::stringifyMachineState(mData->mMachineState));
4642
4643 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4644 Bstr(aName).raw(),
4645 aControllerPort,
4646 aDevice);
4647 if (!pAttach)
4648 return setError(VBOX_E_OBJECT_NOT_FOUND,
4649 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4650 aDevice, aControllerPort, aName.c_str());
4651
4652
4653 i_setModified(IsModified_Storage);
4654 mMediaData.backup();
4655
4656 IBandwidthGroup *iB = aBandwidthGroup;
4657 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4658 if (aBandwidthGroup && group.isNull())
4659 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4660
4661 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4662
4663 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4664 if (strBandwidthGroupOld.isNotEmpty())
4665 {
4666 /* Get the bandwidth group object and release it - this must not fail. */
4667 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4668 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4669 Assert(SUCCEEDED(rc));
4670
4671 pBandwidthGroupOld->i_release();
4672 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4673 }
4674
4675 if (!group.isNull())
4676 {
4677 group->i_reference();
4678 pAttach->i_updateBandwidthGroup(group->i_getName());
4679 }
4680
4681 return S_OK;
4682}
4683
4684HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4685 LONG aControllerPort,
4686 LONG aDevice,
4687 DeviceType_T aType)
4688{
4689 HRESULT rc = S_OK;
4690
4691 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4692 aName.c_str(), aControllerPort, aDevice, aType));
4693
4694 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4695
4696 return rc;
4697}
4698
4699
4700HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4701 LONG aControllerPort,
4702 LONG aDevice,
4703 BOOL aForce)
4704{
4705 int rc = S_OK;
4706 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4707 aName.c_str(), aControllerPort, aForce));
4708
4709 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4710
4711 return rc;
4712}
4713
4714HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4715 LONG aControllerPort,
4716 LONG aDevice,
4717 const ComPtr<IMedium> &aMedium,
4718 BOOL aForce)
4719{
4720 int rc = S_OK;
4721 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4722 aName.c_str(), aControllerPort, aDevice, aForce));
4723
4724 // request the host lock first, since might be calling Host methods for getting host drives;
4725 // next, protect the media tree all the while we're in here, as well as our member variables
4726 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4727 this->lockHandle(),
4728 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4729
4730 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4731 Bstr(aName).raw(),
4732 aControllerPort,
4733 aDevice);
4734 if (pAttach.isNull())
4735 return setError(VBOX_E_OBJECT_NOT_FOUND,
4736 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4737 aDevice, aControllerPort, aName.c_str());
4738
4739 /* Remember previously mounted medium. The medium before taking the
4740 * backup is not necessarily the same thing. */
4741 ComObjPtr<Medium> oldmedium;
4742 oldmedium = pAttach->i_getMedium();
4743
4744 IMedium *iM = aMedium;
4745 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4746 if (aMedium && pMedium.isNull())
4747 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4748
4749 AutoCaller mediumCaller(pMedium);
4750 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4751
4752 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4753 if (pMedium)
4754 {
4755 DeviceType_T mediumType = pAttach->i_getType();
4756 switch (mediumType)
4757 {
4758 case DeviceType_DVD:
4759 case DeviceType_Floppy:
4760 break;
4761
4762 default:
4763 return setError(VBOX_E_INVALID_OBJECT_STATE,
4764 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4765 aControllerPort,
4766 aDevice,
4767 aName.c_str());
4768 }
4769 }
4770
4771 i_setModified(IsModified_Storage);
4772 mMediaData.backup();
4773
4774 {
4775 // The backup operation makes the pAttach reference point to the
4776 // old settings. Re-get the correct reference.
4777 pAttach = i_findAttachment(mMediaData->mAttachments,
4778 Bstr(aName).raw(),
4779 aControllerPort,
4780 aDevice);
4781 if (!oldmedium.isNull())
4782 oldmedium->i_removeBackReference(mData->mUuid);
4783 if (!pMedium.isNull())
4784 {
4785 pMedium->i_addBackReference(mData->mUuid);
4786
4787 mediumLock.release();
4788 multiLock.release();
4789 i_addMediumToRegistry(pMedium);
4790 multiLock.acquire();
4791 mediumLock.acquire();
4792 }
4793
4794 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4795 pAttach->i_updateMedium(pMedium);
4796 }
4797
4798 i_setModified(IsModified_Storage);
4799
4800 mediumLock.release();
4801 multiLock.release();
4802 rc = i_onMediumChange(pAttach, aForce);
4803 multiLock.acquire();
4804 mediumLock.acquire();
4805
4806 /* On error roll back this change only. */
4807 if (FAILED(rc))
4808 {
4809 if (!pMedium.isNull())
4810 pMedium->i_removeBackReference(mData->mUuid);
4811 pAttach = i_findAttachment(mMediaData->mAttachments,
4812 Bstr(aName).raw(),
4813 aControllerPort,
4814 aDevice);
4815 /* If the attachment is gone in the meantime, bail out. */
4816 if (pAttach.isNull())
4817 return rc;
4818 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4819 if (!oldmedium.isNull())
4820 oldmedium->i_addBackReference(mData->mUuid);
4821 pAttach->i_updateMedium(oldmedium);
4822 }
4823
4824 mediumLock.release();
4825 multiLock.release();
4826
4827 /* Save modified registries, but skip this machine as it's the caller's
4828 * job to save its settings like all other settings changes. */
4829 mParent->i_unmarkRegistryModified(i_getId());
4830 mParent->i_saveModifiedRegistries();
4831
4832 return rc;
4833}
4834HRESULT Machine::getMedium(const com::Utf8Str &aName,
4835 LONG aControllerPort,
4836 LONG aDevice,
4837 ComPtr<IMedium> &aMedium)
4838{
4839 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4840 aName.c_str(), aControllerPort, aDevice));
4841
4842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4843
4844 aMedium = NULL;
4845
4846 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4847 Bstr(aName).raw(),
4848 aControllerPort,
4849 aDevice);
4850 if (pAttach.isNull())
4851 return setError(VBOX_E_OBJECT_NOT_FOUND,
4852 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4853 aDevice, aControllerPort, aName.c_str());
4854
4855 aMedium = pAttach->i_getMedium();
4856
4857 return S_OK;
4858}
4859
4860HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4861{
4862
4863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4864
4865 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4866
4867 return S_OK;
4868}
4869
4870HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4871{
4872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4873
4874 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4875
4876 return S_OK;
4877}
4878
4879HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4880{
4881 /* Do not assert if slot is out of range, just return the advertised
4882 status. testdriver/vbox.py triggers this in logVmInfo. */
4883 if (aSlot >= mNetworkAdapters.size())
4884 return setError(E_INVALIDARG,
4885 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4886 aSlot, mNetworkAdapters.size());
4887
4888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4889
4890 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4891
4892 return S_OK;
4893}
4894
4895HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4896{
4897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4898
4899 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4900 size_t i = 0;
4901 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4902 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4903 ++it, ++i)
4904 aKeys[i] = it->first;
4905
4906 return S_OK;
4907}
4908
4909 /**
4910 * @note Locks this object for reading.
4911 */
4912HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4913 com::Utf8Str &aValue)
4914{
4915 /* start with nothing found */
4916 aValue = "";
4917
4918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4919
4920 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4921 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4922 // found:
4923 aValue = it->second; // source is a Utf8Str
4924
4925 /* return the result to caller (may be empty) */
4926 return S_OK;
4927}
4928
4929 /**
4930 * @note Locks mParent for writing + this object for writing.
4931 */
4932HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4933{
4934 Utf8Str strOldValue; // empty
4935
4936 // locking note: we only hold the read lock briefly to look up the old value,
4937 // then release it and call the onExtraCanChange callbacks. There is a small
4938 // chance of a race insofar as the callback might be called twice if two callers
4939 // change the same key at the same time, but that's a much better solution
4940 // than the deadlock we had here before. The actual changing of the extradata
4941 // is then performed under the write lock and race-free.
4942
4943 // look up the old value first; if nothing has changed then we need not do anything
4944 {
4945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4946
4947 // For snapshots don't even think about allowing changes, extradata
4948 // is global for a machine, so there is nothing snapshot specific.
4949 if (i_isSnapshotMachine())
4950 return setError(VBOX_E_INVALID_VM_STATE,
4951 tr("Cannot set extradata for a snapshot"));
4952
4953 // check if the right IMachine instance is used
4954 if (mData->mRegistered && !i_isSessionMachine())
4955 return setError(VBOX_E_INVALID_VM_STATE,
4956 tr("Cannot set extradata for an immutable machine"));
4957
4958 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4959 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4960 strOldValue = it->second;
4961 }
4962
4963 bool fChanged;
4964 if ((fChanged = (strOldValue != aValue)))
4965 {
4966 // ask for permission from all listeners outside the locks;
4967 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4968 // lock to copy the list of callbacks to invoke
4969 Bstr error;
4970 Bstr bstrValue(aValue);
4971
4972 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4973 {
4974 const char *sep = error.isEmpty() ? "" : ": ";
4975 CBSTR err = error.raw();
4976 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4977 sep, err));
4978 return setError(E_ACCESSDENIED,
4979 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4980 aKey.c_str(),
4981 aValue.c_str(),
4982 sep,
4983 err);
4984 }
4985
4986 // data is changing and change not vetoed: then write it out under the lock
4987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4988
4989 if (aValue.isEmpty())
4990 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4991 else
4992 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4993 // creates a new key if needed
4994
4995 bool fNeedsGlobalSaveSettings = false;
4996 // This saving of settings is tricky: there is no "old state" for the
4997 // extradata items at all (unlike all other settings), so the old/new
4998 // settings comparison would give a wrong result!
4999 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5000
5001 if (fNeedsGlobalSaveSettings)
5002 {
5003 // save the global settings; for that we should hold only the VirtualBox lock
5004 alock.release();
5005 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5006 mParent->i_saveSettings();
5007 }
5008 }
5009
5010 // fire notification outside the lock
5011 if (fChanged)
5012 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5013
5014 return S_OK;
5015}
5016
5017HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5018{
5019 aProgress = NULL;
5020 NOREF(aSettingsFilePath);
5021 ReturnComNotImplemented();
5022}
5023
5024HRESULT Machine::saveSettings()
5025{
5026 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5027
5028 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5029 if (FAILED(rc)) return rc;
5030
5031 /* the settings file path may never be null */
5032 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5033
5034 /* save all VM data excluding snapshots */
5035 bool fNeedsGlobalSaveSettings = false;
5036 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5037 mlock.release();
5038
5039 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5040 {
5041 // save the global settings; for that we should hold only the VirtualBox lock
5042 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5043 rc = mParent->i_saveSettings();
5044 }
5045
5046 return rc;
5047}
5048
5049
5050HRESULT Machine::discardSettings()
5051{
5052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5053
5054 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5055 if (FAILED(rc)) return rc;
5056
5057 /*
5058 * during this rollback, the session will be notified if data has
5059 * been actually changed
5060 */
5061 i_rollback(true /* aNotify */);
5062
5063 return S_OK;
5064}
5065
5066/** @note Locks objects! */
5067HRESULT Machine::unregister(AutoCaller &autoCaller,
5068 CleanupMode_T aCleanupMode,
5069 std::vector<ComPtr<IMedium> > &aMedia)
5070{
5071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5072
5073 Guid id(i_getId());
5074
5075 if (mData->mSession.mState != SessionState_Unlocked)
5076 return setError(VBOX_E_INVALID_OBJECT_STATE,
5077 tr("Cannot unregister the machine '%s' while it is locked"),
5078 mUserData->s.strName.c_str());
5079
5080 // wait for state dependents to drop to zero
5081 i_ensureNoStateDependencies();
5082
5083 if (!mData->mAccessible)
5084 {
5085 // inaccessible maschines can only be unregistered; uninitialize ourselves
5086 // here because currently there may be no unregistered that are inaccessible
5087 // (this state combination is not supported). Note releasing the caller and
5088 // leaving the lock before calling uninit()
5089 alock.release();
5090 autoCaller.release();
5091
5092 uninit();
5093
5094 mParent->i_unregisterMachine(this, id);
5095 // calls VirtualBox::i_saveSettings()
5096
5097 return S_OK;
5098 }
5099
5100 HRESULT rc = S_OK;
5101
5102 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5103 // discard saved state
5104 if (mData->mMachineState == MachineState_Saved)
5105 {
5106 // add the saved state file to the list of files the caller should delete
5107 Assert(!mSSData->strStateFilePath.isEmpty());
5108 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5109
5110 mSSData->strStateFilePath.setNull();
5111
5112 // unconditionally set the machine state to powered off, we now
5113 // know no session has locked the machine
5114 mData->mMachineState = MachineState_PoweredOff;
5115 }
5116
5117 size_t cSnapshots = 0;
5118 if (mData->mFirstSnapshot)
5119 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5120 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5121 // fail now before we start detaching media
5122 return setError(VBOX_E_INVALID_OBJECT_STATE,
5123 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5124 mUserData->s.strName.c_str(), cSnapshots);
5125
5126 // This list collects the medium objects from all medium attachments
5127 // which we will detach from the machine and its snapshots, in a specific
5128 // order which allows for closing all media without getting "media in use"
5129 // errors, simply by going through the list from the front to the back:
5130 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5131 // and must be closed before the parent media from the snapshots, or closing the parents
5132 // will fail because they still have children);
5133 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5134 // the root ("first") snapshot of the machine.
5135 MediaList llMedia;
5136
5137 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5138 && mMediaData->mAttachments.size()
5139 )
5140 {
5141 // we have media attachments: detach them all and add the Medium objects to our list
5142 if (aCleanupMode != CleanupMode_UnregisterOnly)
5143 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5144 else
5145 return setError(VBOX_E_INVALID_OBJECT_STATE,
5146 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5147 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5148 }
5149
5150 if (cSnapshots)
5151 {
5152 // add the media from the medium attachments of the snapshots to llMedia
5153 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5154 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5155 // into the children first
5156
5157 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5158 MachineState_T oldState = mData->mMachineState;
5159 mData->mMachineState = MachineState_DeletingSnapshot;
5160
5161 // make a copy of the first snapshot so the refcount does not drop to 0
5162 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5163 // because of the AutoCaller voodoo)
5164 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5165
5166 // GO!
5167 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5168
5169 mData->mMachineState = oldState;
5170 }
5171
5172 if (FAILED(rc))
5173 {
5174 i_rollbackMedia();
5175 return rc;
5176 }
5177
5178 // commit all the media changes made above
5179 i_commitMedia();
5180
5181 mData->mRegistered = false;
5182
5183 // machine lock no longer needed
5184 alock.release();
5185
5186 // return media to caller
5187 size_t i = 0;
5188 aMedia.resize(llMedia.size());
5189 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5190 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5191
5192 mParent->i_unregisterMachine(this, id);
5193 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5194
5195 return S_OK;
5196}
5197
5198/**
5199 * Task record for deleting a machine config.
5200 */
5201struct Machine::DeleteConfigTask
5202 : public Machine::Task
5203{
5204 DeleteConfigTask(Machine *m,
5205 Progress *p,
5206 const Utf8Str &t,
5207 const RTCList<ComPtr<IMedium> > &llMediums,
5208 const StringsList &llFilesToDelete)
5209 : Task(m, p, t),
5210 m_llMediums(llMediums),
5211 m_llFilesToDelete(llFilesToDelete)
5212 {}
5213
5214 void handler()
5215 {
5216 m_pMachine->i_deleteConfigHandler(*this);
5217 }
5218
5219 RTCList<ComPtr<IMedium> > m_llMediums;
5220 StringsList m_llFilesToDelete;
5221};
5222
5223/**
5224 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5225 * SessionMachine::taskHandler().
5226 *
5227 * @note Locks this object for writing.
5228 *
5229 * @param task
5230 * @return
5231 */
5232void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5233{
5234 LogFlowThisFuncEnter();
5235
5236 AutoCaller autoCaller(this);
5237 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5238 if (FAILED(autoCaller.rc()))
5239 {
5240 /* we might have been uninitialized because the session was accidentally
5241 * closed by the client, so don't assert */
5242 HRESULT rc = setError(E_FAIL,
5243 tr("The session has been accidentally closed"));
5244 task.m_pProgress->i_notifyComplete(rc);
5245 LogFlowThisFuncLeave();
5246 return;
5247 }
5248
5249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5250
5251 HRESULT rc = S_OK;
5252
5253 try
5254 {
5255 ULONG uLogHistoryCount = 3;
5256 ComPtr<ISystemProperties> systemProperties;
5257 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5258 if (FAILED(rc)) throw rc;
5259
5260 if (!systemProperties.isNull())
5261 {
5262 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5263 if (FAILED(rc)) throw rc;
5264 }
5265
5266 MachineState_T oldState = mData->mMachineState;
5267 i_setMachineState(MachineState_SettingUp);
5268 alock.release();
5269 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5270 {
5271 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5272 {
5273 AutoCaller mac(pMedium);
5274 if (FAILED(mac.rc())) throw mac.rc();
5275 Utf8Str strLocation = pMedium->i_getLocationFull();
5276 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5277 if (FAILED(rc)) throw rc;
5278 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5279 }
5280 if (pMedium->i_isMediumFormatFile())
5281 {
5282 ComPtr<IProgress> pProgress2;
5283 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5284 if (FAILED(rc)) throw rc;
5285 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5286 if (FAILED(rc)) throw rc;
5287 /* Check the result of the asynchronous process. */
5288 LONG iRc;
5289 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5290 if (FAILED(rc)) throw rc;
5291 /* If the thread of the progress object has an error, then
5292 * retrieve the error info from there, or it'll be lost. */
5293 if (FAILED(iRc))
5294 throw setError(ProgressErrorInfo(pProgress2));
5295 }
5296
5297 /* Close the medium, deliberately without checking the return
5298 * code, and without leaving any trace in the error info, as
5299 * a failure here is a very minor issue, which shouldn't happen
5300 * as above we even managed to delete the medium. */
5301 {
5302 ErrorInfoKeeper eik;
5303 pMedium->Close();
5304 }
5305 }
5306 i_setMachineState(oldState);
5307 alock.acquire();
5308
5309 // delete the files pushed on the task list by Machine::Delete()
5310 // (this includes saved states of the machine and snapshots and
5311 // medium storage files from the IMedium list passed in, and the
5312 // machine XML file)
5313 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5314 while (it != task.m_llFilesToDelete.end())
5315 {
5316 const Utf8Str &strFile = *it;
5317 LogFunc(("Deleting file %s\n", strFile.c_str()));
5318 int vrc = RTFileDelete(strFile.c_str());
5319 if (RT_FAILURE(vrc))
5320 throw setError(VBOX_E_IPRT_ERROR,
5321 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5322
5323 ++it;
5324 if (it == task.m_llFilesToDelete.end())
5325 {
5326 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5327 if (FAILED(rc)) throw rc;
5328 break;
5329 }
5330
5331 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5332 if (FAILED(rc)) throw rc;
5333 }
5334
5335 /* delete the settings only when the file actually exists */
5336 if (mData->pMachineConfigFile->fileExists())
5337 {
5338 /* Delete any backup or uncommitted XML files. Ignore failures.
5339 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5340 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5341 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5342 RTFileDelete(otherXml.c_str());
5343 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5344 RTFileDelete(otherXml.c_str());
5345
5346 /* delete the Logs folder, nothing important should be left
5347 * there (we don't check for errors because the user might have
5348 * some private files there that we don't want to delete) */
5349 Utf8Str logFolder;
5350 getLogFolder(logFolder);
5351 Assert(logFolder.length());
5352 if (RTDirExists(logFolder.c_str()))
5353 {
5354 /* Delete all VBox.log[.N] files from the Logs folder
5355 * (this must be in sync with the rotation logic in
5356 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5357 * files that may have been created by the GUI. */
5358 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5359 logFolder.c_str(), RTPATH_DELIMITER);
5360 RTFileDelete(log.c_str());
5361 log = Utf8StrFmt("%s%cVBox.png",
5362 logFolder.c_str(), RTPATH_DELIMITER);
5363 RTFileDelete(log.c_str());
5364 for (int i = uLogHistoryCount; i > 0; i--)
5365 {
5366 log = Utf8StrFmt("%s%cVBox.log.%d",
5367 logFolder.c_str(), RTPATH_DELIMITER, i);
5368 RTFileDelete(log.c_str());
5369 log = Utf8StrFmt("%s%cVBox.png.%d",
5370 logFolder.c_str(), RTPATH_DELIMITER, i);
5371 RTFileDelete(log.c_str());
5372 }
5373#if defined(RT_OS_WINDOWS)
5374 log = Utf8StrFmt("%s%cVBoxStartup.log",
5375 logFolder.c_str(), RTPATH_DELIMITER);
5376 RTFileDelete(log.c_str());
5377#endif
5378
5379 RTDirRemove(logFolder.c_str());
5380 }
5381
5382 /* delete the Snapshots folder, nothing important should be left
5383 * there (we don't check for errors because the user might have
5384 * some private files there that we don't want to delete) */
5385 Utf8Str strFullSnapshotFolder;
5386 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5387 Assert(!strFullSnapshotFolder.isEmpty());
5388 if (RTDirExists(strFullSnapshotFolder.c_str()))
5389 RTDirRemove(strFullSnapshotFolder.c_str());
5390
5391 // delete the directory that contains the settings file, but only
5392 // if it matches the VM name
5393 Utf8Str settingsDir;
5394 if (i_isInOwnDir(&settingsDir))
5395 RTDirRemove(settingsDir.c_str());
5396 }
5397
5398 alock.release();
5399
5400 mParent->i_saveModifiedRegistries();
5401 }
5402 catch (HRESULT aRC) { rc = aRC; }
5403
5404 task.m_pProgress->i_notifyComplete(rc);
5405
5406 LogFlowThisFuncLeave();
5407}
5408
5409HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5410{
5411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5412
5413 HRESULT rc = i_checkStateDependency(MutableStateDep);
5414 if (FAILED(rc)) return rc;
5415
5416 if (mData->mRegistered)
5417 return setError(VBOX_E_INVALID_VM_STATE,
5418 tr("Cannot delete settings of a registered machine"));
5419
5420 // collect files to delete
5421 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5422 if (mData->pMachineConfigFile->fileExists())
5423 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5424
5425 RTCList<ComPtr<IMedium> > llMediums;
5426 for (size_t i = 0; i < aMedia.size(); ++i)
5427 {
5428 IMedium *pIMedium(aMedia[i]);
5429 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5430 if (pMedium.isNull())
5431 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5432 SafeArray<BSTR> ids;
5433 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5434 if (FAILED(rc)) return rc;
5435 /* At this point the medium should not have any back references
5436 * anymore. If it has it is attached to another VM and *must* not
5437 * deleted. */
5438 if (ids.size() < 1)
5439 llMediums.append(pMedium);
5440 }
5441
5442 ComObjPtr<Progress> pProgress;
5443 pProgress.createObject();
5444 rc = pProgress->init(i_getVirtualBox(),
5445 static_cast<IMachine*>(this) /* aInitiator */,
5446 Bstr(tr("Deleting files")).raw(),
5447 true /* fCancellable */,
5448 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5449 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5450 if (FAILED(rc))
5451 return rc;
5452
5453 /* create and start the task on a separate thread (note that it will not
5454 * start working until we release alock) */
5455 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5456 rc = pTask->createThread();
5457 if (FAILED(rc))
5458 return rc;
5459
5460 pProgress.queryInterfaceTo(aProgress.asOutParam());
5461
5462 LogFlowFuncLeave();
5463
5464 return S_OK;
5465}
5466
5467HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5468{
5469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5470
5471 ComObjPtr<Snapshot> pSnapshot;
5472 HRESULT rc;
5473
5474 if (aNameOrId.isEmpty())
5475 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5476 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5477 else
5478 {
5479 Guid uuid(aNameOrId);
5480 if (uuid.isValid())
5481 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5482 else
5483 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5484 }
5485 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5486
5487 return rc;
5488}
5489
5490HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5491{
5492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5493
5494 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5495 if (FAILED(rc)) return rc;
5496
5497 ComObjPtr<SharedFolder> sharedFolder;
5498 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5499 if (SUCCEEDED(rc))
5500 return setError(VBOX_E_OBJECT_IN_USE,
5501 tr("Shared folder named '%s' already exists"),
5502 aName.c_str());
5503
5504 sharedFolder.createObject();
5505 rc = sharedFolder->init(i_getMachine(),
5506 aName,
5507 aHostPath,
5508 !!aWritable,
5509 !!aAutomount,
5510 true /* fFailOnError */);
5511 if (FAILED(rc)) return rc;
5512
5513 i_setModified(IsModified_SharedFolders);
5514 mHWData.backup();
5515 mHWData->mSharedFolders.push_back(sharedFolder);
5516
5517 /* inform the direct session if any */
5518 alock.release();
5519 i_onSharedFolderChange();
5520
5521 return S_OK;
5522}
5523
5524HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5525{
5526 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5527
5528 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5529 if (FAILED(rc)) return rc;
5530
5531 ComObjPtr<SharedFolder> sharedFolder;
5532 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5533 if (FAILED(rc)) return rc;
5534
5535 i_setModified(IsModified_SharedFolders);
5536 mHWData.backup();
5537 mHWData->mSharedFolders.remove(sharedFolder);
5538
5539 /* inform the direct session if any */
5540 alock.release();
5541 i_onSharedFolderChange();
5542
5543 return S_OK;
5544}
5545
5546HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5547{
5548 /* start with No */
5549 *aCanShow = FALSE;
5550
5551 ComPtr<IInternalSessionControl> directControl;
5552 {
5553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5554
5555 if (mData->mSession.mState != SessionState_Locked)
5556 return setError(VBOX_E_INVALID_VM_STATE,
5557 tr("Machine is not locked for session (session state: %s)"),
5558 Global::stringifySessionState(mData->mSession.mState));
5559
5560 if (mData->mSession.mLockType == LockType_VM)
5561 directControl = mData->mSession.mDirectControl;
5562 }
5563
5564 /* ignore calls made after #OnSessionEnd() is called */
5565 if (!directControl)
5566 return S_OK;
5567
5568 LONG64 dummy;
5569 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5570}
5571
5572HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5573{
5574 ComPtr<IInternalSessionControl> directControl;
5575 {
5576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5577
5578 if (mData->mSession.mState != SessionState_Locked)
5579 return setError(E_FAIL,
5580 tr("Machine is not locked for session (session state: %s)"),
5581 Global::stringifySessionState(mData->mSession.mState));
5582
5583 if (mData->mSession.mLockType == LockType_VM)
5584 directControl = mData->mSession.mDirectControl;
5585 }
5586
5587 /* ignore calls made after #OnSessionEnd() is called */
5588 if (!directControl)
5589 return S_OK;
5590
5591 BOOL dummy;
5592 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5593}
5594
5595#ifdef VBOX_WITH_GUEST_PROPS
5596/**
5597 * Look up a guest property in VBoxSVC's internal structures.
5598 */
5599HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5600 com::Utf8Str &aValue,
5601 LONG64 *aTimestamp,
5602 com::Utf8Str &aFlags) const
5603{
5604 using namespace guestProp;
5605
5606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5607 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5608
5609 if (it != mHWData->mGuestProperties.end())
5610 {
5611 char szFlags[MAX_FLAGS_LEN + 1];
5612 aValue = it->second.strValue;
5613 *aTimestamp = it->second.mTimestamp;
5614 writeFlags(it->second.mFlags, szFlags);
5615 aFlags = Utf8Str(szFlags);
5616 }
5617
5618 return S_OK;
5619}
5620
5621/**
5622 * Query the VM that a guest property belongs to for the property.
5623 * @returns E_ACCESSDENIED if the VM process is not available or not
5624 * currently handling queries and the lookup should then be done in
5625 * VBoxSVC.
5626 */
5627HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5628 com::Utf8Str &aValue,
5629 LONG64 *aTimestamp,
5630 com::Utf8Str &aFlags) const
5631{
5632 HRESULT rc = S_OK;
5633 BSTR bValue = NULL;
5634 BSTR bFlags = NULL;
5635
5636 ComPtr<IInternalSessionControl> directControl;
5637 {
5638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5639 if (mData->mSession.mLockType == LockType_VM)
5640 directControl = mData->mSession.mDirectControl;
5641 }
5642
5643 /* ignore calls made after #OnSessionEnd() is called */
5644 if (!directControl)
5645 rc = E_ACCESSDENIED;
5646 else
5647 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5648 0 /* accessMode */,
5649 &bValue, aTimestamp, &bFlags);
5650
5651 aValue = bValue;
5652 aFlags = bFlags;
5653
5654 return rc;
5655}
5656#endif // VBOX_WITH_GUEST_PROPS
5657
5658HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5659 com::Utf8Str &aValue,
5660 LONG64 *aTimestamp,
5661 com::Utf8Str &aFlags)
5662{
5663#ifndef VBOX_WITH_GUEST_PROPS
5664 ReturnComNotImplemented();
5665#else // VBOX_WITH_GUEST_PROPS
5666
5667 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5668
5669 if (rc == E_ACCESSDENIED)
5670 /* The VM is not running or the service is not (yet) accessible */
5671 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5672 return rc;
5673#endif // VBOX_WITH_GUEST_PROPS
5674}
5675
5676HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5677{
5678 LONG64 dummyTimestamp;
5679 com::Utf8Str dummyFlags;
5680 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5681 return rc;
5682
5683}
5684HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5685{
5686 com::Utf8Str dummyFlags;
5687 com::Utf8Str dummyValue;
5688 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5689 return rc;
5690}
5691
5692#ifdef VBOX_WITH_GUEST_PROPS
5693/**
5694 * Set a guest property in VBoxSVC's internal structures.
5695 */
5696HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5697 const com::Utf8Str &aFlags, bool fDelete)
5698{
5699 using namespace guestProp;
5700
5701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5702 HRESULT rc = S_OK;
5703
5704 rc = i_checkStateDependency(MutableOrSavedStateDep);
5705 if (FAILED(rc)) return rc;
5706
5707 try
5708 {
5709 uint32_t fFlags = NILFLAG;
5710 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5711 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5712
5713 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5714 if (it == mHWData->mGuestProperties.end())
5715 {
5716 if (!fDelete)
5717 {
5718 i_setModified(IsModified_MachineData);
5719 mHWData.backupEx();
5720
5721 RTTIMESPEC time;
5722 HWData::GuestProperty prop;
5723 prop.strValue = Bstr(aValue).raw();
5724 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5725 prop.mFlags = fFlags;
5726 mHWData->mGuestProperties[aName] = prop;
5727 }
5728 }
5729 else
5730 {
5731 if (it->second.mFlags & (RDONLYHOST))
5732 {
5733 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5734 }
5735 else
5736 {
5737 i_setModified(IsModified_MachineData);
5738 mHWData.backupEx();
5739
5740 /* The backupEx() operation invalidates our iterator,
5741 * so get a new one. */
5742 it = mHWData->mGuestProperties.find(aName);
5743 Assert(it != mHWData->mGuestProperties.end());
5744
5745 if (!fDelete)
5746 {
5747 RTTIMESPEC time;
5748 it->second.strValue = aValue;
5749 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5750 it->second.mFlags = fFlags;
5751 }
5752 else
5753 mHWData->mGuestProperties.erase(it);
5754 }
5755 }
5756
5757 if ( SUCCEEDED(rc)
5758 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5759 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5760 RTSTR_MAX,
5761 aName.c_str(),
5762 RTSTR_MAX,
5763 NULL)
5764 )
5765 )
5766 {
5767 alock.release();
5768
5769 mParent->i_onGuestPropertyChange(mData->mUuid,
5770 Bstr(aName).raw(),
5771 Bstr(aValue).raw(),
5772 Bstr(aFlags).raw());
5773 }
5774 }
5775 catch (std::bad_alloc &)
5776 {
5777 rc = E_OUTOFMEMORY;
5778 }
5779
5780 return rc;
5781}
5782
5783/**
5784 * Set a property on the VM that that property belongs to.
5785 * @returns E_ACCESSDENIED if the VM process is not available or not
5786 * currently handling queries and the setting should then be done in
5787 * VBoxSVC.
5788 */
5789HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5790 const com::Utf8Str &aFlags, bool fDelete)
5791{
5792 HRESULT rc;
5793
5794 try
5795 {
5796 ComPtr<IInternalSessionControl> directControl;
5797 {
5798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5799 if (mData->mSession.mLockType == LockType_VM)
5800 directControl = mData->mSession.mDirectControl;
5801 }
5802
5803 BSTR dummy = NULL; /* will not be changed (setter) */
5804 LONG64 dummy64;
5805 if (!directControl)
5806 rc = E_ACCESSDENIED;
5807 else
5808 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5809 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5810 fDelete? 2: 1 /* accessMode */,
5811 &dummy, &dummy64, &dummy);
5812 }
5813 catch (std::bad_alloc &)
5814 {
5815 rc = E_OUTOFMEMORY;
5816 }
5817
5818 return rc;
5819}
5820#endif // VBOX_WITH_GUEST_PROPS
5821
5822HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5823 const com::Utf8Str &aFlags)
5824{
5825#ifndef VBOX_WITH_GUEST_PROPS
5826 ReturnComNotImplemented();
5827#else // VBOX_WITH_GUEST_PROPS
5828 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5829 if (rc == E_ACCESSDENIED)
5830 /* The VM is not running or the service is not (yet) accessible */
5831 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5832 return rc;
5833#endif // VBOX_WITH_GUEST_PROPS
5834}
5835
5836HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5837{
5838 return setGuestProperty(aProperty, aValue, "");
5839}
5840
5841HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5842{
5843#ifndef VBOX_WITH_GUEST_PROPS
5844 ReturnComNotImplemented();
5845#else // VBOX_WITH_GUEST_PROPS
5846 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5847 if (rc == E_ACCESSDENIED)
5848 /* The VM is not running or the service is not (yet) accessible */
5849 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5850 return rc;
5851#endif // VBOX_WITH_GUEST_PROPS
5852}
5853
5854#ifdef VBOX_WITH_GUEST_PROPS
5855/**
5856 * Enumerate the guest properties in VBoxSVC's internal structures.
5857 */
5858HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5859 std::vector<com::Utf8Str> &aNames,
5860 std::vector<com::Utf8Str> &aValues,
5861 std::vector<LONG64> &aTimestamps,
5862 std::vector<com::Utf8Str> &aFlags)
5863{
5864 using namespace guestProp;
5865
5866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5867 Utf8Str strPatterns(aPatterns);
5868
5869 HWData::GuestPropertyMap propMap;
5870
5871 /*
5872 * Look for matching patterns and build up a list.
5873 */
5874 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5875 while (it != mHWData->mGuestProperties.end())
5876 {
5877 if ( strPatterns.isEmpty()
5878 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5879 RTSTR_MAX,
5880 it->first.c_str(),
5881 RTSTR_MAX,
5882 NULL)
5883 )
5884 propMap.insert(*it);
5885 ++it;
5886 }
5887
5888 alock.release();
5889
5890 /*
5891 * And build up the arrays for returning the property information.
5892 */
5893 size_t cEntries = propMap.size();
5894
5895 aNames.resize(cEntries);
5896 aValues.resize(cEntries);
5897 aTimestamps.resize(cEntries);
5898 aFlags.resize(cEntries);
5899
5900 char szFlags[MAX_FLAGS_LEN + 1];
5901 size_t i= 0;
5902 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5903 {
5904 aNames[i] = it->first;
5905 aValues[i] = it->second.strValue;
5906 aTimestamps[i] = it->second.mTimestamp;
5907 writeFlags(it->second.mFlags, szFlags);
5908 aFlags[i] = Utf8Str(szFlags);
5909 }
5910
5911 return S_OK;
5912}
5913
5914/**
5915 * Enumerate the properties managed by a VM.
5916 * @returns E_ACCESSDENIED if the VM process is not available or not
5917 * currently handling queries and the setting should then be done in
5918 * VBoxSVC.
5919 */
5920HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5921 std::vector<com::Utf8Str> &aNames,
5922 std::vector<com::Utf8Str> &aValues,
5923 std::vector<LONG64> &aTimestamps,
5924 std::vector<com::Utf8Str> &aFlags)
5925{
5926 HRESULT rc;
5927 ComPtr<IInternalSessionControl> directControl;
5928 {
5929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5930 if (mData->mSession.mLockType == LockType_VM)
5931 directControl = mData->mSession.mDirectControl;
5932 }
5933
5934 com::SafeArray<BSTR> bNames;
5935 com::SafeArray<BSTR> bValues;
5936 com::SafeArray<LONG64> bTimestamps;
5937 com::SafeArray<BSTR> bFlags;
5938
5939 if (!directControl)
5940 rc = E_ACCESSDENIED;
5941 else
5942 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5943 ComSafeArrayAsOutParam(bNames),
5944 ComSafeArrayAsOutParam(bValues),
5945 ComSafeArrayAsOutParam(bTimestamps),
5946 ComSafeArrayAsOutParam(bFlags));
5947 size_t i;
5948 aNames.resize(bNames.size());
5949 for (i = 0; i < bNames.size(); ++i)
5950 aNames[i] = Utf8Str(bNames[i]);
5951 aValues.resize(bValues.size());
5952 for (i = 0; i < bValues.size(); ++i)
5953 aValues[i] = Utf8Str(bValues[i]);
5954 aTimestamps.resize(bTimestamps.size());
5955 for (i = 0; i < bTimestamps.size(); ++i)
5956 aTimestamps[i] = bTimestamps[i];
5957 aFlags.resize(bFlags.size());
5958 for (i = 0; i < bFlags.size(); ++i)
5959 aFlags[i] = Utf8Str(bFlags[i]);
5960
5961 return rc;
5962}
5963#endif // VBOX_WITH_GUEST_PROPS
5964HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5965 std::vector<com::Utf8Str> &aNames,
5966 std::vector<com::Utf8Str> &aValues,
5967 std::vector<LONG64> &aTimestamps,
5968 std::vector<com::Utf8Str> &aFlags)
5969{
5970#ifndef VBOX_WITH_GUEST_PROPS
5971 ReturnComNotImplemented();
5972#else // VBOX_WITH_GUEST_PROPS
5973
5974 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5975
5976 if (rc == E_ACCESSDENIED)
5977 /* The VM is not running or the service is not (yet) accessible */
5978 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5979 return rc;
5980#endif // VBOX_WITH_GUEST_PROPS
5981}
5982
5983HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5984 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5985{
5986 MediaData::AttachmentList atts;
5987
5988 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5989 if (FAILED(rc)) return rc;
5990
5991 size_t i = 0;
5992 aMediumAttachments.resize(atts.size());
5993 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5994 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5995
5996 return S_OK;
5997}
5998
5999HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6000 LONG aControllerPort,
6001 LONG aDevice,
6002 ComPtr<IMediumAttachment> &aAttachment)
6003{
6004 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6005 aName.c_str(), aControllerPort, aDevice));
6006
6007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6008
6009 aAttachment = NULL;
6010
6011 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
6012 Bstr(aName).raw(),
6013 aControllerPort,
6014 aDevice);
6015 if (pAttach.isNull())
6016 return setError(VBOX_E_OBJECT_NOT_FOUND,
6017 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6018 aDevice, aControllerPort, aName.c_str());
6019
6020 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6021
6022 return S_OK;
6023}
6024
6025
6026HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6027 StorageBus_T aConnectionType,
6028 ComPtr<IStorageController> &aController)
6029{
6030 if ( (aConnectionType <= StorageBus_Null)
6031 || (aConnectionType > StorageBus_USB))
6032 return setError(E_INVALIDARG,
6033 tr("Invalid connection type: %d"),
6034 aConnectionType);
6035
6036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6037
6038 HRESULT rc = i_checkStateDependency(MutableStateDep);
6039 if (FAILED(rc)) return rc;
6040
6041 /* try to find one with the name first. */
6042 ComObjPtr<StorageController> ctrl;
6043
6044 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6045 if (SUCCEEDED(rc))
6046 return setError(VBOX_E_OBJECT_IN_USE,
6047 tr("Storage controller named '%s' already exists"),
6048 aName.c_str());
6049
6050 ctrl.createObject();
6051
6052 /* get a new instance number for the storage controller */
6053 ULONG ulInstance = 0;
6054 bool fBootable = true;
6055 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6056 it != mStorageControllers->end();
6057 ++it)
6058 {
6059 if ((*it)->i_getStorageBus() == aConnectionType)
6060 {
6061 ULONG ulCurInst = (*it)->i_getInstance();
6062
6063 if (ulCurInst >= ulInstance)
6064 ulInstance = ulCurInst + 1;
6065
6066 /* Only one controller of each type can be marked as bootable. */
6067 if ((*it)->i_getBootable())
6068 fBootable = false;
6069 }
6070 }
6071
6072 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6073 if (FAILED(rc)) return rc;
6074
6075 i_setModified(IsModified_Storage);
6076 mStorageControllers.backup();
6077 mStorageControllers->push_back(ctrl);
6078
6079 ctrl.queryInterfaceTo(aController.asOutParam());
6080
6081 /* inform the direct session if any */
6082 alock.release();
6083 i_onStorageControllerChange();
6084
6085 return S_OK;
6086}
6087
6088HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6089 ComPtr<IStorageController> &aStorageController)
6090{
6091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6092
6093 ComObjPtr<StorageController> ctrl;
6094
6095 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6096 if (SUCCEEDED(rc))
6097 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6098
6099 return rc;
6100}
6101
6102HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6103 ComPtr<IStorageController> &aStorageController)
6104{
6105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6106
6107 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6108 it != mStorageControllers->end();
6109 ++it)
6110 {
6111 if ((*it)->i_getInstance() == aInstance)
6112 {
6113 (*it).queryInterfaceTo(aStorageController.asOutParam());
6114 return S_OK;
6115 }
6116 }
6117
6118 return setError(VBOX_E_OBJECT_NOT_FOUND,
6119 tr("Could not find a storage controller with instance number '%lu'"),
6120 aInstance);
6121}
6122
6123HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6124{
6125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6126
6127 HRESULT rc = i_checkStateDependency(MutableStateDep);
6128 if (FAILED(rc)) return rc;
6129
6130 ComObjPtr<StorageController> ctrl;
6131
6132 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6133 if (SUCCEEDED(rc))
6134 {
6135 /* Ensure that only one controller of each type is marked as bootable. */
6136 if (aBootable == TRUE)
6137 {
6138 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6139 it != mStorageControllers->end();
6140 ++it)
6141 {
6142 ComObjPtr<StorageController> aCtrl = (*it);
6143
6144 if ( (aCtrl->i_getName() != aName)
6145 && aCtrl->i_getBootable() == TRUE
6146 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6147 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6148 {
6149 aCtrl->i_setBootable(FALSE);
6150 break;
6151 }
6152 }
6153 }
6154
6155 if (SUCCEEDED(rc))
6156 {
6157 ctrl->i_setBootable(aBootable);
6158 i_setModified(IsModified_Storage);
6159 }
6160 }
6161
6162 if (SUCCEEDED(rc))
6163 {
6164 /* inform the direct session if any */
6165 alock.release();
6166 i_onStorageControllerChange();
6167 }
6168
6169 return rc;
6170}
6171
6172HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6173{
6174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6175
6176 HRESULT rc = i_checkStateDependency(MutableStateDep);
6177 if (FAILED(rc)) return rc;
6178
6179 ComObjPtr<StorageController> ctrl;
6180 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6181 if (FAILED(rc)) return rc;
6182
6183 {
6184 /* find all attached devices to the appropriate storage controller and detach them all */
6185 // make a temporary list because detachDevice invalidates iterators into
6186 // mMediaData->mAttachments
6187 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6188
6189 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6190 it != llAttachments2.end();
6191 ++it)
6192 {
6193 MediumAttachment *pAttachTemp = *it;
6194
6195 AutoCaller localAutoCaller(pAttachTemp);
6196 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6197
6198 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6199
6200 if (pAttachTemp->i_getControllerName() == aName)
6201 {
6202 rc = i_detachDevice(pAttachTemp, alock, NULL);
6203 if (FAILED(rc)) return rc;
6204 }
6205 }
6206 }
6207
6208 /* We can remove it now. */
6209 i_setModified(IsModified_Storage);
6210 mStorageControllers.backup();
6211
6212 ctrl->i_unshare();
6213
6214 mStorageControllers->remove(ctrl);
6215
6216 /* inform the direct session if any */
6217 alock.release();
6218 i_onStorageControllerChange();
6219
6220 return S_OK;
6221}
6222
6223HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6224 ComPtr<IUSBController> &aController)
6225{
6226 if ( (aType <= USBControllerType_Null)
6227 || (aType >= USBControllerType_Last))
6228 return setError(E_INVALIDARG,
6229 tr("Invalid USB controller type: %d"),
6230 aType);
6231
6232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6233
6234 HRESULT rc = i_checkStateDependency(MutableStateDep);
6235 if (FAILED(rc)) return rc;
6236
6237 /* try to find one with the same type first. */
6238 ComObjPtr<USBController> ctrl;
6239
6240 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6241 if (SUCCEEDED(rc))
6242 return setError(VBOX_E_OBJECT_IN_USE,
6243 tr("USB controller named '%s' already exists"),
6244 aName.c_str());
6245
6246 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6247 ULONG maxInstances;
6248 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6249 if (FAILED(rc))
6250 return rc;
6251
6252 ULONG cInstances = i_getUSBControllerCountByType(aType);
6253 if (cInstances >= maxInstances)
6254 return setError(E_INVALIDARG,
6255 tr("Too many USB controllers of this type"));
6256
6257 ctrl.createObject();
6258
6259 rc = ctrl->init(this, aName, aType);
6260 if (FAILED(rc)) return rc;
6261
6262 i_setModified(IsModified_USB);
6263 mUSBControllers.backup();
6264 mUSBControllers->push_back(ctrl);
6265
6266 ctrl.queryInterfaceTo(aController.asOutParam());
6267
6268 /* inform the direct session if any */
6269 alock.release();
6270 i_onUSBControllerChange();
6271
6272 return S_OK;
6273}
6274
6275HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6276{
6277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6278
6279 ComObjPtr<USBController> ctrl;
6280
6281 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6282 if (SUCCEEDED(rc))
6283 ctrl.queryInterfaceTo(aController.asOutParam());
6284
6285 return rc;
6286}
6287
6288HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6289 ULONG *aControllers)
6290{
6291 if ( (aType <= USBControllerType_Null)
6292 || (aType >= USBControllerType_Last))
6293 return setError(E_INVALIDARG,
6294 tr("Invalid USB controller type: %d"),
6295 aType);
6296
6297 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6298
6299 ComObjPtr<USBController> ctrl;
6300
6301 *aControllers = i_getUSBControllerCountByType(aType);
6302
6303 return S_OK;
6304}
6305
6306HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6307{
6308
6309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6310
6311 HRESULT rc = i_checkStateDependency(MutableStateDep);
6312 if (FAILED(rc)) return rc;
6313
6314 ComObjPtr<USBController> ctrl;
6315 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6316 if (FAILED(rc)) return rc;
6317
6318 i_setModified(IsModified_USB);
6319 mUSBControllers.backup();
6320
6321 ctrl->i_unshare();
6322
6323 mUSBControllers->remove(ctrl);
6324
6325 /* inform the direct session if any */
6326 alock.release();
6327 i_onUSBControllerChange();
6328
6329 return S_OK;
6330}
6331
6332HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6333 ULONG *aOriginX,
6334 ULONG *aOriginY,
6335 ULONG *aWidth,
6336 ULONG *aHeight,
6337 BOOL *aEnabled)
6338{
6339 uint32_t u32OriginX= 0;
6340 uint32_t u32OriginY= 0;
6341 uint32_t u32Width = 0;
6342 uint32_t u32Height = 0;
6343 uint16_t u16Flags = 0;
6344
6345 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6346 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6347 if (RT_FAILURE(vrc))
6348 {
6349#ifdef RT_OS_WINDOWS
6350 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6351 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6352 * So just assign fEnable to TRUE again.
6353 * The right fix would be to change GUI API wrappers to make sure that parameters
6354 * are changed only if API succeeds.
6355 */
6356 *aEnabled = TRUE;
6357#endif
6358 return setError(VBOX_E_IPRT_ERROR,
6359 tr("Saved guest size is not available (%Rrc)"),
6360 vrc);
6361 }
6362
6363 *aOriginX = u32OriginX;
6364 *aOriginY = u32OriginY;
6365 *aWidth = u32Width;
6366 *aHeight = u32Height;
6367 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6368
6369 return S_OK;
6370}
6371
6372HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6373{
6374 if (aScreenId != 0)
6375 return E_NOTIMPL;
6376
6377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6378
6379 uint8_t *pu8Data = NULL;
6380 uint32_t cbData = 0;
6381 uint32_t u32Width = 0;
6382 uint32_t u32Height = 0;
6383
6384 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6385
6386 if (RT_FAILURE(vrc))
6387 return setError(VBOX_E_IPRT_ERROR,
6388 tr("Saved screenshot data is not available (%Rrc)"),
6389 vrc);
6390
6391 *aSize = cbData;
6392 *aWidth = u32Width;
6393 *aHeight = u32Height;
6394
6395 freeSavedDisplayScreenshot(pu8Data);
6396
6397 return S_OK;
6398}
6399
6400HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6401 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6402{
6403 if (aScreenId != 0)
6404 return E_NOTIMPL;
6405
6406 if ( aBitmapFormat != BitmapFormat_BGR0
6407 && aBitmapFormat != BitmapFormat_BGRA
6408 && aBitmapFormat != BitmapFormat_RGBA
6409 && aBitmapFormat != BitmapFormat_PNG)
6410 return setError(E_NOTIMPL,
6411 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6412
6413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6414
6415 uint8_t *pu8Data = NULL;
6416 uint32_t cbData = 0;
6417 uint32_t u32Width = 0;
6418 uint32_t u32Height = 0;
6419
6420 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6421
6422 if (RT_FAILURE(vrc))
6423 return setError(VBOX_E_IPRT_ERROR,
6424 tr("Saved thumbnail data is not available (%Rrc)"),
6425 vrc);
6426
6427 HRESULT hr = S_OK;
6428
6429 *aWidth = u32Width;
6430 *aHeight = u32Height;
6431
6432 if (cbData > 0)
6433 {
6434 /* Convert pixels to the format expected by the API caller. */
6435 if (aBitmapFormat == BitmapFormat_BGR0)
6436 {
6437 /* [0] B, [1] G, [2] R, [3] 0. */
6438 aData.resize(cbData);
6439 memcpy(&aData.front(), pu8Data, cbData);
6440 }
6441 else if (aBitmapFormat == BitmapFormat_BGRA)
6442 {
6443 /* [0] B, [1] G, [2] R, [3] A. */
6444 aData.resize(cbData);
6445 for (uint32_t i = 0; i < cbData; i += 4)
6446 {
6447 aData[i] = pu8Data[i];
6448 aData[i + 1] = pu8Data[i + 1];
6449 aData[i + 2] = pu8Data[i + 2];
6450 aData[i + 3] = 0xff;
6451 }
6452 }
6453 else if (aBitmapFormat == BitmapFormat_RGBA)
6454 {
6455 /* [0] R, [1] G, [2] B, [3] A. */
6456 aData.resize(cbData);
6457 for (uint32_t i = 0; i < cbData; i += 4)
6458 {
6459 aData[i] = pu8Data[i + 2];
6460 aData[i + 1] = pu8Data[i + 1];
6461 aData[i + 2] = pu8Data[i];
6462 aData[i + 3] = 0xff;
6463 }
6464 }
6465 else if (aBitmapFormat == BitmapFormat_PNG)
6466 {
6467 uint8_t *pu8PNG = NULL;
6468 uint32_t cbPNG = 0;
6469 uint32_t cxPNG = 0;
6470 uint32_t cyPNG = 0;
6471
6472 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6473
6474 if (RT_SUCCESS(vrc))
6475 {
6476 aData.resize(cbPNG);
6477 if (cbPNG)
6478 memcpy(&aData.front(), pu8PNG, cbPNG);
6479 }
6480 else
6481 hr = setError(VBOX_E_IPRT_ERROR,
6482 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6483 vrc);
6484
6485 RTMemFree(pu8PNG);
6486 }
6487 }
6488
6489 freeSavedDisplayScreenshot(pu8Data);
6490
6491 return hr;
6492}
6493
6494HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6495{
6496 if (aScreenId != 0)
6497 return E_NOTIMPL;
6498
6499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6500
6501 uint8_t *pu8Data = NULL;
6502 uint32_t cbData = 0;
6503 uint32_t u32Width = 0;
6504 uint32_t u32Height = 0;
6505
6506 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6507
6508 if (RT_FAILURE(vrc))
6509 return setError(VBOX_E_IPRT_ERROR,
6510 tr("Saved screenshot data is not available (%Rrc)"),
6511 vrc);
6512
6513 *aSize = cbData;
6514 *aWidth = u32Width;
6515 *aHeight = u32Height;
6516
6517 freeSavedDisplayScreenshot(pu8Data);
6518
6519 return S_OK;
6520}
6521
6522HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6523{
6524 if (aScreenId != 0)
6525 return E_NOTIMPL;
6526
6527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6528
6529 uint8_t *pu8Data = NULL;
6530 uint32_t cbData = 0;
6531 uint32_t u32Width = 0;
6532 uint32_t u32Height = 0;
6533
6534 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6535
6536 if (RT_FAILURE(vrc))
6537 return setError(VBOX_E_IPRT_ERROR,
6538 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6539 vrc);
6540
6541 *aWidth = u32Width;
6542 *aHeight = u32Height;
6543
6544 aData.resize(cbData);
6545 if (cbData)
6546 memcpy(&aData.front(), pu8Data, cbData);
6547
6548 freeSavedDisplayScreenshot(pu8Data);
6549
6550 return S_OK;
6551}
6552
6553HRESULT Machine::hotPlugCPU(ULONG aCpu)
6554{
6555 HRESULT rc = S_OK;
6556 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6557
6558 if (!mHWData->mCPUHotPlugEnabled)
6559 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6560
6561 if (aCpu >= mHWData->mCPUCount)
6562 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6563
6564 if (mHWData->mCPUAttached[aCpu])
6565 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6566
6567 alock.release();
6568 rc = i_onCPUChange(aCpu, false);
6569 alock.acquire();
6570 if (FAILED(rc)) return rc;
6571
6572 i_setModified(IsModified_MachineData);
6573 mHWData.backup();
6574 mHWData->mCPUAttached[aCpu] = true;
6575
6576 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6577 if (Global::IsOnline(mData->mMachineState))
6578 i_saveSettings(NULL);
6579
6580 return S_OK;
6581}
6582
6583HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6584{
6585 HRESULT rc = S_OK;
6586
6587 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6588
6589 if (!mHWData->mCPUHotPlugEnabled)
6590 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6591
6592 if (aCpu >= SchemaDefs::MaxCPUCount)
6593 return setError(E_INVALIDARG,
6594 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6595 SchemaDefs::MaxCPUCount);
6596
6597 if (!mHWData->mCPUAttached[aCpu])
6598 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6599
6600 /* CPU 0 can't be detached */
6601 if (aCpu == 0)
6602 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6603
6604 alock.release();
6605 rc = i_onCPUChange(aCpu, true);
6606 alock.acquire();
6607 if (FAILED(rc)) return rc;
6608
6609 i_setModified(IsModified_MachineData);
6610 mHWData.backup();
6611 mHWData->mCPUAttached[aCpu] = false;
6612
6613 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6614 if (Global::IsOnline(mData->mMachineState))
6615 i_saveSettings(NULL);
6616
6617 return S_OK;
6618}
6619
6620HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6621{
6622 *aAttached = false;
6623
6624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6625
6626 /* If hotplug is enabled the CPU is always enabled. */
6627 if (!mHWData->mCPUHotPlugEnabled)
6628 {
6629 if (aCpu < mHWData->mCPUCount)
6630 *aAttached = true;
6631 }
6632 else
6633 {
6634 if (aCpu < SchemaDefs::MaxCPUCount)
6635 *aAttached = mHWData->mCPUAttached[aCpu];
6636 }
6637
6638 return S_OK;
6639}
6640
6641HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6642{
6643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6644
6645 Utf8Str log = i_queryLogFilename(aIdx);
6646 if (!RTFileExists(log.c_str()))
6647 log.setNull();
6648 aFilename = log;
6649
6650 return S_OK;
6651}
6652
6653HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6654{
6655 if (aSize < 0)
6656 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6657
6658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6659
6660 HRESULT rc = S_OK;
6661 Utf8Str log = i_queryLogFilename(aIdx);
6662
6663 /* do not unnecessarily hold the lock while doing something which does
6664 * not need the lock and potentially takes a long time. */
6665 alock.release();
6666
6667 /* Limit the chunk size to 32K for now, as that gives better performance
6668 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6669 * One byte expands to approx. 25 bytes of breathtaking XML. */
6670 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6671 aData.resize(cbData);
6672
6673 RTFILE LogFile;
6674 int vrc = RTFileOpen(&LogFile, log.c_str(),
6675 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6676 if (RT_SUCCESS(vrc))
6677 {
6678 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6679 if (RT_SUCCESS(vrc))
6680 aData.resize(cbData);
6681 else
6682 rc = setError(VBOX_E_IPRT_ERROR,
6683 tr("Could not read log file '%s' (%Rrc)"),
6684 log.c_str(), vrc);
6685 RTFileClose(LogFile);
6686 }
6687 else
6688 rc = setError(VBOX_E_IPRT_ERROR,
6689 tr("Could not open log file '%s' (%Rrc)"),
6690 log.c_str(), vrc);
6691
6692 if (FAILED(rc))
6693 aData.resize(0);
6694
6695 return rc;
6696}
6697
6698
6699/**
6700 * Currently this method doesn't attach device to the running VM,
6701 * just makes sure it's plugged on next VM start.
6702 */
6703HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6704{
6705 // lock scope
6706 {
6707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6708
6709 HRESULT rc = i_checkStateDependency(MutableStateDep);
6710 if (FAILED(rc)) return rc;
6711
6712 ChipsetType_T aChipset = ChipsetType_PIIX3;
6713 COMGETTER(ChipsetType)(&aChipset);
6714
6715 if (aChipset != ChipsetType_ICH9)
6716 {
6717 return setError(E_INVALIDARG,
6718 tr("Host PCI attachment only supported with ICH9 chipset"));
6719 }
6720
6721 // check if device with this host PCI address already attached
6722 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6723 it != mHWData->mPCIDeviceAssignments.end();
6724 ++it)
6725 {
6726 LONG iHostAddress = -1;
6727 ComPtr<PCIDeviceAttachment> pAttach;
6728 pAttach = *it;
6729 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6730 if (iHostAddress == aHostAddress)
6731 return setError(E_INVALIDARG,
6732 tr("Device with host PCI address already attached to this VM"));
6733 }
6734
6735 ComObjPtr<PCIDeviceAttachment> pda;
6736 char name[32];
6737
6738 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6739 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6740 Bstr bname(name);
6741 pda.createObject();
6742 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6743 i_setModified(IsModified_MachineData);
6744 mHWData.backup();
6745 mHWData->mPCIDeviceAssignments.push_back(pda);
6746 }
6747
6748 return S_OK;
6749}
6750
6751/**
6752 * Currently this method doesn't detach device from the running VM,
6753 * just makes sure it's not plugged on next VM start.
6754 */
6755HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6756{
6757 ComObjPtr<PCIDeviceAttachment> pAttach;
6758 bool fRemoved = false;
6759 HRESULT rc;
6760
6761 // lock scope
6762 {
6763 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6764
6765 rc = i_checkStateDependency(MutableStateDep);
6766 if (FAILED(rc)) return rc;
6767
6768 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6769 it != mHWData->mPCIDeviceAssignments.end();
6770 ++it)
6771 {
6772 LONG iHostAddress = -1;
6773 pAttach = *it;
6774 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6775 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6776 {
6777 i_setModified(IsModified_MachineData);
6778 mHWData.backup();
6779 mHWData->mPCIDeviceAssignments.remove(pAttach);
6780 fRemoved = true;
6781 break;
6782 }
6783 }
6784 }
6785
6786
6787 /* Fire event outside of the lock */
6788 if (fRemoved)
6789 {
6790 Assert(!pAttach.isNull());
6791 ComPtr<IEventSource> es;
6792 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6793 Assert(SUCCEEDED(rc));
6794 Bstr mid;
6795 rc = this->COMGETTER(Id)(mid.asOutParam());
6796 Assert(SUCCEEDED(rc));
6797 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6798 }
6799
6800 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6801 tr("No host PCI device %08x attached"),
6802 aHostAddress
6803 );
6804}
6805
6806HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6807{
6808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6809
6810 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6811
6812 size_t i = 0;
6813 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6814 it != mHWData->mPCIDeviceAssignments.end();
6815 ++i, ++it)
6816 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6817
6818 return S_OK;
6819}
6820
6821HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6822{
6823 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6824
6825 return S_OK;
6826}
6827
6828HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6829{
6830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6831
6832 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6833
6834 return S_OK;
6835}
6836
6837HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6838{
6839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6840 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6841 if (SUCCEEDED(hrc))
6842 {
6843 hrc = mHWData.backupEx();
6844 if (SUCCEEDED(hrc))
6845 {
6846 i_setModified(IsModified_MachineData);
6847 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6848 }
6849 }
6850 return hrc;
6851}
6852
6853HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6854{
6855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6856 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6857 return S_OK;
6858}
6859
6860HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6861{
6862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6863 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6864 if (SUCCEEDED(hrc))
6865 {
6866 hrc = mHWData.backupEx();
6867 if (SUCCEEDED(hrc))
6868 {
6869 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6870 if (SUCCEEDED(hrc))
6871 i_setModified(IsModified_MachineData);
6872 }
6873 }
6874 return hrc;
6875}
6876
6877HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6878{
6879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6880
6881 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6882
6883 return S_OK;
6884}
6885
6886HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6887{
6888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6889 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6890 if (SUCCEEDED(hrc))
6891 {
6892 hrc = mHWData.backupEx();
6893 if (SUCCEEDED(hrc))
6894 {
6895 i_setModified(IsModified_MachineData);
6896 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6897 }
6898 }
6899 return hrc;
6900}
6901
6902HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6903{
6904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6905
6906 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6907
6908 return S_OK;
6909}
6910
6911HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6912{
6913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6914
6915 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6916 if ( SUCCEEDED(hrc)
6917 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6918 {
6919 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6920 int vrc;
6921
6922 if (aAutostartEnabled)
6923 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6924 else
6925 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6926
6927 if (RT_SUCCESS(vrc))
6928 {
6929 hrc = mHWData.backupEx();
6930 if (SUCCEEDED(hrc))
6931 {
6932 i_setModified(IsModified_MachineData);
6933 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6934 }
6935 }
6936 else if (vrc == VERR_NOT_SUPPORTED)
6937 hrc = setError(VBOX_E_NOT_SUPPORTED,
6938 tr("The VM autostart feature is not supported on this platform"));
6939 else if (vrc == VERR_PATH_NOT_FOUND)
6940 hrc = setError(E_FAIL,
6941 tr("The path to the autostart database is not set"));
6942 else
6943 hrc = setError(E_UNEXPECTED,
6944 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6945 aAutostartEnabled ? "Adding" : "Removing",
6946 mUserData->s.strName.c_str(), vrc);
6947 }
6948 return hrc;
6949}
6950
6951HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6952{
6953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6954
6955 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6956
6957 return S_OK;
6958}
6959
6960HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6961{
6962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6963 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6964 if (SUCCEEDED(hrc))
6965 {
6966 hrc = mHWData.backupEx();
6967 if (SUCCEEDED(hrc))
6968 {
6969 i_setModified(IsModified_MachineData);
6970 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6971 }
6972 }
6973 return hrc;
6974}
6975
6976HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6977{
6978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6979
6980 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6981
6982 return S_OK;
6983}
6984
6985HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6986{
6987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6988 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6989 if ( SUCCEEDED(hrc)
6990 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6991 {
6992 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6993 int vrc;
6994
6995 if (aAutostopType != AutostopType_Disabled)
6996 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6997 else
6998 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6999
7000 if (RT_SUCCESS(vrc))
7001 {
7002 hrc = mHWData.backupEx();
7003 if (SUCCEEDED(hrc))
7004 {
7005 i_setModified(IsModified_MachineData);
7006 mHWData->mAutostart.enmAutostopType = aAutostopType;
7007 }
7008 }
7009 else if (vrc == VERR_NOT_SUPPORTED)
7010 hrc = setError(VBOX_E_NOT_SUPPORTED,
7011 tr("The VM autostop feature is not supported on this platform"));
7012 else if (vrc == VERR_PATH_NOT_FOUND)
7013 hrc = setError(E_FAIL,
7014 tr("The path to the autostart database is not set"));
7015 else
7016 hrc = setError(E_UNEXPECTED,
7017 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7018 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7019 mUserData->s.strName.c_str(), vrc);
7020 }
7021 return hrc;
7022}
7023
7024HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7025{
7026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7027
7028 aDefaultFrontend = mHWData->mDefaultFrontend;
7029
7030 return S_OK;
7031}
7032
7033HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7034{
7035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7036 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7037 if (SUCCEEDED(hrc))
7038 {
7039 hrc = mHWData.backupEx();
7040 if (SUCCEEDED(hrc))
7041 {
7042 i_setModified(IsModified_MachineData);
7043 mHWData->mDefaultFrontend = aDefaultFrontend;
7044 }
7045 }
7046 return hrc;
7047}
7048
7049HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7050{
7051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7052 size_t cbIcon = mUserData->mIcon.size();
7053 aIcon.resize(cbIcon);
7054 if (cbIcon)
7055 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7056 return S_OK;
7057}
7058
7059HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7060{
7061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7062 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7063 if (SUCCEEDED(hrc))
7064 {
7065 i_setModified(IsModified_MachineData);
7066 mUserData.backup();
7067 size_t cbIcon = aIcon.size();
7068 mUserData->mIcon.resize(cbIcon);
7069 if (cbIcon)
7070 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7071 }
7072 return hrc;
7073}
7074
7075HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7076{
7077#ifdef VBOX_WITH_USB
7078 *aUSBProxyAvailable = true;
7079#else
7080 *aUSBProxyAvailable = false;
7081#endif
7082 return S_OK;
7083}
7084
7085HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7086 ComPtr<IProgress> &aProgress)
7087{
7088 ComObjPtr<Progress> pP;
7089 Progress *ppP = pP;
7090 IProgress *iP = static_cast<IProgress *>(ppP);
7091 IProgress **pProgress = &iP;
7092
7093 IMachine *pTarget = aTarget;
7094
7095 /* Convert the options. */
7096 RTCList<CloneOptions_T> optList;
7097 if (aOptions.size())
7098 for (size_t i = 0; i < aOptions.size(); ++i)
7099 optList.append(aOptions[i]);
7100
7101 if (optList.contains(CloneOptions_Link))
7102 {
7103 if (!i_isSnapshotMachine())
7104 return setError(E_INVALIDARG,
7105 tr("Linked clone can only be created from a snapshot"));
7106 if (aMode != CloneMode_MachineState)
7107 return setError(E_INVALIDARG,
7108 tr("Linked clone can only be created for a single machine state"));
7109 }
7110 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7111
7112 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7113
7114 HRESULT rc = pWorker->start(pProgress);
7115
7116 pP = static_cast<Progress *>(*pProgress);
7117 pP.queryInterfaceTo(aProgress.asOutParam());
7118
7119 return rc;
7120
7121}
7122
7123HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7124{
7125 NOREF(aProgress);
7126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7127
7128 // This check should always fail.
7129 HRESULT rc = i_checkStateDependency(MutableStateDep);
7130 if (FAILED(rc)) return rc;
7131
7132 AssertFailedReturn(E_NOTIMPL);
7133}
7134
7135HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7136{
7137 NOREF(aSavedStateFile);
7138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7139
7140 // This check should always fail.
7141 HRESULT rc = i_checkStateDependency(MutableStateDep);
7142 if (FAILED(rc)) return rc;
7143
7144 AssertFailedReturn(E_NOTIMPL);
7145}
7146
7147HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7148{
7149 NOREF(aFRemoveFile);
7150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7151
7152 // This check should always fail.
7153 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7154 if (FAILED(rc)) return rc;
7155
7156 AssertFailedReturn(E_NOTIMPL);
7157}
7158
7159// public methods for internal purposes
7160/////////////////////////////////////////////////////////////////////////////
7161
7162/**
7163 * Adds the given IsModified_* flag to the dirty flags of the machine.
7164 * This must be called either during i_loadSettings or under the machine write lock.
7165 * @param fl
7166 */
7167void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7168{
7169 mData->flModifications |= fl;
7170 if (fAllowStateModification && i_isStateModificationAllowed())
7171 mData->mCurrentStateModified = true;
7172}
7173
7174/**
7175 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7176 * care of the write locking.
7177 *
7178 * @param fModifications The flag to add.
7179 */
7180void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7181{
7182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7183 i_setModified(fModification, fAllowStateModification);
7184}
7185
7186/**
7187 * Saves the registry entry of this machine to the given configuration node.
7188 *
7189 * @param aEntryNode Node to save the registry entry to.
7190 *
7191 * @note locks this object for reading.
7192 */
7193HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7194{
7195 AutoLimitedCaller autoCaller(this);
7196 AssertComRCReturnRC(autoCaller.rc());
7197
7198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7199
7200 data.uuid = mData->mUuid;
7201 data.strSettingsFile = mData->m_strConfigFile;
7202
7203 return S_OK;
7204}
7205
7206/**
7207 * Calculates the absolute path of the given path taking the directory of the
7208 * machine settings file as the current directory.
7209 *
7210 * @param aPath Path to calculate the absolute path for.
7211 * @param aResult Where to put the result (used only on success, can be the
7212 * same Utf8Str instance as passed in @a aPath).
7213 * @return IPRT result.
7214 *
7215 * @note Locks this object for reading.
7216 */
7217int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7218{
7219 AutoCaller autoCaller(this);
7220 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7221
7222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7223
7224 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7225
7226 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7227
7228 strSettingsDir.stripFilename();
7229 char folder[RTPATH_MAX];
7230 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7231 if (RT_SUCCESS(vrc))
7232 aResult = folder;
7233
7234 return vrc;
7235}
7236
7237/**
7238 * Copies strSource to strTarget, making it relative to the machine folder
7239 * if it is a subdirectory thereof, or simply copying it otherwise.
7240 *
7241 * @param strSource Path to evaluate and copy.
7242 * @param strTarget Buffer to receive target path.
7243 *
7244 * @note Locks this object for reading.
7245 */
7246void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7247 Utf8Str &strTarget)
7248{
7249 AutoCaller autoCaller(this);
7250 AssertComRCReturn(autoCaller.rc(), (void)0);
7251
7252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7253
7254 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7255 // use strTarget as a temporary buffer to hold the machine settings dir
7256 strTarget = mData->m_strConfigFileFull;
7257 strTarget.stripFilename();
7258 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7259 {
7260 // is relative: then append what's left
7261 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7262 // for empty paths (only possible for subdirs) use "." to avoid
7263 // triggering default settings for not present config attributes.
7264 if (strTarget.isEmpty())
7265 strTarget = ".";
7266 }
7267 else
7268 // is not relative: then overwrite
7269 strTarget = strSource;
7270}
7271
7272/**
7273 * Returns the full path to the machine's log folder in the
7274 * \a aLogFolder argument.
7275 */
7276void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7277{
7278 AutoCaller autoCaller(this);
7279 AssertComRCReturnVoid(autoCaller.rc());
7280
7281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7282
7283 char szTmp[RTPATH_MAX];
7284 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7285 if (RT_SUCCESS(vrc))
7286 {
7287 if (szTmp[0] && !mUserData.isNull())
7288 {
7289 char szTmp2[RTPATH_MAX];
7290 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7291 if (RT_SUCCESS(vrc))
7292 aLogFolder = BstrFmt("%s%c%s",
7293 szTmp2,
7294 RTPATH_DELIMITER,
7295 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7296 }
7297 else
7298 vrc = VERR_PATH_IS_RELATIVE;
7299 }
7300
7301 if (RT_FAILURE(vrc))
7302 {
7303 // fallback if VBOX_USER_LOGHOME is not set or invalid
7304 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7305 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7306 aLogFolder.append(RTPATH_DELIMITER);
7307 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7308 }
7309}
7310
7311/**
7312 * Returns the full path to the machine's log file for an given index.
7313 */
7314Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7315 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7316{
7317 Utf8Str logFolder;
7318 getLogFolder(logFolder);
7319 Assert(logFolder.length());
7320 Utf8Str log;
7321 if (idx == 0)
7322 log = Utf8StrFmt("%s%cVBox.log",
7323 logFolder.c_str(), RTPATH_DELIMITER);
7324 else
7325 log = Utf8StrFmt("%s%cVBox.log.%d",
7326 logFolder.c_str(), RTPATH_DELIMITER, idx);
7327 return log;
7328}
7329
7330/**
7331 * Returns the full path to the machine's (hardened) startup log file.
7332 */
7333Utf8Str Machine::i_getStartupLogFilename(void)
7334{
7335 Utf8Str strFilename;
7336 getLogFolder(strFilename);
7337 Assert(strFilename.length());
7338 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7339 return strFilename;
7340}
7341
7342
7343/**
7344 * Composes a unique saved state filename based on the current system time. The filename is
7345 * granular to the second so this will work so long as no more than one snapshot is taken on
7346 * a machine per second.
7347 *
7348 * Before version 4.1, we used this formula for saved state files:
7349 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7350 * which no longer works because saved state files can now be shared between the saved state of the
7351 * "saved" machine and an online snapshot, and the following would cause problems:
7352 * 1) save machine
7353 * 2) create online snapshot from that machine state --> reusing saved state file
7354 * 3) save machine again --> filename would be reused, breaking the online snapshot
7355 *
7356 * So instead we now use a timestamp.
7357 *
7358 * @param str
7359 */
7360
7361void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7362{
7363 AutoCaller autoCaller(this);
7364 AssertComRCReturnVoid(autoCaller.rc());
7365
7366 {
7367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7368 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7369 }
7370
7371 RTTIMESPEC ts;
7372 RTTimeNow(&ts);
7373 RTTIME time;
7374 RTTimeExplode(&time, &ts);
7375
7376 strStateFilePath += RTPATH_DELIMITER;
7377 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7378 time.i32Year, time.u8Month, time.u8MonthDay,
7379 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7380}
7381
7382/**
7383 * Returns the full path to the default video capture file.
7384 */
7385void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7386{
7387 AutoCaller autoCaller(this);
7388 AssertComRCReturnVoid(autoCaller.rc());
7389
7390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7391
7392 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7393 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7394 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7395}
7396
7397/**
7398 * Returns whether at least one USB controller is present for the VM.
7399 */
7400bool Machine::i_isUSBControllerPresent()
7401{
7402 AutoCaller autoCaller(this);
7403 AssertComRCReturn(autoCaller.rc(), false);
7404
7405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7406
7407 return (mUSBControllers->size() > 0);
7408}
7409
7410/**
7411 * @note Locks this object for writing, calls the client process
7412 * (inside the lock).
7413 */
7414HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7415 const Utf8Str &strFrontend,
7416 const Utf8Str &strEnvironment,
7417 ProgressProxy *aProgress)
7418{
7419 LogFlowThisFuncEnter();
7420
7421 AssertReturn(aControl, E_FAIL);
7422 AssertReturn(aProgress, E_FAIL);
7423 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7424
7425 AutoCaller autoCaller(this);
7426 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7427
7428 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7429
7430 if (!mData->mRegistered)
7431 return setError(E_UNEXPECTED,
7432 tr("The machine '%s' is not registered"),
7433 mUserData->s.strName.c_str());
7434
7435 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7436
7437 /* The process started when launching a VM with separate UI/VM processes is always
7438 * the UI process, i.e. needs special handling as it won't claim the session. */
7439 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7440
7441 if (fSeparate)
7442 {
7443 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName == "headless")
7444 return setError(VBOX_E_INVALID_OBJECT_STATE,
7445 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7446 mUserData->s.strName.c_str());
7447 }
7448 else
7449 {
7450 if ( mData->mSession.mState == SessionState_Locked
7451 || mData->mSession.mState == SessionState_Spawning
7452 || mData->mSession.mState == SessionState_Unlocking)
7453 return setError(VBOX_E_INVALID_OBJECT_STATE,
7454 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7455 mUserData->s.strName.c_str());
7456
7457 /* may not be busy */
7458 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7459 }
7460
7461 /* get the path to the executable */
7462 char szPath[RTPATH_MAX];
7463 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7464 size_t cchBufLeft = strlen(szPath);
7465 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7466 szPath[cchBufLeft] = 0;
7467 char *pszNamePart = szPath + cchBufLeft;
7468 cchBufLeft = sizeof(szPath) - cchBufLeft;
7469
7470 int vrc = VINF_SUCCESS;
7471 RTPROCESS pid = NIL_RTPROCESS;
7472
7473 RTENV env = RTENV_DEFAULT;
7474
7475 if (!strEnvironment.isEmpty())
7476 {
7477 char *newEnvStr = NULL;
7478
7479 do
7480 {
7481 /* clone the current environment */
7482 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7483 AssertRCBreakStmt(vrc2, vrc = vrc2);
7484
7485 newEnvStr = RTStrDup(strEnvironment.c_str());
7486 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7487
7488 /* put new variables to the environment
7489 * (ignore empty variable names here since RTEnv API
7490 * intentionally doesn't do that) */
7491 char *var = newEnvStr;
7492 for (char *p = newEnvStr; *p; ++p)
7493 {
7494 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7495 {
7496 *p = '\0';
7497 if (*var)
7498 {
7499 char *val = strchr(var, '=');
7500 if (val)
7501 {
7502 *val++ = '\0';
7503 vrc2 = RTEnvSetEx(env, var, val);
7504 }
7505 else
7506 vrc2 = RTEnvUnsetEx(env, var);
7507 if (RT_FAILURE(vrc2))
7508 break;
7509 }
7510 var = p + 1;
7511 }
7512 }
7513 if (RT_SUCCESS(vrc2) && *var)
7514 vrc2 = RTEnvPutEx(env, var);
7515
7516 AssertRCBreakStmt(vrc2, vrc = vrc2);
7517 }
7518 while (0);
7519
7520 if (newEnvStr != NULL)
7521 RTStrFree(newEnvStr);
7522 }
7523
7524 /* Hardened startup logging */
7525#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7526 Utf8Str strSupStartLogArg("--sup-startup-log=");
7527 {
7528 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7529 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7530 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7531 {
7532 Utf8Str strStartupLogDir = strStartupLogFile;
7533 strStartupLogDir.stripFilename();
7534 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7535 file without stripping the file. */
7536 }
7537 strSupStartLogArg.append(strStartupLogFile);
7538 }
7539 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7540#else
7541 const char *pszSupStartupLogArg = NULL;
7542#endif
7543
7544 Utf8Str strCanonicalName;
7545
7546#ifdef VBOX_WITH_QTGUI
7547 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7548 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7549 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7550 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7551 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7552 {
7553 strCanonicalName = "GUI/Qt";
7554# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7555 /* Modify the base path so that we don't need to use ".." below. */
7556 RTPathStripTrailingSlash(szPath);
7557 RTPathStripFilename(szPath);
7558 cchBufLeft = strlen(szPath);
7559 pszNamePart = szPath + cchBufLeft;
7560 cchBufLeft = sizeof(szPath) - cchBufLeft;
7561
7562# define OSX_APP_NAME "VirtualBoxVM"
7563# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7564
7565 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7566 if ( strAppOverride.contains(".")
7567 || strAppOverride.contains("/")
7568 || strAppOverride.contains("\\")
7569 || strAppOverride.contains(":"))
7570 strAppOverride.setNull();
7571 Utf8Str strAppPath;
7572 if (!strAppOverride.isEmpty())
7573 {
7574 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7575 Utf8Str strFullPath(szPath);
7576 strFullPath.append(strAppPath);
7577 /* there is a race, but people using this deserve the failure */
7578 if (!RTFileExists(strFullPath.c_str()))
7579 strAppOverride.setNull();
7580 }
7581 if (strAppOverride.isEmpty())
7582 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7583 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7584 strcpy(pszNamePart, strAppPath.c_str());
7585# else
7586 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7587 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7588 strcpy(pszNamePart, s_szVirtualBox_exe);
7589# endif
7590
7591 Utf8Str idStr = mData->mUuid.toString();
7592 const char *apszArgs[] =
7593 {
7594 szPath,
7595 "--comment", mUserData->s.strName.c_str(),
7596 "--startvm", idStr.c_str(),
7597 "--no-startvm-errormsgbox",
7598 NULL, /* For "--separate". */
7599 NULL, /* For "--sup-startup-log". */
7600 NULL
7601 };
7602 unsigned iArg = 6;
7603 if (fSeparate)
7604 apszArgs[iArg++] = "--separate";
7605 apszArgs[iArg++] = pszSupStartupLogArg;
7606
7607 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7608 }
7609#else /* !VBOX_WITH_QTGUI */
7610 if (0)
7611 ;
7612#endif /* VBOX_WITH_QTGUI */
7613
7614 else
7615
7616#ifdef VBOX_WITH_VBOXSDL
7617 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7618 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7619 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7620 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7621 {
7622 strCanonicalName = "GUI/SDL";
7623 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7624 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7625 strcpy(pszNamePart, s_szVBoxSDL_exe);
7626
7627 Utf8Str idStr = mData->mUuid.toString();
7628 const char *apszArgs[] =
7629 {
7630 szPath,
7631 "--comment", mUserData->s.strName.c_str(),
7632 "--startvm", idStr.c_str(),
7633 NULL, /* For "--separate". */
7634 NULL, /* For "--sup-startup-log". */
7635 NULL
7636 };
7637 unsigned iArg = 5;
7638 if (fSeparate)
7639 apszArgs[iArg++] = "--separate";
7640 apszArgs[iArg++] = pszSupStartupLogArg;
7641
7642 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7643 }
7644#else /* !VBOX_WITH_VBOXSDL */
7645 if (0)
7646 ;
7647#endif /* !VBOX_WITH_VBOXSDL */
7648
7649 else
7650
7651#ifdef VBOX_WITH_HEADLESS
7652 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7653 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7654 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7655 )
7656 {
7657 strCanonicalName = "headless";
7658 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7659 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7660 * and a VM works even if the server has not been installed.
7661 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7662 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7663 * differently in 4.0 and 3.x.
7664 */
7665 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7666 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7667 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7668
7669 Utf8Str idStr = mData->mUuid.toString();
7670 const char *apszArgs[] =
7671 {
7672 szPath,
7673 "--comment", mUserData->s.strName.c_str(),
7674 "--startvm", idStr.c_str(),
7675 "--vrde", "config",
7676 NULL, /* For "--capture". */
7677 NULL, /* For "--sup-startup-log". */
7678 NULL
7679 };
7680 unsigned iArg = 7;
7681 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7682 apszArgs[iArg++] = "--capture";
7683 apszArgs[iArg++] = pszSupStartupLogArg;
7684
7685# ifdef RT_OS_WINDOWS
7686 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7687# else
7688 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7689# endif
7690 }
7691#else /* !VBOX_WITH_HEADLESS */
7692 if (0)
7693 ;
7694#endif /* !VBOX_WITH_HEADLESS */
7695 else
7696 {
7697 RTEnvDestroy(env);
7698 return setError(E_INVALIDARG,
7699 tr("Invalid frontend name: '%s'"),
7700 strFrontend.c_str());
7701 }
7702
7703 RTEnvDestroy(env);
7704
7705 if (RT_FAILURE(vrc))
7706 return setError(VBOX_E_IPRT_ERROR,
7707 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7708 mUserData->s.strName.c_str(), vrc);
7709
7710 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7711
7712 if (!fSeparate)
7713 {
7714 /*
7715 * Note that we don't release the lock here before calling the client,
7716 * because it doesn't need to call us back if called with a NULL argument.
7717 * Releasing the lock here is dangerous because we didn't prepare the
7718 * launch data yet, but the client we've just started may happen to be
7719 * too fast and call LockMachine() that will fail (because of PID, etc.),
7720 * so that the Machine will never get out of the Spawning session state.
7721 */
7722
7723 /* inform the session that it will be a remote one */
7724 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7725#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7726 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7727#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7728 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7729#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7730 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7731
7732 if (FAILED(rc))
7733 {
7734 /* restore the session state */
7735 mData->mSession.mState = SessionState_Unlocked;
7736 alock.release();
7737 mParent->i_addProcessToReap(pid);
7738 /* The failure may occur w/o any error info (from RPC), so provide one */
7739 return setError(VBOX_E_VM_ERROR,
7740 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7741 }
7742
7743 /* attach launch data to the machine */
7744 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7745 mData->mSession.mRemoteControls.push_back(aControl);
7746 mData->mSession.mProgress = aProgress;
7747 mData->mSession.mPID = pid;
7748 mData->mSession.mState = SessionState_Spawning;
7749 Assert(strCanonicalName.isNotEmpty());
7750 mData->mSession.mName = strCanonicalName;
7751 }
7752 else
7753 {
7754 /* For separate UI process we declare the launch as completed instantly, as the
7755 * actual headless VM start may or may not come. No point in remembering anything
7756 * yet, as what matters for us is when the headless VM gets started. */
7757 aProgress->i_notifyComplete(S_OK);
7758 }
7759
7760 alock.release();
7761 mParent->i_addProcessToReap(pid);
7762
7763 LogFlowThisFuncLeave();
7764 return S_OK;
7765}
7766
7767/**
7768 * Returns @c true if the given session machine instance has an open direct
7769 * session (and optionally also for direct sessions which are closing) and
7770 * returns the session control machine instance if so.
7771 *
7772 * Note that when the method returns @c false, the arguments remain unchanged.
7773 *
7774 * @param aMachine Session machine object.
7775 * @param aControl Direct session control object (optional).
7776 * @param aAllowClosing If true then additionally a session which is currently
7777 * being closed will also be allowed.
7778 *
7779 * @note locks this object for reading.
7780 */
7781bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7782 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7783 bool aAllowClosing /*= false*/)
7784{
7785 AutoLimitedCaller autoCaller(this);
7786 AssertComRCReturn(autoCaller.rc(), false);
7787
7788 /* just return false for inaccessible machines */
7789 if (getObjectState().getState() != ObjectState::Ready)
7790 return false;
7791
7792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7793
7794 if ( ( mData->mSession.mState == SessionState_Locked
7795 && mData->mSession.mLockType == LockType_VM)
7796 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7797 )
7798 {
7799 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7800
7801 aMachine = mData->mSession.mMachine;
7802
7803 if (aControl != NULL)
7804 *aControl = mData->mSession.mDirectControl;
7805
7806 return true;
7807 }
7808
7809 return false;
7810}
7811
7812/**
7813 * Returns @c true if the given machine has an spawning direct session.
7814 *
7815 * @note locks this object for reading.
7816 */
7817bool Machine::i_isSessionSpawning()
7818{
7819 AutoLimitedCaller autoCaller(this);
7820 AssertComRCReturn(autoCaller.rc(), false);
7821
7822 /* just return false for inaccessible machines */
7823 if (getObjectState().getState() != ObjectState::Ready)
7824 return false;
7825
7826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7827
7828 if (mData->mSession.mState == SessionState_Spawning)
7829 return true;
7830
7831 return false;
7832}
7833
7834/**
7835 * Called from the client watcher thread to check for unexpected client process
7836 * death during Session_Spawning state (e.g. before it successfully opened a
7837 * direct session).
7838 *
7839 * On Win32 and on OS/2, this method is called only when we've got the
7840 * direct client's process termination notification, so it always returns @c
7841 * true.
7842 *
7843 * On other platforms, this method returns @c true if the client process is
7844 * terminated and @c false if it's still alive.
7845 *
7846 * @note Locks this object for writing.
7847 */
7848bool Machine::i_checkForSpawnFailure()
7849{
7850 AutoCaller autoCaller(this);
7851 if (!autoCaller.isOk())
7852 {
7853 /* nothing to do */
7854 LogFlowThisFunc(("Already uninitialized!\n"));
7855 return true;
7856 }
7857
7858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7859
7860 if (mData->mSession.mState != SessionState_Spawning)
7861 {
7862 /* nothing to do */
7863 LogFlowThisFunc(("Not spawning any more!\n"));
7864 return true;
7865 }
7866
7867 HRESULT rc = S_OK;
7868
7869 /* PID not yet initialized, skip check. */
7870 if (mData->mSession.mPID == NIL_RTPROCESS)
7871 return false;
7872
7873 RTPROCSTATUS status;
7874 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7875
7876 if (vrc != VERR_PROCESS_RUNNING)
7877 {
7878 Utf8Str strExtraInfo;
7879
7880#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7881 /* If the startup logfile exists and is of non-zero length, tell the
7882 user to look there for more details to encourage them to attach it
7883 when reporting startup issues. */
7884 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7885 uint64_t cbStartupLogFile = 0;
7886 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7887 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7888 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7889#endif
7890
7891 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7892 rc = setError(E_FAIL,
7893 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7894 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7895 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7896 rc = setError(E_FAIL,
7897 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7898 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7899 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7900 rc = setError(E_FAIL,
7901 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7902 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7903 else
7904 rc = setError(E_FAIL,
7905 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7906 i_getName().c_str(), vrc, strExtraInfo.c_str());
7907 }
7908
7909 if (FAILED(rc))
7910 {
7911 /* Close the remote session, remove the remote control from the list
7912 * and reset session state to Closed (@note keep the code in sync with
7913 * the relevant part in LockMachine()). */
7914
7915 Assert(mData->mSession.mRemoteControls.size() == 1);
7916 if (mData->mSession.mRemoteControls.size() == 1)
7917 {
7918 ErrorInfoKeeper eik;
7919 mData->mSession.mRemoteControls.front()->Uninitialize();
7920 }
7921
7922 mData->mSession.mRemoteControls.clear();
7923 mData->mSession.mState = SessionState_Unlocked;
7924
7925 /* finalize the progress after setting the state */
7926 if (!mData->mSession.mProgress.isNull())
7927 {
7928 mData->mSession.mProgress->notifyComplete(rc);
7929 mData->mSession.mProgress.setNull();
7930 }
7931
7932 mData->mSession.mPID = NIL_RTPROCESS;
7933
7934 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7935 return true;
7936 }
7937
7938 return false;
7939}
7940
7941/**
7942 * Checks whether the machine can be registered. If so, commits and saves
7943 * all settings.
7944 *
7945 * @note Must be called from mParent's write lock. Locks this object and
7946 * children for writing.
7947 */
7948HRESULT Machine::i_prepareRegister()
7949{
7950 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7951
7952 AutoLimitedCaller autoCaller(this);
7953 AssertComRCReturnRC(autoCaller.rc());
7954
7955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7956
7957 /* wait for state dependents to drop to zero */
7958 i_ensureNoStateDependencies();
7959
7960 if (!mData->mAccessible)
7961 return setError(VBOX_E_INVALID_OBJECT_STATE,
7962 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7963 mUserData->s.strName.c_str(),
7964 mData->mUuid.toString().c_str());
7965
7966 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7967
7968 if (mData->mRegistered)
7969 return setError(VBOX_E_INVALID_OBJECT_STATE,
7970 tr("The machine '%s' with UUID {%s} is already registered"),
7971 mUserData->s.strName.c_str(),
7972 mData->mUuid.toString().c_str());
7973
7974 HRESULT rc = S_OK;
7975
7976 // Ensure the settings are saved. If we are going to be registered and
7977 // no config file exists yet, create it by calling i_saveSettings() too.
7978 if ( (mData->flModifications)
7979 || (!mData->pMachineConfigFile->fileExists())
7980 )
7981 {
7982 rc = i_saveSettings(NULL);
7983 // no need to check whether VirtualBox.xml needs saving too since
7984 // we can't have a machine XML file rename pending
7985 if (FAILED(rc)) return rc;
7986 }
7987
7988 /* more config checking goes here */
7989
7990 if (SUCCEEDED(rc))
7991 {
7992 /* we may have had implicit modifications we want to fix on success */
7993 i_commit();
7994
7995 mData->mRegistered = true;
7996 }
7997 else
7998 {
7999 /* we may have had implicit modifications we want to cancel on failure*/
8000 i_rollback(false /* aNotify */);
8001 }
8002
8003 return rc;
8004}
8005
8006/**
8007 * Increases the number of objects dependent on the machine state or on the
8008 * registered state. Guarantees that these two states will not change at least
8009 * until #releaseStateDependency() is called.
8010 *
8011 * Depending on the @a aDepType value, additional state checks may be made.
8012 * These checks will set extended error info on failure. See
8013 * #checkStateDependency() for more info.
8014 *
8015 * If this method returns a failure, the dependency is not added and the caller
8016 * is not allowed to rely on any particular machine state or registration state
8017 * value and may return the failed result code to the upper level.
8018 *
8019 * @param aDepType Dependency type to add.
8020 * @param aState Current machine state (NULL if not interested).
8021 * @param aRegistered Current registered state (NULL if not interested).
8022 *
8023 * @note Locks this object for writing.
8024 */
8025HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8026 MachineState_T *aState /* = NULL */,
8027 BOOL *aRegistered /* = NULL */)
8028{
8029 AutoCaller autoCaller(this);
8030 AssertComRCReturnRC(autoCaller.rc());
8031
8032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8033
8034 HRESULT rc = i_checkStateDependency(aDepType);
8035 if (FAILED(rc)) return rc;
8036
8037 {
8038 if (mData->mMachineStateChangePending != 0)
8039 {
8040 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8041 * drop to zero so don't add more. It may make sense to wait a bit
8042 * and retry before reporting an error (since the pending state
8043 * transition should be really quick) but let's just assert for
8044 * now to see if it ever happens on practice. */
8045
8046 AssertFailed();
8047
8048 return setError(E_ACCESSDENIED,
8049 tr("Machine state change is in progress. Please retry the operation later."));
8050 }
8051
8052 ++mData->mMachineStateDeps;
8053 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8054 }
8055
8056 if (aState)
8057 *aState = mData->mMachineState;
8058 if (aRegistered)
8059 *aRegistered = mData->mRegistered;
8060
8061 return S_OK;
8062}
8063
8064/**
8065 * Decreases the number of objects dependent on the machine state.
8066 * Must always complete the #addStateDependency() call after the state
8067 * dependency is no more necessary.
8068 */
8069void Machine::i_releaseStateDependency()
8070{
8071 AutoCaller autoCaller(this);
8072 AssertComRCReturnVoid(autoCaller.rc());
8073
8074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8075
8076 /* releaseStateDependency() w/o addStateDependency()? */
8077 AssertReturnVoid(mData->mMachineStateDeps != 0);
8078 -- mData->mMachineStateDeps;
8079
8080 if (mData->mMachineStateDeps == 0)
8081 {
8082 /* inform i_ensureNoStateDependencies() that there are no more deps */
8083 if (mData->mMachineStateChangePending != 0)
8084 {
8085 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8086 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8087 }
8088 }
8089}
8090
8091Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8092{
8093 /* start with nothing found */
8094 Utf8Str strResult("");
8095
8096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8097
8098 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8099 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8100 // found:
8101 strResult = it->second; // source is a Utf8Str
8102
8103 return strResult;
8104}
8105
8106// protected methods
8107/////////////////////////////////////////////////////////////////////////////
8108
8109/**
8110 * Performs machine state checks based on the @a aDepType value. If a check
8111 * fails, this method will set extended error info, otherwise it will return
8112 * S_OK. It is supposed, that on failure, the caller will immediately return
8113 * the return value of this method to the upper level.
8114 *
8115 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8116 *
8117 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8118 * current state of this machine object allows to change settings of the
8119 * machine (i.e. the machine is not registered, or registered but not running
8120 * and not saved). It is useful to call this method from Machine setters
8121 * before performing any change.
8122 *
8123 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8124 * as for MutableStateDep except that if the machine is saved, S_OK is also
8125 * returned. This is useful in setters which allow changing machine
8126 * properties when it is in the saved state.
8127 *
8128 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8129 * if the current state of this machine object allows to change runtime
8130 * changeable settings of the machine (i.e. the machine is not registered, or
8131 * registered but either running or not running and not saved). It is useful
8132 * to call this method from Machine setters before performing any changes to
8133 * runtime changeable settings.
8134 *
8135 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8136 * the same as for MutableOrRunningStateDep except that if the machine is
8137 * saved, S_OK is also returned. This is useful in setters which allow
8138 * changing runtime and saved state changeable machine properties.
8139 *
8140 * @param aDepType Dependency type to check.
8141 *
8142 * @note Non Machine based classes should use #addStateDependency() and
8143 * #releaseStateDependency() methods or the smart AutoStateDependency
8144 * template.
8145 *
8146 * @note This method must be called from under this object's read or write
8147 * lock.
8148 */
8149HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8150{
8151 switch (aDepType)
8152 {
8153 case AnyStateDep:
8154 {
8155 break;
8156 }
8157 case MutableStateDep:
8158 {
8159 if ( mData->mRegistered
8160 && ( !i_isSessionMachine()
8161 || ( mData->mMachineState != MachineState_Aborted
8162 && mData->mMachineState != MachineState_Teleported
8163 && mData->mMachineState != MachineState_PoweredOff
8164 )
8165 )
8166 )
8167 return setError(VBOX_E_INVALID_VM_STATE,
8168 tr("The machine is not mutable (state is %s)"),
8169 Global::stringifyMachineState(mData->mMachineState));
8170 break;
8171 }
8172 case MutableOrSavedStateDep:
8173 {
8174 if ( mData->mRegistered
8175 && ( !i_isSessionMachine()
8176 || ( mData->mMachineState != MachineState_Aborted
8177 && mData->mMachineState != MachineState_Teleported
8178 && mData->mMachineState != MachineState_Saved
8179 && mData->mMachineState != MachineState_PoweredOff
8180 )
8181 )
8182 )
8183 return setError(VBOX_E_INVALID_VM_STATE,
8184 tr("The machine is not mutable (state is %s)"),
8185 Global::stringifyMachineState(mData->mMachineState));
8186 break;
8187 }
8188 case MutableOrRunningStateDep:
8189 {
8190 if ( mData->mRegistered
8191 && ( !i_isSessionMachine()
8192 || ( mData->mMachineState != MachineState_Aborted
8193 && mData->mMachineState != MachineState_Teleported
8194 && mData->mMachineState != MachineState_PoweredOff
8195 && !Global::IsOnline(mData->mMachineState)
8196 )
8197 )
8198 )
8199 return setError(VBOX_E_INVALID_VM_STATE,
8200 tr("The machine is not mutable (state is %s)"),
8201 Global::stringifyMachineState(mData->mMachineState));
8202 break;
8203 }
8204 case MutableOrSavedOrRunningStateDep:
8205 {
8206 if ( mData->mRegistered
8207 && ( !i_isSessionMachine()
8208 || ( mData->mMachineState != MachineState_Aborted
8209 && mData->mMachineState != MachineState_Teleported
8210 && mData->mMachineState != MachineState_Saved
8211 && mData->mMachineState != MachineState_PoweredOff
8212 && !Global::IsOnline(mData->mMachineState)
8213 )
8214 )
8215 )
8216 return setError(VBOX_E_INVALID_VM_STATE,
8217 tr("The machine is not mutable (state is %s)"),
8218 Global::stringifyMachineState(mData->mMachineState));
8219 break;
8220 }
8221 }
8222
8223 return S_OK;
8224}
8225
8226/**
8227 * Helper to initialize all associated child objects and allocate data
8228 * structures.
8229 *
8230 * This method must be called as a part of the object's initialization procedure
8231 * (usually done in the #init() method).
8232 *
8233 * @note Must be called only from #init() or from #registeredInit().
8234 */
8235HRESULT Machine::initDataAndChildObjects()
8236{
8237 AutoCaller autoCaller(this);
8238 AssertComRCReturnRC(autoCaller.rc());
8239 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8240 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8241
8242 AssertReturn(!mData->mAccessible, E_FAIL);
8243
8244 /* allocate data structures */
8245 mSSData.allocate();
8246 mUserData.allocate();
8247 mHWData.allocate();
8248 mMediaData.allocate();
8249 mStorageControllers.allocate();
8250 mUSBControllers.allocate();
8251
8252 /* initialize mOSTypeId */
8253 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8254
8255 /* create associated BIOS settings object */
8256 unconst(mBIOSSettings).createObject();
8257 mBIOSSettings->init(this);
8258
8259 /* create an associated VRDE object (default is disabled) */
8260 unconst(mVRDEServer).createObject();
8261 mVRDEServer->init(this);
8262
8263 /* create associated serial port objects */
8264 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8265 {
8266 unconst(mSerialPorts[slot]).createObject();
8267 mSerialPorts[slot]->init(this, slot);
8268 }
8269
8270 /* create associated parallel port objects */
8271 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8272 {
8273 unconst(mParallelPorts[slot]).createObject();
8274 mParallelPorts[slot]->init(this, slot);
8275 }
8276
8277 /* create the audio adapter object (always present, default is disabled) */
8278 unconst(mAudioAdapter).createObject();
8279 mAudioAdapter->init(this);
8280
8281 /* create the USB device filters object (always present) */
8282 unconst(mUSBDeviceFilters).createObject();
8283 mUSBDeviceFilters->init(this);
8284
8285 /* create associated network adapter objects */
8286 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8287 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8288 {
8289 unconst(mNetworkAdapters[slot]).createObject();
8290 mNetworkAdapters[slot]->init(this, slot);
8291 }
8292
8293 /* create the bandwidth control */
8294 unconst(mBandwidthControl).createObject();
8295 mBandwidthControl->init(this);
8296
8297 return S_OK;
8298}
8299
8300/**
8301 * Helper to uninitialize all associated child objects and to free all data
8302 * structures.
8303 *
8304 * This method must be called as a part of the object's uninitialization
8305 * procedure (usually done in the #uninit() method).
8306 *
8307 * @note Must be called only from #uninit() or from #registeredInit().
8308 */
8309void Machine::uninitDataAndChildObjects()
8310{
8311 AutoCaller autoCaller(this);
8312 AssertComRCReturnVoid(autoCaller.rc());
8313 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8314 || getObjectState().getState() == ObjectState::Limited);
8315
8316 /* tell all our other child objects we've been uninitialized */
8317 if (mBandwidthControl)
8318 {
8319 mBandwidthControl->uninit();
8320 unconst(mBandwidthControl).setNull();
8321 }
8322
8323 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8324 {
8325 if (mNetworkAdapters[slot])
8326 {
8327 mNetworkAdapters[slot]->uninit();
8328 unconst(mNetworkAdapters[slot]).setNull();
8329 }
8330 }
8331
8332 if (mUSBDeviceFilters)
8333 {
8334 mUSBDeviceFilters->uninit();
8335 unconst(mUSBDeviceFilters).setNull();
8336 }
8337
8338 if (mAudioAdapter)
8339 {
8340 mAudioAdapter->uninit();
8341 unconst(mAudioAdapter).setNull();
8342 }
8343
8344 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8345 {
8346 if (mParallelPorts[slot])
8347 {
8348 mParallelPorts[slot]->uninit();
8349 unconst(mParallelPorts[slot]).setNull();
8350 }
8351 }
8352
8353 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8354 {
8355 if (mSerialPorts[slot])
8356 {
8357 mSerialPorts[slot]->uninit();
8358 unconst(mSerialPorts[slot]).setNull();
8359 }
8360 }
8361
8362 if (mVRDEServer)
8363 {
8364 mVRDEServer->uninit();
8365 unconst(mVRDEServer).setNull();
8366 }
8367
8368 if (mBIOSSettings)
8369 {
8370 mBIOSSettings->uninit();
8371 unconst(mBIOSSettings).setNull();
8372 }
8373
8374 /* Deassociate media (only when a real Machine or a SnapshotMachine
8375 * instance is uninitialized; SessionMachine instances refer to real
8376 * Machine media). This is necessary for a clean re-initialization of
8377 * the VM after successfully re-checking the accessibility state. Note
8378 * that in case of normal Machine or SnapshotMachine uninitialization (as
8379 * a result of unregistering or deleting the snapshot), outdated media
8380 * attachments will already be uninitialized and deleted, so this
8381 * code will not affect them. */
8382 if ( !!mMediaData
8383 && (!i_isSessionMachine())
8384 )
8385 {
8386 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8387 it != mMediaData->mAttachments.end();
8388 ++it)
8389 {
8390 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8391 if (pMedium.isNull())
8392 continue;
8393 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8394 AssertComRC(rc);
8395 }
8396 }
8397
8398 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8399 {
8400 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8401 if (mData->mFirstSnapshot)
8402 {
8403 // snapshots tree is protected by machine write lock; strictly
8404 // this isn't necessary here since we're deleting the entire
8405 // machine, but otherwise we assert in Snapshot::uninit()
8406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8407 mData->mFirstSnapshot->uninit();
8408 mData->mFirstSnapshot.setNull();
8409 }
8410
8411 mData->mCurrentSnapshot.setNull();
8412 }
8413
8414 /* free data structures (the essential mData structure is not freed here
8415 * since it may be still in use) */
8416 mMediaData.free();
8417 mStorageControllers.free();
8418 mUSBControllers.free();
8419 mHWData.free();
8420 mUserData.free();
8421 mSSData.free();
8422}
8423
8424/**
8425 * Returns a pointer to the Machine object for this machine that acts like a
8426 * parent for complex machine data objects such as shared folders, etc.
8427 *
8428 * For primary Machine objects and for SnapshotMachine objects, returns this
8429 * object's pointer itself. For SessionMachine objects, returns the peer
8430 * (primary) machine pointer.
8431 */
8432Machine* Machine::i_getMachine()
8433{
8434 if (i_isSessionMachine())
8435 return (Machine*)mPeer;
8436 return this;
8437}
8438
8439/**
8440 * Makes sure that there are no machine state dependents. If necessary, waits
8441 * for the number of dependents to drop to zero.
8442 *
8443 * Make sure this method is called from under this object's write lock to
8444 * guarantee that no new dependents may be added when this method returns
8445 * control to the caller.
8446 *
8447 * @note Locks this object for writing. The lock will be released while waiting
8448 * (if necessary).
8449 *
8450 * @warning To be used only in methods that change the machine state!
8451 */
8452void Machine::i_ensureNoStateDependencies()
8453{
8454 AssertReturnVoid(isWriteLockOnCurrentThread());
8455
8456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8457
8458 /* Wait for all state dependents if necessary */
8459 if (mData->mMachineStateDeps != 0)
8460 {
8461 /* lazy semaphore creation */
8462 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8463 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8464
8465 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8466 mData->mMachineStateDeps));
8467
8468 ++mData->mMachineStateChangePending;
8469
8470 /* reset the semaphore before waiting, the last dependent will signal
8471 * it */
8472 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8473
8474 alock.release();
8475
8476 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8477
8478 alock.acquire();
8479
8480 -- mData->mMachineStateChangePending;
8481 }
8482}
8483
8484/**
8485 * Changes the machine state and informs callbacks.
8486 *
8487 * This method is not intended to fail so it either returns S_OK or asserts (and
8488 * returns a failure).
8489 *
8490 * @note Locks this object for writing.
8491 */
8492HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8493{
8494 LogFlowThisFuncEnter();
8495 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8496 Assert(aMachineState != MachineState_Null);
8497
8498 AutoCaller autoCaller(this);
8499 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8500
8501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8502
8503 /* wait for state dependents to drop to zero */
8504 i_ensureNoStateDependencies();
8505
8506 MachineState_T const enmOldState = mData->mMachineState;
8507 if (enmOldState != aMachineState)
8508 {
8509 mData->mMachineState = aMachineState;
8510 RTTimeNow(&mData->mLastStateChange);
8511
8512#ifdef VBOX_WITH_DTRACE_R3_MAIN
8513 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8514#endif
8515 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8516 }
8517
8518 LogFlowThisFuncLeave();
8519 return S_OK;
8520}
8521
8522/**
8523 * Searches for a shared folder with the given logical name
8524 * in the collection of shared folders.
8525 *
8526 * @param aName logical name of the shared folder
8527 * @param aSharedFolder where to return the found object
8528 * @param aSetError whether to set the error info if the folder is
8529 * not found
8530 * @return
8531 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8532 *
8533 * @note
8534 * must be called from under the object's lock!
8535 */
8536HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8537 ComObjPtr<SharedFolder> &aSharedFolder,
8538 bool aSetError /* = false */)
8539{
8540 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8541 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8542 it != mHWData->mSharedFolders.end();
8543 ++it)
8544 {
8545 SharedFolder *pSF = *it;
8546 AutoCaller autoCaller(pSF);
8547 if (pSF->i_getName() == aName)
8548 {
8549 aSharedFolder = pSF;
8550 rc = S_OK;
8551 break;
8552 }
8553 }
8554
8555 if (aSetError && FAILED(rc))
8556 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8557
8558 return rc;
8559}
8560
8561/**
8562 * Initializes all machine instance data from the given settings structures
8563 * from XML. The exception is the machine UUID which needs special handling
8564 * depending on the caller's use case, so the caller needs to set that herself.
8565 *
8566 * This gets called in several contexts during machine initialization:
8567 *
8568 * -- When machine XML exists on disk already and needs to be loaded into memory,
8569 * for example, from registeredInit() to load all registered machines on
8570 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8571 * attached to the machine should be part of some media registry already.
8572 *
8573 * -- During OVF import, when a machine config has been constructed from an
8574 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8575 * ensure that the media listed as attachments in the config (which have
8576 * been imported from the OVF) receive the correct registry ID.
8577 *
8578 * -- During VM cloning.
8579 *
8580 * @param config Machine settings from XML.
8581 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8582 * for each attached medium in the config.
8583 * @return
8584 */
8585HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8586 const Guid *puuidRegistry)
8587{
8588 // copy name, description, OS type, teleporter, UTC etc.
8589 mUserData->s = config.machineUserData;
8590
8591 // Decode the Icon overide data from config userdata and set onto Machine.
8592 #define DECODE_STR_MAX _1M
8593 const char* pszStr = config.machineUserData.ovIcon.c_str();
8594 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8595 if (cbOut > DECODE_STR_MAX)
8596 return setError(E_FAIL,
8597 tr("Icon Data too long.'%d' > '%d'"),
8598 cbOut,
8599 DECODE_STR_MAX);
8600 mUserData->mIcon.resize(cbOut);
8601 int vrc = VINF_SUCCESS;
8602 if (cbOut)
8603 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8604 if (RT_FAILURE(vrc))
8605 {
8606 mUserData->mIcon.resize(0);
8607 return setError(E_FAIL,
8608 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8609 pszStr,
8610 vrc);
8611 }
8612
8613 // look up the object by Id to check it is valid
8614 ComPtr<IGuestOSType> guestOSType;
8615 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8616 guestOSType.asOutParam());
8617 if (FAILED(rc)) return rc;
8618
8619 // stateFile (optional)
8620 if (config.strStateFile.isEmpty())
8621 mSSData->strStateFilePath.setNull();
8622 else
8623 {
8624 Utf8Str stateFilePathFull(config.strStateFile);
8625 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8626 if (RT_FAILURE(vrc))
8627 return setError(E_FAIL,
8628 tr("Invalid saved state file path '%s' (%Rrc)"),
8629 config.strStateFile.c_str(),
8630 vrc);
8631 mSSData->strStateFilePath = stateFilePathFull;
8632 }
8633
8634 // snapshot folder needs special processing so set it again
8635 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8636 if (FAILED(rc)) return rc;
8637
8638 /* Copy the extra data items (Not in any case config is already the same as
8639 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8640 * make sure the extra data map is copied). */
8641 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8642
8643 /* currentStateModified (optional, default is true) */
8644 mData->mCurrentStateModified = config.fCurrentStateModified;
8645
8646 mData->mLastStateChange = config.timeLastStateChange;
8647
8648 /*
8649 * note: all mUserData members must be assigned prior this point because
8650 * we need to commit changes in order to let mUserData be shared by all
8651 * snapshot machine instances.
8652 */
8653 mUserData.commitCopy();
8654
8655 // machine registry, if present (must be loaded before snapshots)
8656 if (config.canHaveOwnMediaRegistry())
8657 {
8658 // determine machine folder
8659 Utf8Str strMachineFolder = i_getSettingsFileFull();
8660 strMachineFolder.stripFilename();
8661 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8662 config.mediaRegistry,
8663 strMachineFolder);
8664 if (FAILED(rc)) return rc;
8665 }
8666
8667 /* Snapshot node (optional) */
8668 size_t cRootSnapshots;
8669 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8670 {
8671 // there must be only one root snapshot
8672 Assert(cRootSnapshots == 1);
8673
8674 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8675
8676 rc = i_loadSnapshot(snap,
8677 config.uuidCurrentSnapshot,
8678 NULL); // no parent == first snapshot
8679 if (FAILED(rc)) return rc;
8680 }
8681
8682 // hardware data
8683 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8684 if (FAILED(rc)) return rc;
8685
8686 // load storage controllers
8687 rc = i_loadStorageControllers(config.storageMachine,
8688 puuidRegistry,
8689 NULL /* puuidSnapshot */);
8690 if (FAILED(rc)) return rc;
8691
8692 /*
8693 * NOTE: the assignment below must be the last thing to do,
8694 * otherwise it will be not possible to change the settings
8695 * somewhere in the code above because all setters will be
8696 * blocked by i_checkStateDependency(MutableStateDep).
8697 */
8698
8699 /* set the machine state to Aborted or Saved when appropriate */
8700 if (config.fAborted)
8701 {
8702 mSSData->strStateFilePath.setNull();
8703
8704 /* no need to use i_setMachineState() during init() */
8705 mData->mMachineState = MachineState_Aborted;
8706 }
8707 else if (!mSSData->strStateFilePath.isEmpty())
8708 {
8709 /* no need to use i_setMachineState() during init() */
8710 mData->mMachineState = MachineState_Saved;
8711 }
8712
8713 // after loading settings, we are no longer different from the XML on disk
8714 mData->flModifications = 0;
8715
8716 return S_OK;
8717}
8718
8719/**
8720 * Recursively loads all snapshots starting from the given.
8721 *
8722 * @param aNode <Snapshot> node.
8723 * @param aCurSnapshotId Current snapshot ID from the settings file.
8724 * @param aParentSnapshot Parent snapshot.
8725 */
8726HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8727 const Guid &aCurSnapshotId,
8728 Snapshot *aParentSnapshot)
8729{
8730 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8731 AssertReturn(!i_isSessionMachine(), E_FAIL);
8732
8733 HRESULT rc = S_OK;
8734
8735 Utf8Str strStateFile;
8736 if (!data.strStateFile.isEmpty())
8737 {
8738 /* optional */
8739 strStateFile = data.strStateFile;
8740 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8741 if (RT_FAILURE(vrc))
8742 return setError(E_FAIL,
8743 tr("Invalid saved state file path '%s' (%Rrc)"),
8744 strStateFile.c_str(),
8745 vrc);
8746 }
8747
8748 /* create a snapshot machine object */
8749 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8750 pSnapshotMachine.createObject();
8751 rc = pSnapshotMachine->initFromSettings(this,
8752 data.hardware,
8753 &data.debugging,
8754 &data.autostart,
8755 data.storage,
8756 data.uuid.ref(),
8757 strStateFile);
8758 if (FAILED(rc)) return rc;
8759
8760 /* create a snapshot object */
8761 ComObjPtr<Snapshot> pSnapshot;
8762 pSnapshot.createObject();
8763 /* initialize the snapshot */
8764 rc = pSnapshot->init(mParent, // VirtualBox object
8765 data.uuid,
8766 data.strName,
8767 data.strDescription,
8768 data.timestamp,
8769 pSnapshotMachine,
8770 aParentSnapshot);
8771 if (FAILED(rc)) return rc;
8772
8773 /* memorize the first snapshot if necessary */
8774 if (!mData->mFirstSnapshot)
8775 mData->mFirstSnapshot = pSnapshot;
8776
8777 /* memorize the current snapshot when appropriate */
8778 if ( !mData->mCurrentSnapshot
8779 && pSnapshot->i_getId() == aCurSnapshotId
8780 )
8781 mData->mCurrentSnapshot = pSnapshot;
8782
8783 // now create the children
8784 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8785 it != data.llChildSnapshots.end();
8786 ++it)
8787 {
8788 const settings::Snapshot &childData = *it;
8789 // recurse
8790 rc = i_loadSnapshot(childData,
8791 aCurSnapshotId,
8792 pSnapshot); // parent = the one we created above
8793 if (FAILED(rc)) return rc;
8794 }
8795
8796 return rc;
8797}
8798
8799/**
8800 * Loads settings into mHWData.
8801 *
8802 * @param data Reference to the hardware settings.
8803 * @param pDbg Pointer to the debugging settings.
8804 * @param pAutostart Pointer to the autostart settings.
8805 */
8806HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8807 const settings::Autostart *pAutostart)
8808{
8809 AssertReturn(!i_isSessionMachine(), E_FAIL);
8810
8811 HRESULT rc = S_OK;
8812
8813 try
8814 {
8815 /* The hardware version attribute (optional). */
8816 mHWData->mHWVersion = data.strVersion;
8817 mHWData->mHardwareUUID = data.uuid;
8818
8819 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8820 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8821 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8822 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8823 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8824 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8825 mHWData->mPAEEnabled = data.fPAE;
8826 mHWData->mLongMode = data.enmLongMode;
8827 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8828 mHWData->mCPUCount = data.cCPUs;
8829 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8830 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8831 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8832
8833 // cpu
8834 if (mHWData->mCPUHotPlugEnabled)
8835 {
8836 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8837 it != data.llCpus.end();
8838 ++it)
8839 {
8840 const settings::Cpu &cpu = *it;
8841
8842 mHWData->mCPUAttached[cpu.ulId] = true;
8843 }
8844 }
8845
8846 // cpuid leafs
8847 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8848 it != data.llCpuIdLeafs.end();
8849 ++it)
8850 {
8851 const settings::CpuIdLeaf &leaf = *it;
8852
8853 switch (leaf.ulId)
8854 {
8855 case 0x0:
8856 case 0x1:
8857 case 0x2:
8858 case 0x3:
8859 case 0x4:
8860 case 0x5:
8861 case 0x6:
8862 case 0x7:
8863 case 0x8:
8864 case 0x9:
8865 case 0xA:
8866 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8867 break;
8868
8869 case 0x80000000:
8870 case 0x80000001:
8871 case 0x80000002:
8872 case 0x80000003:
8873 case 0x80000004:
8874 case 0x80000005:
8875 case 0x80000006:
8876 case 0x80000007:
8877 case 0x80000008:
8878 case 0x80000009:
8879 case 0x8000000A:
8880 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8881 break;
8882
8883 default:
8884 /* just ignore */
8885 break;
8886 }
8887 }
8888
8889 mHWData->mMemorySize = data.ulMemorySizeMB;
8890 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8891
8892 // boot order
8893 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8894 {
8895 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8896 if (it == data.mapBootOrder.end())
8897 mHWData->mBootOrder[i] = DeviceType_Null;
8898 else
8899 mHWData->mBootOrder[i] = it->second;
8900 }
8901
8902 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8903 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8904 mHWData->mMonitorCount = data.cMonitors;
8905 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8906 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8907 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8908 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8909 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8910 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8911 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8912 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8913 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8914 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8915 if (!data.strVideoCaptureFile.isEmpty())
8916 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8917 else
8918 mHWData->mVideoCaptureFile.setNull();
8919 mHWData->mFirmwareType = data.firmwareType;
8920 mHWData->mPointingHIDType = data.pointingHIDType;
8921 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8922 mHWData->mChipsetType = data.chipsetType;
8923 mHWData->mParavirtProvider = data.paravirtProvider;
8924 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8925 mHWData->mHPETEnabled = data.fHPETEnabled;
8926
8927 /* VRDEServer */
8928 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8929 if (FAILED(rc)) return rc;
8930
8931 /* BIOS */
8932 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8933 if (FAILED(rc)) return rc;
8934
8935 // Bandwidth control (must come before network adapters)
8936 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8937 if (FAILED(rc)) return rc;
8938
8939 /* Shared folders */
8940 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8941 it != data.usbSettings.llUSBControllers.end();
8942 ++it)
8943 {
8944 const settings::USBController &settingsCtrl = *it;
8945 ComObjPtr<USBController> newCtrl;
8946
8947 newCtrl.createObject();
8948 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8949 mUSBControllers->push_back(newCtrl);
8950 }
8951
8952 /* USB device filters */
8953 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8954 if (FAILED(rc)) return rc;
8955
8956 // network adapters
8957 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8958 size_t oldCount = mNetworkAdapters.size();
8959 if (newCount > oldCount)
8960 {
8961 mNetworkAdapters.resize(newCount);
8962 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8963 {
8964 unconst(mNetworkAdapters[slot]).createObject();
8965 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8966 }
8967 }
8968 else if (newCount < oldCount)
8969 mNetworkAdapters.resize(newCount);
8970 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8971 it != data.llNetworkAdapters.end();
8972 ++it)
8973 {
8974 const settings::NetworkAdapter &nic = *it;
8975
8976 /* slot unicity is guaranteed by XML Schema */
8977 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8978 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8979 if (FAILED(rc)) return rc;
8980 }
8981
8982 // serial ports
8983 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8984 it != data.llSerialPorts.end();
8985 ++it)
8986 {
8987 const settings::SerialPort &s = *it;
8988
8989 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8990 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8991 if (FAILED(rc)) return rc;
8992 }
8993
8994 // parallel ports (optional)
8995 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8996 it != data.llParallelPorts.end();
8997 ++it)
8998 {
8999 const settings::ParallelPort &p = *it;
9000
9001 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9002 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9003 if (FAILED(rc)) return rc;
9004 }
9005
9006 /* AudioAdapter */
9007 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9008 if (FAILED(rc)) return rc;
9009
9010 /* Shared folders */
9011 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9012 it != data.llSharedFolders.end();
9013 ++it)
9014 {
9015 const settings::SharedFolder &sf = *it;
9016
9017 ComObjPtr<SharedFolder> sharedFolder;
9018 /* Check for double entries. Not allowed! */
9019 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9020 if (SUCCEEDED(rc))
9021 return setError(VBOX_E_OBJECT_IN_USE,
9022 tr("Shared folder named '%s' already exists"),
9023 sf.strName.c_str());
9024
9025 /* Create the new shared folder. Don't break on error. This will be
9026 * reported when the machine starts. */
9027 sharedFolder.createObject();
9028 rc = sharedFolder->init(i_getMachine(),
9029 sf.strName,
9030 sf.strHostPath,
9031 RT_BOOL(sf.fWritable),
9032 RT_BOOL(sf.fAutoMount),
9033 false /* fFailOnError */);
9034 if (FAILED(rc)) return rc;
9035 mHWData->mSharedFolders.push_back(sharedFolder);
9036 }
9037
9038 // Clipboard
9039 mHWData->mClipboardMode = data.clipboardMode;
9040
9041 // drag'n'drop
9042 mHWData->mDnDMode = data.dndMode;
9043
9044 // guest settings
9045 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9046
9047 // IO settings
9048 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9049 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9050
9051 // Host PCI devices
9052 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9053 it != data.pciAttachments.end();
9054 ++it)
9055 {
9056 const settings::HostPCIDeviceAttachment &hpda = *it;
9057 ComObjPtr<PCIDeviceAttachment> pda;
9058
9059 pda.createObject();
9060 pda->i_loadSettings(this, hpda);
9061 mHWData->mPCIDeviceAssignments.push_back(pda);
9062 }
9063
9064 /*
9065 * (The following isn't really real hardware, but it lives in HWData
9066 * for reasons of convenience.)
9067 */
9068
9069#ifdef VBOX_WITH_GUEST_PROPS
9070 /* Guest properties (optional) */
9071 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9072 it != data.llGuestProperties.end();
9073 ++it)
9074 {
9075 const settings::GuestProperty &prop = *it;
9076 uint32_t fFlags = guestProp::NILFLAG;
9077 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9078 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9079 mHWData->mGuestProperties[prop.strName] = property;
9080 }
9081
9082 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9083#endif /* VBOX_WITH_GUEST_PROPS defined */
9084
9085 rc = i_loadDebugging(pDbg);
9086 if (FAILED(rc))
9087 return rc;
9088
9089 mHWData->mAutostart = *pAutostart;
9090
9091 /* default frontend */
9092 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9093 }
9094 catch(std::bad_alloc &)
9095 {
9096 return E_OUTOFMEMORY;
9097 }
9098
9099 AssertComRC(rc);
9100 return rc;
9101}
9102
9103/**
9104 * Called from Machine::loadHardware() to load the debugging settings of the
9105 * machine.
9106 *
9107 * @param pDbg Pointer to the settings.
9108 */
9109HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9110{
9111 mHWData->mDebugging = *pDbg;
9112 /* no more processing currently required, this will probably change. */
9113 return S_OK;
9114}
9115
9116/**
9117 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9118 *
9119 * @param data
9120 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9121 * @param puuidSnapshot
9122 * @return
9123 */
9124HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9125 const Guid *puuidRegistry,
9126 const Guid *puuidSnapshot)
9127{
9128 AssertReturn(!i_isSessionMachine(), E_FAIL);
9129
9130 HRESULT rc = S_OK;
9131
9132 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9133 it != data.llStorageControllers.end();
9134 ++it)
9135 {
9136 const settings::StorageController &ctlData = *it;
9137
9138 ComObjPtr<StorageController> pCtl;
9139 /* Try to find one with the name first. */
9140 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9141 if (SUCCEEDED(rc))
9142 return setError(VBOX_E_OBJECT_IN_USE,
9143 tr("Storage controller named '%s' already exists"),
9144 ctlData.strName.c_str());
9145
9146 pCtl.createObject();
9147 rc = pCtl->init(this,
9148 ctlData.strName,
9149 ctlData.storageBus,
9150 ctlData.ulInstance,
9151 ctlData.fBootable);
9152 if (FAILED(rc)) return rc;
9153
9154 mStorageControllers->push_back(pCtl);
9155
9156 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9157 if (FAILED(rc)) return rc;
9158
9159 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9160 if (FAILED(rc)) return rc;
9161
9162 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9163 if (FAILED(rc)) return rc;
9164
9165 /* Set IDE emulation settings (only for AHCI controller). */
9166 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9167 {
9168 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9169 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9170 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9171 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9172 )
9173 return rc;
9174 }
9175
9176 /* Load the attached devices now. */
9177 rc = i_loadStorageDevices(pCtl,
9178 ctlData,
9179 puuidRegistry,
9180 puuidSnapshot);
9181 if (FAILED(rc)) return rc;
9182 }
9183
9184 return S_OK;
9185}
9186
9187/**
9188 * Called from i_loadStorageControllers for a controller's devices.
9189 *
9190 * @param aStorageController
9191 * @param data
9192 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9193 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9194 * @return
9195 */
9196HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9197 const settings::StorageController &data,
9198 const Guid *puuidRegistry,
9199 const Guid *puuidSnapshot)
9200{
9201 HRESULT rc = S_OK;
9202
9203 /* paranoia: detect duplicate attachments */
9204 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9205 it != data.llAttachedDevices.end();
9206 ++it)
9207 {
9208 const settings::AttachedDevice &ad = *it;
9209
9210 for (settings::AttachedDevicesList::const_iterator it2 = it;
9211 it2 != data.llAttachedDevices.end();
9212 ++it2)
9213 {
9214 if (it == it2)
9215 continue;
9216
9217 const settings::AttachedDevice &ad2 = *it2;
9218
9219 if ( ad.lPort == ad2.lPort
9220 && ad.lDevice == ad2.lDevice)
9221 {
9222 return setError(E_FAIL,
9223 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9224 aStorageController->i_getName().c_str(),
9225 ad.lPort,
9226 ad.lDevice,
9227 mUserData->s.strName.c_str());
9228 }
9229 }
9230 }
9231
9232 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9233 it != data.llAttachedDevices.end();
9234 ++it)
9235 {
9236 const settings::AttachedDevice &dev = *it;
9237 ComObjPtr<Medium> medium;
9238
9239 switch (dev.deviceType)
9240 {
9241 case DeviceType_Floppy:
9242 case DeviceType_DVD:
9243 if (dev.strHostDriveSrc.isNotEmpty())
9244 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9245 false /* fRefresh */, medium);
9246 else
9247 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9248 dev.uuid,
9249 false /* fRefresh */,
9250 false /* aSetError */,
9251 medium);
9252 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9253 // This is not an error. The host drive or UUID might have vanished, so just go
9254 // ahead without this removeable medium attachment
9255 rc = S_OK;
9256 break;
9257
9258 case DeviceType_HardDisk:
9259 {
9260 /* find a hard disk by UUID */
9261 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9262 if (FAILED(rc))
9263 {
9264 if (i_isSnapshotMachine())
9265 {
9266 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9267 // so the user knows that the bad disk is in a snapshot somewhere
9268 com::ErrorInfo info;
9269 return setError(E_FAIL,
9270 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9271 puuidSnapshot->raw(),
9272 info.getText().raw());
9273 }
9274 else
9275 return rc;
9276 }
9277
9278 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9279
9280 if (medium->i_getType() == MediumType_Immutable)
9281 {
9282 if (i_isSnapshotMachine())
9283 return setError(E_FAIL,
9284 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9285 "of the virtual machine '%s' ('%s')"),
9286 medium->i_getLocationFull().c_str(),
9287 dev.uuid.raw(),
9288 puuidSnapshot->raw(),
9289 mUserData->s.strName.c_str(),
9290 mData->m_strConfigFileFull.c_str());
9291
9292 return setError(E_FAIL,
9293 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9294 medium->i_getLocationFull().c_str(),
9295 dev.uuid.raw(),
9296 mUserData->s.strName.c_str(),
9297 mData->m_strConfigFileFull.c_str());
9298 }
9299
9300 if (medium->i_getType() == MediumType_MultiAttach)
9301 {
9302 if (i_isSnapshotMachine())
9303 return setError(E_FAIL,
9304 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9305 "of the virtual machine '%s' ('%s')"),
9306 medium->i_getLocationFull().c_str(),
9307 dev.uuid.raw(),
9308 puuidSnapshot->raw(),
9309 mUserData->s.strName.c_str(),
9310 mData->m_strConfigFileFull.c_str());
9311
9312 return setError(E_FAIL,
9313 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9314 medium->i_getLocationFull().c_str(),
9315 dev.uuid.raw(),
9316 mUserData->s.strName.c_str(),
9317 mData->m_strConfigFileFull.c_str());
9318 }
9319
9320 if ( !i_isSnapshotMachine()
9321 && medium->i_getChildren().size() != 0
9322 )
9323 return setError(E_FAIL,
9324 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9325 "because it has %d differencing child hard disks"),
9326 medium->i_getLocationFull().c_str(),
9327 dev.uuid.raw(),
9328 mUserData->s.strName.c_str(),
9329 mData->m_strConfigFileFull.c_str(),
9330 medium->i_getChildren().size());
9331
9332 if (i_findAttachment(mMediaData->mAttachments,
9333 medium))
9334 return setError(E_FAIL,
9335 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9336 medium->i_getLocationFull().c_str(),
9337 dev.uuid.raw(),
9338 mUserData->s.strName.c_str(),
9339 mData->m_strConfigFileFull.c_str());
9340
9341 break;
9342 }
9343
9344 default:
9345 return setError(E_FAIL,
9346 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9347 medium->i_getLocationFull().c_str(),
9348 mUserData->s.strName.c_str(),
9349 mData->m_strConfigFileFull.c_str());
9350 }
9351
9352 if (FAILED(rc))
9353 break;
9354
9355 /* Bandwidth groups are loaded at this point. */
9356 ComObjPtr<BandwidthGroup> pBwGroup;
9357
9358 if (!dev.strBwGroup.isEmpty())
9359 {
9360 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9361 if (FAILED(rc))
9362 return setError(E_FAIL,
9363 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9364 medium->i_getLocationFull().c_str(),
9365 dev.strBwGroup.c_str(),
9366 mUserData->s.strName.c_str(),
9367 mData->m_strConfigFileFull.c_str());
9368 pBwGroup->i_reference();
9369 }
9370
9371 const Bstr controllerName = aStorageController->i_getName();
9372 ComObjPtr<MediumAttachment> pAttachment;
9373 pAttachment.createObject();
9374 rc = pAttachment->init(this,
9375 medium,
9376 controllerName,
9377 dev.lPort,
9378 dev.lDevice,
9379 dev.deviceType,
9380 false,
9381 dev.fPassThrough,
9382 dev.fTempEject,
9383 dev.fNonRotational,
9384 dev.fDiscard,
9385 dev.fHotPluggable,
9386 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9387 if (FAILED(rc)) break;
9388
9389 /* associate the medium with this machine and snapshot */
9390 if (!medium.isNull())
9391 {
9392 AutoCaller medCaller(medium);
9393 if (FAILED(medCaller.rc())) return medCaller.rc();
9394 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9395
9396 if (i_isSnapshotMachine())
9397 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9398 else
9399 rc = medium->i_addBackReference(mData->mUuid);
9400 /* If the medium->addBackReference fails it sets an appropriate
9401 * error message, so no need to do any guesswork here. */
9402
9403 if (puuidRegistry)
9404 // caller wants registry ID to be set on all attached media (OVF import case)
9405 medium->i_addRegistry(*puuidRegistry);
9406 }
9407
9408 if (FAILED(rc))
9409 break;
9410
9411 /* back up mMediaData to let registeredInit() properly rollback on failure
9412 * (= limited accessibility) */
9413 i_setModified(IsModified_Storage);
9414 mMediaData.backup();
9415 mMediaData->mAttachments.push_back(pAttachment);
9416 }
9417
9418 return rc;
9419}
9420
9421/**
9422 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9423 *
9424 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9425 * @param aSnapshot where to return the found snapshot
9426 * @param aSetError true to set extended error info on failure
9427 */
9428HRESULT Machine::i_findSnapshotById(const Guid &aId,
9429 ComObjPtr<Snapshot> &aSnapshot,
9430 bool aSetError /* = false */)
9431{
9432 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9433
9434 if (!mData->mFirstSnapshot)
9435 {
9436 if (aSetError)
9437 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9438 return E_FAIL;
9439 }
9440
9441 if (aId.isZero())
9442 aSnapshot = mData->mFirstSnapshot;
9443 else
9444 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9445
9446 if (!aSnapshot)
9447 {
9448 if (aSetError)
9449 return setError(E_FAIL,
9450 tr("Could not find a snapshot with UUID {%s}"),
9451 aId.toString().c_str());
9452 return E_FAIL;
9453 }
9454
9455 return S_OK;
9456}
9457
9458/**
9459 * Returns the snapshot with the given name or fails of no such snapshot.
9460 *
9461 * @param aName snapshot name to find
9462 * @param aSnapshot where to return the found snapshot
9463 * @param aSetError true to set extended error info on failure
9464 */
9465HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9466 ComObjPtr<Snapshot> &aSnapshot,
9467 bool aSetError /* = false */)
9468{
9469 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9470
9471 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9472
9473 if (!mData->mFirstSnapshot)
9474 {
9475 if (aSetError)
9476 return setError(VBOX_E_OBJECT_NOT_FOUND,
9477 tr("This machine does not have any snapshots"));
9478 return VBOX_E_OBJECT_NOT_FOUND;
9479 }
9480
9481 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9482
9483 if (!aSnapshot)
9484 {
9485 if (aSetError)
9486 return setError(VBOX_E_OBJECT_NOT_FOUND,
9487 tr("Could not find a snapshot named '%s'"), strName.c_str());
9488 return VBOX_E_OBJECT_NOT_FOUND;
9489 }
9490
9491 return S_OK;
9492}
9493
9494/**
9495 * Returns a storage controller object with the given name.
9496 *
9497 * @param aName storage controller name to find
9498 * @param aStorageController where to return the found storage controller
9499 * @param aSetError true to set extended error info on failure
9500 */
9501HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9502 ComObjPtr<StorageController> &aStorageController,
9503 bool aSetError /* = false */)
9504{
9505 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9506
9507 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9508 it != mStorageControllers->end();
9509 ++it)
9510 {
9511 if ((*it)->i_getName() == aName)
9512 {
9513 aStorageController = (*it);
9514 return S_OK;
9515 }
9516 }
9517
9518 if (aSetError)
9519 return setError(VBOX_E_OBJECT_NOT_FOUND,
9520 tr("Could not find a storage controller named '%s'"),
9521 aName.c_str());
9522 return VBOX_E_OBJECT_NOT_FOUND;
9523}
9524
9525/**
9526 * Returns a USB controller object with the given name.
9527 *
9528 * @param aName USB controller name to find
9529 * @param aUSBController where to return the found USB controller
9530 * @param aSetError true to set extended error info on failure
9531 */
9532HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9533 ComObjPtr<USBController> &aUSBController,
9534 bool aSetError /* = false */)
9535{
9536 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9537
9538 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9539 it != mUSBControllers->end();
9540 ++it)
9541 {
9542 if ((*it)->i_getName() == aName)
9543 {
9544 aUSBController = (*it);
9545 return S_OK;
9546 }
9547 }
9548
9549 if (aSetError)
9550 return setError(VBOX_E_OBJECT_NOT_FOUND,
9551 tr("Could not find a storage controller named '%s'"),
9552 aName.c_str());
9553 return VBOX_E_OBJECT_NOT_FOUND;
9554}
9555
9556/**
9557 * Returns the number of USB controller instance of the given type.
9558 *
9559 * @param enmType USB controller type.
9560 */
9561ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9562{
9563 ULONG cCtrls = 0;
9564
9565 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9566 it != mUSBControllers->end();
9567 ++it)
9568 {
9569 if ((*it)->i_getControllerType() == enmType)
9570 cCtrls++;
9571 }
9572
9573 return cCtrls;
9574}
9575
9576HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9577 MediaData::AttachmentList &atts)
9578{
9579 AutoCaller autoCaller(this);
9580 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9581
9582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9583
9584 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9585 it != mMediaData->mAttachments.end();
9586 ++it)
9587 {
9588 const ComObjPtr<MediumAttachment> &pAtt = *it;
9589 // should never happen, but deal with NULL pointers in the list.
9590 AssertStmt(!pAtt.isNull(), continue);
9591
9592 // getControllerName() needs caller+read lock
9593 AutoCaller autoAttCaller(pAtt);
9594 if (FAILED(autoAttCaller.rc()))
9595 {
9596 atts.clear();
9597 return autoAttCaller.rc();
9598 }
9599 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9600
9601 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9602 atts.push_back(pAtt);
9603 }
9604
9605 return S_OK;
9606}
9607
9608
9609/**
9610 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9611 * file if the machine name was changed and about creating a new settings file
9612 * if this is a new machine.
9613 *
9614 * @note Must be never called directly but only from #saveSettings().
9615 */
9616HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9617{
9618 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9619
9620 HRESULT rc = S_OK;
9621
9622 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9623
9624 /// @todo need to handle primary group change, too
9625
9626 /* attempt to rename the settings file if machine name is changed */
9627 if ( mUserData->s.fNameSync
9628 && mUserData.isBackedUp()
9629 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9630 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9631 )
9632 {
9633 bool dirRenamed = false;
9634 bool fileRenamed = false;
9635
9636 Utf8Str configFile, newConfigFile;
9637 Utf8Str configFilePrev, newConfigFilePrev;
9638 Utf8Str configDir, newConfigDir;
9639
9640 do
9641 {
9642 int vrc = VINF_SUCCESS;
9643
9644 Utf8Str name = mUserData.backedUpData()->s.strName;
9645 Utf8Str newName = mUserData->s.strName;
9646 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9647 if (group == "/")
9648 group.setNull();
9649 Utf8Str newGroup = mUserData->s.llGroups.front();
9650 if (newGroup == "/")
9651 newGroup.setNull();
9652
9653 configFile = mData->m_strConfigFileFull;
9654
9655 /* first, rename the directory if it matches the group and machine name */
9656 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9657 group.c_str(), RTPATH_DELIMITER, name.c_str());
9658 /** @todo hack, make somehow use of ComposeMachineFilename */
9659 if (mUserData->s.fDirectoryIncludesUUID)
9660 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9661 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9662 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9663 /** @todo hack, make somehow use of ComposeMachineFilename */
9664 if (mUserData->s.fDirectoryIncludesUUID)
9665 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9666 configDir = configFile;
9667 configDir.stripFilename();
9668 newConfigDir = configDir;
9669 if ( configDir.length() >= groupPlusName.length()
9670 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9671 groupPlusName.c_str()))
9672 {
9673 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9674 Utf8Str newConfigBaseDir(newConfigDir);
9675 newConfigDir.append(newGroupPlusName);
9676 /* consistency: use \ if appropriate on the platform */
9677 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9678 /* new dir and old dir cannot be equal here because of 'if'
9679 * above and because name != newName */
9680 Assert(configDir != newConfigDir);
9681 if (!fSettingsFileIsNew)
9682 {
9683 /* perform real rename only if the machine is not new */
9684 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9685 if ( vrc == VERR_FILE_NOT_FOUND
9686 || vrc == VERR_PATH_NOT_FOUND)
9687 {
9688 /* create the parent directory, then retry renaming */
9689 Utf8Str parent(newConfigDir);
9690 parent.stripFilename();
9691 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9692 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9693 }
9694 if (RT_FAILURE(vrc))
9695 {
9696 rc = setError(E_FAIL,
9697 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9698 configDir.c_str(),
9699 newConfigDir.c_str(),
9700 vrc);
9701 break;
9702 }
9703 /* delete subdirectories which are no longer needed */
9704 Utf8Str dir(configDir);
9705 dir.stripFilename();
9706 while (dir != newConfigBaseDir && dir != ".")
9707 {
9708 vrc = RTDirRemove(dir.c_str());
9709 if (RT_FAILURE(vrc))
9710 break;
9711 dir.stripFilename();
9712 }
9713 dirRenamed = true;
9714 }
9715 }
9716
9717 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9718 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9719
9720 /* then try to rename the settings file itself */
9721 if (newConfigFile != configFile)
9722 {
9723 /* get the path to old settings file in renamed directory */
9724 configFile = Utf8StrFmt("%s%c%s",
9725 newConfigDir.c_str(),
9726 RTPATH_DELIMITER,
9727 RTPathFilename(configFile.c_str()));
9728 if (!fSettingsFileIsNew)
9729 {
9730 /* perform real rename only if the machine is not new */
9731 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9732 if (RT_FAILURE(vrc))
9733 {
9734 rc = setError(E_FAIL,
9735 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9736 configFile.c_str(),
9737 newConfigFile.c_str(),
9738 vrc);
9739 break;
9740 }
9741 fileRenamed = true;
9742 configFilePrev = configFile;
9743 configFilePrev += "-prev";
9744 newConfigFilePrev = newConfigFile;
9745 newConfigFilePrev += "-prev";
9746 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9747 }
9748 }
9749
9750 // update m_strConfigFileFull amd mConfigFile
9751 mData->m_strConfigFileFull = newConfigFile;
9752 // compute the relative path too
9753 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9754
9755 // store the old and new so that VirtualBox::i_saveSettings() can update
9756 // the media registry
9757 if ( mData->mRegistered
9758 && (configDir != newConfigDir || configFile != newConfigFile))
9759 {
9760 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9761
9762 if (pfNeedsGlobalSaveSettings)
9763 *pfNeedsGlobalSaveSettings = true;
9764 }
9765
9766 // in the saved state file path, replace the old directory with the new directory
9767 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9768 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9769
9770 // and do the same thing for the saved state file paths of all the online snapshots
9771 if (mData->mFirstSnapshot)
9772 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9773 newConfigDir.c_str());
9774 }
9775 while (0);
9776
9777 if (FAILED(rc))
9778 {
9779 /* silently try to rename everything back */
9780 if (fileRenamed)
9781 {
9782 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9783 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9784 }
9785 if (dirRenamed)
9786 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9787 }
9788
9789 if (FAILED(rc)) return rc;
9790 }
9791
9792 if (fSettingsFileIsNew)
9793 {
9794 /* create a virgin config file */
9795 int vrc = VINF_SUCCESS;
9796
9797 /* ensure the settings directory exists */
9798 Utf8Str path(mData->m_strConfigFileFull);
9799 path.stripFilename();
9800 if (!RTDirExists(path.c_str()))
9801 {
9802 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9803 if (RT_FAILURE(vrc))
9804 {
9805 return setError(E_FAIL,
9806 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9807 path.c_str(),
9808 vrc);
9809 }
9810 }
9811
9812 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9813 path = Utf8Str(mData->m_strConfigFileFull);
9814 RTFILE f = NIL_RTFILE;
9815 vrc = RTFileOpen(&f, path.c_str(),
9816 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9817 if (RT_FAILURE(vrc))
9818 return setError(E_FAIL,
9819 tr("Could not create the settings file '%s' (%Rrc)"),
9820 path.c_str(),
9821 vrc);
9822 RTFileClose(f);
9823 }
9824
9825 return rc;
9826}
9827
9828/**
9829 * Saves and commits machine data, user data and hardware data.
9830 *
9831 * Note that on failure, the data remains uncommitted.
9832 *
9833 * @a aFlags may combine the following flags:
9834 *
9835 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9836 * Used when saving settings after an operation that makes them 100%
9837 * correspond to the settings from the current snapshot.
9838 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9839 * #isReallyModified() returns false. This is necessary for cases when we
9840 * change machine data directly, not through the backup()/commit() mechanism.
9841 * - SaveS_Force: settings will be saved without doing a deep compare of the
9842 * settings structures. This is used when this is called because snapshots
9843 * have changed to avoid the overhead of the deep compare.
9844 *
9845 * @note Must be called from under this object's write lock. Locks children for
9846 * writing.
9847 *
9848 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9849 * initialized to false and that will be set to true by this function if
9850 * the caller must invoke VirtualBox::i_saveSettings() because the global
9851 * settings have changed. This will happen if a machine rename has been
9852 * saved and the global machine and media registries will therefore need
9853 * updating.
9854 */
9855HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9856 int aFlags /*= 0*/)
9857{
9858 LogFlowThisFuncEnter();
9859
9860 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9861
9862 /* make sure child objects are unable to modify the settings while we are
9863 * saving them */
9864 i_ensureNoStateDependencies();
9865
9866 AssertReturn(!i_isSnapshotMachine(),
9867 E_FAIL);
9868
9869 HRESULT rc = S_OK;
9870 bool fNeedsWrite = false;
9871
9872 /* First, prepare to save settings. It will care about renaming the
9873 * settings directory and file if the machine name was changed and about
9874 * creating a new settings file if this is a new machine. */
9875 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9876 if (FAILED(rc)) return rc;
9877
9878 // keep a pointer to the current settings structures
9879 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9880 settings::MachineConfigFile *pNewConfig = NULL;
9881
9882 try
9883 {
9884 // make a fresh one to have everyone write stuff into
9885 pNewConfig = new settings::MachineConfigFile(NULL);
9886 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9887
9888 // now go and copy all the settings data from COM to the settings structures
9889 // (this calles i_saveSettings() on all the COM objects in the machine)
9890 i_copyMachineDataToSettings(*pNewConfig);
9891
9892 if (aFlags & SaveS_ResetCurStateModified)
9893 {
9894 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9895 mData->mCurrentStateModified = FALSE;
9896 fNeedsWrite = true; // always, no need to compare
9897 }
9898 else if (aFlags & SaveS_Force)
9899 {
9900 fNeedsWrite = true; // always, no need to compare
9901 }
9902 else
9903 {
9904 if (!mData->mCurrentStateModified)
9905 {
9906 // do a deep compare of the settings that we just saved with the settings
9907 // previously stored in the config file; this invokes MachineConfigFile::operator==
9908 // which does a deep compare of all the settings, which is expensive but less expensive
9909 // than writing out XML in vain
9910 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9911
9912 // could still be modified if any settings changed
9913 mData->mCurrentStateModified = fAnySettingsChanged;
9914
9915 fNeedsWrite = fAnySettingsChanged;
9916 }
9917 else
9918 fNeedsWrite = true;
9919 }
9920
9921 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9922
9923 if (fNeedsWrite)
9924 // now spit it all out!
9925 pNewConfig->write(mData->m_strConfigFileFull);
9926
9927 mData->pMachineConfigFile = pNewConfig;
9928 delete pOldConfig;
9929 i_commit();
9930
9931 // after saving settings, we are no longer different from the XML on disk
9932 mData->flModifications = 0;
9933 }
9934 catch (HRESULT err)
9935 {
9936 // we assume that error info is set by the thrower
9937 rc = err;
9938
9939 // restore old config
9940 delete pNewConfig;
9941 mData->pMachineConfigFile = pOldConfig;
9942 }
9943 catch (...)
9944 {
9945 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9946 }
9947
9948 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9949 {
9950 /* Fire the data change event, even on failure (since we've already
9951 * committed all data). This is done only for SessionMachines because
9952 * mutable Machine instances are always not registered (i.e. private
9953 * to the client process that creates them) and thus don't need to
9954 * inform callbacks. */
9955 if (i_isSessionMachine())
9956 mParent->i_onMachineDataChange(mData->mUuid);
9957 }
9958
9959 LogFlowThisFunc(("rc=%08X\n", rc));
9960 LogFlowThisFuncLeave();
9961 return rc;
9962}
9963
9964/**
9965 * Implementation for saving the machine settings into the given
9966 * settings::MachineConfigFile instance. This copies machine extradata
9967 * from the previous machine config file in the instance data, if any.
9968 *
9969 * This gets called from two locations:
9970 *
9971 * -- Machine::i_saveSettings(), during the regular XML writing;
9972 *
9973 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9974 * exported to OVF and we write the VirtualBox proprietary XML
9975 * into a <vbox:Machine> tag.
9976 *
9977 * This routine fills all the fields in there, including snapshots, *except*
9978 * for the following:
9979 *
9980 * -- fCurrentStateModified. There is some special logic associated with that.
9981 *
9982 * The caller can then call MachineConfigFile::write() or do something else
9983 * with it.
9984 *
9985 * Caller must hold the machine lock!
9986 *
9987 * This throws XML errors and HRESULT, so the caller must have a catch block!
9988 */
9989void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9990{
9991 // deep copy extradata
9992 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9993
9994 config.uuid = mData->mUuid;
9995
9996 // copy name, description, OS type, teleport, UTC etc.
9997 config.machineUserData = mUserData->s;
9998
9999 // Encode the Icon Override data from Machine and store on config userdata.
10000 std::vector<BYTE> iconByte;
10001 getIcon(iconByte);
10002 ssize_t cbData = iconByte.size();
10003 if (cbData > 0)
10004 {
10005 ssize_t cchOut = RTBase64EncodedLength(cbData);
10006 Utf8Str strIconData;
10007 strIconData.reserve(cchOut+1);
10008 int vrc = RTBase64Encode(&iconByte.front(), cbData,
10009 strIconData.mutableRaw(), strIconData.capacity(),
10010 NULL);
10011 if (RT_FAILURE(vrc))
10012 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10013 strIconData.jolt();
10014 config.machineUserData.ovIcon = strIconData;
10015 }
10016 else
10017 config.machineUserData.ovIcon.setNull();
10018
10019 if ( mData->mMachineState == MachineState_Saved
10020 || mData->mMachineState == MachineState_Restoring
10021 // when doing certain snapshot operations we may or may not have
10022 // a saved state in the current state, so keep everything as is
10023 || ( ( mData->mMachineState == MachineState_Snapshotting
10024 || mData->mMachineState == MachineState_DeletingSnapshot
10025 || mData->mMachineState == MachineState_RestoringSnapshot)
10026 && (!mSSData->strStateFilePath.isEmpty())
10027 )
10028 )
10029 {
10030 Assert(!mSSData->strStateFilePath.isEmpty());
10031 /* try to make the file name relative to the settings file dir */
10032 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10033 }
10034 else
10035 {
10036 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10037 config.strStateFile.setNull();
10038 }
10039
10040 if (mData->mCurrentSnapshot)
10041 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10042 else
10043 config.uuidCurrentSnapshot.clear();
10044
10045 config.timeLastStateChange = mData->mLastStateChange;
10046 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10047 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10048
10049 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10050 if (FAILED(rc)) throw rc;
10051
10052 rc = i_saveStorageControllers(config.storageMachine);
10053 if (FAILED(rc)) throw rc;
10054
10055 // save machine's media registry if this is VirtualBox 4.0 or later
10056 if (config.canHaveOwnMediaRegistry())
10057 {
10058 // determine machine folder
10059 Utf8Str strMachineFolder = i_getSettingsFileFull();
10060 strMachineFolder.stripFilename();
10061 mParent->i_saveMediaRegistry(config.mediaRegistry,
10062 i_getId(), // only media with registry ID == machine UUID
10063 strMachineFolder);
10064 // this throws HRESULT
10065 }
10066
10067 // save snapshots
10068 rc = i_saveAllSnapshots(config);
10069 if (FAILED(rc)) throw rc;
10070}
10071
10072/**
10073 * Saves all snapshots of the machine into the given machine config file. Called
10074 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10075 * @param config
10076 * @return
10077 */
10078HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10079{
10080 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10081
10082 HRESULT rc = S_OK;
10083
10084 try
10085 {
10086 config.llFirstSnapshot.clear();
10087
10088 if (mData->mFirstSnapshot)
10089 {
10090 // the settings use a list for "the first snapshot"
10091 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
10092
10093 // get reference to the snapshot on the list and work on that
10094 // element straight in the list to avoid excessive copying later
10095 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10096 if (FAILED(rc)) throw rc;
10097 }
10098
10099// if (mType == IsSessionMachine)
10100// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10101
10102 }
10103 catch (HRESULT err)
10104 {
10105 /* we assume that error info is set by the thrower */
10106 rc = err;
10107 }
10108 catch (...)
10109 {
10110 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10111 }
10112
10113 return rc;
10114}
10115
10116/**
10117 * Saves the VM hardware configuration. It is assumed that the
10118 * given node is empty.
10119 *
10120 * @param data Reference to the settings object for the hardware config.
10121 * @param pDbg Pointer to the settings object for the debugging config
10122 * which happens to live in mHWData.
10123 * @param pAutostart Pointer to the settings object for the autostart config
10124 * which happens to live in mHWData.
10125 */
10126HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10127 settings::Autostart *pAutostart)
10128{
10129 HRESULT rc = S_OK;
10130
10131 try
10132 {
10133 /* The hardware version attribute (optional).
10134 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10135 if ( mHWData->mHWVersion == "1"
10136 && mSSData->strStateFilePath.isEmpty()
10137 )
10138 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10139 other point needs to be found where this can be done. */
10140
10141 data.strVersion = mHWData->mHWVersion;
10142 data.uuid = mHWData->mHardwareUUID;
10143
10144 // CPU
10145 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10146 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10147 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10148 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10149 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10150 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10151 data.fPAE = !!mHWData->mPAEEnabled;
10152 data.enmLongMode = mHWData->mLongMode;
10153 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10154 data.cCPUs = mHWData->mCPUCount;
10155 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10156 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10157 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10158
10159 data.llCpus.clear();
10160 if (data.fCpuHotPlug)
10161 {
10162 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10163 {
10164 if (mHWData->mCPUAttached[idx])
10165 {
10166 settings::Cpu cpu;
10167 cpu.ulId = idx;
10168 data.llCpus.push_back(cpu);
10169 }
10170 }
10171 }
10172
10173 /* Standard and Extended CPUID leafs. */
10174 data.llCpuIdLeafs.clear();
10175 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10176 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10177 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10178 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10179 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10180 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10181
10182 // memory
10183 data.ulMemorySizeMB = mHWData->mMemorySize;
10184 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10185
10186 // firmware
10187 data.firmwareType = mHWData->mFirmwareType;
10188
10189 // HID
10190 data.pointingHIDType = mHWData->mPointingHIDType;
10191 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10192
10193 // chipset
10194 data.chipsetType = mHWData->mChipsetType;
10195
10196 // paravirt
10197 data.paravirtProvider = mHWData->mParavirtProvider;
10198
10199
10200 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10201
10202 // HPET
10203 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10204
10205 // boot order
10206 data.mapBootOrder.clear();
10207 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10208 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10209
10210 // display
10211 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10212 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10213 data.cMonitors = mHWData->mMonitorCount;
10214 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10215 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10216 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10217 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10218 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10219 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10220 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10221 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10222 {
10223 if (mHWData->maVideoCaptureScreens[i])
10224 ASMBitSet(&data.u64VideoCaptureScreens, i);
10225 else
10226 ASMBitClear(&data.u64VideoCaptureScreens, i);
10227 }
10228 /* store relative video capture file if possible */
10229 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10230
10231 /* VRDEServer settings (optional) */
10232 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10233 if (FAILED(rc)) throw rc;
10234
10235 /* BIOS (required) */
10236 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10237 if (FAILED(rc)) throw rc;
10238
10239 /* USB Controller (required) */
10240 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10241 {
10242 ComObjPtr<USBController> ctrl = *it;
10243 settings::USBController settingsCtrl;
10244
10245 settingsCtrl.strName = ctrl->i_getName();
10246 settingsCtrl.enmType = ctrl->i_getControllerType();
10247
10248 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10249 }
10250
10251 /* USB device filters (required) */
10252 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10253 if (FAILED(rc)) throw rc;
10254
10255 /* Network adapters (required) */
10256 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10257 data.llNetworkAdapters.clear();
10258 /* Write out only the nominal number of network adapters for this
10259 * chipset type. Since Machine::commit() hasn't been called there
10260 * may be extra NIC settings in the vector. */
10261 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10262 {
10263 settings::NetworkAdapter nic;
10264 nic.ulSlot = (uint32_t)slot;
10265 /* paranoia check... must not be NULL, but must not crash either. */
10266 if (mNetworkAdapters[slot])
10267 {
10268 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10269 if (FAILED(rc)) throw rc;
10270
10271 data.llNetworkAdapters.push_back(nic);
10272 }
10273 }
10274
10275 /* Serial ports */
10276 data.llSerialPorts.clear();
10277 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10278 {
10279 settings::SerialPort s;
10280 s.ulSlot = slot;
10281 rc = mSerialPorts[slot]->i_saveSettings(s);
10282 if (FAILED(rc)) return rc;
10283
10284 data.llSerialPorts.push_back(s);
10285 }
10286
10287 /* Parallel ports */
10288 data.llParallelPorts.clear();
10289 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10290 {
10291 settings::ParallelPort p;
10292 p.ulSlot = slot;
10293 rc = mParallelPorts[slot]->i_saveSettings(p);
10294 if (FAILED(rc)) return rc;
10295
10296 data.llParallelPorts.push_back(p);
10297 }
10298
10299 /* Audio adapter */
10300 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10301 if (FAILED(rc)) return rc;
10302
10303 /* Shared folders */
10304 data.llSharedFolders.clear();
10305 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10306 it != mHWData->mSharedFolders.end();
10307 ++it)
10308 {
10309 SharedFolder *pSF = *it;
10310 AutoCaller sfCaller(pSF);
10311 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10312 settings::SharedFolder sf;
10313 sf.strName = pSF->i_getName();
10314 sf.strHostPath = pSF->i_getHostPath();
10315 sf.fWritable = !!pSF->i_isWritable();
10316 sf.fAutoMount = !!pSF->i_isAutoMounted();
10317
10318 data.llSharedFolders.push_back(sf);
10319 }
10320
10321 // clipboard
10322 data.clipboardMode = mHWData->mClipboardMode;
10323
10324 // drag'n'drop
10325 data.dndMode = mHWData->mDnDMode;
10326
10327 /* Guest */
10328 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10329
10330 // IO settings
10331 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10332 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10333
10334 /* BandwidthControl (required) */
10335 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10336 if (FAILED(rc)) throw rc;
10337
10338 /* Host PCI devices */
10339 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10340 it != mHWData->mPCIDeviceAssignments.end();
10341 ++it)
10342 {
10343 ComObjPtr<PCIDeviceAttachment> pda = *it;
10344 settings::HostPCIDeviceAttachment hpda;
10345
10346 rc = pda->i_saveSettings(hpda);
10347 if (FAILED(rc)) throw rc;
10348
10349 data.pciAttachments.push_back(hpda);
10350 }
10351
10352
10353 // guest properties
10354 data.llGuestProperties.clear();
10355#ifdef VBOX_WITH_GUEST_PROPS
10356 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10357 it != mHWData->mGuestProperties.end();
10358 ++it)
10359 {
10360 HWData::GuestProperty property = it->second;
10361
10362 /* Remove transient guest properties at shutdown unless we
10363 * are saving state */
10364 if ( ( mData->mMachineState == MachineState_PoweredOff
10365 || mData->mMachineState == MachineState_Aborted
10366 || mData->mMachineState == MachineState_Teleported)
10367 && ( property.mFlags & guestProp::TRANSIENT
10368 || property.mFlags & guestProp::TRANSRESET))
10369 continue;
10370 settings::GuestProperty prop;
10371 prop.strName = it->first;
10372 prop.strValue = property.strValue;
10373 prop.timestamp = property.mTimestamp;
10374 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10375 guestProp::writeFlags(property.mFlags, szFlags);
10376 prop.strFlags = szFlags;
10377
10378 data.llGuestProperties.push_back(prop);
10379 }
10380
10381 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10382 /* I presume this doesn't require a backup(). */
10383 mData->mGuestPropertiesModified = FALSE;
10384#endif /* VBOX_WITH_GUEST_PROPS defined */
10385
10386 *pDbg = mHWData->mDebugging;
10387 *pAutostart = mHWData->mAutostart;
10388
10389 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10390 }
10391 catch(std::bad_alloc &)
10392 {
10393 return E_OUTOFMEMORY;
10394 }
10395
10396 AssertComRC(rc);
10397 return rc;
10398}
10399
10400/**
10401 * Saves the storage controller configuration.
10402 *
10403 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10404 */
10405HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10406{
10407 data.llStorageControllers.clear();
10408
10409 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10410 it != mStorageControllers->end();
10411 ++it)
10412 {
10413 HRESULT rc;
10414 ComObjPtr<StorageController> pCtl = *it;
10415
10416 settings::StorageController ctl;
10417 ctl.strName = pCtl->i_getName();
10418 ctl.controllerType = pCtl->i_getControllerType();
10419 ctl.storageBus = pCtl->i_getStorageBus();
10420 ctl.ulInstance = pCtl->i_getInstance();
10421 ctl.fBootable = pCtl->i_getBootable();
10422
10423 /* Save the port count. */
10424 ULONG portCount;
10425 rc = pCtl->COMGETTER(PortCount)(&portCount);
10426 ComAssertComRCRet(rc, rc);
10427 ctl.ulPortCount = portCount;
10428
10429 /* Save fUseHostIOCache */
10430 BOOL fUseHostIOCache;
10431 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10432 ComAssertComRCRet(rc, rc);
10433 ctl.fUseHostIOCache = !!fUseHostIOCache;
10434
10435 /* Save IDE emulation settings. */
10436 if (ctl.controllerType == StorageControllerType_IntelAhci)
10437 {
10438 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10439 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10440 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10441 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10442 )
10443 ComAssertComRCRet(rc, rc);
10444 }
10445
10446 /* save the devices now. */
10447 rc = i_saveStorageDevices(pCtl, ctl);
10448 ComAssertComRCRet(rc, rc);
10449
10450 data.llStorageControllers.push_back(ctl);
10451 }
10452
10453 return S_OK;
10454}
10455
10456/**
10457 * Saves the hard disk configuration.
10458 */
10459HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10460 settings::StorageController &data)
10461{
10462 MediaData::AttachmentList atts;
10463
10464 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10465 if (FAILED(rc)) return rc;
10466
10467 data.llAttachedDevices.clear();
10468 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10469 it != atts.end();
10470 ++it)
10471 {
10472 settings::AttachedDevice dev;
10473 IMediumAttachment *iA = *it;
10474 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10475 Medium *pMedium = pAttach->i_getMedium();
10476
10477 dev.deviceType = pAttach->i_getType();
10478 dev.lPort = pAttach->i_getPort();
10479 dev.lDevice = pAttach->i_getDevice();
10480 dev.fPassThrough = pAttach->i_getPassthrough();
10481 dev.fHotPluggable = pAttach->i_getHotPluggable();
10482 if (pMedium)
10483 {
10484 if (pMedium->i_isHostDrive())
10485 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10486 else
10487 dev.uuid = pMedium->i_getId();
10488 dev.fTempEject = pAttach->i_getTempEject();
10489 dev.fNonRotational = pAttach->i_getNonRotational();
10490 dev.fDiscard = pAttach->i_getDiscard();
10491 }
10492
10493 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10494
10495 data.llAttachedDevices.push_back(dev);
10496 }
10497
10498 return S_OK;
10499}
10500
10501/**
10502 * Saves machine state settings as defined by aFlags
10503 * (SaveSTS_* values).
10504 *
10505 * @param aFlags Combination of SaveSTS_* flags.
10506 *
10507 * @note Locks objects for writing.
10508 */
10509HRESULT Machine::i_saveStateSettings(int aFlags)
10510{
10511 if (aFlags == 0)
10512 return S_OK;
10513
10514 AutoCaller autoCaller(this);
10515 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10516
10517 /* This object's write lock is also necessary to serialize file access
10518 * (prevent concurrent reads and writes) */
10519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10520
10521 HRESULT rc = S_OK;
10522
10523 Assert(mData->pMachineConfigFile);
10524
10525 try
10526 {
10527 if (aFlags & SaveSTS_CurStateModified)
10528 mData->pMachineConfigFile->fCurrentStateModified = true;
10529
10530 if (aFlags & SaveSTS_StateFilePath)
10531 {
10532 if (!mSSData->strStateFilePath.isEmpty())
10533 /* try to make the file name relative to the settings file dir */
10534 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10535 else
10536 mData->pMachineConfigFile->strStateFile.setNull();
10537 }
10538
10539 if (aFlags & SaveSTS_StateTimeStamp)
10540 {
10541 Assert( mData->mMachineState != MachineState_Aborted
10542 || mSSData->strStateFilePath.isEmpty());
10543
10544 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10545
10546 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10547//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10548 }
10549
10550 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10551 }
10552 catch (...)
10553 {
10554 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10555 }
10556
10557 return rc;
10558}
10559
10560/**
10561 * Ensures that the given medium is added to a media registry. If this machine
10562 * was created with 4.0 or later, then the machine registry is used. Otherwise
10563 * the global VirtualBox media registry is used.
10564 *
10565 * Caller must NOT hold machine lock, media tree or any medium locks!
10566 *
10567 * @param pMedium
10568 */
10569void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10570{
10571 /* Paranoia checks: do not hold machine or media tree locks. */
10572 AssertReturnVoid(!isWriteLockOnCurrentThread());
10573 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10574
10575 ComObjPtr<Medium> pBase;
10576 {
10577 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10578 pBase = pMedium->i_getBase();
10579 }
10580
10581 /* Paranoia checks: do not hold medium locks. */
10582 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10583 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10584
10585 // decide which medium registry to use now that the medium is attached:
10586 Guid uuid;
10587 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10588 // machine XML is VirtualBox 4.0 or higher:
10589 uuid = i_getId(); // machine UUID
10590 else
10591 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10592
10593 if (pMedium->i_addRegistry(uuid))
10594 mParent->i_markRegistryModified(uuid);
10595
10596 /* For more complex hard disk structures it can happen that the base
10597 * medium isn't yet associated with any medium registry. Do that now. */
10598 if (pMedium != pBase)
10599 {
10600 /* Tree lock needed by Medium::addRegistry when recursing. */
10601 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10602 if (pBase->i_addRegistryRecursive(uuid))
10603 {
10604 treeLock.release();
10605 mParent->i_markRegistryModified(uuid);
10606 }
10607 }
10608}
10609
10610/**
10611 * Creates differencing hard disks for all normal hard disks attached to this
10612 * machine and a new set of attachments to refer to created disks.
10613 *
10614 * Used when taking a snapshot or when deleting the current state. Gets called
10615 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10616 *
10617 * This method assumes that mMediaData contains the original hard disk attachments
10618 * it needs to create diffs for. On success, these attachments will be replaced
10619 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10620 * called to delete created diffs which will also rollback mMediaData and restore
10621 * whatever was backed up before calling this method.
10622 *
10623 * Attachments with non-normal hard disks are left as is.
10624 *
10625 * If @a aOnline is @c false then the original hard disks that require implicit
10626 * diffs will be locked for reading. Otherwise it is assumed that they are
10627 * already locked for writing (when the VM was started). Note that in the latter
10628 * case it is responsibility of the caller to lock the newly created diffs for
10629 * writing if this method succeeds.
10630 *
10631 * @param aProgress Progress object to run (must contain at least as
10632 * many operations left as the number of hard disks
10633 * attached).
10634 * @param aOnline Whether the VM was online prior to this operation.
10635 *
10636 * @note The progress object is not marked as completed, neither on success nor
10637 * on failure. This is a responsibility of the caller.
10638 *
10639 * @note Locks this object and the media tree for writing.
10640 */
10641HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10642 ULONG aWeight,
10643 bool aOnline)
10644{
10645 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10646
10647 AutoCaller autoCaller(this);
10648 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10649
10650 AutoMultiWriteLock2 alock(this->lockHandle(),
10651 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10652
10653 /* must be in a protective state because we release the lock below */
10654 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10655 || mData->mMachineState == MachineState_OnlineSnapshotting
10656 || mData->mMachineState == MachineState_LiveSnapshotting
10657 || mData->mMachineState == MachineState_RestoringSnapshot
10658 || mData->mMachineState == MachineState_DeletingSnapshot
10659 , E_FAIL);
10660
10661 HRESULT rc = S_OK;
10662
10663 // use appropriate locked media map (online or offline)
10664 MediumLockListMap lockedMediaOffline;
10665 MediumLockListMap *lockedMediaMap;
10666 if (aOnline)
10667 lockedMediaMap = &mData->mSession.mLockedMedia;
10668 else
10669 lockedMediaMap = &lockedMediaOffline;
10670
10671 try
10672 {
10673 if (!aOnline)
10674 {
10675 /* lock all attached hard disks early to detect "in use"
10676 * situations before creating actual diffs */
10677 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10678 it != mMediaData->mAttachments.end();
10679 ++it)
10680 {
10681 MediumAttachment* pAtt = *it;
10682 if (pAtt->i_getType() == DeviceType_HardDisk)
10683 {
10684 Medium* pMedium = pAtt->i_getMedium();
10685 Assert(pMedium);
10686
10687 MediumLockList *pMediumLockList(new MediumLockList());
10688 alock.release();
10689 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10690 false /* fMediumLockWrite */,
10691 false /* fMediumLockWriteAll */,
10692 NULL,
10693 *pMediumLockList);
10694 alock.acquire();
10695 if (FAILED(rc))
10696 {
10697 delete pMediumLockList;
10698 throw rc;
10699 }
10700 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10701 if (FAILED(rc))
10702 {
10703 throw setError(rc,
10704 tr("Collecting locking information for all attached media failed"));
10705 }
10706 }
10707 }
10708
10709 /* Now lock all media. If this fails, nothing is locked. */
10710 alock.release();
10711 rc = lockedMediaMap->Lock();
10712 alock.acquire();
10713 if (FAILED(rc))
10714 {
10715 throw setError(rc,
10716 tr("Locking of attached media failed"));
10717 }
10718 }
10719
10720 /* remember the current list (note that we don't use backup() since
10721 * mMediaData may be already backed up) */
10722 MediaData::AttachmentList atts = mMediaData->mAttachments;
10723
10724 /* start from scratch */
10725 mMediaData->mAttachments.clear();
10726
10727 /* go through remembered attachments and create diffs for normal hard
10728 * disks and attach them */
10729 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10730 it != atts.end();
10731 ++it)
10732 {
10733 MediumAttachment* pAtt = *it;
10734
10735 DeviceType_T devType = pAtt->i_getType();
10736 Medium* pMedium = pAtt->i_getMedium();
10737
10738 if ( devType != DeviceType_HardDisk
10739 || pMedium == NULL
10740 || pMedium->i_getType() != MediumType_Normal)
10741 {
10742 /* copy the attachment as is */
10743
10744 /** @todo the progress object created in SessionMachine::TakeSnaphot
10745 * only expects operations for hard disks. Later other
10746 * device types need to show up in the progress as well. */
10747 if (devType == DeviceType_HardDisk)
10748 {
10749 if (pMedium == NULL)
10750 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10751 aWeight); // weight
10752 else
10753 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10754 pMedium->i_getBase()->i_getName().c_str()).raw(),
10755 aWeight); // weight
10756 }
10757
10758 mMediaData->mAttachments.push_back(pAtt);
10759 continue;
10760 }
10761
10762 /* need a diff */
10763 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10764 pMedium->i_getBase()->i_getName().c_str()).raw(),
10765 aWeight); // weight
10766
10767 Utf8Str strFullSnapshotFolder;
10768 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10769
10770 ComObjPtr<Medium> diff;
10771 diff.createObject();
10772 // store the diff in the same registry as the parent
10773 // (this cannot fail here because we can't create implicit diffs for
10774 // unregistered images)
10775 Guid uuidRegistryParent;
10776 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10777 Assert(fInRegistry); NOREF(fInRegistry);
10778 rc = diff->init(mParent,
10779 pMedium->i_getPreferredDiffFormat(),
10780 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10781 uuidRegistryParent,
10782 DeviceType_HardDisk);
10783 if (FAILED(rc)) throw rc;
10784
10785 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10786 * the push_back? Looks like we're going to release medium with the
10787 * wrong kind of lock (general issue with if we fail anywhere at all)
10788 * and an orphaned VDI in the snapshots folder. */
10789
10790 /* update the appropriate lock list */
10791 MediumLockList *pMediumLockList;
10792 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10793 AssertComRCThrowRC(rc);
10794 if (aOnline)
10795 {
10796 alock.release();
10797 /* The currently attached medium will be read-only, change
10798 * the lock type to read. */
10799 rc = pMediumLockList->Update(pMedium, false);
10800 alock.acquire();
10801 AssertComRCThrowRC(rc);
10802 }
10803
10804 /* release the locks before the potentially lengthy operation */
10805 alock.release();
10806 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10807 pMediumLockList,
10808 NULL /* aProgress */,
10809 true /* aWait */);
10810 alock.acquire();
10811 if (FAILED(rc)) throw rc;
10812
10813 /* actual lock list update is done in Medium::commitMedia */
10814
10815 rc = diff->i_addBackReference(mData->mUuid);
10816 AssertComRCThrowRC(rc);
10817
10818 /* add a new attachment */
10819 ComObjPtr<MediumAttachment> attachment;
10820 attachment.createObject();
10821 rc = attachment->init(this,
10822 diff,
10823 pAtt->i_getControllerName(),
10824 pAtt->i_getPort(),
10825 pAtt->i_getDevice(),
10826 DeviceType_HardDisk,
10827 true /* aImplicit */,
10828 false /* aPassthrough */,
10829 false /* aTempEject */,
10830 pAtt->i_getNonRotational(),
10831 pAtt->i_getDiscard(),
10832 pAtt->i_getHotPluggable(),
10833 pAtt->i_getBandwidthGroup());
10834 if (FAILED(rc)) throw rc;
10835
10836 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10837 AssertComRCThrowRC(rc);
10838 mMediaData->mAttachments.push_back(attachment);
10839 }
10840 }
10841 catch (HRESULT aRC) { rc = aRC; }
10842
10843 /* unlock all hard disks we locked when there is no VM */
10844 if (!aOnline)
10845 {
10846 ErrorInfoKeeper eik;
10847
10848 HRESULT rc1 = lockedMediaMap->Clear();
10849 AssertComRC(rc1);
10850 }
10851
10852 return rc;
10853}
10854
10855/**
10856 * Deletes implicit differencing hard disks created either by
10857 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10858 *
10859 * Note that to delete hard disks created by #AttachDevice() this method is
10860 * called from #fixupMedia() when the changes are rolled back.
10861 *
10862 * @note Locks this object and the media tree for writing.
10863 */
10864HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10865{
10866 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10867
10868 AutoCaller autoCaller(this);
10869 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10870
10871 AutoMultiWriteLock2 alock(this->lockHandle(),
10872 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10873
10874 /* We absolutely must have backed up state. */
10875 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10876
10877 /* Check if there are any implicitly created diff images. */
10878 bool fImplicitDiffs = false;
10879 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10880 it != mMediaData->mAttachments.end();
10881 ++it)
10882 {
10883 const ComObjPtr<MediumAttachment> &pAtt = *it;
10884 if (pAtt->i_isImplicit())
10885 {
10886 fImplicitDiffs = true;
10887 break;
10888 }
10889 }
10890 /* If there is nothing to do, leave early. This saves lots of image locking
10891 * effort. It also avoids a MachineStateChanged event without real reason.
10892 * This is important e.g. when loading a VM config, because there should be
10893 * no events. Otherwise API clients can become thoroughly confused for
10894 * inaccessible VMs (the code for loading VM configs uses this method for
10895 * cleanup if the config makes no sense), as they take such events as an
10896 * indication that the VM is alive, and they would force the VM config to
10897 * be reread, leading to an endless loop. */
10898 if (!fImplicitDiffs)
10899 return S_OK;
10900
10901 HRESULT rc = S_OK;
10902 MachineState_T oldState = mData->mMachineState;
10903
10904 /* will release the lock before the potentially lengthy operation,
10905 * so protect with the special state (unless already protected) */
10906 if ( oldState != MachineState_Snapshotting
10907 && oldState != MachineState_OnlineSnapshotting
10908 && oldState != MachineState_LiveSnapshotting
10909 && oldState != MachineState_RestoringSnapshot
10910 && oldState != MachineState_DeletingSnapshot
10911 && oldState != MachineState_DeletingSnapshotOnline
10912 && oldState != MachineState_DeletingSnapshotPaused
10913 )
10914 i_setMachineState(MachineState_SettingUp);
10915
10916 // use appropriate locked media map (online or offline)
10917 MediumLockListMap lockedMediaOffline;
10918 MediumLockListMap *lockedMediaMap;
10919 if (aOnline)
10920 lockedMediaMap = &mData->mSession.mLockedMedia;
10921 else
10922 lockedMediaMap = &lockedMediaOffline;
10923
10924 try
10925 {
10926 if (!aOnline)
10927 {
10928 /* lock all attached hard disks early to detect "in use"
10929 * situations before deleting actual diffs */
10930 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10931 it != mMediaData->mAttachments.end();
10932 ++it)
10933 {
10934 MediumAttachment* pAtt = *it;
10935 if (pAtt->i_getType() == DeviceType_HardDisk)
10936 {
10937 Medium* pMedium = pAtt->i_getMedium();
10938 Assert(pMedium);
10939
10940 MediumLockList *pMediumLockList(new MediumLockList());
10941 alock.release();
10942 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10943 false /* fMediumLockWrite */,
10944 false /* fMediumLockWriteAll */,
10945 NULL,
10946 *pMediumLockList);
10947 alock.acquire();
10948
10949 if (FAILED(rc))
10950 {
10951 delete pMediumLockList;
10952 throw rc;
10953 }
10954
10955 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10956 if (FAILED(rc))
10957 throw rc;
10958 }
10959 }
10960
10961 if (FAILED(rc))
10962 throw rc;
10963 } // end of offline
10964
10965 /* Lock lists are now up to date and include implicitly created media */
10966
10967 /* Go through remembered attachments and delete all implicitly created
10968 * diffs and fix up the attachment information */
10969 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10970 MediaData::AttachmentList implicitAtts;
10971 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10972 it != mMediaData->mAttachments.end();
10973 ++it)
10974 {
10975 ComObjPtr<MediumAttachment> pAtt = *it;
10976 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10977 if (pMedium.isNull())
10978 continue;
10979
10980 // Implicit attachments go on the list for deletion and back references are removed.
10981 if (pAtt->i_isImplicit())
10982 {
10983 /* Deassociate and mark for deletion */
10984 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10985 rc = pMedium->i_removeBackReference(mData->mUuid);
10986 if (FAILED(rc))
10987 throw rc;
10988 implicitAtts.push_back(pAtt);
10989 continue;
10990 }
10991
10992 /* Was this medium attached before? */
10993 if (!i_findAttachment(oldAtts, pMedium))
10994 {
10995 /* no: de-associate */
10996 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10997 rc = pMedium->i_removeBackReference(mData->mUuid);
10998 if (FAILED(rc))
10999 throw rc;
11000 continue;
11001 }
11002 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11003 }
11004
11005 /* If there are implicit attachments to delete, throw away the lock
11006 * map contents (which will unlock all media) since the medium
11007 * attachments will be rolled back. Below we need to completely
11008 * recreate the lock map anyway since it is infinitely complex to
11009 * do this incrementally (would need reconstructing each attachment
11010 * change, which would be extremely hairy). */
11011 if (implicitAtts.size() != 0)
11012 {
11013 ErrorInfoKeeper eik;
11014
11015 HRESULT rc1 = lockedMediaMap->Clear();
11016 AssertComRC(rc1);
11017 }
11018
11019 /* rollback hard disk changes */
11020 mMediaData.rollback();
11021
11022 MultiResult mrc(S_OK);
11023
11024 // Delete unused implicit diffs.
11025 if (implicitAtts.size() != 0)
11026 {
11027 alock.release();
11028
11029 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11030 {
11031 // Remove medium associated with this attachment.
11032 ComObjPtr<MediumAttachment> pAtt = *it;
11033 Assert(pAtt);
11034 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11035 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11036 Assert(pMedium);
11037
11038 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11039 // continue on delete failure, just collect error messages
11040 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11041 pMedium->i_getLocationFull().c_str() ));
11042 mrc = rc;
11043 }
11044 // Clear the list of deleted implicit attachments now, while not
11045 // holding the lock, as it will ultimately trigger Medium::uninit()
11046 // calls which assume that the media tree lock isn't held.
11047 implicitAtts.clear();
11048
11049 alock.acquire();
11050
11051 /* if there is a VM recreate media lock map as mentioned above,
11052 * otherwise it is a waste of time and we leave things unlocked */
11053 if (aOnline)
11054 {
11055 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11056 /* must never be NULL, but better safe than sorry */
11057 if (!pMachine.isNull())
11058 {
11059 alock.release();
11060 rc = mData->mSession.mMachine->i_lockMedia();
11061 alock.acquire();
11062 if (FAILED(rc))
11063 throw rc;
11064 }
11065 }
11066 }
11067 }
11068 catch (HRESULT aRC) {rc = aRC;}
11069
11070 if (mData->mMachineState == MachineState_SettingUp)
11071 i_setMachineState(oldState);
11072
11073 /* unlock all hard disks we locked when there is no VM */
11074 if (!aOnline)
11075 {
11076 ErrorInfoKeeper eik;
11077
11078 HRESULT rc1 = lockedMediaMap->Clear();
11079 AssertComRC(rc1);
11080 }
11081
11082 return rc;
11083}
11084
11085
11086/**
11087 * Looks through the given list of media attachments for one with the given parameters
11088 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11089 * can be searched as well if needed.
11090 *
11091 * @param list
11092 * @param aControllerName
11093 * @param aControllerPort
11094 * @param aDevice
11095 * @return
11096 */
11097MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11098 IN_BSTR aControllerName,
11099 LONG aControllerPort,
11100 LONG aDevice)
11101{
11102 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11103 {
11104 MediumAttachment *pAttach = *it;
11105 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11106 return pAttach;
11107 }
11108
11109 return NULL;
11110}
11111
11112/**
11113 * Looks through the given list of media attachments for one with the given parameters
11114 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11115 * can be searched as well if needed.
11116 *
11117 * @param list
11118 * @param aControllerName
11119 * @param aControllerPort
11120 * @param aDevice
11121 * @return
11122 */
11123MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11124 ComObjPtr<Medium> pMedium)
11125{
11126 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11127 {
11128 MediumAttachment *pAttach = *it;
11129 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11130 if (pMediumThis == pMedium)
11131 return pAttach;
11132 }
11133
11134 return NULL;
11135}
11136
11137/**
11138 * Looks through the given list of media attachments for one with the given parameters
11139 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11140 * can be searched as well if needed.
11141 *
11142 * @param list
11143 * @param aControllerName
11144 * @param aControllerPort
11145 * @param aDevice
11146 * @return
11147 */
11148MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11149 Guid &id)
11150{
11151 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11152 {
11153 MediumAttachment *pAttach = *it;
11154 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11155 if (pMediumThis->i_getId() == id)
11156 return pAttach;
11157 }
11158
11159 return NULL;
11160}
11161
11162/**
11163 * Main implementation for Machine::DetachDevice. This also gets called
11164 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11165 *
11166 * @param pAttach Medium attachment to detach.
11167 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11168 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11169 * SnapshotMachine, and this must be its snapshot.
11170 * @return
11171 */
11172HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11173 AutoWriteLock &writeLock,
11174 Snapshot *pSnapshot)
11175{
11176 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11177 DeviceType_T mediumType = pAttach->i_getType();
11178
11179 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11180
11181 if (pAttach->i_isImplicit())
11182 {
11183 /* attempt to implicitly delete the implicitly created diff */
11184
11185 /// @todo move the implicit flag from MediumAttachment to Medium
11186 /// and forbid any hard disk operation when it is implicit. Or maybe
11187 /// a special media state for it to make it even more simple.
11188
11189 Assert(mMediaData.isBackedUp());
11190
11191 /* will release the lock before the potentially lengthy operation, so
11192 * protect with the special state */
11193 MachineState_T oldState = mData->mMachineState;
11194 i_setMachineState(MachineState_SettingUp);
11195
11196 writeLock.release();
11197
11198 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11199 true /*aWait*/);
11200
11201 writeLock.acquire();
11202
11203 i_setMachineState(oldState);
11204
11205 if (FAILED(rc)) return rc;
11206 }
11207
11208 i_setModified(IsModified_Storage);
11209 mMediaData.backup();
11210 mMediaData->mAttachments.remove(pAttach);
11211
11212 if (!oldmedium.isNull())
11213 {
11214 // if this is from a snapshot, do not defer detachment to commitMedia()
11215 if (pSnapshot)
11216 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11217 // else if non-hard disk media, do not defer detachment to commitMedia() either
11218 else if (mediumType != DeviceType_HardDisk)
11219 oldmedium->i_removeBackReference(mData->mUuid);
11220 }
11221
11222 return S_OK;
11223}
11224
11225/**
11226 * Goes thru all media of the given list and
11227 *
11228 * 1) calls i_detachDevice() on each of them for this machine and
11229 * 2) adds all Medium objects found in the process to the given list,
11230 * depending on cleanupMode.
11231 *
11232 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11233 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11234 * media to the list.
11235 *
11236 * This gets called from Machine::Unregister, both for the actual Machine and
11237 * the SnapshotMachine objects that might be found in the snapshots.
11238 *
11239 * Requires caller and locking. The machine lock must be passed in because it
11240 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11241 *
11242 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11243 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11244 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11245 * Full, then all media get added;
11246 * otherwise no media get added.
11247 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11248 * @return
11249 */
11250HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11251 Snapshot *pSnapshot,
11252 CleanupMode_T cleanupMode,
11253 MediaList &llMedia)
11254{
11255 Assert(isWriteLockOnCurrentThread());
11256
11257 HRESULT rc;
11258
11259 // make a temporary list because i_detachDevice invalidates iterators into
11260 // mMediaData->mAttachments
11261 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11262
11263 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11264 {
11265 ComObjPtr<MediumAttachment> &pAttach = *it;
11266 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11267
11268 if (!pMedium.isNull())
11269 {
11270 AutoCaller mac(pMedium);
11271 if (FAILED(mac.rc())) return mac.rc();
11272 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11273 DeviceType_T devType = pMedium->i_getDeviceType();
11274 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11275 && devType == DeviceType_HardDisk)
11276 || (cleanupMode == CleanupMode_Full)
11277 )
11278 {
11279 llMedia.push_back(pMedium);
11280 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11281 /* Not allowed to keep this lock as below we need the parent
11282 * medium lock, and the lock order is parent to child. */
11283 lock.release();
11284 /*
11285 * Search for medias which are not attached to any machine, but
11286 * in the chain to an attached disk. Mediums are only consided
11287 * if they are:
11288 * - have only one child
11289 * - no references to any machines
11290 * - are of normal medium type
11291 */
11292 while (!pParent.isNull())
11293 {
11294 AutoCaller mac1(pParent);
11295 if (FAILED(mac1.rc())) return mac1.rc();
11296 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11297 if (pParent->i_getChildren().size() == 1)
11298 {
11299 if ( pParent->i_getMachineBackRefCount() == 0
11300 && pParent->i_getType() == MediumType_Normal
11301 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11302 llMedia.push_back(pParent);
11303 }
11304 else
11305 break;
11306 pParent = pParent->i_getParent();
11307 }
11308 }
11309 }
11310
11311 // real machine: then we need to use the proper method
11312 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11313
11314 if (FAILED(rc))
11315 return rc;
11316 }
11317
11318 return S_OK;
11319}
11320
11321/**
11322 * Perform deferred hard disk detachments.
11323 *
11324 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11325 * backed up).
11326 *
11327 * If @a aOnline is @c true then this method will also unlock the old hard disks
11328 * for which the new implicit diffs were created and will lock these new diffs for
11329 * writing.
11330 *
11331 * @param aOnline Whether the VM was online prior to this operation.
11332 *
11333 * @note Locks this object for writing!
11334 */
11335void Machine::i_commitMedia(bool aOnline /*= false*/)
11336{
11337 AutoCaller autoCaller(this);
11338 AssertComRCReturnVoid(autoCaller.rc());
11339
11340 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11341
11342 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11343
11344 HRESULT rc = S_OK;
11345
11346 /* no attach/detach operations -- nothing to do */
11347 if (!mMediaData.isBackedUp())
11348 return;
11349
11350 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11351 bool fMediaNeedsLocking = false;
11352
11353 /* enumerate new attachments */
11354 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11355 it != mMediaData->mAttachments.end();
11356 ++it)
11357 {
11358 MediumAttachment *pAttach = *it;
11359
11360 pAttach->i_commit();
11361
11362 Medium* pMedium = pAttach->i_getMedium();
11363 bool fImplicit = pAttach->i_isImplicit();
11364
11365 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11366 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11367 fImplicit));
11368
11369 /** @todo convert all this Machine-based voodoo to MediumAttachment
11370 * based commit logic. */
11371 if (fImplicit)
11372 {
11373 /* convert implicit attachment to normal */
11374 pAttach->i_setImplicit(false);
11375
11376 if ( aOnline
11377 && pMedium
11378 && pAttach->i_getType() == DeviceType_HardDisk
11379 )
11380 {
11381 /* update the appropriate lock list */
11382 MediumLockList *pMediumLockList;
11383 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11384 AssertComRC(rc);
11385 if (pMediumLockList)
11386 {
11387 /* unlock if there's a need to change the locking */
11388 if (!fMediaNeedsLocking)
11389 {
11390 rc = mData->mSession.mLockedMedia.Unlock();
11391 AssertComRC(rc);
11392 fMediaNeedsLocking = true;
11393 }
11394 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11395 AssertComRC(rc);
11396 rc = pMediumLockList->Append(pMedium, true);
11397 AssertComRC(rc);
11398 }
11399 }
11400
11401 continue;
11402 }
11403
11404 if (pMedium)
11405 {
11406 /* was this medium attached before? */
11407 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11408 {
11409 MediumAttachment *pOldAttach = *oldIt;
11410 if (pOldAttach->i_getMedium() == pMedium)
11411 {
11412 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11413
11414 /* yes: remove from old to avoid de-association */
11415 oldAtts.erase(oldIt);
11416 break;
11417 }
11418 }
11419 }
11420 }
11421
11422 /* enumerate remaining old attachments and de-associate from the
11423 * current machine state */
11424 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11425 {
11426 MediumAttachment *pAttach = *it;
11427 Medium* pMedium = pAttach->i_getMedium();
11428
11429 /* Detach only hard disks, since DVD/floppy media is detached
11430 * instantly in MountMedium. */
11431 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11432 {
11433 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11434
11435 /* now de-associate from the current machine state */
11436 rc = pMedium->i_removeBackReference(mData->mUuid);
11437 AssertComRC(rc);
11438
11439 if (aOnline)
11440 {
11441 /* unlock since medium is not used anymore */
11442 MediumLockList *pMediumLockList;
11443 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11444 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11445 {
11446 /* this happens for online snapshots, there the attachment
11447 * is changing, but only to a diff image created under
11448 * the old one, so there is no separate lock list */
11449 Assert(!pMediumLockList);
11450 }
11451 else
11452 {
11453 AssertComRC(rc);
11454 if (pMediumLockList)
11455 {
11456 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11457 AssertComRC(rc);
11458 }
11459 }
11460 }
11461 }
11462 }
11463
11464 /* take media locks again so that the locking state is consistent */
11465 if (fMediaNeedsLocking)
11466 {
11467 Assert(aOnline);
11468 rc = mData->mSession.mLockedMedia.Lock();
11469 AssertComRC(rc);
11470 }
11471
11472 /* commit the hard disk changes */
11473 mMediaData.commit();
11474
11475 if (i_isSessionMachine())
11476 {
11477 /*
11478 * Update the parent machine to point to the new owner.
11479 * This is necessary because the stored parent will point to the
11480 * session machine otherwise and cause crashes or errors later
11481 * when the session machine gets invalid.
11482 */
11483 /** @todo Change the MediumAttachment class to behave like any other
11484 * class in this regard by creating peer MediumAttachment
11485 * objects for session machines and share the data with the peer
11486 * machine.
11487 */
11488 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11489 it != mMediaData->mAttachments.end();
11490 ++it)
11491 (*it)->i_updateParentMachine(mPeer);
11492
11493 /* attach new data to the primary machine and reshare it */
11494 mPeer->mMediaData.attach(mMediaData);
11495 }
11496
11497 return;
11498}
11499
11500/**
11501 * Perform deferred deletion of implicitly created diffs.
11502 *
11503 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11504 * backed up).
11505 *
11506 * @note Locks this object for writing!
11507 */
11508void Machine::i_rollbackMedia()
11509{
11510 AutoCaller autoCaller(this);
11511 AssertComRCReturnVoid(autoCaller.rc());
11512
11513 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11514 LogFlowThisFunc(("Entering rollbackMedia\n"));
11515
11516 HRESULT rc = S_OK;
11517
11518 /* no attach/detach operations -- nothing to do */
11519 if (!mMediaData.isBackedUp())
11520 return;
11521
11522 /* enumerate new attachments */
11523 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11524 it != mMediaData->mAttachments.end();
11525 ++it)
11526 {
11527 MediumAttachment *pAttach = *it;
11528 /* Fix up the backrefs for DVD/floppy media. */
11529 if (pAttach->i_getType() != DeviceType_HardDisk)
11530 {
11531 Medium* pMedium = pAttach->i_getMedium();
11532 if (pMedium)
11533 {
11534 rc = pMedium->i_removeBackReference(mData->mUuid);
11535 AssertComRC(rc);
11536 }
11537 }
11538
11539 (*it)->i_rollback();
11540
11541 pAttach = *it;
11542 /* Fix up the backrefs for DVD/floppy media. */
11543 if (pAttach->i_getType() != DeviceType_HardDisk)
11544 {
11545 Medium* pMedium = pAttach->i_getMedium();
11546 if (pMedium)
11547 {
11548 rc = pMedium->i_addBackReference(mData->mUuid);
11549 AssertComRC(rc);
11550 }
11551 }
11552 }
11553
11554 /** @todo convert all this Machine-based voodoo to MediumAttachment
11555 * based rollback logic. */
11556 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11557
11558 return;
11559}
11560
11561/**
11562 * Returns true if the settings file is located in the directory named exactly
11563 * as the machine; this means, among other things, that the machine directory
11564 * should be auto-renamed.
11565 *
11566 * @param aSettingsDir if not NULL, the full machine settings file directory
11567 * name will be assigned there.
11568 *
11569 * @note Doesn't lock anything.
11570 * @note Not thread safe (must be called from this object's lock).
11571 */
11572bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11573{
11574 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11575 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11576 if (aSettingsDir)
11577 *aSettingsDir = strMachineDirName;
11578 strMachineDirName.stripPath(); // vmname
11579 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11580 strConfigFileOnly.stripPath() // vmname.vbox
11581 .stripSuffix(); // vmname
11582 /** @todo hack, make somehow use of ComposeMachineFilename */
11583 if (mUserData->s.fDirectoryIncludesUUID)
11584 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11585
11586 AssertReturn(!strMachineDirName.isEmpty(), false);
11587 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11588
11589 return strMachineDirName == strConfigFileOnly;
11590}
11591
11592/**
11593 * Discards all changes to machine settings.
11594 *
11595 * @param aNotify Whether to notify the direct session about changes or not.
11596 *
11597 * @note Locks objects for writing!
11598 */
11599void Machine::i_rollback(bool aNotify)
11600{
11601 AutoCaller autoCaller(this);
11602 AssertComRCReturn(autoCaller.rc(), (void)0);
11603
11604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11605
11606 if (!mStorageControllers.isNull())
11607 {
11608 if (mStorageControllers.isBackedUp())
11609 {
11610 /* unitialize all new devices (absent in the backed up list). */
11611 StorageControllerList::const_iterator it = mStorageControllers->begin();
11612 StorageControllerList *backedList = mStorageControllers.backedUpData();
11613 while (it != mStorageControllers->end())
11614 {
11615 if ( std::find(backedList->begin(), backedList->end(), *it)
11616 == backedList->end()
11617 )
11618 {
11619 (*it)->uninit();
11620 }
11621 ++it;
11622 }
11623
11624 /* restore the list */
11625 mStorageControllers.rollback();
11626 }
11627
11628 /* rollback any changes to devices after restoring the list */
11629 if (mData->flModifications & IsModified_Storage)
11630 {
11631 StorageControllerList::const_iterator it = mStorageControllers->begin();
11632 while (it != mStorageControllers->end())
11633 {
11634 (*it)->i_rollback();
11635 ++it;
11636 }
11637 }
11638 }
11639
11640 if (!mUSBControllers.isNull())
11641 {
11642 if (mUSBControllers.isBackedUp())
11643 {
11644 /* unitialize all new devices (absent in the backed up list). */
11645 USBControllerList::const_iterator it = mUSBControllers->begin();
11646 USBControllerList *backedList = mUSBControllers.backedUpData();
11647 while (it != mUSBControllers->end())
11648 {
11649 if ( std::find(backedList->begin(), backedList->end(), *it)
11650 == backedList->end()
11651 )
11652 {
11653 (*it)->uninit();
11654 }
11655 ++it;
11656 }
11657
11658 /* restore the list */
11659 mUSBControllers.rollback();
11660 }
11661
11662 /* rollback any changes to devices after restoring the list */
11663 if (mData->flModifications & IsModified_USB)
11664 {
11665 USBControllerList::const_iterator it = mUSBControllers->begin();
11666 while (it != mUSBControllers->end())
11667 {
11668 (*it)->i_rollback();
11669 ++it;
11670 }
11671 }
11672 }
11673
11674 mUserData.rollback();
11675
11676 mHWData.rollback();
11677
11678 if (mData->flModifications & IsModified_Storage)
11679 i_rollbackMedia();
11680
11681 if (mBIOSSettings)
11682 mBIOSSettings->i_rollback();
11683
11684 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11685 mVRDEServer->i_rollback();
11686
11687 if (mAudioAdapter)
11688 mAudioAdapter->i_rollback();
11689
11690 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11691 mUSBDeviceFilters->i_rollback();
11692
11693 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11694 mBandwidthControl->i_rollback();
11695
11696 if (!mHWData.isNull())
11697 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11698 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11699 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11700 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11701
11702 if (mData->flModifications & IsModified_NetworkAdapters)
11703 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11704 if ( mNetworkAdapters[slot]
11705 && mNetworkAdapters[slot]->i_isModified())
11706 {
11707 mNetworkAdapters[slot]->i_rollback();
11708 networkAdapters[slot] = mNetworkAdapters[slot];
11709 }
11710
11711 if (mData->flModifications & IsModified_SerialPorts)
11712 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11713 if ( mSerialPorts[slot]
11714 && mSerialPorts[slot]->i_isModified())
11715 {
11716 mSerialPorts[slot]->i_rollback();
11717 serialPorts[slot] = mSerialPorts[slot];
11718 }
11719
11720 if (mData->flModifications & IsModified_ParallelPorts)
11721 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11722 if ( mParallelPorts[slot]
11723 && mParallelPorts[slot]->i_isModified())
11724 {
11725 mParallelPorts[slot]->i_rollback();
11726 parallelPorts[slot] = mParallelPorts[slot];
11727 }
11728
11729 if (aNotify)
11730 {
11731 /* inform the direct session about changes */
11732
11733 ComObjPtr<Machine> that = this;
11734 uint32_t flModifications = mData->flModifications;
11735 alock.release();
11736
11737 if (flModifications & IsModified_SharedFolders)
11738 that->i_onSharedFolderChange();
11739
11740 if (flModifications & IsModified_VRDEServer)
11741 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11742 if (flModifications & IsModified_USB)
11743 that->i_onUSBControllerChange();
11744
11745 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11746 if (networkAdapters[slot])
11747 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11748 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11749 if (serialPorts[slot])
11750 that->i_onSerialPortChange(serialPorts[slot]);
11751 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11752 if (parallelPorts[slot])
11753 that->i_onParallelPortChange(parallelPorts[slot]);
11754
11755 if (flModifications & IsModified_Storage)
11756 that->i_onStorageControllerChange();
11757
11758#if 0
11759 if (flModifications & IsModified_BandwidthControl)
11760 that->onBandwidthControlChange();
11761#endif
11762 }
11763}
11764
11765/**
11766 * Commits all the changes to machine settings.
11767 *
11768 * Note that this operation is supposed to never fail.
11769 *
11770 * @note Locks this object and children for writing.
11771 */
11772void Machine::i_commit()
11773{
11774 AutoCaller autoCaller(this);
11775 AssertComRCReturnVoid(autoCaller.rc());
11776
11777 AutoCaller peerCaller(mPeer);
11778 AssertComRCReturnVoid(peerCaller.rc());
11779
11780 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11781
11782 /*
11783 * use safe commit to ensure Snapshot machines (that share mUserData)
11784 * will still refer to a valid memory location
11785 */
11786 mUserData.commitCopy();
11787
11788 mHWData.commit();
11789
11790 if (mMediaData.isBackedUp())
11791 i_commitMedia(Global::IsOnline(mData->mMachineState));
11792
11793 mBIOSSettings->i_commit();
11794 mVRDEServer->i_commit();
11795 mAudioAdapter->i_commit();
11796 mUSBDeviceFilters->i_commit();
11797 mBandwidthControl->i_commit();
11798
11799 /* Since mNetworkAdapters is a list which might have been changed (resized)
11800 * without using the Backupable<> template we need to handle the copying
11801 * of the list entries manually, including the creation of peers for the
11802 * new objects. */
11803 bool commitNetworkAdapters = false;
11804 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11805 if (mPeer)
11806 {
11807 /* commit everything, even the ones which will go away */
11808 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11809 mNetworkAdapters[slot]->i_commit();
11810 /* copy over the new entries, creating a peer and uninit the original */
11811 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11812 for (size_t slot = 0; slot < newSize; slot++)
11813 {
11814 /* look if this adapter has a peer device */
11815 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11816 if (!peer)
11817 {
11818 /* no peer means the adapter is a newly created one;
11819 * create a peer owning data this data share it with */
11820 peer.createObject();
11821 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11822 }
11823 mPeer->mNetworkAdapters[slot] = peer;
11824 }
11825 /* uninit any no longer needed network adapters */
11826 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11827 mNetworkAdapters[slot]->uninit();
11828 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11829 {
11830 if (mPeer->mNetworkAdapters[slot])
11831 mPeer->mNetworkAdapters[slot]->uninit();
11832 }
11833 /* Keep the original network adapter count until this point, so that
11834 * discarding a chipset type change will not lose settings. */
11835 mNetworkAdapters.resize(newSize);
11836 mPeer->mNetworkAdapters.resize(newSize);
11837 }
11838 else
11839 {
11840 /* we have no peer (our parent is the newly created machine);
11841 * just commit changes to the network adapters */
11842 commitNetworkAdapters = true;
11843 }
11844 if (commitNetworkAdapters)
11845 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11846 mNetworkAdapters[slot]->i_commit();
11847
11848 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11849 mSerialPorts[slot]->i_commit();
11850 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11851 mParallelPorts[slot]->i_commit();
11852
11853 bool commitStorageControllers = false;
11854
11855 if (mStorageControllers.isBackedUp())
11856 {
11857 mStorageControllers.commit();
11858
11859 if (mPeer)
11860 {
11861 /* Commit all changes to new controllers (this will reshare data with
11862 * peers for those who have peers) */
11863 StorageControllerList *newList = new StorageControllerList();
11864 StorageControllerList::const_iterator it = mStorageControllers->begin();
11865 while (it != mStorageControllers->end())
11866 {
11867 (*it)->i_commit();
11868
11869 /* look if this controller has a peer device */
11870 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11871 if (!peer)
11872 {
11873 /* no peer means the device is a newly created one;
11874 * create a peer owning data this device share it with */
11875 peer.createObject();
11876 peer->init(mPeer, *it, true /* aReshare */);
11877 }
11878 else
11879 {
11880 /* remove peer from the old list */
11881 mPeer->mStorageControllers->remove(peer);
11882 }
11883 /* and add it to the new list */
11884 newList->push_back(peer);
11885
11886 ++it;
11887 }
11888
11889 /* uninit old peer's controllers that are left */
11890 it = mPeer->mStorageControllers->begin();
11891 while (it != mPeer->mStorageControllers->end())
11892 {
11893 (*it)->uninit();
11894 ++it;
11895 }
11896
11897 /* attach new list of controllers to our peer */
11898 mPeer->mStorageControllers.attach(newList);
11899 }
11900 else
11901 {
11902 /* we have no peer (our parent is the newly created machine);
11903 * just commit changes to devices */
11904 commitStorageControllers = true;
11905 }
11906 }
11907 else
11908 {
11909 /* the list of controllers itself is not changed,
11910 * just commit changes to controllers themselves */
11911 commitStorageControllers = true;
11912 }
11913
11914 if (commitStorageControllers)
11915 {
11916 StorageControllerList::const_iterator it = mStorageControllers->begin();
11917 while (it != mStorageControllers->end())
11918 {
11919 (*it)->i_commit();
11920 ++it;
11921 }
11922 }
11923
11924 bool commitUSBControllers = false;
11925
11926 if (mUSBControllers.isBackedUp())
11927 {
11928 mUSBControllers.commit();
11929
11930 if (mPeer)
11931 {
11932 /* Commit all changes to new controllers (this will reshare data with
11933 * peers for those who have peers) */
11934 USBControllerList *newList = new USBControllerList();
11935 USBControllerList::const_iterator it = mUSBControllers->begin();
11936 while (it != mUSBControllers->end())
11937 {
11938 (*it)->i_commit();
11939
11940 /* look if this controller has a peer device */
11941 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11942 if (!peer)
11943 {
11944 /* no peer means the device is a newly created one;
11945 * create a peer owning data this device share it with */
11946 peer.createObject();
11947 peer->init(mPeer, *it, true /* aReshare */);
11948 }
11949 else
11950 {
11951 /* remove peer from the old list */
11952 mPeer->mUSBControllers->remove(peer);
11953 }
11954 /* and add it to the new list */
11955 newList->push_back(peer);
11956
11957 ++it;
11958 }
11959
11960 /* uninit old peer's controllers that are left */
11961 it = mPeer->mUSBControllers->begin();
11962 while (it != mPeer->mUSBControllers->end())
11963 {
11964 (*it)->uninit();
11965 ++it;
11966 }
11967
11968 /* attach new list of controllers to our peer */
11969 mPeer->mUSBControllers.attach(newList);
11970 }
11971 else
11972 {
11973 /* we have no peer (our parent is the newly created machine);
11974 * just commit changes to devices */
11975 commitUSBControllers = true;
11976 }
11977 }
11978 else
11979 {
11980 /* the list of controllers itself is not changed,
11981 * just commit changes to controllers themselves */
11982 commitUSBControllers = true;
11983 }
11984
11985 if (commitUSBControllers)
11986 {
11987 USBControllerList::const_iterator it = mUSBControllers->begin();
11988 while (it != mUSBControllers->end())
11989 {
11990 (*it)->i_commit();
11991 ++it;
11992 }
11993 }
11994
11995 if (i_isSessionMachine())
11996 {
11997 /* attach new data to the primary machine and reshare it */
11998 mPeer->mUserData.attach(mUserData);
11999 mPeer->mHWData.attach(mHWData);
12000 /* mMediaData is reshared by fixupMedia */
12001 // mPeer->mMediaData.attach(mMediaData);
12002 Assert(mPeer->mMediaData.data() == mMediaData.data());
12003 }
12004}
12005
12006/**
12007 * Copies all the hardware data from the given machine.
12008 *
12009 * Currently, only called when the VM is being restored from a snapshot. In
12010 * particular, this implies that the VM is not running during this method's
12011 * call.
12012 *
12013 * @note This method must be called from under this object's lock.
12014 *
12015 * @note This method doesn't call #commit(), so all data remains backed up and
12016 * unsaved.
12017 */
12018void Machine::i_copyFrom(Machine *aThat)
12019{
12020 AssertReturnVoid(!i_isSnapshotMachine());
12021 AssertReturnVoid(aThat->i_isSnapshotMachine());
12022
12023 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12024
12025 mHWData.assignCopy(aThat->mHWData);
12026
12027 // create copies of all shared folders (mHWData after attaching a copy
12028 // contains just references to original objects)
12029 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12030 it != mHWData->mSharedFolders.end();
12031 ++it)
12032 {
12033 ComObjPtr<SharedFolder> folder;
12034 folder.createObject();
12035 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12036 AssertComRC(rc);
12037 *it = folder;
12038 }
12039
12040 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12041 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12042 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12043 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12044 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12045
12046 /* create private copies of all controllers */
12047 mStorageControllers.backup();
12048 mStorageControllers->clear();
12049 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12050 it != aThat->mStorageControllers->end();
12051 ++it)
12052 {
12053 ComObjPtr<StorageController> ctrl;
12054 ctrl.createObject();
12055 ctrl->initCopy(this, *it);
12056 mStorageControllers->push_back(ctrl);
12057 }
12058
12059 /* create private copies of all USB controllers */
12060 mUSBControllers.backup();
12061 mUSBControllers->clear();
12062 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12063 it != aThat->mUSBControllers->end();
12064 ++it)
12065 {
12066 ComObjPtr<USBController> ctrl;
12067 ctrl.createObject();
12068 ctrl->initCopy(this, *it);
12069 mUSBControllers->push_back(ctrl);
12070 }
12071
12072 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12073 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12074 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12075 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12076 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12077 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12078 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12079}
12080
12081/**
12082 * Returns whether the given storage controller is hotplug capable.
12083 *
12084 * @returns true if the controller supports hotplugging
12085 * false otherwise.
12086 * @param enmCtrlType The controller type to check for.
12087 */
12088bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12089{
12090 ComPtr<ISystemProperties> systemProperties;
12091 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12092 if (FAILED(rc))
12093 return false;
12094
12095 BOOL aHotplugCapable = FALSE;
12096 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12097
12098 return RT_BOOL(aHotplugCapable);
12099}
12100
12101#ifdef VBOX_WITH_RESOURCE_USAGE_API
12102
12103void Machine::i_getDiskList(MediaList &list)
12104{
12105 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12106 it != mMediaData->mAttachments.end();
12107 ++it)
12108 {
12109 MediumAttachment* pAttach = *it;
12110 /* just in case */
12111 AssertStmt(pAttach, continue);
12112
12113 AutoCaller localAutoCallerA(pAttach);
12114 if (FAILED(localAutoCallerA.rc())) continue;
12115
12116 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12117
12118 if (pAttach->i_getType() == DeviceType_HardDisk)
12119 list.push_back(pAttach->i_getMedium());
12120 }
12121}
12122
12123void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12124{
12125 AssertReturnVoid(isWriteLockOnCurrentThread());
12126 AssertPtrReturnVoid(aCollector);
12127
12128 pm::CollectorHAL *hal = aCollector->getHAL();
12129 /* Create sub metrics */
12130 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12131 "Percentage of processor time spent in user mode by the VM process.");
12132 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12133 "Percentage of processor time spent in kernel mode by the VM process.");
12134 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12135 "Size of resident portion of VM process in memory.");
12136 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12137 "Actual size of all VM disks combined.");
12138 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12139 "Network receive rate.");
12140 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12141 "Network transmit rate.");
12142 /* Create and register base metrics */
12143 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12144 cpuLoadUser, cpuLoadKernel);
12145 aCollector->registerBaseMetric(cpuLoad);
12146 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12147 ramUsageUsed);
12148 aCollector->registerBaseMetric(ramUsage);
12149 MediaList disks;
12150 i_getDiskList(disks);
12151 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12152 diskUsageUsed);
12153 aCollector->registerBaseMetric(diskUsage);
12154
12155 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12156 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12157 new pm::AggregateAvg()));
12158 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12159 new pm::AggregateMin()));
12160 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12161 new pm::AggregateMax()));
12162 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12163 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12164 new pm::AggregateAvg()));
12165 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12166 new pm::AggregateMin()));
12167 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12168 new pm::AggregateMax()));
12169
12170 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12171 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12172 new pm::AggregateAvg()));
12173 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12174 new pm::AggregateMin()));
12175 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12176 new pm::AggregateMax()));
12177
12178 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12179 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12180 new pm::AggregateAvg()));
12181 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12182 new pm::AggregateMin()));
12183 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12184 new pm::AggregateMax()));
12185
12186
12187 /* Guest metrics collector */
12188 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12189 aCollector->registerGuest(mCollectorGuest);
12190 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12191 this, __PRETTY_FUNCTION__, mCollectorGuest));
12192
12193 /* Create sub metrics */
12194 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12195 "Percentage of processor time spent in user mode as seen by the guest.");
12196 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12197 "Percentage of processor time spent in kernel mode as seen by the guest.");
12198 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12199 "Percentage of processor time spent idling as seen by the guest.");
12200
12201 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12202 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12203 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12204 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12205 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12206 pm::SubMetric *guestMemCache = new pm::SubMetric(
12207 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12208
12209 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12210 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12211
12212 /* Create and register base metrics */
12213 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12214 machineNetRx, machineNetTx);
12215 aCollector->registerBaseMetric(machineNetRate);
12216
12217 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12218 guestLoadUser, guestLoadKernel, guestLoadIdle);
12219 aCollector->registerBaseMetric(guestCpuLoad);
12220
12221 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12222 guestMemTotal, guestMemFree,
12223 guestMemBalloon, guestMemShared,
12224 guestMemCache, guestPagedTotal);
12225 aCollector->registerBaseMetric(guestCpuMem);
12226
12227 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12228 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12229 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12230 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12231
12232 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12233 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12234 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12235 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12236
12237 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12238 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12239 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12240 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12241
12242 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12243 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12244 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12245 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12246
12247 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12248 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12249 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12250 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12251
12252 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12253 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12254 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12255 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12256
12257 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12258 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12259 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12260 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12261
12262 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12263 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12264 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12265 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12266
12267 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12268 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12269 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12270 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12271
12272 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12273 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12274 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12275 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12276
12277 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12278 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12279 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12280 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12281}
12282
12283void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12284{
12285 AssertReturnVoid(isWriteLockOnCurrentThread());
12286
12287 if (aCollector)
12288 {
12289 aCollector->unregisterMetricsFor(aMachine);
12290 aCollector->unregisterBaseMetricsFor(aMachine);
12291 }
12292}
12293
12294#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12295
12296
12297////////////////////////////////////////////////////////////////////////////////
12298
12299DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12300
12301HRESULT SessionMachine::FinalConstruct()
12302{
12303 LogFlowThisFunc(("\n"));
12304
12305 mClientToken = NULL;
12306
12307 return BaseFinalConstruct();
12308}
12309
12310void SessionMachine::FinalRelease()
12311{
12312 LogFlowThisFunc(("\n"));
12313
12314 Assert(!mClientToken);
12315 /* paranoia, should not hang around any more */
12316 if (mClientToken)
12317 {
12318 delete mClientToken;
12319 mClientToken = NULL;
12320 }
12321
12322 uninit(Uninit::Unexpected);
12323
12324 BaseFinalRelease();
12325}
12326
12327/**
12328 * @note Must be called only by Machine::LockMachine() from its own write lock.
12329 */
12330HRESULT SessionMachine::init(Machine *aMachine)
12331{
12332 LogFlowThisFuncEnter();
12333 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12334
12335 AssertReturn(aMachine, E_INVALIDARG);
12336
12337 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12338
12339 /* Enclose the state transition NotReady->InInit->Ready */
12340 AutoInitSpan autoInitSpan(this);
12341 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12342
12343 HRESULT rc = S_OK;
12344
12345 /* create the machine client token */
12346 try
12347 {
12348 mClientToken = new ClientToken(aMachine, this);
12349 if (!mClientToken->isReady())
12350 {
12351 delete mClientToken;
12352 mClientToken = NULL;
12353 rc = E_FAIL;
12354 }
12355 }
12356 catch (std::bad_alloc &)
12357 {
12358 rc = E_OUTOFMEMORY;
12359 }
12360 if (FAILED(rc))
12361 return rc;
12362
12363 /* memorize the peer Machine */
12364 unconst(mPeer) = aMachine;
12365 /* share the parent pointer */
12366 unconst(mParent) = aMachine->mParent;
12367
12368 /* take the pointers to data to share */
12369 mData.share(aMachine->mData);
12370 mSSData.share(aMachine->mSSData);
12371
12372 mUserData.share(aMachine->mUserData);
12373 mHWData.share(aMachine->mHWData);
12374 mMediaData.share(aMachine->mMediaData);
12375
12376 mStorageControllers.allocate();
12377 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12378 it != aMachine->mStorageControllers->end();
12379 ++it)
12380 {
12381 ComObjPtr<StorageController> ctl;
12382 ctl.createObject();
12383 ctl->init(this, *it);
12384 mStorageControllers->push_back(ctl);
12385 }
12386
12387 mUSBControllers.allocate();
12388 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12389 it != aMachine->mUSBControllers->end();
12390 ++it)
12391 {
12392 ComObjPtr<USBController> ctl;
12393 ctl.createObject();
12394 ctl->init(this, *it);
12395 mUSBControllers->push_back(ctl);
12396 }
12397
12398 unconst(mBIOSSettings).createObject();
12399 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12400 /* create another VRDEServer object that will be mutable */
12401 unconst(mVRDEServer).createObject();
12402 mVRDEServer->init(this, aMachine->mVRDEServer);
12403 /* create another audio adapter object that will be mutable */
12404 unconst(mAudioAdapter).createObject();
12405 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12406 /* create a list of serial ports that will be mutable */
12407 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12408 {
12409 unconst(mSerialPorts[slot]).createObject();
12410 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12411 }
12412 /* create a list of parallel ports that will be mutable */
12413 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12414 {
12415 unconst(mParallelPorts[slot]).createObject();
12416 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12417 }
12418
12419 /* create another USB device filters object that will be mutable */
12420 unconst(mUSBDeviceFilters).createObject();
12421 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12422
12423 /* create a list of network adapters that will be mutable */
12424 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12425 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12426 {
12427 unconst(mNetworkAdapters[slot]).createObject();
12428 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12429 }
12430
12431 /* create another bandwidth control object that will be mutable */
12432 unconst(mBandwidthControl).createObject();
12433 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12434
12435 /* default is to delete saved state on Saved -> PoweredOff transition */
12436 mRemoveSavedState = true;
12437
12438 /* Confirm a successful initialization when it's the case */
12439 autoInitSpan.setSucceeded();
12440
12441 miNATNetworksStarted = 0;
12442
12443 LogFlowThisFuncLeave();
12444 return rc;
12445}
12446
12447/**
12448 * Uninitializes this session object. If the reason is other than
12449 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12450 * or the client watcher code.
12451 *
12452 * @param aReason uninitialization reason
12453 *
12454 * @note Locks mParent + this object for writing.
12455 */
12456void SessionMachine::uninit(Uninit::Reason aReason)
12457{
12458 LogFlowThisFuncEnter();
12459 LogFlowThisFunc(("reason=%d\n", aReason));
12460
12461 /*
12462 * Strongly reference ourselves to prevent this object deletion after
12463 * mData->mSession.mMachine.setNull() below (which can release the last
12464 * reference and call the destructor). Important: this must be done before
12465 * accessing any members (and before AutoUninitSpan that does it as well).
12466 * This self reference will be released as the very last step on return.
12467 */
12468 ComObjPtr<SessionMachine> selfRef = this;
12469
12470 /* Enclose the state transition Ready->InUninit->NotReady */
12471 AutoUninitSpan autoUninitSpan(this);
12472 if (autoUninitSpan.uninitDone())
12473 {
12474 LogFlowThisFunc(("Already uninitialized\n"));
12475 LogFlowThisFuncLeave();
12476 return;
12477 }
12478
12479 if (autoUninitSpan.initFailed())
12480 {
12481 /* We've been called by init() because it's failed. It's not really
12482 * necessary (nor it's safe) to perform the regular uninit sequence
12483 * below, the following is enough.
12484 */
12485 LogFlowThisFunc(("Initialization failed.\n"));
12486 /* destroy the machine client token */
12487 if (mClientToken)
12488 {
12489 delete mClientToken;
12490 mClientToken = NULL;
12491 }
12492 uninitDataAndChildObjects();
12493 mData.free();
12494 unconst(mParent) = NULL;
12495 unconst(mPeer) = NULL;
12496 LogFlowThisFuncLeave();
12497 return;
12498 }
12499
12500 MachineState_T lastState;
12501 {
12502 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12503 lastState = mData->mMachineState;
12504 }
12505 NOREF(lastState);
12506
12507#ifdef VBOX_WITH_USB
12508 // release all captured USB devices, but do this before requesting the locks below
12509 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12510 {
12511 /* Console::captureUSBDevices() is called in the VM process only after
12512 * setting the machine state to Starting or Restoring.
12513 * Console::detachAllUSBDevices() will be called upon successful
12514 * termination. So, we need to release USB devices only if there was
12515 * an abnormal termination of a running VM.
12516 *
12517 * This is identical to SessionMachine::DetachAllUSBDevices except
12518 * for the aAbnormal argument. */
12519 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12520 AssertComRC(rc);
12521 NOREF(rc);
12522
12523 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12524 if (service)
12525 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12526 }
12527#endif /* VBOX_WITH_USB */
12528
12529 // we need to lock this object in uninit() because the lock is shared
12530 // with mPeer (as well as data we modify below). mParent lock is needed
12531 // by several calls to it, and USB needs host lock.
12532 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12533
12534#ifdef VBOX_WITH_RESOURCE_USAGE_API
12535 /*
12536 * It is safe to call Machine::i_unregisterMetrics() here because
12537 * PerformanceCollector::samplerCallback no longer accesses guest methods
12538 * holding the lock.
12539 */
12540 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12541 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12542 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12543 this, __PRETTY_FUNCTION__, mCollectorGuest));
12544 if (mCollectorGuest)
12545 {
12546 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12547 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12548 mCollectorGuest = NULL;
12549 }
12550#endif
12551
12552 if (aReason == Uninit::Abnormal)
12553 {
12554 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12555 Global::IsOnlineOrTransient(lastState)));
12556
12557 /* reset the state to Aborted */
12558 if (mData->mMachineState != MachineState_Aborted)
12559 i_setMachineState(MachineState_Aborted);
12560 }
12561
12562 // any machine settings modified?
12563 if (mData->flModifications)
12564 {
12565 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12566 i_rollback(false /* aNotify */);
12567 }
12568
12569 mData->mSession.mPID = NIL_RTPROCESS;
12570
12571 if (aReason == Uninit::Unexpected)
12572 {
12573 /* Uninitialization didn't come from #checkForDeath(), so tell the
12574 * client watcher thread to update the set of machines that have open
12575 * sessions. */
12576 mParent->i_updateClientWatcher();
12577 }
12578
12579 /* uninitialize all remote controls */
12580 if (mData->mSession.mRemoteControls.size())
12581 {
12582 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12583 mData->mSession.mRemoteControls.size()));
12584
12585 Data::Session::RemoteControlList::iterator it =
12586 mData->mSession.mRemoteControls.begin();
12587 while (it != mData->mSession.mRemoteControls.end())
12588 {
12589 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12590 HRESULT rc = (*it)->Uninitialize();
12591 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12592 if (FAILED(rc))
12593 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12594 ++it;
12595 }
12596 mData->mSession.mRemoteControls.clear();
12597 }
12598
12599 /* Remove all references to the NAT network service. The service will stop
12600 * if all references (also from other VMs) are removed. */
12601 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12602 {
12603 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12604 {
12605 NetworkAttachmentType_T type;
12606 HRESULT hrc;
12607
12608 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12609 if ( SUCCEEDED(hrc)
12610 && type == NetworkAttachmentType_NATNetwork)
12611 {
12612 Bstr name;
12613 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12614 if (SUCCEEDED(hrc))
12615 {
12616 multilock.release();
12617 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12618 mUserData->s.strName.c_str(), name.raw()));
12619 mParent->i_natNetworkRefDec(name.raw());
12620 multilock.acquire();
12621 }
12622 }
12623 }
12624 }
12625
12626 /*
12627 * An expected uninitialization can come only from #checkForDeath().
12628 * Otherwise it means that something's gone really wrong (for example,
12629 * the Session implementation has released the VirtualBox reference
12630 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12631 * etc). However, it's also possible, that the client releases the IPC
12632 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12633 * but the VirtualBox release event comes first to the server process.
12634 * This case is practically possible, so we should not assert on an
12635 * unexpected uninit, just log a warning.
12636 */
12637
12638 if ((aReason == Uninit::Unexpected))
12639 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12640
12641 if (aReason != Uninit::Normal)
12642 {
12643 mData->mSession.mDirectControl.setNull();
12644 }
12645 else
12646 {
12647 /* this must be null here (see #OnSessionEnd()) */
12648 Assert(mData->mSession.mDirectControl.isNull());
12649 Assert(mData->mSession.mState == SessionState_Unlocking);
12650 Assert(!mData->mSession.mProgress.isNull());
12651 }
12652 if (mData->mSession.mProgress)
12653 {
12654 if (aReason == Uninit::Normal)
12655 mData->mSession.mProgress->i_notifyComplete(S_OK);
12656 else
12657 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12658 COM_IIDOF(ISession),
12659 getComponentName(),
12660 tr("The VM session was aborted"));
12661 mData->mSession.mProgress.setNull();
12662 }
12663
12664 /* remove the association between the peer machine and this session machine */
12665 Assert( (SessionMachine*)mData->mSession.mMachine == this
12666 || aReason == Uninit::Unexpected);
12667
12668 /* reset the rest of session data */
12669 mData->mSession.mLockType = LockType_Null;
12670 mData->mSession.mMachine.setNull();
12671 mData->mSession.mState = SessionState_Unlocked;
12672 mData->mSession.mName.setNull();
12673
12674 /* destroy the machine client token before leaving the exclusive lock */
12675 if (mClientToken)
12676 {
12677 delete mClientToken;
12678 mClientToken = NULL;
12679 }
12680
12681 /* fire an event */
12682 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12683
12684 uninitDataAndChildObjects();
12685
12686 /* free the essential data structure last */
12687 mData.free();
12688
12689 /* release the exclusive lock before setting the below two to NULL */
12690 multilock.release();
12691
12692 unconst(mParent) = NULL;
12693 unconst(mPeer) = NULL;
12694
12695 LogFlowThisFuncLeave();
12696}
12697
12698// util::Lockable interface
12699////////////////////////////////////////////////////////////////////////////////
12700
12701/**
12702 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12703 * with the primary Machine instance (mPeer).
12704 */
12705RWLockHandle *SessionMachine::lockHandle() const
12706{
12707 AssertReturn(mPeer != NULL, NULL);
12708 return mPeer->lockHandle();
12709}
12710
12711// IInternalMachineControl methods
12712////////////////////////////////////////////////////////////////////////////////
12713
12714/**
12715 * Passes collected guest statistics to performance collector object
12716 */
12717HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12718 ULONG aCpuKernel, ULONG aCpuIdle,
12719 ULONG aMemTotal, ULONG aMemFree,
12720 ULONG aMemBalloon, ULONG aMemShared,
12721 ULONG aMemCache, ULONG aPageTotal,
12722 ULONG aAllocVMM, ULONG aFreeVMM,
12723 ULONG aBalloonedVMM, ULONG aSharedVMM,
12724 ULONG aVmNetRx, ULONG aVmNetTx)
12725{
12726#ifdef VBOX_WITH_RESOURCE_USAGE_API
12727 if (mCollectorGuest)
12728 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12729 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12730 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12731 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12732
12733 return S_OK;
12734#else
12735 NOREF(aValidStats);
12736 NOREF(aCpuUser);
12737 NOREF(aCpuKernel);
12738 NOREF(aCpuIdle);
12739 NOREF(aMemTotal);
12740 NOREF(aMemFree);
12741 NOREF(aMemBalloon);
12742 NOREF(aMemShared);
12743 NOREF(aMemCache);
12744 NOREF(aPageTotal);
12745 NOREF(aAllocVMM);
12746 NOREF(aFreeVMM);
12747 NOREF(aBalloonedVMM);
12748 NOREF(aSharedVMM);
12749 NOREF(aVmNetRx);
12750 NOREF(aVmNetTx);
12751 return E_NOTIMPL;
12752#endif
12753}
12754
12755////////////////////////////////////////////////////////////////////////////////
12756//
12757// SessionMachine task records
12758//
12759////////////////////////////////////////////////////////////////////////////////
12760
12761/**
12762 * Task record for saving the machine state.
12763 */
12764struct SessionMachine::SaveStateTask
12765 : public Machine::Task
12766{
12767 SaveStateTask(SessionMachine *m,
12768 Progress *p,
12769 const Utf8Str &t,
12770 Reason_T enmReason,
12771 const Utf8Str &strStateFilePath)
12772 : Task(m, p, t),
12773 m_enmReason(enmReason),
12774 m_strStateFilePath(strStateFilePath)
12775 {}
12776
12777 void handler()
12778 {
12779 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12780 }
12781
12782 Reason_T m_enmReason;
12783 Utf8Str m_strStateFilePath;
12784};
12785
12786/**
12787 * Task thread implementation for SessionMachine::SaveState(), called from
12788 * SessionMachine::taskHandler().
12789 *
12790 * @note Locks this object for writing.
12791 *
12792 * @param task
12793 * @return
12794 */
12795void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12796{
12797 LogFlowThisFuncEnter();
12798
12799 AutoCaller autoCaller(this);
12800 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12801 if (FAILED(autoCaller.rc()))
12802 {
12803 /* we might have been uninitialized because the session was accidentally
12804 * closed by the client, so don't assert */
12805 HRESULT rc = setError(E_FAIL,
12806 tr("The session has been accidentally closed"));
12807 task.m_pProgress->i_notifyComplete(rc);
12808 LogFlowThisFuncLeave();
12809 return;
12810 }
12811
12812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12813
12814 HRESULT rc = S_OK;
12815
12816 try
12817 {
12818 ComPtr<IInternalSessionControl> directControl;
12819 if (mData->mSession.mLockType == LockType_VM)
12820 directControl = mData->mSession.mDirectControl;
12821 if (directControl.isNull())
12822 throw setError(VBOX_E_INVALID_VM_STATE,
12823 tr("Trying to save state without a running VM"));
12824 alock.release();
12825 BOOL fSuspendedBySave;
12826 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12827 Assert(!fSuspendedBySave);
12828 alock.acquire();
12829
12830 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12831 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12832 throw E_FAIL);
12833
12834 if (SUCCEEDED(rc))
12835 {
12836 mSSData->strStateFilePath = task.m_strStateFilePath;
12837
12838 /* save all VM settings */
12839 rc = i_saveSettings(NULL);
12840 // no need to check whether VirtualBox.xml needs saving also since
12841 // we can't have a name change pending at this point
12842 }
12843 else
12844 {
12845 // On failure, set the state to the state we had at the beginning.
12846 i_setMachineState(task.m_machineStateBackup);
12847 i_updateMachineStateOnClient();
12848
12849 // Delete the saved state file (might have been already created).
12850 // No need to check whether this is shared with a snapshot here
12851 // because we certainly created a fresh saved state file here.
12852 RTFileDelete(task.m_strStateFilePath.c_str());
12853 }
12854 }
12855 catch (HRESULT aRC) { rc = aRC; }
12856
12857 task.m_pProgress->i_notifyComplete(rc);
12858
12859 LogFlowThisFuncLeave();
12860}
12861
12862/**
12863 * @note Locks this object for writing.
12864 */
12865HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12866{
12867 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12868}
12869
12870HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12871{
12872 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12873
12874 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12875 if (FAILED(rc)) return rc;
12876
12877 if ( mData->mMachineState != MachineState_Running
12878 && mData->mMachineState != MachineState_Paused
12879 )
12880 return setError(VBOX_E_INVALID_VM_STATE,
12881 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12882 Global::stringifyMachineState(mData->mMachineState));
12883
12884 ComObjPtr<Progress> pProgress;
12885 pProgress.createObject();
12886 rc = pProgress->init(i_getVirtualBox(),
12887 static_cast<IMachine *>(this) /* aInitiator */,
12888 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12889 FALSE /* aCancelable */);
12890 if (FAILED(rc))
12891 return rc;
12892
12893 Utf8Str strStateFilePath;
12894 i_composeSavedStateFilename(strStateFilePath);
12895
12896 /* create and start the task on a separate thread (note that it will not
12897 * start working until we release alock) */
12898 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12899 rc = pTask->createThread();
12900 if (FAILED(rc))
12901 return rc;
12902
12903 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12904 i_setMachineState(MachineState_Saving);
12905 i_updateMachineStateOnClient();
12906
12907 pProgress.queryInterfaceTo(aProgress.asOutParam());
12908
12909 return S_OK;
12910}
12911
12912/**
12913 * @note Locks this object for writing.
12914 */
12915HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12916{
12917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12918
12919 HRESULT rc = i_checkStateDependency(MutableStateDep);
12920 if (FAILED(rc)) return rc;
12921
12922 if ( mData->mMachineState != MachineState_PoweredOff
12923 && mData->mMachineState != MachineState_Teleported
12924 && mData->mMachineState != MachineState_Aborted
12925 )
12926 return setError(VBOX_E_INVALID_VM_STATE,
12927 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12928 Global::stringifyMachineState(mData->mMachineState));
12929
12930 com::Utf8Str stateFilePathFull;
12931 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12932 if (RT_FAILURE(vrc))
12933 return setError(VBOX_E_FILE_ERROR,
12934 tr("Invalid saved state file path '%s' (%Rrc)"),
12935 aSavedStateFile.c_str(),
12936 vrc);
12937
12938 mSSData->strStateFilePath = stateFilePathFull;
12939
12940 /* The below i_setMachineState() will detect the state transition and will
12941 * update the settings file */
12942
12943 return i_setMachineState(MachineState_Saved);
12944}
12945
12946/**
12947 * @note Locks this object for writing.
12948 */
12949HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12950{
12951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12952
12953 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12954 if (FAILED(rc)) return rc;
12955
12956 if (mData->mMachineState != MachineState_Saved)
12957 return setError(VBOX_E_INVALID_VM_STATE,
12958 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12959 Global::stringifyMachineState(mData->mMachineState));
12960
12961 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12962
12963 /*
12964 * Saved -> PoweredOff transition will be detected in the SessionMachine
12965 * and properly handled.
12966 */
12967 rc = i_setMachineState(MachineState_PoweredOff);
12968 return rc;
12969}
12970
12971
12972/**
12973 * @note Locks the same as #i_setMachineState() does.
12974 */
12975HRESULT SessionMachine::updateState(MachineState_T aState)
12976{
12977 return i_setMachineState(aState);
12978}
12979
12980/**
12981 * @note Locks this object for writing.
12982 */
12983HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12984{
12985 IProgress* pProgress(aProgress);
12986
12987 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12988
12989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12990
12991 if (mData->mSession.mState != SessionState_Locked)
12992 return VBOX_E_INVALID_OBJECT_STATE;
12993
12994 if (!mData->mSession.mProgress.isNull())
12995 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12996
12997 /* If we didn't reference the NAT network service yet, add a reference to
12998 * force a start */
12999 if (miNATNetworksStarted < 1)
13000 {
13001 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13002 {
13003 NetworkAttachmentType_T type;
13004 HRESULT hrc;
13005 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13006 if ( SUCCEEDED(hrc)
13007 && type == NetworkAttachmentType_NATNetwork)
13008 {
13009 Bstr name;
13010 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13011 if (SUCCEEDED(hrc))
13012 {
13013 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13014 mUserData->s.strName.c_str(), name.raw()));
13015 mPeer->lockHandle()->unlockWrite();
13016 mParent->i_natNetworkRefInc(name.raw());
13017#ifdef RT_LOCK_STRICT
13018 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13019#else
13020 mPeer->lockHandle()->lockWrite();
13021#endif
13022 }
13023 }
13024 }
13025 miNATNetworksStarted++;
13026 }
13027
13028 LogFlowThisFunc(("returns S_OK.\n"));
13029 return S_OK;
13030}
13031
13032/**
13033 * @note Locks this object for writing.
13034 */
13035HRESULT SessionMachine::endPowerUp(LONG aResult)
13036{
13037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13038
13039 if (mData->mSession.mState != SessionState_Locked)
13040 return VBOX_E_INVALID_OBJECT_STATE;
13041
13042 /* Finalize the LaunchVMProcess progress object. */
13043 if (mData->mSession.mProgress)
13044 {
13045 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13046 mData->mSession.mProgress.setNull();
13047 }
13048
13049 if (SUCCEEDED((HRESULT)aResult))
13050 {
13051#ifdef VBOX_WITH_RESOURCE_USAGE_API
13052 /* The VM has been powered up successfully, so it makes sense
13053 * now to offer the performance metrics for a running machine
13054 * object. Doing it earlier wouldn't be safe. */
13055 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13056 mData->mSession.mPID);
13057#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13058 }
13059
13060 return S_OK;
13061}
13062
13063/**
13064 * @note Locks this object for writing.
13065 */
13066HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13067{
13068 LogFlowThisFuncEnter();
13069
13070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13071
13072 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13073 E_FAIL);
13074
13075 /* create a progress object to track operation completion */
13076 ComObjPtr<Progress> pProgress;
13077 pProgress.createObject();
13078 pProgress->init(i_getVirtualBox(),
13079 static_cast<IMachine *>(this) /* aInitiator */,
13080 Bstr(tr("Stopping the virtual machine")).raw(),
13081 FALSE /* aCancelable */);
13082
13083 /* fill in the console task data */
13084 mConsoleTaskData.mLastState = mData->mMachineState;
13085 mConsoleTaskData.mProgress = pProgress;
13086
13087 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13088 i_setMachineState(MachineState_Stopping);
13089
13090 pProgress.queryInterfaceTo(aProgress.asOutParam());
13091
13092 return S_OK;
13093}
13094
13095/**
13096 * @note Locks this object for writing.
13097 */
13098HRESULT SessionMachine::endPoweringDown(LONG aResult,
13099 const com::Utf8Str &aErrMsg)
13100{
13101 LogFlowThisFuncEnter();
13102
13103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13104
13105 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13106 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13107 && mConsoleTaskData.mLastState != MachineState_Null,
13108 E_FAIL);
13109
13110 /*
13111 * On failure, set the state to the state we had when BeginPoweringDown()
13112 * was called (this is expected by Console::PowerDown() and the associated
13113 * task). On success the VM process already changed the state to
13114 * MachineState_PoweredOff, so no need to do anything.
13115 */
13116 if (FAILED(aResult))
13117 i_setMachineState(mConsoleTaskData.mLastState);
13118
13119 /* notify the progress object about operation completion */
13120 Assert(mConsoleTaskData.mProgress);
13121 if (SUCCEEDED(aResult))
13122 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13123 else
13124 {
13125 if (aErrMsg.length())
13126 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13127 COM_IIDOF(ISession),
13128 getComponentName(),
13129 aErrMsg.c_str());
13130 else
13131 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13132 }
13133
13134 /* clear out the temporary saved state data */
13135 mConsoleTaskData.mLastState = MachineState_Null;
13136 mConsoleTaskData.mProgress.setNull();
13137
13138 LogFlowThisFuncLeave();
13139 return S_OK;
13140}
13141
13142
13143/**
13144 * Goes through the USB filters of the given machine to see if the given
13145 * device matches any filter or not.
13146 *
13147 * @note Locks the same as USBController::hasMatchingFilter() does.
13148 */
13149HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13150 BOOL *aMatched,
13151 ULONG *aMaskedInterfaces)
13152{
13153 LogFlowThisFunc(("\n"));
13154
13155#ifdef VBOX_WITH_USB
13156 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13157#else
13158 NOREF(aDevice);
13159 NOREF(aMaskedInterfaces);
13160 *aMatched = FALSE;
13161#endif
13162
13163 return S_OK;
13164}
13165
13166/**
13167 * @note Locks the same as Host::captureUSBDevice() does.
13168 */
13169HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13170{
13171 LogFlowThisFunc(("\n"));
13172
13173#ifdef VBOX_WITH_USB
13174 /* if captureDeviceForVM() fails, it must have set extended error info */
13175 clearError();
13176 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13177 if (FAILED(rc)) return rc;
13178
13179 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13180 AssertReturn(service, E_FAIL);
13181 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13182#else
13183 NOREF(aId);
13184 return E_NOTIMPL;
13185#endif
13186}
13187
13188/**
13189 * @note Locks the same as Host::detachUSBDevice() does.
13190 */
13191HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13192 BOOL aDone)
13193{
13194 LogFlowThisFunc(("\n"));
13195
13196#ifdef VBOX_WITH_USB
13197 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13198 AssertReturn(service, E_FAIL);
13199 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13200#else
13201 NOREF(aId);
13202 NOREF(aDone);
13203 return E_NOTIMPL;
13204#endif
13205}
13206
13207/**
13208 * Inserts all machine filters to the USB proxy service and then calls
13209 * Host::autoCaptureUSBDevices().
13210 *
13211 * Called by Console from the VM process upon VM startup.
13212 *
13213 * @note Locks what called methods lock.
13214 */
13215HRESULT SessionMachine::autoCaptureUSBDevices()
13216{
13217 LogFlowThisFunc(("\n"));
13218
13219#ifdef VBOX_WITH_USB
13220 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13221 AssertComRC(rc);
13222 NOREF(rc);
13223
13224 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13225 AssertReturn(service, E_FAIL);
13226 return service->autoCaptureDevicesForVM(this);
13227#else
13228 return S_OK;
13229#endif
13230}
13231
13232/**
13233 * Removes all machine filters from the USB proxy service and then calls
13234 * Host::detachAllUSBDevices().
13235 *
13236 * Called by Console from the VM process upon normal VM termination or by
13237 * SessionMachine::uninit() upon abnormal VM termination (from under the
13238 * Machine/SessionMachine lock).
13239 *
13240 * @note Locks what called methods lock.
13241 */
13242HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13243{
13244 LogFlowThisFunc(("\n"));
13245
13246#ifdef VBOX_WITH_USB
13247 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13248 AssertComRC(rc);
13249 NOREF(rc);
13250
13251 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13252 AssertReturn(service, E_FAIL);
13253 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13254#else
13255 NOREF(aDone);
13256 return S_OK;
13257#endif
13258}
13259
13260/**
13261 * @note Locks this object for writing.
13262 */
13263HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13264 ComPtr<IProgress> &aProgress)
13265{
13266 LogFlowThisFuncEnter();
13267
13268 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13269 /*
13270 * We don't assert below because it might happen that a non-direct session
13271 * informs us it is closed right after we've been uninitialized -- it's ok.
13272 */
13273
13274 /* get IInternalSessionControl interface */
13275 ComPtr<IInternalSessionControl> control(aSession);
13276
13277 ComAssertRet(!control.isNull(), E_INVALIDARG);
13278
13279 /* Creating a Progress object requires the VirtualBox lock, and
13280 * thus locking it here is required by the lock order rules. */
13281 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13282
13283 if (control == mData->mSession.mDirectControl)
13284 {
13285 /* The direct session is being normally closed by the client process
13286 * ----------------------------------------------------------------- */
13287
13288 /* go to the closing state (essential for all open*Session() calls and
13289 * for #checkForDeath()) */
13290 Assert(mData->mSession.mState == SessionState_Locked);
13291 mData->mSession.mState = SessionState_Unlocking;
13292
13293 /* set direct control to NULL to release the remote instance */
13294 mData->mSession.mDirectControl.setNull();
13295 LogFlowThisFunc(("Direct control is set to NULL\n"));
13296
13297 if (mData->mSession.mProgress)
13298 {
13299 /* finalize the progress, someone might wait if a frontend
13300 * closes the session before powering on the VM. */
13301 mData->mSession.mProgress->notifyComplete(E_FAIL,
13302 COM_IIDOF(ISession),
13303 getComponentName(),
13304 tr("The VM session was closed before any attempt to power it on"));
13305 mData->mSession.mProgress.setNull();
13306 }
13307
13308 /* Create the progress object the client will use to wait until
13309 * #checkForDeath() is called to uninitialize this session object after
13310 * it releases the IPC semaphore.
13311 * Note! Because we're "reusing" mProgress here, this must be a proxy
13312 * object just like for LaunchVMProcess. */
13313 Assert(mData->mSession.mProgress.isNull());
13314 ComObjPtr<ProgressProxy> progress;
13315 progress.createObject();
13316 ComPtr<IUnknown> pPeer(mPeer);
13317 progress->init(mParent, pPeer,
13318 Bstr(tr("Closing session")).raw(),
13319 FALSE /* aCancelable */);
13320 progress.queryInterfaceTo(aProgress.asOutParam());
13321 mData->mSession.mProgress = progress;
13322 }
13323 else
13324 {
13325 /* the remote session is being normally closed */
13326 Data::Session::RemoteControlList::iterator it =
13327 mData->mSession.mRemoteControls.begin();
13328 while (it != mData->mSession.mRemoteControls.end())
13329 {
13330 if (control == *it)
13331 break;
13332 ++it;
13333 }
13334 BOOL found = it != mData->mSession.mRemoteControls.end();
13335 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13336 E_INVALIDARG);
13337 // This MUST be erase(it), not remove(*it) as the latter triggers a
13338 // very nasty use after free due to the place where the value "lives".
13339 mData->mSession.mRemoteControls.erase(it);
13340 }
13341
13342 /* signal the client watcher thread, because the client is going away */
13343 mParent->i_updateClientWatcher();
13344
13345 LogFlowThisFuncLeave();
13346 return S_OK;
13347}
13348
13349HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13350 std::vector<com::Utf8Str> &aValues,
13351 std::vector<LONG64> &aTimestamps,
13352 std::vector<com::Utf8Str> &aFlags)
13353{
13354 LogFlowThisFunc(("\n"));
13355
13356#ifdef VBOX_WITH_GUEST_PROPS
13357 using namespace guestProp;
13358
13359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13360
13361 size_t cEntries = mHWData->mGuestProperties.size();
13362 aNames.resize(cEntries);
13363 aValues.resize(cEntries);
13364 aTimestamps.resize(cEntries);
13365 aFlags.resize(cEntries);
13366
13367 size_t i = 0;
13368 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13369 it != mHWData->mGuestProperties.end();
13370 ++it, ++i)
13371 {
13372 char szFlags[MAX_FLAGS_LEN + 1];
13373 aNames[i] = it->first;
13374 aValues[i] = it->second.strValue;
13375 aTimestamps[i] = it->second.mTimestamp;
13376
13377 /* If it is NULL, keep it NULL. */
13378 if (it->second.mFlags)
13379 {
13380 writeFlags(it->second.mFlags, szFlags);
13381 aFlags[i] = szFlags;
13382 }
13383 else
13384 aFlags[i] = "";
13385 }
13386 return S_OK;
13387#else
13388 ReturnComNotImplemented();
13389#endif
13390}
13391
13392HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13393 const com::Utf8Str &aValue,
13394 LONG64 aTimestamp,
13395 const com::Utf8Str &aFlags,
13396 BOOL *aNotify)
13397{
13398 LogFlowThisFunc(("\n"));
13399
13400#ifdef VBOX_WITH_GUEST_PROPS
13401 using namespace guestProp;
13402
13403 *aNotify = FALSE;
13404
13405 try
13406 {
13407 /*
13408 * Convert input up front.
13409 */
13410 uint32_t fFlags = NILFLAG;
13411 if (aFlags.length())
13412 {
13413 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13414 AssertRCReturn(vrc, E_INVALIDARG);
13415 }
13416
13417 /*
13418 * Now grab the object lock, validate the state and do the update.
13419 */
13420
13421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13422
13423 switch (mData->mMachineState)
13424 {
13425 case MachineState_Paused:
13426 case MachineState_Running:
13427 case MachineState_Teleporting:
13428 case MachineState_TeleportingPausedVM:
13429 case MachineState_OnlineSnapshotting:
13430 case MachineState_LiveSnapshotting:
13431 case MachineState_DeletingSnapshotOnline:
13432 case MachineState_DeletingSnapshotPaused:
13433 case MachineState_Saving:
13434 case MachineState_Stopping:
13435 break;
13436
13437 default:
13438 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13439 VBOX_E_INVALID_VM_STATE);
13440 }
13441
13442 i_setModified(IsModified_MachineData);
13443 mHWData.backup();
13444
13445 bool fDelete = !aValue.length();
13446 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13447 if (it != mHWData->mGuestProperties.end())
13448 {
13449 if (!fDelete)
13450 {
13451 it->second.strValue = aValue;
13452 it->second.mTimestamp = aTimestamp;
13453 it->second.mFlags = fFlags;
13454 }
13455 else
13456 mHWData->mGuestProperties.erase(it);
13457
13458 mData->mGuestPropertiesModified = TRUE;
13459 }
13460 else if (!fDelete)
13461 {
13462 HWData::GuestProperty prop;
13463 prop.strValue = aValue;
13464 prop.mTimestamp = aTimestamp;
13465 prop.mFlags = fFlags;
13466
13467 mHWData->mGuestProperties[aName] = prop;
13468 mData->mGuestPropertiesModified = TRUE;
13469 }
13470
13471 /*
13472 * Send a callback notification if appropriate
13473 */
13474 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13475 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13476 RTSTR_MAX,
13477 aName.c_str(),
13478 RTSTR_MAX, NULL)
13479 )
13480 {
13481 alock.release();
13482
13483 mParent->i_onGuestPropertyChange(mData->mUuid,
13484 Bstr(aName).raw(),
13485 Bstr(aValue).raw(),
13486 Bstr(aFlags).raw());
13487 *aNotify = TRUE;
13488 }
13489 }
13490 catch (...)
13491 {
13492 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13493 }
13494 return S_OK;
13495#else
13496 ReturnComNotImplemented();
13497#endif
13498}
13499
13500
13501HRESULT SessionMachine::lockMedia()
13502{
13503 AutoMultiWriteLock2 alock(this->lockHandle(),
13504 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13505
13506 AssertReturn( mData->mMachineState == MachineState_Starting
13507 || mData->mMachineState == MachineState_Restoring
13508 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13509
13510 clearError();
13511 alock.release();
13512 return i_lockMedia();
13513}
13514
13515HRESULT SessionMachine::unlockMedia()
13516{
13517 HRESULT hrc = i_unlockMedia();
13518 return hrc;
13519}
13520
13521HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13522 ComPtr<IMediumAttachment> &aNewAttachment)
13523{
13524 // request the host lock first, since might be calling Host methods for getting host drives;
13525 // next, protect the media tree all the while we're in here, as well as our member variables
13526 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13527 this->lockHandle(),
13528 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13529
13530 IMediumAttachment *iAttach = aAttachment;
13531 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13532
13533 Bstr ctrlName;
13534 LONG lPort;
13535 LONG lDevice;
13536 bool fTempEject;
13537 {
13538 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13539
13540 /* Need to query the details first, as the IMediumAttachment reference
13541 * might be to the original settings, which we are going to change. */
13542 ctrlName = pAttach->i_getControllerName();
13543 lPort = pAttach->i_getPort();
13544 lDevice = pAttach->i_getDevice();
13545 fTempEject = pAttach->i_getTempEject();
13546 }
13547
13548 if (!fTempEject)
13549 {
13550 /* Remember previously mounted medium. The medium before taking the
13551 * backup is not necessarily the same thing. */
13552 ComObjPtr<Medium> oldmedium;
13553 oldmedium = pAttach->i_getMedium();
13554
13555 i_setModified(IsModified_Storage);
13556 mMediaData.backup();
13557
13558 // The backup operation makes the pAttach reference point to the
13559 // old settings. Re-get the correct reference.
13560 pAttach = i_findAttachment(mMediaData->mAttachments,
13561 ctrlName.raw(),
13562 lPort,
13563 lDevice);
13564
13565 {
13566 AutoCaller autoAttachCaller(this);
13567 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13568
13569 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13570 if (!oldmedium.isNull())
13571 oldmedium->i_removeBackReference(mData->mUuid);
13572
13573 pAttach->i_updateMedium(NULL);
13574 pAttach->i_updateEjected();
13575 }
13576
13577 i_setModified(IsModified_Storage);
13578 }
13579 else
13580 {
13581 {
13582 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13583 pAttach->i_updateEjected();
13584 }
13585 }
13586
13587 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13588
13589 return S_OK;
13590}
13591
13592// public methods only for internal purposes
13593/////////////////////////////////////////////////////////////////////////////
13594
13595#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13596/**
13597 * Called from the client watcher thread to check for expected or unexpected
13598 * death of the client process that has a direct session to this machine.
13599 *
13600 * On Win32 and on OS/2, this method is called only when we've got the
13601 * mutex (i.e. the client has either died or terminated normally) so it always
13602 * returns @c true (the client is terminated, the session machine is
13603 * uninitialized).
13604 *
13605 * On other platforms, the method returns @c true if the client process has
13606 * terminated normally or abnormally and the session machine was uninitialized,
13607 * and @c false if the client process is still alive.
13608 *
13609 * @note Locks this object for writing.
13610 */
13611bool SessionMachine::i_checkForDeath()
13612{
13613 Uninit::Reason reason;
13614 bool terminated = false;
13615
13616 /* Enclose autoCaller with a block because calling uninit() from under it
13617 * will deadlock. */
13618 {
13619 AutoCaller autoCaller(this);
13620 if (!autoCaller.isOk())
13621 {
13622 /* return true if not ready, to cause the client watcher to exclude
13623 * the corresponding session from watching */
13624 LogFlowThisFunc(("Already uninitialized!\n"));
13625 return true;
13626 }
13627
13628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13629
13630 /* Determine the reason of death: if the session state is Closing here,
13631 * everything is fine. Otherwise it means that the client did not call
13632 * OnSessionEnd() before it released the IPC semaphore. This may happen
13633 * either because the client process has abnormally terminated, or
13634 * because it simply forgot to call ISession::Close() before exiting. We
13635 * threat the latter also as an abnormal termination (see
13636 * Session::uninit() for details). */
13637 reason = mData->mSession.mState == SessionState_Unlocking ?
13638 Uninit::Normal :
13639 Uninit::Abnormal;
13640
13641 if (mClientToken)
13642 terminated = mClientToken->release();
13643 } /* AutoCaller block */
13644
13645 if (terminated)
13646 uninit(reason);
13647
13648 return terminated;
13649}
13650
13651void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13652{
13653 LogFlowThisFunc(("\n"));
13654
13655 strTokenId.setNull();
13656
13657 AutoCaller autoCaller(this);
13658 AssertComRCReturnVoid(autoCaller.rc());
13659
13660 Assert(mClientToken);
13661 if (mClientToken)
13662 mClientToken->getId(strTokenId);
13663}
13664#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13665IToken *SessionMachine::i_getToken()
13666{
13667 LogFlowThisFunc(("\n"));
13668
13669 AutoCaller autoCaller(this);
13670 AssertComRCReturn(autoCaller.rc(), NULL);
13671
13672 Assert(mClientToken);
13673 if (mClientToken)
13674 return mClientToken->getToken();
13675 else
13676 return NULL;
13677}
13678#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13679
13680Machine::ClientToken *SessionMachine::i_getClientToken()
13681{
13682 LogFlowThisFunc(("\n"));
13683
13684 AutoCaller autoCaller(this);
13685 AssertComRCReturn(autoCaller.rc(), NULL);
13686
13687 return mClientToken;
13688}
13689
13690
13691/**
13692 * @note Locks this object for reading.
13693 */
13694HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13695{
13696 LogFlowThisFunc(("\n"));
13697
13698 AutoCaller autoCaller(this);
13699 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13700
13701 ComPtr<IInternalSessionControl> directControl;
13702 {
13703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13704 if (mData->mSession.mLockType == LockType_VM)
13705 directControl = mData->mSession.mDirectControl;
13706 }
13707
13708 /* ignore notifications sent after #OnSessionEnd() is called */
13709 if (!directControl)
13710 return S_OK;
13711
13712 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13713}
13714
13715/**
13716 * @note Locks this object for reading.
13717 */
13718HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13719 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13720 IN_BSTR aGuestIp, LONG aGuestPort)
13721{
13722 LogFlowThisFunc(("\n"));
13723
13724 AutoCaller autoCaller(this);
13725 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13726
13727 ComPtr<IInternalSessionControl> directControl;
13728 {
13729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13730 if (mData->mSession.mLockType == LockType_VM)
13731 directControl = mData->mSession.mDirectControl;
13732 }
13733
13734 /* ignore notifications sent after #OnSessionEnd() is called */
13735 if (!directControl)
13736 return S_OK;
13737 /*
13738 * instead acting like callback we ask IVirtualBox deliver corresponding event
13739 */
13740
13741 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13742 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13743 return S_OK;
13744}
13745
13746/**
13747 * @note Locks this object for reading.
13748 */
13749HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13750{
13751 LogFlowThisFunc(("\n"));
13752
13753 AutoCaller autoCaller(this);
13754 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13755
13756 ComPtr<IInternalSessionControl> directControl;
13757 {
13758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13759 if (mData->mSession.mLockType == LockType_VM)
13760 directControl = mData->mSession.mDirectControl;
13761 }
13762
13763 /* ignore notifications sent after #OnSessionEnd() is called */
13764 if (!directControl)
13765 return S_OK;
13766
13767 return directControl->OnSerialPortChange(serialPort);
13768}
13769
13770/**
13771 * @note Locks this object for reading.
13772 */
13773HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13774{
13775 LogFlowThisFunc(("\n"));
13776
13777 AutoCaller autoCaller(this);
13778 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13779
13780 ComPtr<IInternalSessionControl> directControl;
13781 {
13782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13783 if (mData->mSession.mLockType == LockType_VM)
13784 directControl = mData->mSession.mDirectControl;
13785 }
13786
13787 /* ignore notifications sent after #OnSessionEnd() is called */
13788 if (!directControl)
13789 return S_OK;
13790
13791 return directControl->OnParallelPortChange(parallelPort);
13792}
13793
13794/**
13795 * @note Locks this object for reading.
13796 */
13797HRESULT SessionMachine::i_onStorageControllerChange()
13798{
13799 LogFlowThisFunc(("\n"));
13800
13801 AutoCaller autoCaller(this);
13802 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13803
13804 ComPtr<IInternalSessionControl> directControl;
13805 {
13806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13807 if (mData->mSession.mLockType == LockType_VM)
13808 directControl = mData->mSession.mDirectControl;
13809 }
13810
13811 /* ignore notifications sent after #OnSessionEnd() is called */
13812 if (!directControl)
13813 return S_OK;
13814
13815 return directControl->OnStorageControllerChange();
13816}
13817
13818/**
13819 * @note Locks this object for reading.
13820 */
13821HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13822{
13823 LogFlowThisFunc(("\n"));
13824
13825 AutoCaller autoCaller(this);
13826 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13827
13828 ComPtr<IInternalSessionControl> directControl;
13829 {
13830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13831 if (mData->mSession.mLockType == LockType_VM)
13832 directControl = mData->mSession.mDirectControl;
13833 }
13834
13835 /* ignore notifications sent after #OnSessionEnd() is called */
13836 if (!directControl)
13837 return S_OK;
13838
13839 return directControl->OnMediumChange(aAttachment, aForce);
13840}
13841
13842/**
13843 * @note Locks this object for reading.
13844 */
13845HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13846{
13847 LogFlowThisFunc(("\n"));
13848
13849 AutoCaller autoCaller(this);
13850 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13851
13852 ComPtr<IInternalSessionControl> directControl;
13853 {
13854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13855 if (mData->mSession.mLockType == LockType_VM)
13856 directControl = mData->mSession.mDirectControl;
13857 }
13858
13859 /* ignore notifications sent after #OnSessionEnd() is called */
13860 if (!directControl)
13861 return S_OK;
13862
13863 return directControl->OnCPUChange(aCPU, aRemove);
13864}
13865
13866HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13867{
13868 LogFlowThisFunc(("\n"));
13869
13870 AutoCaller autoCaller(this);
13871 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13872
13873 ComPtr<IInternalSessionControl> directControl;
13874 {
13875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13876 if (mData->mSession.mLockType == LockType_VM)
13877 directControl = mData->mSession.mDirectControl;
13878 }
13879
13880 /* ignore notifications sent after #OnSessionEnd() is called */
13881 if (!directControl)
13882 return S_OK;
13883
13884 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13885}
13886
13887/**
13888 * @note Locks this object for reading.
13889 */
13890HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13891{
13892 LogFlowThisFunc(("\n"));
13893
13894 AutoCaller autoCaller(this);
13895 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13896
13897 ComPtr<IInternalSessionControl> directControl;
13898 {
13899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13900 if (mData->mSession.mLockType == LockType_VM)
13901 directControl = mData->mSession.mDirectControl;
13902 }
13903
13904 /* ignore notifications sent after #OnSessionEnd() is called */
13905 if (!directControl)
13906 return S_OK;
13907
13908 return directControl->OnVRDEServerChange(aRestart);
13909}
13910
13911/**
13912 * @note Locks this object for reading.
13913 */
13914HRESULT SessionMachine::i_onVideoCaptureChange()
13915{
13916 LogFlowThisFunc(("\n"));
13917
13918 AutoCaller autoCaller(this);
13919 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13920
13921 ComPtr<IInternalSessionControl> directControl;
13922 {
13923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13924 if (mData->mSession.mLockType == LockType_VM)
13925 directControl = mData->mSession.mDirectControl;
13926 }
13927
13928 /* ignore notifications sent after #OnSessionEnd() is called */
13929 if (!directControl)
13930 return S_OK;
13931
13932 return directControl->OnVideoCaptureChange();
13933}
13934
13935/**
13936 * @note Locks this object for reading.
13937 */
13938HRESULT SessionMachine::i_onUSBControllerChange()
13939{
13940 LogFlowThisFunc(("\n"));
13941
13942 AutoCaller autoCaller(this);
13943 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13944
13945 ComPtr<IInternalSessionControl> directControl;
13946 {
13947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13948 if (mData->mSession.mLockType == LockType_VM)
13949 directControl = mData->mSession.mDirectControl;
13950 }
13951
13952 /* ignore notifications sent after #OnSessionEnd() is called */
13953 if (!directControl)
13954 return S_OK;
13955
13956 return directControl->OnUSBControllerChange();
13957}
13958
13959/**
13960 * @note Locks this object for reading.
13961 */
13962HRESULT SessionMachine::i_onSharedFolderChange()
13963{
13964 LogFlowThisFunc(("\n"));
13965
13966 AutoCaller autoCaller(this);
13967 AssertComRCReturnRC(autoCaller.rc());
13968
13969 ComPtr<IInternalSessionControl> directControl;
13970 {
13971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13972 if (mData->mSession.mLockType == LockType_VM)
13973 directControl = mData->mSession.mDirectControl;
13974 }
13975
13976 /* ignore notifications sent after #OnSessionEnd() is called */
13977 if (!directControl)
13978 return S_OK;
13979
13980 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13981}
13982
13983/**
13984 * @note Locks this object for reading.
13985 */
13986HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13987{
13988 LogFlowThisFunc(("\n"));
13989
13990 AutoCaller autoCaller(this);
13991 AssertComRCReturnRC(autoCaller.rc());
13992
13993 ComPtr<IInternalSessionControl> directControl;
13994 {
13995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13996 if (mData->mSession.mLockType == LockType_VM)
13997 directControl = mData->mSession.mDirectControl;
13998 }
13999
14000 /* ignore notifications sent after #OnSessionEnd() is called */
14001 if (!directControl)
14002 return S_OK;
14003
14004 return directControl->OnClipboardModeChange(aClipboardMode);
14005}
14006
14007/**
14008 * @note Locks this object for reading.
14009 */
14010HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14011{
14012 LogFlowThisFunc(("\n"));
14013
14014 AutoCaller autoCaller(this);
14015 AssertComRCReturnRC(autoCaller.rc());
14016
14017 ComPtr<IInternalSessionControl> directControl;
14018 {
14019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14020 if (mData->mSession.mLockType == LockType_VM)
14021 directControl = mData->mSession.mDirectControl;
14022 }
14023
14024 /* ignore notifications sent after #OnSessionEnd() is called */
14025 if (!directControl)
14026 return S_OK;
14027
14028 return directControl->OnDnDModeChange(aDnDMode);
14029}
14030
14031/**
14032 * @note Locks this object for reading.
14033 */
14034HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14035{
14036 LogFlowThisFunc(("\n"));
14037
14038 AutoCaller autoCaller(this);
14039 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14040
14041 ComPtr<IInternalSessionControl> directControl;
14042 {
14043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14044 if (mData->mSession.mLockType == LockType_VM)
14045 directControl = mData->mSession.mDirectControl;
14046 }
14047
14048 /* ignore notifications sent after #OnSessionEnd() is called */
14049 if (!directControl)
14050 return S_OK;
14051
14052 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14053}
14054
14055/**
14056 * @note Locks this object for reading.
14057 */
14058HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14059{
14060 LogFlowThisFunc(("\n"));
14061
14062 AutoCaller autoCaller(this);
14063 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14064
14065 ComPtr<IInternalSessionControl> directControl;
14066 {
14067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14068 if (mData->mSession.mLockType == LockType_VM)
14069 directControl = mData->mSession.mDirectControl;
14070 }
14071
14072 /* ignore notifications sent after #OnSessionEnd() is called */
14073 if (!directControl)
14074 return S_OK;
14075
14076 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14077}
14078
14079/**
14080 * Returns @c true if this machine's USB controller reports it has a matching
14081 * filter for the given USB device and @c false otherwise.
14082 *
14083 * @note locks this object for reading.
14084 */
14085bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14086{
14087 AutoCaller autoCaller(this);
14088 /* silently return if not ready -- this method may be called after the
14089 * direct machine session has been called */
14090 if (!autoCaller.isOk())
14091 return false;
14092
14093#ifdef VBOX_WITH_USB
14094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14095
14096 switch (mData->mMachineState)
14097 {
14098 case MachineState_Starting:
14099 case MachineState_Restoring:
14100 case MachineState_TeleportingIn:
14101 case MachineState_Paused:
14102 case MachineState_Running:
14103 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14104 * elsewhere... */
14105 alock.release();
14106 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14107 default: break;
14108 }
14109#else
14110 NOREF(aDevice);
14111 NOREF(aMaskedIfs);
14112#endif
14113 return false;
14114}
14115
14116/**
14117 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14118 */
14119HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14120 IVirtualBoxErrorInfo *aError,
14121 ULONG aMaskedIfs,
14122 const com::Utf8Str &aCaptureFilename)
14123{
14124 LogFlowThisFunc(("\n"));
14125
14126 AutoCaller autoCaller(this);
14127
14128 /* This notification may happen after the machine object has been
14129 * uninitialized (the session was closed), so don't assert. */
14130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14131
14132 ComPtr<IInternalSessionControl> directControl;
14133 {
14134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14135 if (mData->mSession.mLockType == LockType_VM)
14136 directControl = mData->mSession.mDirectControl;
14137 }
14138
14139 /* fail on notifications sent after #OnSessionEnd() is called, it is
14140 * expected by the caller */
14141 if (!directControl)
14142 return E_FAIL;
14143
14144 /* No locks should be held at this point. */
14145 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14146 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14147
14148 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14149}
14150
14151/**
14152 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14153 */
14154HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14155 IVirtualBoxErrorInfo *aError)
14156{
14157 LogFlowThisFunc(("\n"));
14158
14159 AutoCaller autoCaller(this);
14160
14161 /* This notification may happen after the machine object has been
14162 * uninitialized (the session was closed), so don't assert. */
14163 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14164
14165 ComPtr<IInternalSessionControl> directControl;
14166 {
14167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14168 if (mData->mSession.mLockType == LockType_VM)
14169 directControl = mData->mSession.mDirectControl;
14170 }
14171
14172 /* fail on notifications sent after #OnSessionEnd() is called, it is
14173 * expected by the caller */
14174 if (!directControl)
14175 return E_FAIL;
14176
14177 /* No locks should be held at this point. */
14178 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14179 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14180
14181 return directControl->OnUSBDeviceDetach(aId, aError);
14182}
14183
14184// protected methods
14185/////////////////////////////////////////////////////////////////////////////
14186
14187/**
14188 * Deletes the given file if it is no longer in use by either the current machine state
14189 * (if the machine is "saved") or any of the machine's snapshots.
14190 *
14191 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14192 * but is different for each SnapshotMachine. When calling this, the order of calling this
14193 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14194 * is therefore critical. I know, it's all rather messy.
14195 *
14196 * @param strStateFile
14197 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14198 * the test for whether the saved state file is in use.
14199 */
14200void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14201 Snapshot *pSnapshotToIgnore)
14202{
14203 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14204 if ( (strStateFile.isNotEmpty())
14205 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14206 )
14207 // ... and it must also not be shared with other snapshots
14208 if ( !mData->mFirstSnapshot
14209 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14210 // this checks the SnapshotMachine's state file paths
14211 )
14212 RTFileDelete(strStateFile.c_str());
14213}
14214
14215/**
14216 * Locks the attached media.
14217 *
14218 * All attached hard disks are locked for writing and DVD/floppy are locked for
14219 * reading. Parents of attached hard disks (if any) are locked for reading.
14220 *
14221 * This method also performs accessibility check of all media it locks: if some
14222 * media is inaccessible, the method will return a failure and a bunch of
14223 * extended error info objects per each inaccessible medium.
14224 *
14225 * Note that this method is atomic: if it returns a success, all media are
14226 * locked as described above; on failure no media is locked at all (all
14227 * succeeded individual locks will be undone).
14228 *
14229 * The caller is responsible for doing the necessary state sanity checks.
14230 *
14231 * The locks made by this method must be undone by calling #unlockMedia() when
14232 * no more needed.
14233 */
14234HRESULT SessionMachine::i_lockMedia()
14235{
14236 AutoCaller autoCaller(this);
14237 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14238
14239 AutoMultiWriteLock2 alock(this->lockHandle(),
14240 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14241
14242 /* bail out if trying to lock things with already set up locking */
14243 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14244
14245 MultiResult mrc(S_OK);
14246
14247 /* Collect locking information for all medium objects attached to the VM. */
14248 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14249 it != mMediaData->mAttachments.end();
14250 ++it)
14251 {
14252 MediumAttachment* pAtt = *it;
14253 DeviceType_T devType = pAtt->i_getType();
14254 Medium *pMedium = pAtt->i_getMedium();
14255
14256 MediumLockList *pMediumLockList(new MediumLockList());
14257 // There can be attachments without a medium (floppy/dvd), and thus
14258 // it's impossible to create a medium lock list. It still makes sense
14259 // to have the empty medium lock list in the map in case a medium is
14260 // attached later.
14261 if (pMedium != NULL)
14262 {
14263 MediumType_T mediumType = pMedium->i_getType();
14264 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14265 || mediumType == MediumType_Shareable;
14266 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14267
14268 alock.release();
14269 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14270 !fIsReadOnlyLock /* fMediumLockWrite */,
14271 false /* fMediumLockWriteAll */,
14272 NULL,
14273 *pMediumLockList);
14274 alock.acquire();
14275 if (FAILED(mrc))
14276 {
14277 delete pMediumLockList;
14278 mData->mSession.mLockedMedia.Clear();
14279 break;
14280 }
14281 }
14282
14283 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14284 if (FAILED(rc))
14285 {
14286 mData->mSession.mLockedMedia.Clear();
14287 mrc = setError(rc,
14288 tr("Collecting locking information for all attached media failed"));
14289 break;
14290 }
14291 }
14292
14293 if (SUCCEEDED(mrc))
14294 {
14295 /* Now lock all media. If this fails, nothing is locked. */
14296 alock.release();
14297 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14298 alock.acquire();
14299 if (FAILED(rc))
14300 {
14301 mrc = setError(rc,
14302 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14303 }
14304 }
14305
14306 return mrc;
14307}
14308
14309/**
14310 * Undoes the locks made by by #lockMedia().
14311 */
14312HRESULT SessionMachine::i_unlockMedia()
14313{
14314 AutoCaller autoCaller(this);
14315 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14316
14317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14318
14319 /* we may be holding important error info on the current thread;
14320 * preserve it */
14321 ErrorInfoKeeper eik;
14322
14323 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14324 AssertComRC(rc);
14325 return rc;
14326}
14327
14328/**
14329 * Helper to change the machine state (reimplementation).
14330 *
14331 * @note Locks this object for writing.
14332 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14333 * it can cause crashes in random places due to unexpectedly committing
14334 * the current settings. The caller is responsible for that. The call
14335 * to saveStateSettings is fine, because this method does not commit.
14336 */
14337HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14338{
14339 LogFlowThisFuncEnter();
14340 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14341
14342 AutoCaller autoCaller(this);
14343 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14344
14345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14346
14347 MachineState_T oldMachineState = mData->mMachineState;
14348
14349 AssertMsgReturn(oldMachineState != aMachineState,
14350 ("oldMachineState=%s, aMachineState=%s\n",
14351 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14352 E_FAIL);
14353
14354 HRESULT rc = S_OK;
14355
14356 int stsFlags = 0;
14357 bool deleteSavedState = false;
14358
14359 /* detect some state transitions */
14360
14361 if ( ( oldMachineState == MachineState_Saved
14362 && aMachineState == MachineState_Restoring)
14363 || ( ( oldMachineState == MachineState_PoweredOff
14364 || oldMachineState == MachineState_Teleported
14365 || oldMachineState == MachineState_Aborted
14366 )
14367 && ( aMachineState == MachineState_TeleportingIn
14368 || aMachineState == MachineState_Starting
14369 )
14370 )
14371 )
14372 {
14373 /* The EMT thread is about to start */
14374
14375 /* Nothing to do here for now... */
14376
14377 /// @todo NEWMEDIA don't let mDVDDrive and other children
14378 /// change anything when in the Starting/Restoring state
14379 }
14380 else if ( ( oldMachineState == MachineState_Running
14381 || oldMachineState == MachineState_Paused
14382 || oldMachineState == MachineState_Teleporting
14383 || oldMachineState == MachineState_OnlineSnapshotting
14384 || oldMachineState == MachineState_LiveSnapshotting
14385 || oldMachineState == MachineState_Stuck
14386 || oldMachineState == MachineState_Starting
14387 || oldMachineState == MachineState_Stopping
14388 || oldMachineState == MachineState_Saving
14389 || oldMachineState == MachineState_Restoring
14390 || oldMachineState == MachineState_TeleportingPausedVM
14391 || oldMachineState == MachineState_TeleportingIn
14392 )
14393 && ( aMachineState == MachineState_PoweredOff
14394 || aMachineState == MachineState_Saved
14395 || aMachineState == MachineState_Teleported
14396 || aMachineState == MachineState_Aborted
14397 )
14398 )
14399 {
14400 /* The EMT thread has just stopped, unlock attached media. Note that as
14401 * opposed to locking that is done from Console, we do unlocking here
14402 * because the VM process may have aborted before having a chance to
14403 * properly unlock all media it locked. */
14404
14405 unlockMedia();
14406 }
14407
14408 if (oldMachineState == MachineState_Restoring)
14409 {
14410 if (aMachineState != MachineState_Saved)
14411 {
14412 /*
14413 * delete the saved state file once the machine has finished
14414 * restoring from it (note that Console sets the state from
14415 * Restoring to Saved if the VM couldn't restore successfully,
14416 * to give the user an ability to fix an error and retry --
14417 * we keep the saved state file in this case)
14418 */
14419 deleteSavedState = true;
14420 }
14421 }
14422 else if ( oldMachineState == MachineState_Saved
14423 && ( aMachineState == MachineState_PoweredOff
14424 || aMachineState == MachineState_Aborted
14425 || aMachineState == MachineState_Teleported
14426 )
14427 )
14428 {
14429 /*
14430 * delete the saved state after SessionMachine::ForgetSavedState() is called
14431 * or if the VM process (owning a direct VM session) crashed while the
14432 * VM was Saved
14433 */
14434
14435 /// @todo (dmik)
14436 // Not sure that deleting the saved state file just because of the
14437 // client death before it attempted to restore the VM is a good
14438 // thing. But when it crashes we need to go to the Aborted state
14439 // which cannot have the saved state file associated... The only
14440 // way to fix this is to make the Aborted condition not a VM state
14441 // but a bool flag: i.e., when a crash occurs, set it to true and
14442 // change the state to PoweredOff or Saved depending on the
14443 // saved state presence.
14444
14445 deleteSavedState = true;
14446 mData->mCurrentStateModified = TRUE;
14447 stsFlags |= SaveSTS_CurStateModified;
14448 }
14449
14450 if ( aMachineState == MachineState_Starting
14451 || aMachineState == MachineState_Restoring
14452 || aMachineState == MachineState_TeleportingIn
14453 )
14454 {
14455 /* set the current state modified flag to indicate that the current
14456 * state is no more identical to the state in the
14457 * current snapshot */
14458 if (!mData->mCurrentSnapshot.isNull())
14459 {
14460 mData->mCurrentStateModified = TRUE;
14461 stsFlags |= SaveSTS_CurStateModified;
14462 }
14463 }
14464
14465 if (deleteSavedState)
14466 {
14467 if (mRemoveSavedState)
14468 {
14469 Assert(!mSSData->strStateFilePath.isEmpty());
14470
14471 // it is safe to delete the saved state file if ...
14472 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14473 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14474 // ... none of the snapshots share the saved state file
14475 )
14476 RTFileDelete(mSSData->strStateFilePath.c_str());
14477 }
14478
14479 mSSData->strStateFilePath.setNull();
14480 stsFlags |= SaveSTS_StateFilePath;
14481 }
14482
14483 /* redirect to the underlying peer machine */
14484 mPeer->i_setMachineState(aMachineState);
14485
14486 if ( aMachineState == MachineState_PoweredOff
14487 || aMachineState == MachineState_Teleported
14488 || aMachineState == MachineState_Aborted
14489 || aMachineState == MachineState_Saved)
14490 {
14491 /* the machine has stopped execution
14492 * (or the saved state file was adopted) */
14493 stsFlags |= SaveSTS_StateTimeStamp;
14494 }
14495
14496 if ( ( oldMachineState == MachineState_PoweredOff
14497 || oldMachineState == MachineState_Aborted
14498 || oldMachineState == MachineState_Teleported
14499 )
14500 && aMachineState == MachineState_Saved)
14501 {
14502 /* the saved state file was adopted */
14503 Assert(!mSSData->strStateFilePath.isEmpty());
14504 stsFlags |= SaveSTS_StateFilePath;
14505 }
14506
14507#ifdef VBOX_WITH_GUEST_PROPS
14508 if ( aMachineState == MachineState_PoweredOff
14509 || aMachineState == MachineState_Aborted
14510 || aMachineState == MachineState_Teleported)
14511 {
14512 /* Make sure any transient guest properties get removed from the
14513 * property store on shutdown. */
14514 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14515
14516 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14517 settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14518 while (it != llGuestProperties.end())
14519 {
14520 const settings::GuestProperty &prop = *it;
14521 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14522 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14523 {
14524 it = llGuestProperties.erase(it);
14525 fNeedsSaving = true;
14526 }
14527 else
14528 {
14529 ++it;
14530 }
14531 }
14532
14533 if (fNeedsSaving)
14534 {
14535 mData->mCurrentStateModified = TRUE;
14536 stsFlags |= SaveSTS_CurStateModified;
14537 }
14538 }
14539#endif /* VBOX_WITH_GUEST_PROPS */
14540
14541 rc = i_saveStateSettings(stsFlags);
14542
14543 if ( ( oldMachineState != MachineState_PoweredOff
14544 && oldMachineState != MachineState_Aborted
14545 && oldMachineState != MachineState_Teleported
14546 )
14547 && ( aMachineState == MachineState_PoweredOff
14548 || aMachineState == MachineState_Aborted
14549 || aMachineState == MachineState_Teleported
14550 )
14551 )
14552 {
14553 /* we've been shut down for any reason */
14554 /* no special action so far */
14555 }
14556
14557 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14558 LogFlowThisFuncLeave();
14559 return rc;
14560}
14561
14562/**
14563 * Sends the current machine state value to the VM process.
14564 *
14565 * @note Locks this object for reading, then calls a client process.
14566 */
14567HRESULT SessionMachine::i_updateMachineStateOnClient()
14568{
14569 AutoCaller autoCaller(this);
14570 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14571
14572 ComPtr<IInternalSessionControl> directControl;
14573 {
14574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14575 AssertReturn(!!mData, E_FAIL);
14576 if (mData->mSession.mLockType == LockType_VM)
14577 directControl = mData->mSession.mDirectControl;
14578
14579 /* directControl may be already set to NULL here in #OnSessionEnd()
14580 * called too early by the direct session process while there is still
14581 * some operation (like deleting the snapshot) in progress. The client
14582 * process in this case is waiting inside Session::close() for the
14583 * "end session" process object to complete, while #uninit() called by
14584 * #checkForDeath() on the Watcher thread is waiting for the pending
14585 * operation to complete. For now, we accept this inconsistent behavior
14586 * and simply do nothing here. */
14587
14588 if (mData->mSession.mState == SessionState_Unlocking)
14589 return S_OK;
14590 }
14591
14592 /* ignore notifications sent after #OnSessionEnd() is called */
14593 if (!directControl)
14594 return S_OK;
14595
14596 return directControl->UpdateMachineState(mData->mMachineState);
14597}
14598
14599
14600/**
14601 * Static Machine method that can get passed to RTThreadCreate to
14602 * have a thread started for a Task. See Machine::Task.
14603 */
14604/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14605{
14606 AssertReturn(pvUser, VERR_INVALID_POINTER);
14607
14608 Task *pTask = static_cast<Task *>(pvUser);
14609 pTask->handler();
14610 /** @todo r=klaus it would be safer to update the progress object here,
14611 * as it avoids possible races due to scoping issues/tricks in the handler */
14612 // it's our responsibility to delete the task
14613 delete pTask;
14614
14615 return 0;
14616}
14617
14618/*static*/
14619HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14620{
14621 va_list args;
14622 va_start(args, pcszMsg);
14623 HRESULT rc = setErrorInternal(aResultCode,
14624 getStaticClassIID(),
14625 getStaticComponentName(),
14626 Utf8Str(pcszMsg, args),
14627 false /* aWarning */,
14628 true /* aLogIt */);
14629 va_end(args);
14630 return rc;
14631}
14632
14633
14634HRESULT Machine::updateState(MachineState_T aState)
14635{
14636 NOREF(aState);
14637 ReturnComNotImplemented();
14638}
14639
14640HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14641{
14642 NOREF(aProgress);
14643 ReturnComNotImplemented();
14644}
14645
14646HRESULT Machine::endPowerUp(LONG aResult)
14647{
14648 NOREF(aResult);
14649 ReturnComNotImplemented();
14650}
14651
14652HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14653{
14654 NOREF(aProgress);
14655 ReturnComNotImplemented();
14656}
14657
14658HRESULT Machine::endPoweringDown(LONG aResult,
14659 const com::Utf8Str &aErrMsg)
14660{
14661 NOREF(aResult);
14662 NOREF(aErrMsg);
14663 ReturnComNotImplemented();
14664}
14665
14666HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14667 BOOL *aMatched,
14668 ULONG *aMaskedInterfaces)
14669{
14670 NOREF(aDevice);
14671 NOREF(aMatched);
14672 NOREF(aMaskedInterfaces);
14673 ReturnComNotImplemented();
14674
14675}
14676
14677HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14678{
14679 NOREF(aId); NOREF(aCaptureFilename);
14680 ReturnComNotImplemented();
14681}
14682
14683HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14684 BOOL aDone)
14685{
14686 NOREF(aId);
14687 NOREF(aDone);
14688 ReturnComNotImplemented();
14689}
14690
14691HRESULT Machine::autoCaptureUSBDevices()
14692{
14693 ReturnComNotImplemented();
14694}
14695
14696HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14697{
14698 NOREF(aDone);
14699 ReturnComNotImplemented();
14700}
14701
14702HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14703 ComPtr<IProgress> &aProgress)
14704{
14705 NOREF(aSession);
14706 NOREF(aProgress);
14707 ReturnComNotImplemented();
14708}
14709
14710HRESULT Machine::finishOnlineMergeMedium()
14711{
14712 ReturnComNotImplemented();
14713}
14714
14715HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14716 std::vector<com::Utf8Str> &aValues,
14717 std::vector<LONG64> &aTimestamps,
14718 std::vector<com::Utf8Str> &aFlags)
14719{
14720 NOREF(aNames);
14721 NOREF(aValues);
14722 NOREF(aTimestamps);
14723 NOREF(aFlags);
14724 ReturnComNotImplemented();
14725}
14726
14727HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14728 const com::Utf8Str &aValue,
14729 LONG64 aTimestamp,
14730 const com::Utf8Str &aFlags,
14731 BOOL *aNotify)
14732{
14733 NOREF(aName);
14734 NOREF(aValue);
14735 NOREF(aTimestamp);
14736 NOREF(aFlags);
14737 NOREF(aNotify);
14738 ReturnComNotImplemented();
14739}
14740
14741HRESULT Machine::lockMedia()
14742{
14743 ReturnComNotImplemented();
14744}
14745
14746HRESULT Machine::unlockMedia()
14747{
14748 ReturnComNotImplemented();
14749}
14750
14751HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14752 ComPtr<IMediumAttachment> &aNewAttachment)
14753{
14754 NOREF(aAttachment);
14755 NOREF(aNewAttachment);
14756 ReturnComNotImplemented();
14757}
14758
14759HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14760 ULONG aCpuUser,
14761 ULONG aCpuKernel,
14762 ULONG aCpuIdle,
14763 ULONG aMemTotal,
14764 ULONG aMemFree,
14765 ULONG aMemBalloon,
14766 ULONG aMemShared,
14767 ULONG aMemCache,
14768 ULONG aPagedTotal,
14769 ULONG aMemAllocTotal,
14770 ULONG aMemFreeTotal,
14771 ULONG aMemBalloonTotal,
14772 ULONG aMemSharedTotal,
14773 ULONG aVmNetRx,
14774 ULONG aVmNetTx)
14775{
14776 NOREF(aValidStats);
14777 NOREF(aCpuUser);
14778 NOREF(aCpuKernel);
14779 NOREF(aCpuIdle);
14780 NOREF(aMemTotal);
14781 NOREF(aMemFree);
14782 NOREF(aMemBalloon);
14783 NOREF(aMemShared);
14784 NOREF(aMemCache);
14785 NOREF(aPagedTotal);
14786 NOREF(aMemAllocTotal);
14787 NOREF(aMemFreeTotal);
14788 NOREF(aMemBalloonTotal);
14789 NOREF(aMemSharedTotal);
14790 NOREF(aVmNetRx);
14791 NOREF(aVmNetTx);
14792 ReturnComNotImplemented();
14793}
Note: See TracBrowser for help on using the repository browser.

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