VirtualBox

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

Last change on this file since 47920 was 47916, checked in by vboxsync, 11 years ago

Main/Machine: fix the code which sets custom icons, and also fix loading of settings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 500.2 KB
Line 
1/* $Id: MachineImpl.cpp 47916 2013-08-20 14:01:53Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
90# define HOSTSUFF_EXE ".exe"
91#else /* !RT_OS_WINDOWS */
92# define HOSTSUFF_EXE ""
93#endif /* !RT_OS_WINDOWS */
94
95// defines / prototypes
96/////////////////////////////////////////////////////////////////////////////
97
98/////////////////////////////////////////////////////////////////////////////
99// Machine::Data structure
100/////////////////////////////////////////////////////////////////////////////
101
102Machine::Data::Data()
103{
104 mRegistered = FALSE;
105 pMachineConfigFile = NULL;
106 /* Contains hints on what has changed when the user is using the VM (config
107 * changes, running the VM, ...). This is used to decide if a config needs
108 * to be written to disk. */
109 flModifications = 0;
110 /* VM modification usually also trigger setting the current state to
111 * "Modified". Although this is not always the case. An e.g. is the VM
112 * initialization phase or when snapshot related data is changed. The
113 * actually behavior is controlled by the following flag. */
114 m_fAllowStateModification = false;
115 mAccessible = FALSE;
116 /* mUuid is initialized in Machine::init() */
117
118 mMachineState = MachineState_PoweredOff;
119 RTTimeNow(&mLastStateChange);
120
121 mMachineStateDeps = 0;
122 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
123 mMachineStateChangePending = 0;
124
125 mCurrentStateModified = TRUE;
126 mGuestPropertiesModified = FALSE;
127
128 mSession.mPID = NIL_RTPROCESS;
129 mSession.mState = SessionState_Unlocked;
130}
131
132Machine::Data::~Data()
133{
134 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
135 {
136 RTSemEventMultiDestroy(mMachineStateDepsSem);
137 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
138 }
139 if (pMachineConfigFile)
140 {
141 delete pMachineConfigFile;
142 pMachineConfigFile = NULL;
143 }
144}
145
146/////////////////////////////////////////////////////////////////////////////
147// Machine::HWData structure
148/////////////////////////////////////////////////////////////////////////////
149
150Machine::HWData::HWData()
151{
152 /* default values for a newly created machine */
153 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
154 mMemorySize = 128;
155 mCPUCount = 1;
156 mCPUHotPlugEnabled = false;
157 mMemoryBalloonSize = 0;
158 mPageFusionEnabled = false;
159 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
160 mVRAMSize = 8;
161 mAccelerate3DEnabled = false;
162 mAccelerate2DVideoEnabled = false;
163 mMonitorCount = 1;
164 mVideoCaptureWidth = 1024;
165 mVideoCaptureHeight = 768;
166 mVideoCaptureRate = 512;
167 mVideoCaptureFPS = 25;
168 mVideoCaptureEnabled = false;
169 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++)
170 maVideoCaptureScreens[i] = true;
171
172 mHWVirtExEnabled = true;
173 mHWVirtExNestedPagingEnabled = true;
174#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
175 mHWVirtExLargePagesEnabled = true;
176#else
177 /* Not supported on 32 bits hosts. */
178 mHWVirtExLargePagesEnabled = false;
179#endif
180 mHWVirtExVPIDEnabled = true;
181 mHWVirtExUXEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
184 mHWVirtExExclusive = false;
185#else
186 mHWVirtExExclusive = true;
187#endif
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
194 mSyntheticCpu = false;
195 mHPETEnabled = false;
196
197 /* default boot order: floppy - DVD - HDD */
198 mBootOrder[0] = DeviceType_Floppy;
199 mBootOrder[1] = DeviceType_DVD;
200 mBootOrder[2] = DeviceType_HardDisk;
201 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
202 mBootOrder[i] = DeviceType_Null;
203
204 mClipboardMode = ClipboardMode_Disabled;
205 mDragAndDropMode = DragAndDropMode_Disabled;
206 mGuestPropertyNotificationPatterns = "";
207
208 mFirmwareType = FirmwareType_BIOS;
209 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
210 mPointingHIDType = PointingHIDType_PS2Mouse;
211 mChipsetType = ChipsetType_PIIX3;
212 mEmulatedUSBWebcamEnabled = FALSE;
213 mEmulatedUSBCardReaderEnabled = FALSE;
214
215 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
216 mCPUAttached[i] = false;
217
218 mIOCacheEnabled = true;
219 mIOCacheSize = 5; /* 5MB */
220
221 /* Maximum CPU execution cap by default. */
222 mCpuExecutionCap = 100;
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 = 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 = 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->id();
345
346 /* Apply BIOS defaults */
347 mBIOSSettings->applyDefaults(aOsType);
348
349 /* Apply network adapters defaults */
350 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
351 mNetworkAdapters[slot]->applyDefaults(aOsType);
352
353 /* Apply serial port defaults */
354 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
355 mSerialPorts[slot]->applyDefaults(aOsType);
356
357 /* Let the OS type select 64-bit ness. */
358 mHWData->mLongMode = aOsType->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 allowStateModification();
365
366 /* commit all changes made during the initialization */
367 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 = 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->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 = 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 allowStateModification();
467
468 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->unregisterMachineMedia(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->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 = 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 = 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 allowStateModification();
566
567 /* commit all changes made during the initialization */
568 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 autoInitSpan.setLimited();
580
581 // uninit media from this machine's media registry, or else
582 // reloading the settings will fail
583 mParent->unregisterMachineMedia(getId());
584 }
585 }
586
587 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
588 "rc=%08X\n",
589 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
590 mData->mRegistered, mData->mAccessible, rc));
591
592 LogFlowThisFuncLeave();
593
594 return rc;
595}
596
597/**
598 * Shared code between the various init() implementations.
599 * @param aParent
600 * @return
601 */
602HRESULT Machine::initImpl(VirtualBox *aParent,
603 const Utf8Str &strConfigFile)
604{
605 LogFlowThisFuncEnter();
606
607 AssertReturn(aParent, E_INVALIDARG);
608 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
609
610 HRESULT rc = S_OK;
611
612 /* share the parent weakly */
613 unconst(mParent) = aParent;
614
615 /* allocate the essential machine data structure (the rest will be
616 * allocated later by initDataAndChildObjects() */
617 mData.allocate();
618
619 /* memorize the config file name (as provided) */
620 mData->m_strConfigFile = strConfigFile;
621
622 /* get the full file name */
623 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
624 if (RT_FAILURE(vrc1))
625 return setError(VBOX_E_FILE_ERROR,
626 tr("Invalid machine settings file name '%s' (%Rrc)"),
627 strConfigFile.c_str(),
628 vrc1);
629
630 LogFlowThisFuncLeave();
631
632 return rc;
633}
634
635/**
636 * Tries to create a machine settings file in the path stored in the machine
637 * instance data. Used when a new machine is created to fail gracefully if
638 * the settings file could not be written (e.g. because machine dir is read-only).
639 * @return
640 */
641HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
642{
643 HRESULT rc = S_OK;
644
645 // when we create a new machine, we must be able to create the settings file
646 RTFILE f = NIL_RTFILE;
647 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
648 if ( RT_SUCCESS(vrc)
649 || vrc == VERR_SHARING_VIOLATION
650 )
651 {
652 if (RT_SUCCESS(vrc))
653 RTFileClose(f);
654 if (!fForceOverwrite)
655 rc = setError(VBOX_E_FILE_ERROR,
656 tr("Machine settings file '%s' already exists"),
657 mData->m_strConfigFileFull.c_str());
658 else
659 {
660 /* try to delete the config file, as otherwise the creation
661 * of a new settings file will fail. */
662 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
663 if (RT_FAILURE(vrc2))
664 rc = setError(VBOX_E_FILE_ERROR,
665 tr("Could not delete the existing settings file '%s' (%Rrc)"),
666 mData->m_strConfigFileFull.c_str(), vrc2);
667 }
668 }
669 else if ( vrc != VERR_FILE_NOT_FOUND
670 && vrc != VERR_PATH_NOT_FOUND
671 )
672 rc = setError(VBOX_E_FILE_ERROR,
673 tr("Invalid machine settings file name '%s' (%Rrc)"),
674 mData->m_strConfigFileFull.c_str(),
675 vrc);
676 return rc;
677}
678
679/**
680 * Initializes the registered machine by loading the settings file.
681 * This method is separated from #init() in order to make it possible to
682 * retry the operation after VirtualBox startup instead of refusing to
683 * startup the whole VirtualBox server in case if the settings file of some
684 * registered VM is invalid or inaccessible.
685 *
686 * @note Must be always called from this object's write lock
687 * (unless called from #init() that doesn't need any locking).
688 * @note Locks the mUSBController method for writing.
689 * @note Subclasses must not call this method.
690 */
691HRESULT Machine::registeredInit()
692{
693 AssertReturn(!isSessionMachine(), E_FAIL);
694 AssertReturn(!isSnapshotMachine(), E_FAIL);
695 AssertReturn(mData->mUuid.isValid(), E_FAIL);
696 AssertReturn(!mData->mAccessible, E_FAIL);
697
698 HRESULT rc = initDataAndChildObjects();
699
700 if (SUCCEEDED(rc))
701 {
702 /* Temporarily reset the registered flag in order to let setters
703 * potentially called from loadSettings() succeed (isMutable() used in
704 * all setters will return FALSE for a Machine instance if mRegistered
705 * is TRUE). */
706 mData->mRegistered = FALSE;
707
708 try
709 {
710 // load and parse machine XML; this will throw on XML or logic errors
711 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
712
713 if (mData->mUuid != mData->pMachineConfigFile->uuid)
714 throw setError(E_FAIL,
715 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
716 mData->pMachineConfigFile->uuid.raw(),
717 mData->m_strConfigFileFull.c_str(),
718 mData->mUuid.toString().c_str(),
719 mParent->settingsFilePath().c_str());
720
721 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
722 NULL /* const Guid *puuidRegistry */);
723 if (FAILED(rc)) throw rc;
724 }
725 catch (HRESULT err)
726 {
727 /* we assume that error info is set by the thrower */
728 rc = err;
729 }
730 catch (...)
731 {
732 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
733 }
734
735 /* Restore the registered flag (even on failure) */
736 mData->mRegistered = TRUE;
737 }
738
739 if (SUCCEEDED(rc))
740 {
741 /* Set mAccessible to TRUE only if we successfully locked and loaded
742 * the settings file */
743 mData->mAccessible = TRUE;
744
745 /* commit all changes made during loading the settings file */
746 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
747 /// @todo r=klaus for some reason the settings loading logic backs up
748 // the settings, and therefore a commit is needed. Should probably be changed.
749 }
750 else
751 {
752 /* If the machine is registered, then, instead of returning a
753 * failure, we mark it as inaccessible and set the result to
754 * success to give it a try later */
755
756 /* fetch the current error info */
757 mData->mAccessError = com::ErrorInfo();
758 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
759 mData->mUuid.raw(),
760 mData->mAccessError.getText().raw()));
761
762 /* rollback all changes */
763 rollback(false /* aNotify */);
764
765 // uninit media from this machine's media registry, or else
766 // reloading the settings will fail
767 mParent->unregisterMachineMedia(getId());
768
769 /* uninitialize the common part to make sure all data is reset to
770 * default (null) values */
771 uninitDataAndChildObjects();
772
773 rc = S_OK;
774 }
775
776 return rc;
777}
778
779/**
780 * Uninitializes the instance.
781 * Called either from FinalRelease() or by the parent when it gets destroyed.
782 *
783 * @note The caller of this method must make sure that this object
784 * a) doesn't have active callers on the current thread and b) is not locked
785 * by the current thread; otherwise uninit() will hang either a) due to
786 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
787 * a dead-lock caused by this thread waiting for all callers on the other
788 * threads are done but preventing them from doing so by holding a lock.
789 */
790void Machine::uninit()
791{
792 LogFlowThisFuncEnter();
793
794 Assert(!isWriteLockOnCurrentThread());
795
796 Assert(!uRegistryNeedsSaving);
797 if (uRegistryNeedsSaving)
798 {
799 AutoCaller autoCaller(this);
800 if (SUCCEEDED(autoCaller.rc()))
801 {
802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
803 saveSettings(NULL, Machine::SaveS_Force);
804 }
805 }
806
807 /* Enclose the state transition Ready->InUninit->NotReady */
808 AutoUninitSpan autoUninitSpan(this);
809 if (autoUninitSpan.uninitDone())
810 return;
811
812 Assert(!isSnapshotMachine());
813 Assert(!isSessionMachine());
814 Assert(!!mData);
815
816 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
817 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
818
819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
820
821 if (!mData->mSession.mMachine.isNull())
822 {
823 /* Theoretically, this can only happen if the VirtualBox server has been
824 * terminated while there were clients running that owned open direct
825 * sessions. Since in this case we are definitely called by
826 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
827 * won't happen on the client watcher thread (because it does
828 * VirtualBox::addCaller() for the duration of the
829 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
830 * cannot happen until the VirtualBox caller is released). This is
831 * important, because SessionMachine::uninit() cannot correctly operate
832 * after we return from this method (it expects the Machine instance is
833 * still valid). We'll call it ourselves below.
834 */
835 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
836 (SessionMachine*)mData->mSession.mMachine));
837
838 if (Global::IsOnlineOrTransient(mData->mMachineState))
839 {
840 LogWarningThisFunc(("Setting state to Aborted!\n"));
841 /* set machine state using SessionMachine reimplementation */
842 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
843 }
844
845 /*
846 * Uninitialize SessionMachine using public uninit() to indicate
847 * an unexpected uninitialization.
848 */
849 mData->mSession.mMachine->uninit();
850 /* SessionMachine::uninit() must set mSession.mMachine to null */
851 Assert(mData->mSession.mMachine.isNull());
852 }
853
854 // uninit media from this machine's media registry, if they're still there
855 Guid uuidMachine(getId());
856
857 /* the lock is no more necessary (SessionMachine is uninitialized) */
858 alock.release();
859
860 /* XXX This will fail with
861 * "cannot be closed because it is still attached to 1 virtual machines"
862 * because at this point we did not call uninitDataAndChildObjects() yet
863 * and therefore also removeBackReference() for all these mediums was not called! */
864
865 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
866 mParent->unregisterMachineMedia(uuidMachine);
867
868 // has machine been modified?
869 if (mData->flModifications)
870 {
871 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
872 rollback(false /* aNotify */);
873 }
874
875 if (mData->mAccessible)
876 uninitDataAndChildObjects();
877
878 /* free the essential data structure last */
879 mData.free();
880
881 LogFlowThisFuncLeave();
882}
883
884// IMachine properties
885/////////////////////////////////////////////////////////////////////////////
886
887STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
888{
889 CheckComArgOutPointerValid(aParent);
890
891 AutoLimitedCaller autoCaller(this);
892 if (FAILED(autoCaller.rc())) return autoCaller.rc();
893
894 /* mParent is constant during life time, no need to lock */
895 ComObjPtr<VirtualBox> pVirtualBox(mParent);
896 pVirtualBox.queryInterfaceTo(aParent);
897
898 return S_OK;
899}
900
901STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
902{
903 CheckComArgOutPointerValid(aAccessible);
904
905 AutoLimitedCaller autoCaller(this);
906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
907
908 LogFlowThisFunc(("ENTER\n"));
909
910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
911
912 HRESULT rc = S_OK;
913
914 if (!mData->mAccessible)
915 {
916 /* try to initialize the VM once more if not accessible */
917
918 AutoReinitSpan autoReinitSpan(this);
919 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
920
921#ifdef DEBUG
922 LogFlowThisFunc(("Dumping media backreferences\n"));
923 mParent->dumpAllBackRefs();
924#endif
925
926 if (mData->pMachineConfigFile)
927 {
928 // reset the XML file to force loadSettings() (called from registeredInit())
929 // to parse it again; the file might have changed
930 delete mData->pMachineConfigFile;
931 mData->pMachineConfigFile = NULL;
932 }
933
934 rc = registeredInit();
935
936 if (SUCCEEDED(rc) && mData->mAccessible)
937 {
938 autoReinitSpan.setSucceeded();
939
940 /* make sure interesting parties will notice the accessibility
941 * state change */
942 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
943 mParent->onMachineDataChange(mData->mUuid);
944 }
945 }
946
947 if (SUCCEEDED(rc))
948 *aAccessible = mData->mAccessible;
949
950 LogFlowThisFuncLeave();
951
952 return rc;
953}
954
955STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
956{
957 CheckComArgOutPointerValid(aAccessError);
958
959 AutoLimitedCaller autoCaller(this);
960 if (FAILED(autoCaller.rc())) return autoCaller.rc();
961
962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
963
964 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
965 {
966 /* return shortly */
967 aAccessError = NULL;
968 return S_OK;
969 }
970
971 HRESULT rc = S_OK;
972
973 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
974 rc = errorInfo.createObject();
975 if (SUCCEEDED(rc))
976 {
977 errorInfo->init(mData->mAccessError.getResultCode(),
978 mData->mAccessError.getInterfaceID().ref(),
979 Utf8Str(mData->mAccessError.getComponent()).c_str(),
980 Utf8Str(mData->mAccessError.getText()));
981 rc = errorInfo.queryInterfaceTo(aAccessError);
982 }
983
984 return rc;
985}
986
987STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
988{
989 CheckComArgOutPointerValid(aName);
990
991 AutoCaller autoCaller(this);
992 if (FAILED(autoCaller.rc())) return autoCaller.rc();
993
994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
995
996 mUserData->s.strName.cloneTo(aName);
997
998 return S_OK;
999}
1000
1001STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1002{
1003 CheckComArgStrNotEmptyOrNull(aName);
1004
1005 AutoCaller autoCaller(this);
1006 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1007
1008 // prohibit setting a UUID only as the machine name, or else it can
1009 // never be found by findMachine()
1010 Guid test(aName);
1011
1012 if (test.isValid())
1013 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1014
1015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1016
1017 HRESULT rc = checkStateDependency(MutableStateDep);
1018 if (FAILED(rc)) return rc;
1019
1020 setModified(IsModified_MachineData);
1021 mUserData.backup();
1022 mUserData->s.strName = aName;
1023
1024 return S_OK;
1025}
1026
1027STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1028{
1029 CheckComArgOutPointerValid(aDescription);
1030
1031 AutoCaller autoCaller(this);
1032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1033
1034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1035
1036 mUserData->s.strDescription.cloneTo(aDescription);
1037
1038 return S_OK;
1039}
1040
1041STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1042{
1043 AutoCaller autoCaller(this);
1044 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1045
1046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1047
1048 // this can be done in principle in any state as it doesn't affect the VM
1049 // significantly, but play safe by not messing around while complex
1050 // activities are going on
1051 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1052 if (FAILED(rc)) return rc;
1053
1054 setModified(IsModified_MachineData);
1055 mUserData.backup();
1056 mUserData->s.strDescription = aDescription;
1057
1058 return S_OK;
1059}
1060
1061STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1062{
1063 CheckComArgOutPointerValid(aId);
1064
1065 AutoLimitedCaller autoCaller(this);
1066 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1067
1068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1069
1070 mData->mUuid.toUtf16().cloneTo(aId);
1071
1072 return S_OK;
1073}
1074
1075STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1076{
1077 CheckComArgOutSafeArrayPointerValid(aGroups);
1078
1079 AutoCaller autoCaller(this);
1080 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1081
1082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1083 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1084 size_t i = 0;
1085 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1086 it != mUserData->s.llGroups.end();
1087 ++it, i++)
1088 {
1089 Bstr tmp = *it;
1090 tmp.cloneTo(&groups[i]);
1091 }
1092 groups.detachTo(ComSafeArrayOutArg(aGroups));
1093
1094 return S_OK;
1095}
1096
1097STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1098{
1099 AutoCaller autoCaller(this);
1100 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1101
1102 StringsList llGroups;
1103 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1104 if (FAILED(rc))
1105 return rc;
1106
1107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1108
1109 // changing machine groups is possible while the VM is offline
1110 rc = checkStateDependency(OfflineStateDep);
1111 if (FAILED(rc)) return rc;
1112
1113 setModified(IsModified_MachineData);
1114 mUserData.backup();
1115 mUserData->s.llGroups = llGroups;
1116
1117 return S_OK;
1118}
1119
1120STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1121{
1122 CheckComArgOutPointerValid(aOSTypeId);
1123
1124 AutoCaller autoCaller(this);
1125 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1126
1127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1128
1129 mUserData->s.strOsType.cloneTo(aOSTypeId);
1130
1131 return S_OK;
1132}
1133
1134STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1135{
1136 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1137
1138 AutoCaller autoCaller(this);
1139 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1140
1141 /* look up the object by Id to check it is valid */
1142 ComPtr<IGuestOSType> guestOSType;
1143 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1144 if (FAILED(rc)) return rc;
1145
1146 /* when setting, always use the "etalon" value for consistency -- lookup
1147 * by ID is case-insensitive and the input value may have different case */
1148 Bstr osTypeId;
1149 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1150 if (FAILED(rc)) return rc;
1151
1152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1153
1154 rc = checkStateDependency(MutableStateDep);
1155 if (FAILED(rc)) return rc;
1156
1157 setModified(IsModified_MachineData);
1158 mUserData.backup();
1159 mUserData->s.strOsType = osTypeId;
1160
1161 return S_OK;
1162}
1163
1164
1165STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1166{
1167 CheckComArgOutPointerValid(aFirmwareType);
1168
1169 AutoCaller autoCaller(this);
1170 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1171
1172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1173
1174 *aFirmwareType = mHWData->mFirmwareType;
1175
1176 return S_OK;
1177}
1178
1179STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1180{
1181 AutoCaller autoCaller(this);
1182 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1184
1185 HRESULT rc = checkStateDependency(MutableStateDep);
1186 if (FAILED(rc)) return rc;
1187
1188 setModified(IsModified_MachineData);
1189 mHWData.backup();
1190 mHWData->mFirmwareType = aFirmwareType;
1191
1192 return S_OK;
1193}
1194
1195STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1196{
1197 CheckComArgOutPointerValid(aKeyboardHIDType);
1198
1199 AutoCaller autoCaller(this);
1200 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1201
1202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1203
1204 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1205
1206 return S_OK;
1207}
1208
1209STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1210{
1211 AutoCaller autoCaller(this);
1212 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1214
1215 HRESULT rc = checkStateDependency(MutableStateDep);
1216 if (FAILED(rc)) return rc;
1217
1218 setModified(IsModified_MachineData);
1219 mHWData.backup();
1220 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1221
1222 return S_OK;
1223}
1224
1225STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1226{
1227 CheckComArgOutPointerValid(aPointingHIDType);
1228
1229 AutoCaller autoCaller(this);
1230 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1231
1232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1233
1234 *aPointingHIDType = mHWData->mPointingHIDType;
1235
1236 return S_OK;
1237}
1238
1239STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1240{
1241 AutoCaller autoCaller(this);
1242 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1244
1245 HRESULT rc = checkStateDependency(MutableStateDep);
1246 if (FAILED(rc)) return rc;
1247
1248 setModified(IsModified_MachineData);
1249 mHWData.backup();
1250 mHWData->mPointingHIDType = aPointingHIDType;
1251
1252 return S_OK;
1253}
1254
1255STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1256{
1257 CheckComArgOutPointerValid(aChipsetType);
1258
1259 AutoCaller autoCaller(this);
1260 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1261
1262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1263
1264 *aChipsetType = mHWData->mChipsetType;
1265
1266 return S_OK;
1267}
1268
1269STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1270{
1271 AutoCaller autoCaller(this);
1272 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1273 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1274
1275 HRESULT rc = checkStateDependency(MutableStateDep);
1276 if (FAILED(rc)) return rc;
1277
1278 if (aChipsetType != mHWData->mChipsetType)
1279 {
1280 setModified(IsModified_MachineData);
1281 mHWData.backup();
1282 mHWData->mChipsetType = aChipsetType;
1283
1284 // Resize network adapter array, to be finalized on commit/rollback.
1285 // We must not throw away entries yet, otherwise settings are lost
1286 // without a way to roll back.
1287 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1288 size_t oldCount = mNetworkAdapters.size();
1289 if (newCount > oldCount)
1290 {
1291 mNetworkAdapters.resize(newCount);
1292 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1293 {
1294 unconst(mNetworkAdapters[slot]).createObject();
1295 mNetworkAdapters[slot]->init(this, slot);
1296 }
1297 }
1298 }
1299
1300 return S_OK;
1301}
1302
1303STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1304{
1305 CheckComArgOutPointerValid(aHWVersion);
1306
1307 AutoCaller autoCaller(this);
1308 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1309
1310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1311
1312 mHWData->mHWVersion.cloneTo(aHWVersion);
1313
1314 return S_OK;
1315}
1316
1317STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1318{
1319 /* check known version */
1320 Utf8Str hwVersion = aHWVersion;
1321 if ( hwVersion.compare("1") != 0
1322 && hwVersion.compare("2") != 0)
1323 return setError(E_INVALIDARG,
1324 tr("Invalid hardware version: %ls\n"), aHWVersion);
1325
1326 AutoCaller autoCaller(this);
1327 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1328
1329 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1330
1331 HRESULT rc = checkStateDependency(MutableStateDep);
1332 if (FAILED(rc)) return rc;
1333
1334 setModified(IsModified_MachineData);
1335 mHWData.backup();
1336 mHWData->mHWVersion = hwVersion;
1337
1338 return S_OK;
1339}
1340
1341STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1342{
1343 CheckComArgOutPointerValid(aUUID);
1344
1345 AutoCaller autoCaller(this);
1346 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1347
1348 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1349
1350 if (mHWData->mHardwareUUID.isValid())
1351 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1352 else
1353 mData->mUuid.toUtf16().cloneTo(aUUID);
1354
1355 return S_OK;
1356}
1357
1358STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1359{
1360 Guid hardwareUUID(aUUID);
1361 if (!hardwareUUID.isValid())
1362 return E_INVALIDARG;
1363
1364 AutoCaller autoCaller(this);
1365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1366
1367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1368
1369 HRESULT rc = checkStateDependency(MutableStateDep);
1370 if (FAILED(rc)) return rc;
1371
1372 setModified(IsModified_MachineData);
1373 mHWData.backup();
1374 if (hardwareUUID == mData->mUuid)
1375 mHWData->mHardwareUUID.clear();
1376 else
1377 mHWData->mHardwareUUID = hardwareUUID;
1378
1379 return S_OK;
1380}
1381
1382STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1383{
1384 CheckComArgOutPointerValid(memorySize);
1385
1386 AutoCaller autoCaller(this);
1387 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1388
1389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1390
1391 *memorySize = mHWData->mMemorySize;
1392
1393 return S_OK;
1394}
1395
1396STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1397{
1398 /* check RAM limits */
1399 if ( memorySize < MM_RAM_MIN_IN_MB
1400 || memorySize > MM_RAM_MAX_IN_MB
1401 )
1402 return setError(E_INVALIDARG,
1403 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1404 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1405
1406 AutoCaller autoCaller(this);
1407 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1408
1409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1410
1411 HRESULT rc = checkStateDependency(MutableStateDep);
1412 if (FAILED(rc)) return rc;
1413
1414 setModified(IsModified_MachineData);
1415 mHWData.backup();
1416 mHWData->mMemorySize = memorySize;
1417
1418 return S_OK;
1419}
1420
1421STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1422{
1423 CheckComArgOutPointerValid(CPUCount);
1424
1425 AutoCaller autoCaller(this);
1426 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1427
1428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1429
1430 *CPUCount = mHWData->mCPUCount;
1431
1432 return S_OK;
1433}
1434
1435STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1436{
1437 /* check CPU limits */
1438 if ( CPUCount < SchemaDefs::MinCPUCount
1439 || CPUCount > SchemaDefs::MaxCPUCount
1440 )
1441 return setError(E_INVALIDARG,
1442 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1443 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1444
1445 AutoCaller autoCaller(this);
1446 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1447
1448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1449
1450 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1451 if (mHWData->mCPUHotPlugEnabled)
1452 {
1453 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1454 {
1455 if (mHWData->mCPUAttached[idx])
1456 return setError(E_INVALIDARG,
1457 tr("There is still a CPU attached to socket %lu."
1458 "Detach the CPU before removing the socket"),
1459 CPUCount, idx+1);
1460 }
1461 }
1462
1463 HRESULT rc = checkStateDependency(MutableStateDep);
1464 if (FAILED(rc)) return rc;
1465
1466 setModified(IsModified_MachineData);
1467 mHWData.backup();
1468 mHWData->mCPUCount = CPUCount;
1469
1470 return S_OK;
1471}
1472
1473STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1474{
1475 CheckComArgOutPointerValid(aExecutionCap);
1476
1477 AutoCaller autoCaller(this);
1478 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1479
1480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1481
1482 *aExecutionCap = mHWData->mCpuExecutionCap;
1483
1484 return S_OK;
1485}
1486
1487STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1488{
1489 HRESULT rc = S_OK;
1490
1491 /* check throttle limits */
1492 if ( aExecutionCap < 1
1493 || aExecutionCap > 100
1494 )
1495 return setError(E_INVALIDARG,
1496 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1497 aExecutionCap, 1, 100);
1498
1499 AutoCaller autoCaller(this);
1500 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1501
1502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1503
1504 alock.release();
1505 rc = onCPUExecutionCapChange(aExecutionCap);
1506 alock.acquire();
1507 if (FAILED(rc)) return rc;
1508
1509 setModified(IsModified_MachineData);
1510 mHWData.backup();
1511 mHWData->mCpuExecutionCap = aExecutionCap;
1512
1513 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1514 if (Global::IsOnline(mData->mMachineState))
1515 saveSettings(NULL);
1516
1517 return S_OK;
1518}
1519
1520
1521STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1522{
1523 CheckComArgOutPointerValid(aEnabled);
1524
1525 AutoCaller autoCaller(this);
1526 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1527
1528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1529
1530 *aEnabled = mHWData->mCPUHotPlugEnabled;
1531
1532 return S_OK;
1533}
1534
1535STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1536{
1537 HRESULT rc = S_OK;
1538
1539 AutoCaller autoCaller(this);
1540 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1541
1542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1543
1544 rc = checkStateDependency(MutableStateDep);
1545 if (FAILED(rc)) return rc;
1546
1547 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1548 {
1549 if (aEnabled)
1550 {
1551 setModified(IsModified_MachineData);
1552 mHWData.backup();
1553
1554 /* Add the amount of CPUs currently attached */
1555 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1556 {
1557 mHWData->mCPUAttached[i] = true;
1558 }
1559 }
1560 else
1561 {
1562 /*
1563 * We can disable hotplug only if the amount of maximum CPUs is equal
1564 * to the amount of attached CPUs
1565 */
1566 unsigned cCpusAttached = 0;
1567 unsigned iHighestId = 0;
1568
1569 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1570 {
1571 if (mHWData->mCPUAttached[i])
1572 {
1573 cCpusAttached++;
1574 iHighestId = i;
1575 }
1576 }
1577
1578 if ( (cCpusAttached != mHWData->mCPUCount)
1579 || (iHighestId >= mHWData->mCPUCount))
1580 return setError(E_INVALIDARG,
1581 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1582
1583 setModified(IsModified_MachineData);
1584 mHWData.backup();
1585 }
1586 }
1587
1588 mHWData->mCPUHotPlugEnabled = aEnabled;
1589
1590 return rc;
1591}
1592
1593STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1594{
1595#ifdef VBOX_WITH_USB_CARDREADER
1596 CheckComArgOutPointerValid(aEnabled);
1597
1598 AutoCaller autoCaller(this);
1599 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1600
1601 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1602
1603 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1604
1605 return S_OK;
1606#else
1607 NOREF(aEnabled);
1608 return E_NOTIMPL;
1609#endif
1610}
1611
1612STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1613{
1614#ifdef VBOX_WITH_USB_CARDREADER
1615 AutoCaller autoCaller(this);
1616 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1618
1619 HRESULT rc = checkStateDependency(MutableStateDep);
1620 if (FAILED(rc)) return rc;
1621
1622 setModified(IsModified_MachineData);
1623 mHWData.backup();
1624 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1625
1626 return S_OK;
1627#else
1628 NOREF(aEnabled);
1629 return E_NOTIMPL;
1630#endif
1631}
1632
1633STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled)
1634{
1635#ifdef VBOX_WITH_USB_VIDEO
1636 CheckComArgOutPointerValid(aEnabled);
1637
1638 AutoCaller autoCaller(this);
1639 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1640
1641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1642
1643 *aEnabled = mHWData->mEmulatedUSBWebcamEnabled;
1644
1645 return S_OK;
1646#else
1647 NOREF(aEnabled);
1648 return E_NOTIMPL;
1649#endif
1650}
1651
1652STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled)
1653{
1654#ifdef VBOX_WITH_USB_VIDEO
1655 AutoCaller autoCaller(this);
1656 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1657 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1658
1659 HRESULT rc = checkStateDependency(MutableStateDep);
1660 if (FAILED(rc)) return rc;
1661
1662 setModified(IsModified_MachineData);
1663 mHWData.backup();
1664 mHWData->mEmulatedUSBWebcamEnabled = aEnabled;
1665
1666 return S_OK;
1667#else
1668 NOREF(aEnabled);
1669 return E_NOTIMPL;
1670#endif
1671}
1672
1673STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1674{
1675 CheckComArgOutPointerValid(aEnabled);
1676
1677 AutoCaller autoCaller(this);
1678 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1680
1681 *aEnabled = mHWData->mHPETEnabled;
1682
1683 return S_OK;
1684}
1685
1686STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1687{
1688 HRESULT rc = S_OK;
1689
1690 AutoCaller autoCaller(this);
1691 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1692 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1693
1694 rc = checkStateDependency(MutableStateDep);
1695 if (FAILED(rc)) return rc;
1696
1697 setModified(IsModified_MachineData);
1698 mHWData.backup();
1699
1700 mHWData->mHPETEnabled = aEnabled;
1701
1702 return rc;
1703}
1704
1705STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1706{
1707 AutoCaller autoCaller(this);
1708 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1709
1710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1711
1712 *fEnabled = mHWData->mVideoCaptureEnabled;
1713 return S_OK;
1714}
1715
1716STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1717{
1718 HRESULT rc = S_OK;
1719
1720 AutoCaller autoCaller(this);
1721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1723
1724 setModified(IsModified_MachineData);
1725 mHWData.backup();
1726 mHWData->mVideoCaptureEnabled = fEnabled;
1727
1728 alock.release();
1729 rc = onVideoCaptureChange();
1730 alock.acquire();
1731 if (FAILED(rc))
1732 {
1733 /*
1734 * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded.
1735 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1736 * determine if it should start or stop capturing. Therefore we need to manually
1737 * undo change.
1738 */
1739 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1740 return rc;
1741 }
1742
1743 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1744 if (Global::IsOnline(mData->mMachineState))
1745 saveSettings(NULL);
1746
1747 return rc;
1748}
1749
1750STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1751{
1752 CheckComArgOutSafeArrayPointerValid(aScreens);
1753
1754 AutoCaller autoCaller(this);
1755 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1756
1757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1758
1759 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1760 for (unsigned i = 0; i < screens.size(); i++)
1761 screens[i] = mHWData->maVideoCaptureScreens[i];
1762 screens.detachTo(ComSafeArrayOutArg(aScreens));
1763 return S_OK;
1764}
1765
1766STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1767{
1768 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1769 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1770 bool fChanged = false;
1771
1772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1773
1774 for (unsigned i = 0; i < screens.size(); i++)
1775 {
1776 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1777 {
1778 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1779 fChanged = true;
1780 }
1781 }
1782 if (fChanged)
1783 {
1784 alock.release();
1785 HRESULT rc = onVideoCaptureChange();
1786 alock.acquire();
1787 if (FAILED(rc)) return rc;
1788 setModified(IsModified_MachineData);
1789
1790 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1791 if (Global::IsOnline(mData->mMachineState))
1792 saveSettings(NULL);
1793 }
1794
1795 return S_OK;
1796}
1797
1798STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1799{
1800 AutoCaller autoCaller(this);
1801 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1802
1803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1804 if (mHWData->mVideoCaptureFile.isEmpty())
1805 {
1806 Utf8Str defaultFile;
1807 getDefaultVideoCaptureFile(defaultFile);
1808 defaultFile.cloneTo(apFile);
1809 }
1810 else
1811 mHWData->mVideoCaptureFile.cloneTo(apFile);
1812 return S_OK;
1813}
1814
1815STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1816{
1817 Utf8Str strFile(aFile);
1818 AutoCaller autoCaller(this);
1819 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1820
1821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1822
1823 if ( Global::IsOnline(mData->mMachineState)
1824 && mHWData->mVideoCaptureEnabled)
1825 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1826
1827 if (!RTPathStartsWithRoot(strFile.c_str()))
1828 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1829
1830 if (!strFile.isEmpty())
1831 {
1832 Utf8Str defaultFile;
1833 getDefaultVideoCaptureFile(defaultFile);
1834 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1835 strFile.setNull();
1836 }
1837
1838 setModified(IsModified_MachineData);
1839 mHWData.backup();
1840 mHWData->mVideoCaptureFile = strFile;
1841
1842 return S_OK;
1843}
1844
1845STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1846{
1847 AutoCaller autoCaller(this);
1848 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1849
1850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1851 *aHorzRes = mHWData->mVideoCaptureWidth;
1852 return S_OK;
1853}
1854
1855STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1856{
1857 AutoCaller autoCaller(this);
1858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1859
1860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 if ( Global::IsOnline(mData->mMachineState)
1863 && mHWData->mVideoCaptureEnabled)
1864 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1865
1866 setModified(IsModified_MachineData);
1867 mHWData.backup();
1868 mHWData->mVideoCaptureWidth = aHorzRes;
1869
1870 return S_OK;
1871}
1872
1873STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1874{
1875 AutoCaller autoCaller(this);
1876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1877
1878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1879 *aVertRes = mHWData->mVideoCaptureHeight;
1880 return S_OK;
1881}
1882
1883STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1884{
1885 AutoCaller autoCaller(this);
1886 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1887
1888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1889
1890 if ( Global::IsOnline(mData->mMachineState)
1891 && mHWData->mVideoCaptureEnabled)
1892 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1893
1894 setModified(IsModified_MachineData);
1895 mHWData.backup();
1896 mHWData->mVideoCaptureHeight = aVertRes;
1897
1898 return S_OK;
1899}
1900
1901STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1902{
1903 AutoCaller autoCaller(this);
1904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1905
1906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1907 *aRate = mHWData->mVideoCaptureRate;
1908 return S_OK;
1909}
1910
1911STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1912{
1913 AutoCaller autoCaller(this);
1914 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1915
1916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1917
1918 if ( Global::IsOnline(mData->mMachineState)
1919 && mHWData->mVideoCaptureEnabled)
1920 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1921
1922 setModified(IsModified_MachineData);
1923 mHWData.backup();
1924 mHWData->mVideoCaptureRate = aRate;
1925
1926 return S_OK;
1927}
1928
1929STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1930{
1931 AutoCaller autoCaller(this);
1932 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1933
1934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1935 *aFPS = mHWData->mVideoCaptureFPS;
1936 return S_OK;
1937}
1938
1939STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1940{
1941 AutoCaller autoCaller(this);
1942 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1943
1944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1945
1946 if ( Global::IsOnline(mData->mMachineState)
1947 && mHWData->mVideoCaptureEnabled)
1948 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1949
1950 setModified(IsModified_MachineData);
1951 mHWData.backup();
1952 mHWData->mVideoCaptureFPS = aFPS;
1953
1954 return S_OK;
1955}
1956
1957STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1958{
1959 CheckComArgOutPointerValid(aGraphicsControllerType);
1960
1961 AutoCaller autoCaller(this);
1962 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1963
1964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1965
1966 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1967
1968 return S_OK;
1969}
1970
1971STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1972{
1973 switch (aGraphicsControllerType)
1974 {
1975 case GraphicsControllerType_Null:
1976 case GraphicsControllerType_VBoxVGA:
1977 break;
1978 default:
1979 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1980 }
1981
1982 AutoCaller autoCaller(this);
1983 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1984
1985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1986
1987 HRESULT rc = checkStateDependency(MutableStateDep);
1988 if (FAILED(rc)) return rc;
1989
1990 setModified(IsModified_MachineData);
1991 mHWData.backup();
1992 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1993
1994 return S_OK;
1995}
1996
1997STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1998{
1999 CheckComArgOutPointerValid(memorySize);
2000
2001 AutoCaller autoCaller(this);
2002 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2003
2004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2005
2006 *memorySize = mHWData->mVRAMSize;
2007
2008 return S_OK;
2009}
2010
2011STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
2012{
2013 /* check VRAM limits */
2014 if (memorySize < SchemaDefs::MinGuestVRAM ||
2015 memorySize > SchemaDefs::MaxGuestVRAM)
2016 return setError(E_INVALIDARG,
2017 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2018 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2019
2020 AutoCaller autoCaller(this);
2021 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2022
2023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2024
2025 HRESULT rc = checkStateDependency(MutableStateDep);
2026 if (FAILED(rc)) return rc;
2027
2028 setModified(IsModified_MachineData);
2029 mHWData.backup();
2030 mHWData->mVRAMSize = memorySize;
2031
2032 return S_OK;
2033}
2034
2035/** @todo this method should not be public */
2036STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
2037{
2038 CheckComArgOutPointerValid(memoryBalloonSize);
2039
2040 AutoCaller autoCaller(this);
2041 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2042
2043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2044
2045 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2046
2047 return S_OK;
2048}
2049
2050/**
2051 * Set the memory balloon size.
2052 *
2053 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2054 * we have to make sure that we never call IGuest from here.
2055 */
2056STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2057{
2058 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2059#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2060 /* check limits */
2061 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2062 return setError(E_INVALIDARG,
2063 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2064 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2065
2066 AutoCaller autoCaller(this);
2067 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2068
2069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2070
2071 setModified(IsModified_MachineData);
2072 mHWData.backup();
2073 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2074
2075 return S_OK;
2076#else
2077 NOREF(memoryBalloonSize);
2078 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2079#endif
2080}
2081
2082STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2083{
2084 CheckComArgOutPointerValid(aEnabled);
2085
2086 AutoCaller autoCaller(this);
2087 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2088
2089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2090
2091 *aEnabled = mHWData->mPageFusionEnabled;
2092 return S_OK;
2093}
2094
2095STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2096{
2097#ifdef VBOX_WITH_PAGE_SHARING
2098 AutoCaller autoCaller(this);
2099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2100
2101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2102
2103 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2104 setModified(IsModified_MachineData);
2105 mHWData.backup();
2106 mHWData->mPageFusionEnabled = aEnabled;
2107 return S_OK;
2108#else
2109 NOREF(aEnabled);
2110 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2111#endif
2112}
2113
2114STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2115{
2116 CheckComArgOutPointerValid(aEnabled);
2117
2118 AutoCaller autoCaller(this);
2119 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2120
2121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2122
2123 *aEnabled = mHWData->mAccelerate3DEnabled;
2124
2125 return S_OK;
2126}
2127
2128STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2129{
2130 AutoCaller autoCaller(this);
2131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2132
2133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2134
2135 HRESULT rc = checkStateDependency(MutableStateDep);
2136 if (FAILED(rc)) return rc;
2137
2138 /** @todo check validity! */
2139
2140 setModified(IsModified_MachineData);
2141 mHWData.backup();
2142 mHWData->mAccelerate3DEnabled = enable;
2143
2144 return S_OK;
2145}
2146
2147
2148STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2149{
2150 CheckComArgOutPointerValid(aEnabled);
2151
2152 AutoCaller autoCaller(this);
2153 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2154
2155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2156
2157 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2158
2159 return S_OK;
2160}
2161
2162STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2163{
2164 AutoCaller autoCaller(this);
2165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2166
2167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2168
2169 HRESULT rc = checkStateDependency(MutableStateDep);
2170 if (FAILED(rc)) return rc;
2171
2172 /** @todo check validity! */
2173
2174 setModified(IsModified_MachineData);
2175 mHWData.backup();
2176 mHWData->mAccelerate2DVideoEnabled = enable;
2177
2178 return S_OK;
2179}
2180
2181STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2182{
2183 CheckComArgOutPointerValid(monitorCount);
2184
2185 AutoCaller autoCaller(this);
2186 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2187
2188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2189
2190 *monitorCount = mHWData->mMonitorCount;
2191
2192 return S_OK;
2193}
2194
2195STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2196{
2197 /* make sure monitor count is a sensible number */
2198 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2199 return setError(E_INVALIDARG,
2200 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2201 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2202
2203 AutoCaller autoCaller(this);
2204 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2205
2206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2207
2208 HRESULT rc = checkStateDependency(MutableStateDep);
2209 if (FAILED(rc)) return rc;
2210
2211 setModified(IsModified_MachineData);
2212 mHWData.backup();
2213 mHWData->mMonitorCount = monitorCount;
2214
2215 return S_OK;
2216}
2217
2218STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2219{
2220 CheckComArgOutPointerValid(biosSettings);
2221
2222 AutoCaller autoCaller(this);
2223 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2224
2225 /* mBIOSSettings is constant during life time, no need to lock */
2226 mBIOSSettings.queryInterfaceTo(biosSettings);
2227
2228 return S_OK;
2229}
2230
2231STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2232{
2233 CheckComArgOutPointerValid(aVal);
2234
2235 AutoCaller autoCaller(this);
2236 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2237
2238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2239
2240 switch (property)
2241 {
2242 case CPUPropertyType_PAE:
2243 *aVal = mHWData->mPAEEnabled;
2244 break;
2245
2246 case CPUPropertyType_Synthetic:
2247 *aVal = mHWData->mSyntheticCpu;
2248 break;
2249
2250 case CPUPropertyType_LongMode:
2251 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2252 *aVal = TRUE;
2253 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2254 *aVal = FALSE;
2255#if HC_ARCH_BITS == 64
2256 else
2257 *aVal = TRUE;
2258#else
2259 else
2260 {
2261 *aVal = FALSE;
2262
2263 ComPtr<IGuestOSType> ptrGuestOSType;
2264 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2265 if (SUCCEEDED(hrc2))
2266 {
2267 BOOL fIs64Bit = FALSE;
2268 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2269 if (SUCCEEDED(hrc2) && fIs64Bit)
2270 {
2271 ComObjPtr<Host> ptrHost = mParent->host();
2272 alock.release();
2273
2274 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2275 if (FAILED(hrc2))
2276 *aVal = FALSE;
2277 }
2278 }
2279 }
2280#endif
2281 break;
2282
2283 default:
2284 return E_INVALIDARG;
2285 }
2286 return S_OK;
2287}
2288
2289STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2290{
2291 AutoCaller autoCaller(this);
2292 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2293
2294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2295
2296 HRESULT rc = checkStateDependency(MutableStateDep);
2297 if (FAILED(rc)) return rc;
2298
2299 switch (property)
2300 {
2301 case CPUPropertyType_PAE:
2302 setModified(IsModified_MachineData);
2303 mHWData.backup();
2304 mHWData->mPAEEnabled = !!aVal;
2305 break;
2306
2307 case CPUPropertyType_Synthetic:
2308 setModified(IsModified_MachineData);
2309 mHWData.backup();
2310 mHWData->mSyntheticCpu = !!aVal;
2311 break;
2312
2313 case CPUPropertyType_LongMode:
2314 setModified(IsModified_MachineData);
2315 mHWData.backup();
2316 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2317 break;
2318
2319 default:
2320 return E_INVALIDARG;
2321 }
2322 return S_OK;
2323}
2324
2325STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2326{
2327 CheckComArgOutPointerValid(aValEax);
2328 CheckComArgOutPointerValid(aValEbx);
2329 CheckComArgOutPointerValid(aValEcx);
2330 CheckComArgOutPointerValid(aValEdx);
2331
2332 AutoCaller autoCaller(this);
2333 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2334
2335 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2336
2337 switch(aId)
2338 {
2339 case 0x0:
2340 case 0x1:
2341 case 0x2:
2342 case 0x3:
2343 case 0x4:
2344 case 0x5:
2345 case 0x6:
2346 case 0x7:
2347 case 0x8:
2348 case 0x9:
2349 case 0xA:
2350 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2351 return E_INVALIDARG;
2352
2353 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2354 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2355 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2356 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2357 break;
2358
2359 case 0x80000000:
2360 case 0x80000001:
2361 case 0x80000002:
2362 case 0x80000003:
2363 case 0x80000004:
2364 case 0x80000005:
2365 case 0x80000006:
2366 case 0x80000007:
2367 case 0x80000008:
2368 case 0x80000009:
2369 case 0x8000000A:
2370 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2371 return E_INVALIDARG;
2372
2373 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2374 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2375 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2376 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2377 break;
2378
2379 default:
2380 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2381 }
2382 return S_OK;
2383}
2384
2385STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2386{
2387 AutoCaller autoCaller(this);
2388 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2389
2390 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2391
2392 HRESULT rc = checkStateDependency(MutableStateDep);
2393 if (FAILED(rc)) return rc;
2394
2395 switch(aId)
2396 {
2397 case 0x0:
2398 case 0x1:
2399 case 0x2:
2400 case 0x3:
2401 case 0x4:
2402 case 0x5:
2403 case 0x6:
2404 case 0x7:
2405 case 0x8:
2406 case 0x9:
2407 case 0xA:
2408 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2409 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2410 setModified(IsModified_MachineData);
2411 mHWData.backup();
2412 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2413 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2414 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2415 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2416 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2417 break;
2418
2419 case 0x80000000:
2420 case 0x80000001:
2421 case 0x80000002:
2422 case 0x80000003:
2423 case 0x80000004:
2424 case 0x80000005:
2425 case 0x80000006:
2426 case 0x80000007:
2427 case 0x80000008:
2428 case 0x80000009:
2429 case 0x8000000A:
2430 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2431 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2432 setModified(IsModified_MachineData);
2433 mHWData.backup();
2434 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2435 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2436 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2437 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2438 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2439 break;
2440
2441 default:
2442 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2443 }
2444 return S_OK;
2445}
2446
2447STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2448{
2449 AutoCaller autoCaller(this);
2450 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2451
2452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2453
2454 HRESULT rc = checkStateDependency(MutableStateDep);
2455 if (FAILED(rc)) return rc;
2456
2457 switch(aId)
2458 {
2459 case 0x0:
2460 case 0x1:
2461 case 0x2:
2462 case 0x3:
2463 case 0x4:
2464 case 0x5:
2465 case 0x6:
2466 case 0x7:
2467 case 0x8:
2468 case 0x9:
2469 case 0xA:
2470 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2471 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2472 setModified(IsModified_MachineData);
2473 mHWData.backup();
2474 /* Invalidate leaf. */
2475 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2476 break;
2477
2478 case 0x80000000:
2479 case 0x80000001:
2480 case 0x80000002:
2481 case 0x80000003:
2482 case 0x80000004:
2483 case 0x80000005:
2484 case 0x80000006:
2485 case 0x80000007:
2486 case 0x80000008:
2487 case 0x80000009:
2488 case 0x8000000A:
2489 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2490 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2491 setModified(IsModified_MachineData);
2492 mHWData.backup();
2493 /* Invalidate leaf. */
2494 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2495 break;
2496
2497 default:
2498 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2499 }
2500 return S_OK;
2501}
2502
2503STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2504{
2505 AutoCaller autoCaller(this);
2506 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2507
2508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2509
2510 HRESULT rc = checkStateDependency(MutableStateDep);
2511 if (FAILED(rc)) return rc;
2512
2513 setModified(IsModified_MachineData);
2514 mHWData.backup();
2515
2516 /* Invalidate all standard leafs. */
2517 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2518 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2519
2520 /* Invalidate all extended leafs. */
2521 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2522 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2523
2524 return S_OK;
2525}
2526
2527STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2528{
2529 CheckComArgOutPointerValid(aVal);
2530
2531 AutoCaller autoCaller(this);
2532 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2533
2534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2535
2536 switch(property)
2537 {
2538 case HWVirtExPropertyType_Enabled:
2539 *aVal = mHWData->mHWVirtExEnabled;
2540 break;
2541
2542 case HWVirtExPropertyType_Exclusive:
2543 *aVal = mHWData->mHWVirtExExclusive;
2544 break;
2545
2546 case HWVirtExPropertyType_VPID:
2547 *aVal = mHWData->mHWVirtExVPIDEnabled;
2548 break;
2549
2550 case HWVirtExPropertyType_NestedPaging:
2551 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2552 break;
2553
2554 case HWVirtExPropertyType_UnrestrictedExecution:
2555 *aVal = mHWData->mHWVirtExUXEnabled;
2556 break;
2557
2558 case HWVirtExPropertyType_LargePages:
2559 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2560#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2561 *aVal = FALSE;
2562#endif
2563 break;
2564
2565 case HWVirtExPropertyType_Force:
2566 *aVal = mHWData->mHWVirtExForceEnabled;
2567 break;
2568
2569 default:
2570 return E_INVALIDARG;
2571 }
2572 return S_OK;
2573}
2574
2575STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2576{
2577 AutoCaller autoCaller(this);
2578 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2579
2580 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2581
2582 HRESULT rc = checkStateDependency(MutableStateDep);
2583 if (FAILED(rc)) return rc;
2584
2585 switch(property)
2586 {
2587 case HWVirtExPropertyType_Enabled:
2588 setModified(IsModified_MachineData);
2589 mHWData.backup();
2590 mHWData->mHWVirtExEnabled = !!aVal;
2591 break;
2592
2593 case HWVirtExPropertyType_Exclusive:
2594 setModified(IsModified_MachineData);
2595 mHWData.backup();
2596 mHWData->mHWVirtExExclusive = !!aVal;
2597 break;
2598
2599 case HWVirtExPropertyType_VPID:
2600 setModified(IsModified_MachineData);
2601 mHWData.backup();
2602 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2603 break;
2604
2605 case HWVirtExPropertyType_NestedPaging:
2606 setModified(IsModified_MachineData);
2607 mHWData.backup();
2608 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2609 break;
2610
2611 case HWVirtExPropertyType_UnrestrictedExecution:
2612 setModified(IsModified_MachineData);
2613 mHWData.backup();
2614 mHWData->mHWVirtExUXEnabled = !!aVal;
2615 break;
2616
2617 case HWVirtExPropertyType_LargePages:
2618 setModified(IsModified_MachineData);
2619 mHWData.backup();
2620 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2621 break;
2622
2623 case HWVirtExPropertyType_Force:
2624 setModified(IsModified_MachineData);
2625 mHWData.backup();
2626 mHWData->mHWVirtExForceEnabled = !!aVal;
2627 break;
2628
2629 default:
2630 return E_INVALIDARG;
2631 }
2632
2633 return S_OK;
2634}
2635
2636STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2637{
2638 CheckComArgOutPointerValid(aSnapshotFolder);
2639
2640 AutoCaller autoCaller(this);
2641 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2642
2643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2644
2645 Utf8Str strFullSnapshotFolder;
2646 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2647 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2648
2649 return S_OK;
2650}
2651
2652STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2653{
2654 /* @todo (r=dmik):
2655 * 1. Allow to change the name of the snapshot folder containing snapshots
2656 * 2. Rename the folder on disk instead of just changing the property
2657 * value (to be smart and not to leave garbage). Note that it cannot be
2658 * done here because the change may be rolled back. Thus, the right
2659 * place is #saveSettings().
2660 */
2661
2662 AutoCaller autoCaller(this);
2663 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2664
2665 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2666
2667 HRESULT rc = checkStateDependency(MutableStateDep);
2668 if (FAILED(rc)) return rc;
2669
2670 if (!mData->mCurrentSnapshot.isNull())
2671 return setError(E_FAIL,
2672 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2673
2674 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2675
2676 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2677 if (strSnapshotFolder.isEmpty())
2678 strSnapshotFolder = "Snapshots";
2679 int vrc = calculateFullPath(strSnapshotFolder,
2680 strSnapshotFolder);
2681 if (RT_FAILURE(vrc))
2682 return setError(E_FAIL,
2683 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2684 aSnapshotFolder, vrc);
2685
2686 setModified(IsModified_MachineData);
2687 mUserData.backup();
2688
2689 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2690
2691 return S_OK;
2692}
2693
2694STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2695{
2696 CheckComArgOutSafeArrayPointerValid(aAttachments);
2697
2698 AutoCaller autoCaller(this);
2699 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2700
2701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2702
2703 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2704 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2705
2706 return S_OK;
2707}
2708
2709STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2710{
2711 CheckComArgOutPointerValid(vrdeServer);
2712
2713 AutoCaller autoCaller(this);
2714 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2715
2716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2717
2718 Assert(!!mVRDEServer);
2719 mVRDEServer.queryInterfaceTo(vrdeServer);
2720
2721 return S_OK;
2722}
2723
2724STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2725{
2726 CheckComArgOutPointerValid(audioAdapter);
2727
2728 AutoCaller autoCaller(this);
2729 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2730
2731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2732
2733 mAudioAdapter.queryInterfaceTo(audioAdapter);
2734 return S_OK;
2735}
2736
2737STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers))
2738{
2739#ifdef VBOX_WITH_VUSB
2740 CheckComArgOutPointerValid(aUSBControllers);
2741
2742 AutoCaller autoCaller(this);
2743 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2744
2745 clearError();
2746 MultiResult rc(S_OK);
2747
2748# ifdef VBOX_WITH_USB
2749 rc = mParent->host()->checkUSBProxyService();
2750 if (FAILED(rc)) return rc;
2751# endif
2752
2753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2754
2755 SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data());
2756 ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers));
2757 return S_OK;
2758#else
2759 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2760 * extended error info to indicate that USB is simply not available
2761 * (w/o treating it as a failure), for example, as in OSE */
2762 NOREF(aUSBControllers);
2763 ReturnComNotImplemented();
2764#endif /* VBOX_WITH_VUSB */
2765}
2766
2767STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters)
2768{
2769#ifdef VBOX_WITH_VUSB
2770 CheckComArgOutPointerValid(aUSBDeviceFilters);
2771
2772 AutoCaller autoCaller(this);
2773 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2774
2775 clearError();
2776 MultiResult rc(S_OK);
2777
2778# ifdef VBOX_WITH_USB
2779 rc = mParent->host()->checkUSBProxyService();
2780 if (FAILED(rc)) return rc;
2781# endif
2782
2783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2784
2785 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters);
2786#else
2787 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2788 * extended error info to indicate that USB is simply not available
2789 * (w/o treating it as a failure), for example, as in OSE */
2790 NOREF(aUSBDeviceFilters);
2791 ReturnComNotImplemented();
2792#endif /* VBOX_WITH_VUSB */
2793}
2794
2795STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2796{
2797 CheckComArgOutPointerValid(aFilePath);
2798
2799 AutoLimitedCaller autoCaller(this);
2800 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2801
2802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2803
2804 mData->m_strConfigFileFull.cloneTo(aFilePath);
2805 return S_OK;
2806}
2807
2808STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2809{
2810 CheckComArgOutPointerValid(aModified);
2811
2812 AutoCaller autoCaller(this);
2813 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2814
2815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2816
2817 HRESULT rc = checkStateDependency(MutableStateDep);
2818 if (FAILED(rc)) return rc;
2819
2820 if (!mData->pMachineConfigFile->fileExists())
2821 // this is a new machine, and no config file exists yet:
2822 *aModified = TRUE;
2823 else
2824 *aModified = (mData->flModifications != 0);
2825
2826 return S_OK;
2827}
2828
2829STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2830{
2831 CheckComArgOutPointerValid(aSessionState);
2832
2833 AutoCaller autoCaller(this);
2834 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2835
2836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2837
2838 *aSessionState = mData->mSession.mState;
2839
2840 return S_OK;
2841}
2842
2843STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2844{
2845 CheckComArgOutPointerValid(aSessionType);
2846
2847 AutoCaller autoCaller(this);
2848 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2849
2850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2851
2852 mData->mSession.mType.cloneTo(aSessionType);
2853
2854 return S_OK;
2855}
2856
2857STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2858{
2859 CheckComArgOutPointerValid(aSessionPID);
2860
2861 AutoCaller autoCaller(this);
2862 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2863
2864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2865
2866 *aSessionPID = mData->mSession.mPID;
2867
2868 return S_OK;
2869}
2870
2871STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2872{
2873 CheckComArgOutPointerValid(machineState);
2874
2875 AutoCaller autoCaller(this);
2876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2877
2878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2879
2880 *machineState = mData->mMachineState;
2881
2882 return S_OK;
2883}
2884
2885STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2886{
2887 CheckComArgOutPointerValid(aLastStateChange);
2888
2889 AutoCaller autoCaller(this);
2890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2891
2892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2893
2894 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2895
2896 return S_OK;
2897}
2898
2899STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2900{
2901 CheckComArgOutPointerValid(aStateFilePath);
2902
2903 AutoCaller autoCaller(this);
2904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2905
2906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2909
2910 return S_OK;
2911}
2912
2913STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2914{
2915 CheckComArgOutPointerValid(aLogFolder);
2916
2917 AutoCaller autoCaller(this);
2918 AssertComRCReturnRC(autoCaller.rc());
2919
2920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2921
2922 Utf8Str logFolder;
2923 getLogFolder(logFolder);
2924 logFolder.cloneTo(aLogFolder);
2925
2926 return S_OK;
2927}
2928
2929STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2930{
2931 CheckComArgOutPointerValid(aCurrentSnapshot);
2932
2933 AutoCaller autoCaller(this);
2934 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2935
2936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2937
2938 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2939
2940 return S_OK;
2941}
2942
2943STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2944{
2945 CheckComArgOutPointerValid(aSnapshotCount);
2946
2947 AutoCaller autoCaller(this);
2948 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2949
2950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2953 ? 0
2954 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2955
2956 return S_OK;
2957}
2958
2959STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2960{
2961 CheckComArgOutPointerValid(aCurrentStateModified);
2962
2963 AutoCaller autoCaller(this);
2964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2965
2966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2967
2968 /* Note: for machines with no snapshots, we always return FALSE
2969 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2970 * reasons :) */
2971
2972 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2973 ? FALSE
2974 : mData->mCurrentStateModified;
2975
2976 return S_OK;
2977}
2978
2979STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2980{
2981 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2982
2983 AutoCaller autoCaller(this);
2984 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2985
2986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2987
2988 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2989 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2990
2991 return S_OK;
2992}
2993
2994STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2995{
2996 CheckComArgOutPointerValid(aClipboardMode);
2997
2998 AutoCaller autoCaller(this);
2999 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3000
3001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3002
3003 *aClipboardMode = mHWData->mClipboardMode;
3004
3005 return S_OK;
3006}
3007
3008STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
3009{
3010 HRESULT rc = S_OK;
3011
3012 AutoCaller autoCaller(this);
3013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3014
3015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3016
3017 alock.release();
3018 rc = onClipboardModeChange(aClipboardMode);
3019 alock.acquire();
3020 if (FAILED(rc)) return rc;
3021
3022 setModified(IsModified_MachineData);
3023 mHWData.backup();
3024 mHWData->mClipboardMode = aClipboardMode;
3025
3026 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3027 if (Global::IsOnline(mData->mMachineState))
3028 saveSettings(NULL);
3029
3030 return S_OK;
3031}
3032
3033STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
3034{
3035 CheckComArgOutPointerValid(aDragAndDropMode);
3036
3037 AutoCaller autoCaller(this);
3038 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3039
3040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3041
3042 *aDragAndDropMode = mHWData->mDragAndDropMode;
3043
3044 return S_OK;
3045}
3046
3047STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3048{
3049 HRESULT rc = S_OK;
3050
3051 AutoCaller autoCaller(this);
3052 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3053
3054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3055
3056 alock.release();
3057 rc = onDragAndDropModeChange(aDragAndDropMode);
3058 alock.acquire();
3059 if (FAILED(rc)) return rc;
3060
3061 setModified(IsModified_MachineData);
3062 mHWData.backup();
3063 mHWData->mDragAndDropMode = aDragAndDropMode;
3064
3065 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3066 if (Global::IsOnline(mData->mMachineState))
3067 saveSettings(NULL);
3068
3069 return S_OK;
3070}
3071
3072STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3073{
3074 CheckComArgOutPointerValid(aPatterns);
3075
3076 AutoCaller autoCaller(this);
3077 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3078
3079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3080
3081 try
3082 {
3083 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3084 }
3085 catch (...)
3086 {
3087 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3088 }
3089
3090 return S_OK;
3091}
3092
3093STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3094{
3095 AutoCaller autoCaller(this);
3096 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3097
3098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3099
3100 HRESULT rc = checkStateDependency(MutableStateDep);
3101 if (FAILED(rc)) return rc;
3102
3103 setModified(IsModified_MachineData);
3104 mHWData.backup();
3105 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3106 return rc;
3107}
3108
3109STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3110{
3111 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3112
3113 AutoCaller autoCaller(this);
3114 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3115
3116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3117
3118 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3119 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3120
3121 return S_OK;
3122}
3123
3124STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3125{
3126 CheckComArgOutPointerValid(aEnabled);
3127
3128 AutoCaller autoCaller(this);
3129 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3130
3131 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3132
3133 *aEnabled = mUserData->s.fTeleporterEnabled;
3134
3135 return S_OK;
3136}
3137
3138STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3139{
3140 AutoCaller autoCaller(this);
3141 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3142
3143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3144
3145 /* Only allow it to be set to true when PoweredOff or Aborted.
3146 (Clearing it is always permitted.) */
3147 if ( aEnabled
3148 && mData->mRegistered
3149 && ( !isSessionMachine()
3150 || ( mData->mMachineState != MachineState_PoweredOff
3151 && mData->mMachineState != MachineState_Teleported
3152 && mData->mMachineState != MachineState_Aborted
3153 )
3154 )
3155 )
3156 return setError(VBOX_E_INVALID_VM_STATE,
3157 tr("The machine is not powered off (state is %s)"),
3158 Global::stringifyMachineState(mData->mMachineState));
3159
3160 setModified(IsModified_MachineData);
3161 mUserData.backup();
3162 mUserData->s.fTeleporterEnabled = !!aEnabled;
3163
3164 return S_OK;
3165}
3166
3167STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3168{
3169 CheckComArgOutPointerValid(aPort);
3170
3171 AutoCaller autoCaller(this);
3172 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3173
3174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3175
3176 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3177
3178 return S_OK;
3179}
3180
3181STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3182{
3183 if (aPort >= _64K)
3184 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3185
3186 AutoCaller autoCaller(this);
3187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3188
3189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3190
3191 HRESULT rc = checkStateDependency(MutableStateDep);
3192 if (FAILED(rc)) return rc;
3193
3194 setModified(IsModified_MachineData);
3195 mUserData.backup();
3196 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3197
3198 return S_OK;
3199}
3200
3201STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3202{
3203 CheckComArgOutPointerValid(aAddress);
3204
3205 AutoCaller autoCaller(this);
3206 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3207
3208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3209
3210 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3211
3212 return S_OK;
3213}
3214
3215STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3216{
3217 AutoCaller autoCaller(this);
3218 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3219
3220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3221
3222 HRESULT rc = checkStateDependency(MutableStateDep);
3223 if (FAILED(rc)) return rc;
3224
3225 setModified(IsModified_MachineData);
3226 mUserData.backup();
3227 mUserData->s.strTeleporterAddress = aAddress;
3228
3229 return S_OK;
3230}
3231
3232STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3233{
3234 CheckComArgOutPointerValid(aPassword);
3235
3236 AutoCaller autoCaller(this);
3237 HRESULT hrc = autoCaller.rc();
3238 if (SUCCEEDED(hrc))
3239 {
3240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3241 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3242 }
3243
3244 return hrc;
3245}
3246
3247STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3248{
3249 /*
3250 * Hash the password first.
3251 */
3252 Utf8Str strPassword(aPassword);
3253 if (!strPassword.isEmpty())
3254 {
3255 if (VBoxIsPasswordHashed(&strPassword))
3256 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3257 VBoxHashPassword(&strPassword);
3258 }
3259
3260 /*
3261 * Do the update.
3262 */
3263 AutoCaller autoCaller(this);
3264 HRESULT hrc = autoCaller.rc();
3265 if (SUCCEEDED(hrc))
3266 {
3267 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3268 hrc = checkStateDependency(MutableStateDep);
3269 if (SUCCEEDED(hrc))
3270 {
3271 setModified(IsModified_MachineData);
3272 mUserData.backup();
3273 mUserData->s.strTeleporterPassword = strPassword;
3274 }
3275 }
3276
3277 return hrc;
3278}
3279
3280STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3281{
3282 CheckComArgOutPointerValid(aState);
3283
3284 AutoCaller autoCaller(this);
3285 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3286
3287 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3288
3289 *aState = mUserData->s.enmFaultToleranceState;
3290 return S_OK;
3291}
3292
3293STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3294{
3295 AutoCaller autoCaller(this);
3296 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3297
3298 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3299
3300 /* @todo deal with running state change. */
3301 HRESULT rc = checkStateDependency(MutableStateDep);
3302 if (FAILED(rc)) return rc;
3303
3304 setModified(IsModified_MachineData);
3305 mUserData.backup();
3306 mUserData->s.enmFaultToleranceState = aState;
3307 return S_OK;
3308}
3309
3310STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3311{
3312 CheckComArgOutPointerValid(aAddress);
3313
3314 AutoCaller autoCaller(this);
3315 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3316
3317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3318
3319 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3320 return S_OK;
3321}
3322
3323STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3324{
3325 AutoCaller autoCaller(this);
3326 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3327
3328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3329
3330 /* @todo deal with running state change. */
3331 HRESULT rc = checkStateDependency(MutableStateDep);
3332 if (FAILED(rc)) return rc;
3333
3334 setModified(IsModified_MachineData);
3335 mUserData.backup();
3336 mUserData->s.strFaultToleranceAddress = aAddress;
3337 return S_OK;
3338}
3339
3340STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3341{
3342 CheckComArgOutPointerValid(aPort);
3343
3344 AutoCaller autoCaller(this);
3345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3346
3347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3348
3349 *aPort = mUserData->s.uFaultTolerancePort;
3350 return S_OK;
3351}
3352
3353STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3354{
3355 AutoCaller autoCaller(this);
3356 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3357
3358 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3359
3360 /* @todo deal with running state change. */
3361 HRESULT rc = checkStateDependency(MutableStateDep);
3362 if (FAILED(rc)) return rc;
3363
3364 setModified(IsModified_MachineData);
3365 mUserData.backup();
3366 mUserData->s.uFaultTolerancePort = aPort;
3367 return S_OK;
3368}
3369
3370STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3371{
3372 CheckComArgOutPointerValid(aPassword);
3373
3374 AutoCaller autoCaller(this);
3375 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3376
3377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3378
3379 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3380
3381 return S_OK;
3382}
3383
3384STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3385{
3386 AutoCaller autoCaller(this);
3387 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3388
3389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3390
3391 /* @todo deal with running state change. */
3392 HRESULT rc = checkStateDependency(MutableStateDep);
3393 if (FAILED(rc)) return rc;
3394
3395 setModified(IsModified_MachineData);
3396 mUserData.backup();
3397 mUserData->s.strFaultTolerancePassword = aPassword;
3398
3399 return S_OK;
3400}
3401
3402STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3403{
3404 CheckComArgOutPointerValid(aInterval);
3405
3406 AutoCaller autoCaller(this);
3407 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3408
3409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3410
3411 *aInterval = mUserData->s.uFaultToleranceInterval;
3412 return S_OK;
3413}
3414
3415STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3416{
3417 AutoCaller autoCaller(this);
3418 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3419
3420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3421
3422 /* @todo deal with running state change. */
3423 HRESULT rc = checkStateDependency(MutableStateDep);
3424 if (FAILED(rc)) return rc;
3425
3426 setModified(IsModified_MachineData);
3427 mUserData.backup();
3428 mUserData->s.uFaultToleranceInterval = aInterval;
3429 return S_OK;
3430}
3431
3432STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3433{
3434 CheckComArgOutPointerValid(aEnabled);
3435
3436 AutoCaller autoCaller(this);
3437 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3438
3439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3440
3441 *aEnabled = mUserData->s.fRTCUseUTC;
3442
3443 return S_OK;
3444}
3445
3446STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3447{
3448 AutoCaller autoCaller(this);
3449 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3450
3451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3452
3453 /* Only allow it to be set to true when PoweredOff or Aborted.
3454 (Clearing it is always permitted.) */
3455 if ( aEnabled
3456 && mData->mRegistered
3457 && ( !isSessionMachine()
3458 || ( mData->mMachineState != MachineState_PoweredOff
3459 && mData->mMachineState != MachineState_Teleported
3460 && mData->mMachineState != MachineState_Aborted
3461 )
3462 )
3463 )
3464 return setError(VBOX_E_INVALID_VM_STATE,
3465 tr("The machine is not powered off (state is %s)"),
3466 Global::stringifyMachineState(mData->mMachineState));
3467
3468 setModified(IsModified_MachineData);
3469 mUserData.backup();
3470 mUserData->s.fRTCUseUTC = !!aEnabled;
3471
3472 return S_OK;
3473}
3474
3475STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3476{
3477 CheckComArgOutPointerValid(aEnabled);
3478
3479 AutoCaller autoCaller(this);
3480 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3481
3482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3483
3484 *aEnabled = mHWData->mIOCacheEnabled;
3485
3486 return S_OK;
3487}
3488
3489STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3490{
3491 AutoCaller autoCaller(this);
3492 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3493
3494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3495
3496 HRESULT rc = checkStateDependency(MutableStateDep);
3497 if (FAILED(rc)) return rc;
3498
3499 setModified(IsModified_MachineData);
3500 mHWData.backup();
3501 mHWData->mIOCacheEnabled = aEnabled;
3502
3503 return S_OK;
3504}
3505
3506STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3507{
3508 CheckComArgOutPointerValid(aIOCacheSize);
3509
3510 AutoCaller autoCaller(this);
3511 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3512
3513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3514
3515 *aIOCacheSize = mHWData->mIOCacheSize;
3516
3517 return S_OK;
3518}
3519
3520STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3521{
3522 AutoCaller autoCaller(this);
3523 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3524
3525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3526
3527 HRESULT rc = checkStateDependency(MutableStateDep);
3528 if (FAILED(rc)) return rc;
3529
3530 setModified(IsModified_MachineData);
3531 mHWData.backup();
3532 mHWData->mIOCacheSize = aIOCacheSize;
3533
3534 return S_OK;
3535}
3536
3537
3538/**
3539 * @note Locks objects!
3540 */
3541STDMETHODIMP Machine::LockMachine(ISession *aSession,
3542 LockType_T lockType)
3543{
3544 CheckComArgNotNull(aSession);
3545
3546 AutoCaller autoCaller(this);
3547 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3548
3549 /* check the session state */
3550 SessionState_T state;
3551 HRESULT rc = aSession->COMGETTER(State)(&state);
3552 if (FAILED(rc)) return rc;
3553
3554 if (state != SessionState_Unlocked)
3555 return setError(VBOX_E_INVALID_OBJECT_STATE,
3556 tr("The given session is busy"));
3557
3558 // get the client's IInternalSessionControl interface
3559 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3560 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3561 E_INVALIDARG);
3562
3563 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3564
3565 if (!mData->mRegistered)
3566 return setError(E_UNEXPECTED,
3567 tr("The machine '%s' is not registered"),
3568 mUserData->s.strName.c_str());
3569
3570 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3571
3572 SessionState_T oldState = mData->mSession.mState;
3573 /* Hack: in case the session is closing and there is a progress object
3574 * which allows waiting for the session to be closed, take the opportunity
3575 * and do a limited wait (max. 1 second). This helps a lot when the system
3576 * is busy and thus session closing can take a little while. */
3577 if ( mData->mSession.mState == SessionState_Unlocking
3578 && mData->mSession.mProgress)
3579 {
3580 alock.release();
3581 mData->mSession.mProgress->WaitForCompletion(1000);
3582 alock.acquire();
3583 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3584 }
3585
3586 // try again now
3587 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3588 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3589 )
3590 {
3591 // OK, share the session... we are now dealing with three processes:
3592 // 1) VBoxSVC (where this code runs);
3593 // 2) process C: the caller's client process (who wants a shared session);
3594 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3595
3596 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3597 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3598 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3599 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3600 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3601
3602 /*
3603 * Release the lock before calling the client process. It's safe here
3604 * since the only thing to do after we get the lock again is to add
3605 * the remote control to the list (which doesn't directly influence
3606 * anything).
3607 */
3608 alock.release();
3609
3610 // get the console of the session holding the write lock (this is a remote call)
3611 ComPtr<IConsole> pConsoleW;
3612 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3613 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3614 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3615 if (FAILED(rc))
3616 // the failure may occur w/o any error info (from RPC), so provide one
3617 return setError(VBOX_E_VM_ERROR,
3618 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3619
3620 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3621
3622 // share the session machine and W's console with the caller's session
3623 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3624 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3625 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3626
3627 if (FAILED(rc))
3628 // the failure may occur w/o any error info (from RPC), so provide one
3629 return setError(VBOX_E_VM_ERROR,
3630 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3631 alock.acquire();
3632
3633 // need to revalidate the state after acquiring the lock again
3634 if (mData->mSession.mState != SessionState_Locked)
3635 {
3636 pSessionControl->Uninitialize();
3637 return setError(VBOX_E_INVALID_SESSION_STATE,
3638 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3639 mUserData->s.strName.c_str());
3640 }
3641
3642 // add the caller's session to the list
3643 mData->mSession.mRemoteControls.push_back(pSessionControl);
3644 }
3645 else if ( mData->mSession.mState == SessionState_Locked
3646 || mData->mSession.mState == SessionState_Unlocking
3647 )
3648 {
3649 // sharing not permitted, or machine still unlocking:
3650 return setError(VBOX_E_INVALID_OBJECT_STATE,
3651 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3652 mUserData->s.strName.c_str());
3653 }
3654 else
3655 {
3656 // machine is not locked: then write-lock the machine (create the session machine)
3657
3658 // must not be busy
3659 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3660
3661 // get the caller's session PID
3662 RTPROCESS pid = NIL_RTPROCESS;
3663 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3664 pSessionControl->GetPID((ULONG*)&pid);
3665 Assert(pid != NIL_RTPROCESS);
3666
3667 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3668
3669 if (fLaunchingVMProcess)
3670 {
3671 // this machine is awaiting for a spawning session to be opened:
3672 // then the calling process must be the one that got started by
3673 // LaunchVMProcess()
3674
3675 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3676 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3677
3678 if (mData->mSession.mPID != pid)
3679 return setError(E_ACCESSDENIED,
3680 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3681 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3682 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3683 }
3684
3685 // create the mutable SessionMachine from the current machine
3686 ComObjPtr<SessionMachine> sessionMachine;
3687 sessionMachine.createObject();
3688 rc = sessionMachine->init(this);
3689 AssertComRC(rc);
3690
3691 /* NOTE: doing return from this function after this point but
3692 * before the end is forbidden since it may call SessionMachine::uninit()
3693 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3694 * lock while still holding the Machine lock in alock so that a deadlock
3695 * is possible due to the wrong lock order. */
3696
3697 if (SUCCEEDED(rc))
3698 {
3699 /*
3700 * Set the session state to Spawning to protect against subsequent
3701 * attempts to open a session and to unregister the machine after
3702 * we release the lock.
3703 */
3704 SessionState_T origState = mData->mSession.mState;
3705 mData->mSession.mState = SessionState_Spawning;
3706
3707 /* Get the client token ID to be passed to the client process */
3708 Utf8Str strTokenId;
3709 sessionMachine->getTokenId(strTokenId);
3710 Assert(!strTokenId.isEmpty());
3711
3712 /*
3713 * Release the lock before calling the client process -- it will call
3714 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3715 * because the state is Spawning, so that LaunchVMProcess() and
3716 * LockMachine() calls will fail. This method, called before we
3717 * acquire the lock again, will fail because of the wrong PID.
3718 *
3719 * Note that mData->mSession.mRemoteControls accessed outside
3720 * the lock may not be modified when state is Spawning, so it's safe.
3721 */
3722 alock.release();
3723
3724 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3725 rc = pSessionControl->AssignMachine(sessionMachine, lockType, Bstr(strTokenId).raw());
3726 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3727
3728 /* The failure may occur w/o any error info (from RPC), so provide one */
3729 if (FAILED(rc))
3730 setError(VBOX_E_VM_ERROR,
3731 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3732
3733 if ( SUCCEEDED(rc)
3734 && fLaunchingVMProcess
3735 )
3736 {
3737 /* complete the remote session initialization */
3738
3739 /* get the console from the direct session */
3740 ComPtr<IConsole> console;
3741 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3742 ComAssertComRC(rc);
3743
3744 if (SUCCEEDED(rc) && !console)
3745 {
3746 ComAssert(!!console);
3747 rc = E_FAIL;
3748 }
3749
3750 /* assign machine & console to the remote session */
3751 if (SUCCEEDED(rc))
3752 {
3753 /*
3754 * after LaunchVMProcess(), the first and the only
3755 * entry in remoteControls is that remote session
3756 */
3757 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3758 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3759 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3760
3761 /* The failure may occur w/o any error info (from RPC), so provide one */
3762 if (FAILED(rc))
3763 setError(VBOX_E_VM_ERROR,
3764 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3765 }
3766
3767 if (FAILED(rc))
3768 pSessionControl->Uninitialize();
3769 }
3770
3771 /* acquire the lock again */
3772 alock.acquire();
3773
3774 /* Restore the session state */
3775 mData->mSession.mState = origState;
3776 }
3777
3778 // finalize spawning anyway (this is why we don't return on errors above)
3779 if (fLaunchingVMProcess)
3780 {
3781 /* Note that the progress object is finalized later */
3782 /** @todo Consider checking mData->mSession.mProgress for cancellation
3783 * around here. */
3784
3785 /* We don't reset mSession.mPID here because it is necessary for
3786 * SessionMachine::uninit() to reap the child process later. */
3787
3788 if (FAILED(rc))
3789 {
3790 /* Close the remote session, remove the remote control from the list
3791 * and reset session state to Closed (@note keep the code in sync
3792 * with the relevant part in checkForSpawnFailure()). */
3793
3794 Assert(mData->mSession.mRemoteControls.size() == 1);
3795 if (mData->mSession.mRemoteControls.size() == 1)
3796 {
3797 ErrorInfoKeeper eik;
3798 mData->mSession.mRemoteControls.front()->Uninitialize();
3799 }
3800
3801 mData->mSession.mRemoteControls.clear();
3802 mData->mSession.mState = SessionState_Unlocked;
3803 }
3804 }
3805 else
3806 {
3807 /* memorize PID of the directly opened session */
3808 if (SUCCEEDED(rc))
3809 mData->mSession.mPID = pid;
3810 }
3811
3812 if (SUCCEEDED(rc))
3813 {
3814 /* memorize the direct session control and cache IUnknown for it */
3815 mData->mSession.mDirectControl = pSessionControl;
3816 mData->mSession.mState = SessionState_Locked;
3817 /* associate the SessionMachine with this Machine */
3818 mData->mSession.mMachine = sessionMachine;
3819
3820 /* request an IUnknown pointer early from the remote party for later
3821 * identity checks (it will be internally cached within mDirectControl
3822 * at least on XPCOM) */
3823 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3824 NOREF(unk);
3825 }
3826
3827 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3828 * would break the lock order */
3829 alock.release();
3830
3831 /* uninitialize the created session machine on failure */
3832 if (FAILED(rc))
3833 sessionMachine->uninit();
3834
3835 }
3836
3837 if (SUCCEEDED(rc))
3838 {
3839 /*
3840 * tell the client watcher thread to update the set of
3841 * machines that have open sessions
3842 */
3843 mParent->updateClientWatcher();
3844
3845 if (oldState != SessionState_Locked)
3846 /* fire an event */
3847 mParent->onSessionStateChange(getId(), SessionState_Locked);
3848 }
3849
3850 return rc;
3851}
3852
3853/**
3854 * @note Locks objects!
3855 */
3856STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3857 IN_BSTR aFrontend,
3858 IN_BSTR aEnvironment,
3859 IProgress **aProgress)
3860{
3861 CheckComArgStr(aFrontend);
3862 Utf8Str strFrontend(aFrontend);
3863 Utf8Str strEnvironment(aEnvironment);
3864 /* "emergencystop" doesn't need the session, so skip the checks/interface
3865 * retrieval. This code doesn't quite fit in here, but introducing a
3866 * special API method would be even more effort, and would require explicit
3867 * support by every API client. It's better to hide the feature a bit. */
3868 if (strFrontend != "emergencystop")
3869 CheckComArgNotNull(aSession);
3870 CheckComArgOutPointerValid(aProgress);
3871
3872 AutoCaller autoCaller(this);
3873 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3874
3875 HRESULT rc = S_OK;
3876 if (strFrontend.isEmpty())
3877 {
3878 Bstr bstrFrontend;
3879 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3880 if (FAILED(rc))
3881 return rc;
3882 strFrontend = bstrFrontend;
3883 if (strFrontend.isEmpty())
3884 {
3885 ComPtr<ISystemProperties> systemProperties;
3886 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3887 if (FAILED(rc))
3888 return rc;
3889 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3890 if (FAILED(rc))
3891 return rc;
3892 strFrontend = bstrFrontend;
3893 }
3894 /* paranoia - emergencystop is not a valid default */
3895 if (strFrontend == "emergencystop")
3896 strFrontend = Utf8Str::Empty;
3897 }
3898 /* default frontend: Qt GUI */
3899 if (strFrontend.isEmpty())
3900 strFrontend = "GUI/Qt";
3901
3902 if (strFrontend != "emergencystop")
3903 {
3904 /* check the session state */
3905 SessionState_T state;
3906 rc = aSession->COMGETTER(State)(&state);
3907 if (FAILED(rc))
3908 return rc;
3909
3910 if (state != SessionState_Unlocked)
3911 return setError(VBOX_E_INVALID_OBJECT_STATE,
3912 tr("The given session is busy"));
3913
3914 /* get the IInternalSessionControl interface */
3915 ComPtr<IInternalSessionControl> control(aSession);
3916 ComAssertMsgRet(!control.isNull(),
3917 ("No IInternalSessionControl interface"),
3918 E_INVALIDARG);
3919
3920 /* get the teleporter enable state for the progress object init. */
3921 BOOL fTeleporterEnabled;
3922 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3923 if (FAILED(rc))
3924 return rc;
3925
3926 /* create a progress object */
3927 ComObjPtr<ProgressProxy> progress;
3928 progress.createObject();
3929 rc = progress->init(mParent,
3930 static_cast<IMachine*>(this),
3931 Bstr(tr("Starting VM")).raw(),
3932 TRUE /* aCancelable */,
3933 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3934 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3935 2 /* uFirstOperationWeight */,
3936 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3937
3938 if (SUCCEEDED(rc))
3939 {
3940 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3941 if (SUCCEEDED(rc))
3942 {
3943 progress.queryInterfaceTo(aProgress);
3944
3945 /* signal the client watcher thread */
3946 mParent->updateClientWatcher();
3947
3948 /* fire an event */
3949 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3950 }
3951 }
3952 }
3953 else
3954 {
3955 /* no progress object - either instant success or failure */
3956 *aProgress = NULL;
3957
3958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3959
3960 if (mData->mSession.mState != SessionState_Locked)
3961 return setError(VBOX_E_INVALID_OBJECT_STATE,
3962 tr("The machine '%s' is not locked by a session"),
3963 mUserData->s.strName.c_str());
3964
3965 /* must have a VM process associated - do not kill normal API clients
3966 * with an open session */
3967 if (!Global::IsOnline(mData->mMachineState))
3968 return setError(VBOX_E_INVALID_OBJECT_STATE,
3969 tr("The machine '%s' does not have a VM process"),
3970 mUserData->s.strName.c_str());
3971
3972 /* forcibly terminate the VM process */
3973 if (mData->mSession.mPID != NIL_RTPROCESS)
3974 RTProcTerminate(mData->mSession.mPID);
3975
3976 /* signal the client watcher thread, as most likely the client has
3977 * been terminated */
3978 mParent->updateClientWatcher();
3979 }
3980
3981 return rc;
3982}
3983
3984STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3985{
3986 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3987 return setError(E_INVALIDARG,
3988 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3989 aPosition, SchemaDefs::MaxBootPosition);
3990
3991 if (aDevice == DeviceType_USB)
3992 return setError(E_NOTIMPL,
3993 tr("Booting from USB device is currently not supported"));
3994
3995 AutoCaller autoCaller(this);
3996 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3997
3998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3999
4000 HRESULT rc = checkStateDependency(MutableStateDep);
4001 if (FAILED(rc)) return rc;
4002
4003 setModified(IsModified_MachineData);
4004 mHWData.backup();
4005 mHWData->mBootOrder[aPosition - 1] = aDevice;
4006
4007 return S_OK;
4008}
4009
4010STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
4011{
4012 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
4013 return setError(E_INVALIDARG,
4014 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
4015 aPosition, SchemaDefs::MaxBootPosition);
4016
4017 AutoCaller autoCaller(this);
4018 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4019
4020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4021
4022 *aDevice = mHWData->mBootOrder[aPosition - 1];
4023
4024 return S_OK;
4025}
4026
4027STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
4028 LONG aControllerPort,
4029 LONG aDevice,
4030 DeviceType_T aType,
4031 IMedium *aMedium)
4032{
4033 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4034 aControllerName, aControllerPort, aDevice, aType, aMedium));
4035
4036 CheckComArgStrNotEmptyOrNull(aControllerName);
4037
4038 AutoCaller autoCaller(this);
4039 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4040
4041 // request the host lock first, since might be calling Host methods for getting host drives;
4042 // next, protect the media tree all the while we're in here, as well as our member variables
4043 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
4044 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4045
4046 HRESULT rc = checkStateDependency(MutableStateDep);
4047 if (FAILED(rc)) return rc;
4048
4049 /// @todo NEWMEDIA implicit machine registration
4050 if (!mData->mRegistered)
4051 return setError(VBOX_E_INVALID_OBJECT_STATE,
4052 tr("Cannot attach storage devices to an unregistered machine"));
4053
4054 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4055
4056 /* Check for an existing controller. */
4057 ComObjPtr<StorageController> ctl;
4058 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4059 if (FAILED(rc)) return rc;
4060
4061 StorageControllerType_T ctrlType;
4062 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4063 if (FAILED(rc))
4064 return setError(E_FAIL,
4065 tr("Could not get type of controller '%ls'"),
4066 aControllerName);
4067
4068 bool fSilent = false;
4069 Utf8Str strReconfig;
4070
4071 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4072 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4073 if ( mData->mMachineState == MachineState_Paused
4074 && strReconfig == "1")
4075 fSilent = true;
4076
4077 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4078 bool fHotplug = false;
4079 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4080 fHotplug = true;
4081
4082 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4083 return setError(VBOX_E_INVALID_VM_STATE,
4084 tr("Controller '%ls' does not support hotplugging"),
4085 aControllerName);
4086
4087 // check that the port and device are not out of range
4088 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
4089 if (FAILED(rc)) return rc;
4090
4091 /* check if the device slot is already busy */
4092 MediumAttachment *pAttachTemp;
4093 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4094 aControllerName,
4095 aControllerPort,
4096 aDevice)))
4097 {
4098 Medium *pMedium = pAttachTemp->getMedium();
4099 if (pMedium)
4100 {
4101 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4102 return setError(VBOX_E_OBJECT_IN_USE,
4103 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4104 pMedium->getLocationFull().c_str(),
4105 aControllerPort,
4106 aDevice,
4107 aControllerName);
4108 }
4109 else
4110 return setError(VBOX_E_OBJECT_IN_USE,
4111 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4112 aControllerPort, aDevice, aControllerName);
4113 }
4114
4115 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4116 if (aMedium && medium.isNull())
4117 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4118
4119 AutoCaller mediumCaller(medium);
4120 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4121
4122 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4123
4124 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4125 && !medium.isNull()
4126 )
4127 return setError(VBOX_E_OBJECT_IN_USE,
4128 tr("Medium '%s' is already attached to this virtual machine"),
4129 medium->getLocationFull().c_str());
4130
4131 if (!medium.isNull())
4132 {
4133 MediumType_T mtype = medium->getType();
4134 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4135 // For DVDs it's not written to the config file, so needs no global config
4136 // version bump. For floppies it's a new attribute "type", which is ignored
4137 // by older VirtualBox version, so needs no global config version bump either.
4138 // For hard disks this type is not accepted.
4139 if (mtype == MediumType_MultiAttach)
4140 {
4141 // This type is new with VirtualBox 4.0 and therefore requires settings
4142 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4143 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4144 // two reasons: The medium type is a property of the media registry tree, which
4145 // can reside in the global config file (for pre-4.0 media); we would therefore
4146 // possibly need to bump the global config version. We don't want to do that though
4147 // because that might make downgrading to pre-4.0 impossible.
4148 // As a result, we can only use these two new types if the medium is NOT in the
4149 // global registry:
4150 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4151 if ( medium->isInRegistry(uuidGlobalRegistry)
4152 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4153 )
4154 return setError(VBOX_E_INVALID_OBJECT_STATE,
4155 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4156 "to machines that were created with VirtualBox 4.0 or later"),
4157 medium->getLocationFull().c_str());
4158 }
4159 }
4160
4161 bool fIndirect = false;
4162 if (!medium.isNull())
4163 fIndirect = medium->isReadOnly();
4164 bool associate = true;
4165
4166 do
4167 {
4168 if ( aType == DeviceType_HardDisk
4169 && mMediaData.isBackedUp())
4170 {
4171 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4172
4173 /* check if the medium was attached to the VM before we started
4174 * changing attachments in which case the attachment just needs to
4175 * be restored */
4176 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4177 {
4178 AssertReturn(!fIndirect, E_FAIL);
4179
4180 /* see if it's the same bus/channel/device */
4181 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4182 {
4183 /* the simplest case: restore the whole attachment
4184 * and return, nothing else to do */
4185 mMediaData->mAttachments.push_back(pAttachTemp);
4186
4187 /* Reattach the medium to the VM. */
4188 if (fHotplug || fSilent)
4189 {
4190 mediumLock.release();
4191 treeLock.release();
4192 alock.release();
4193
4194 MediumLockList *pMediumLockList(new MediumLockList());
4195
4196 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4197 true /* fMediumLockWrite */,
4198 NULL,
4199 *pMediumLockList);
4200 alock.acquire();
4201 if (FAILED(rc))
4202 delete pMediumLockList;
4203 else
4204 {
4205 mData->mSession.mLockedMedia.Unlock();
4206 alock.release();
4207 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4208 mData->mSession.mLockedMedia.Lock();
4209 alock.acquire();
4210 }
4211 alock.release();
4212
4213 if (SUCCEEDED(rc))
4214 {
4215 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4216 /* Remove lock list in case of error. */
4217 if (FAILED(rc))
4218 {
4219 mData->mSession.mLockedMedia.Unlock();
4220 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4221 mData->mSession.mLockedMedia.Lock();
4222 }
4223 }
4224 }
4225
4226 return S_OK;
4227 }
4228
4229 /* bus/channel/device differ; we need a new attachment object,
4230 * but don't try to associate it again */
4231 associate = false;
4232 break;
4233 }
4234 }
4235
4236 /* go further only if the attachment is to be indirect */
4237 if (!fIndirect)
4238 break;
4239
4240 /* perform the so called smart attachment logic for indirect
4241 * attachments. Note that smart attachment is only applicable to base
4242 * hard disks. */
4243
4244 if (medium->getParent().isNull())
4245 {
4246 /* first, investigate the backup copy of the current hard disk
4247 * attachments to make it possible to re-attach existing diffs to
4248 * another device slot w/o losing their contents */
4249 if (mMediaData.isBackedUp())
4250 {
4251 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4252
4253 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4254 uint32_t foundLevel = 0;
4255
4256 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4257 it != oldAtts.end();
4258 ++it)
4259 {
4260 uint32_t level = 0;
4261 MediumAttachment *pAttach = *it;
4262 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4263 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4264 if (pMedium.isNull())
4265 continue;
4266
4267 if (pMedium->getBase(&level) == medium)
4268 {
4269 /* skip the hard disk if its currently attached (we
4270 * cannot attach the same hard disk twice) */
4271 if (findAttachment(mMediaData->mAttachments,
4272 pMedium))
4273 continue;
4274
4275 /* matched device, channel and bus (i.e. attached to the
4276 * same place) will win and immediately stop the search;
4277 * otherwise the attachment that has the youngest
4278 * descendant of medium will be used
4279 */
4280 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4281 {
4282 /* the simplest case: restore the whole attachment
4283 * and return, nothing else to do */
4284 mMediaData->mAttachments.push_back(*it);
4285
4286 /* Reattach the medium to the VM. */
4287 if (fHotplug || fSilent)
4288 {
4289 mediumLock.release();
4290 treeLock.release();
4291 alock.release();
4292
4293 MediumLockList *pMediumLockList(new MediumLockList());
4294
4295 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4296 true /* fMediumLockWrite */,
4297 NULL,
4298 *pMediumLockList);
4299 alock.acquire();
4300 if (FAILED(rc))
4301 delete pMediumLockList;
4302 else
4303 {
4304 mData->mSession.mLockedMedia.Unlock();
4305 alock.release();
4306 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4307 mData->mSession.mLockedMedia.Lock();
4308 alock.acquire();
4309 }
4310 alock.release();
4311
4312 if (SUCCEEDED(rc))
4313 {
4314 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4315 /* Remove lock list in case of error. */
4316 if (FAILED(rc))
4317 {
4318 mData->mSession.mLockedMedia.Unlock();
4319 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4320 mData->mSession.mLockedMedia.Lock();
4321 }
4322 }
4323 }
4324
4325 return S_OK;
4326 }
4327 else if ( foundIt == oldAtts.end()
4328 || level > foundLevel /* prefer younger */
4329 )
4330 {
4331 foundIt = it;
4332 foundLevel = level;
4333 }
4334 }
4335 }
4336
4337 if (foundIt != oldAtts.end())
4338 {
4339 /* use the previously attached hard disk */
4340 medium = (*foundIt)->getMedium();
4341 mediumCaller.attach(medium);
4342 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4343 mediumLock.attach(medium);
4344 /* not implicit, doesn't require association with this VM */
4345 fIndirect = false;
4346 associate = false;
4347 /* go right to the MediumAttachment creation */
4348 break;
4349 }
4350 }
4351
4352 /* must give up the medium lock and medium tree lock as below we
4353 * go over snapshots, which needs a lock with higher lock order. */
4354 mediumLock.release();
4355 treeLock.release();
4356
4357 /* then, search through snapshots for the best diff in the given
4358 * hard disk's chain to base the new diff on */
4359
4360 ComObjPtr<Medium> base;
4361 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4362 while (snap)
4363 {
4364 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4365
4366 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4367
4368 MediumAttachment *pAttachFound = NULL;
4369 uint32_t foundLevel = 0;
4370
4371 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4372 it != snapAtts.end();
4373 ++it)
4374 {
4375 MediumAttachment *pAttach = *it;
4376 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4377 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4378 if (pMedium.isNull())
4379 continue;
4380
4381 uint32_t level = 0;
4382 if (pMedium->getBase(&level) == medium)
4383 {
4384 /* matched device, channel and bus (i.e. attached to the
4385 * same place) will win and immediately stop the search;
4386 * otherwise the attachment that has the youngest
4387 * descendant of medium will be used
4388 */
4389 if ( pAttach->getDevice() == aDevice
4390 && pAttach->getPort() == aControllerPort
4391 && pAttach->getControllerName() == aControllerName
4392 )
4393 {
4394 pAttachFound = pAttach;
4395 break;
4396 }
4397 else if ( !pAttachFound
4398 || level > foundLevel /* prefer younger */
4399 )
4400 {
4401 pAttachFound = pAttach;
4402 foundLevel = level;
4403 }
4404 }
4405 }
4406
4407 if (pAttachFound)
4408 {
4409 base = pAttachFound->getMedium();
4410 break;
4411 }
4412
4413 snap = snap->getParent();
4414 }
4415
4416 /* re-lock medium tree and the medium, as we need it below */
4417 treeLock.acquire();
4418 mediumLock.acquire();
4419
4420 /* found a suitable diff, use it as a base */
4421 if (!base.isNull())
4422 {
4423 medium = base;
4424 mediumCaller.attach(medium);
4425 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4426 mediumLock.attach(medium);
4427 }
4428 }
4429
4430 Utf8Str strFullSnapshotFolder;
4431 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4432
4433 ComObjPtr<Medium> diff;
4434 diff.createObject();
4435 // store this diff in the same registry as the parent
4436 Guid uuidRegistryParent;
4437 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4438 {
4439 // parent image has no registry: this can happen if we're attaching a new immutable
4440 // image that has not yet been attached (medium then points to the base and we're
4441 // creating the diff image for the immutable, and the parent is not yet registered);
4442 // put the parent in the machine registry then
4443 mediumLock.release();
4444 treeLock.release();
4445 alock.release();
4446 addMediumToRegistry(medium);
4447 alock.acquire();
4448 treeLock.acquire();
4449 mediumLock.acquire();
4450 medium->getFirstRegistryMachineId(uuidRegistryParent);
4451 }
4452 rc = diff->init(mParent,
4453 medium->getPreferredDiffFormat(),
4454 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4455 uuidRegistryParent);
4456 if (FAILED(rc)) return rc;
4457
4458 /* Apply the normal locking logic to the entire chain. */
4459 MediumLockList *pMediumLockList(new MediumLockList());
4460 mediumLock.release();
4461 treeLock.release();
4462 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4463 true /* fMediumLockWrite */,
4464 medium,
4465 *pMediumLockList);
4466 treeLock.acquire();
4467 mediumLock.acquire();
4468 if (SUCCEEDED(rc))
4469 {
4470 mediumLock.release();
4471 treeLock.release();
4472 rc = pMediumLockList->Lock();
4473 treeLock.acquire();
4474 mediumLock.acquire();
4475 if (FAILED(rc))
4476 setError(rc,
4477 tr("Could not lock medium when creating diff '%s'"),
4478 diff->getLocationFull().c_str());
4479 else
4480 {
4481 /* will release the lock before the potentially lengthy
4482 * operation, so protect with the special state */
4483 MachineState_T oldState = mData->mMachineState;
4484 setMachineState(MachineState_SettingUp);
4485
4486 mediumLock.release();
4487 treeLock.release();
4488 alock.release();
4489
4490 rc = medium->createDiffStorage(diff,
4491 MediumVariant_Standard,
4492 pMediumLockList,
4493 NULL /* aProgress */,
4494 true /* aWait */);
4495
4496 alock.acquire();
4497 treeLock.acquire();
4498 mediumLock.acquire();
4499
4500 setMachineState(oldState);
4501 }
4502 }
4503
4504 /* Unlock the media and free the associated memory. */
4505 delete pMediumLockList;
4506
4507 if (FAILED(rc)) return rc;
4508
4509 /* use the created diff for the actual attachment */
4510 medium = diff;
4511 mediumCaller.attach(medium);
4512 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4513 mediumLock.attach(medium);
4514 }
4515 while (0);
4516
4517 ComObjPtr<MediumAttachment> attachment;
4518 attachment.createObject();
4519 rc = attachment->init(this,
4520 medium,
4521 aControllerName,
4522 aControllerPort,
4523 aDevice,
4524 aType,
4525 fIndirect,
4526 false /* fPassthrough */,
4527 false /* fTempEject */,
4528 false /* fNonRotational */,
4529 false /* fDiscard */,
4530 Utf8Str::Empty);
4531 if (FAILED(rc)) return rc;
4532
4533 if (associate && !medium.isNull())
4534 {
4535 // as the last step, associate the medium to the VM
4536 rc = medium->addBackReference(mData->mUuid);
4537 // here we can fail because of Deleting, or being in process of creating a Diff
4538 if (FAILED(rc)) return rc;
4539
4540 mediumLock.release();
4541 treeLock.release();
4542 alock.release();
4543 addMediumToRegistry(medium);
4544 alock.acquire();
4545 treeLock.acquire();
4546 mediumLock.acquire();
4547 }
4548
4549 /* success: finally remember the attachment */
4550 setModified(IsModified_Storage);
4551 mMediaData.backup();
4552 mMediaData->mAttachments.push_back(attachment);
4553
4554 mediumLock.release();
4555 treeLock.release();
4556 alock.release();
4557
4558 if (fHotplug || fSilent)
4559 {
4560 if (!medium.isNull())
4561 {
4562 MediumLockList *pMediumLockList(new MediumLockList());
4563
4564 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4565 true /* fMediumLockWrite */,
4566 NULL,
4567 *pMediumLockList);
4568 alock.acquire();
4569 if (FAILED(rc))
4570 delete pMediumLockList;
4571 else
4572 {
4573 mData->mSession.mLockedMedia.Unlock();
4574 alock.release();
4575 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4576 mData->mSession.mLockedMedia.Lock();
4577 alock.acquire();
4578 }
4579 alock.release();
4580 }
4581
4582 if (SUCCEEDED(rc))
4583 {
4584 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4585 /* Remove lock list in case of error. */
4586 if (FAILED(rc))
4587 {
4588 mData->mSession.mLockedMedia.Unlock();
4589 mData->mSession.mLockedMedia.Remove(attachment);
4590 mData->mSession.mLockedMedia.Lock();
4591 }
4592 }
4593 }
4594
4595 mParent->saveModifiedRegistries();
4596
4597 return rc;
4598}
4599
4600STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4601 LONG aDevice)
4602{
4603 CheckComArgStrNotEmptyOrNull(aControllerName);
4604
4605 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4606 aControllerName, aControllerPort, aDevice));
4607
4608 AutoCaller autoCaller(this);
4609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4610
4611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4612
4613 HRESULT rc = checkStateDependency(MutableStateDep);
4614 if (FAILED(rc)) return rc;
4615
4616 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4617
4618 /* Check for an existing controller. */
4619 ComObjPtr<StorageController> ctl;
4620 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4621 if (FAILED(rc)) return rc;
4622
4623 StorageControllerType_T ctrlType;
4624 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4625 if (FAILED(rc))
4626 return setError(E_FAIL,
4627 tr("Could not get type of controller '%ls'"),
4628 aControllerName);
4629
4630 bool fSilent = false;
4631 Utf8Str strReconfig;
4632
4633 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4634 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4635 if ( mData->mMachineState == MachineState_Paused
4636 && strReconfig == "1")
4637 fSilent = true;
4638
4639 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4640 bool fHotplug = false;
4641 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4642 fHotplug = true;
4643
4644 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4645 return setError(VBOX_E_INVALID_VM_STATE,
4646 tr("Controller '%ls' does not support hotplugging"),
4647 aControllerName);
4648
4649 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4650 aControllerName,
4651 aControllerPort,
4652 aDevice);
4653 if (!pAttach)
4654 return setError(VBOX_E_OBJECT_NOT_FOUND,
4655 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4656 aDevice, aControllerPort, aControllerName);
4657
4658 /*
4659 * The VM has to detach the device before we delete any implicit diffs.
4660 * If this fails we can roll back without loosing data.
4661 */
4662 if (fHotplug || fSilent)
4663 {
4664 alock.release();
4665 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4666 alock.acquire();
4667 }
4668 if (FAILED(rc)) return rc;
4669
4670 /* If we are here everything went well and we can delete the implicit now. */
4671 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4672
4673 alock.release();
4674
4675 mParent->saveModifiedRegistries();
4676
4677 return rc;
4678}
4679
4680STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4681 LONG aDevice, BOOL aPassthrough)
4682{
4683 CheckComArgStrNotEmptyOrNull(aControllerName);
4684
4685 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4686 aControllerName, aControllerPort, aDevice, aPassthrough));
4687
4688 AutoCaller autoCaller(this);
4689 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4690
4691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4692
4693 HRESULT rc = checkStateDependency(MutableStateDep);
4694 if (FAILED(rc)) return rc;
4695
4696 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4697
4698 if (Global::IsOnlineOrTransient(mData->mMachineState))
4699 return setError(VBOX_E_INVALID_VM_STATE,
4700 tr("Invalid machine state: %s"),
4701 Global::stringifyMachineState(mData->mMachineState));
4702
4703 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4704 aControllerName,
4705 aControllerPort,
4706 aDevice);
4707 if (!pAttach)
4708 return setError(VBOX_E_OBJECT_NOT_FOUND,
4709 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4710 aDevice, aControllerPort, aControllerName);
4711
4712
4713 setModified(IsModified_Storage);
4714 mMediaData.backup();
4715
4716 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4717
4718 if (pAttach->getType() != DeviceType_DVD)
4719 return setError(E_INVALIDARG,
4720 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4721 aDevice, aControllerPort, aControllerName);
4722 pAttach->updatePassthrough(!!aPassthrough);
4723
4724 return S_OK;
4725}
4726
4727STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4728 LONG aDevice, BOOL aTemporaryEject)
4729{
4730 CheckComArgStrNotEmptyOrNull(aControllerName);
4731
4732 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4733 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4734
4735 AutoCaller autoCaller(this);
4736 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4737
4738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4739
4740 HRESULT rc = checkStateDependency(MutableStateDep);
4741 if (FAILED(rc)) return rc;
4742
4743 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4744 aControllerName,
4745 aControllerPort,
4746 aDevice);
4747 if (!pAttach)
4748 return setError(VBOX_E_OBJECT_NOT_FOUND,
4749 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4750 aDevice, aControllerPort, aControllerName);
4751
4752
4753 setModified(IsModified_Storage);
4754 mMediaData.backup();
4755
4756 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4757
4758 if (pAttach->getType() != DeviceType_DVD)
4759 return setError(E_INVALIDARG,
4760 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4761 aDevice, aControllerPort, aControllerName);
4762 pAttach->updateTempEject(!!aTemporaryEject);
4763
4764 return S_OK;
4765}
4766
4767STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4768 LONG aDevice, BOOL aNonRotational)
4769{
4770 CheckComArgStrNotEmptyOrNull(aControllerName);
4771
4772 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4773 aControllerName, aControllerPort, aDevice, aNonRotational));
4774
4775 AutoCaller autoCaller(this);
4776 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4777
4778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4779
4780 HRESULT rc = checkStateDependency(MutableStateDep);
4781 if (FAILED(rc)) return rc;
4782
4783 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4784
4785 if (Global::IsOnlineOrTransient(mData->mMachineState))
4786 return setError(VBOX_E_INVALID_VM_STATE,
4787 tr("Invalid machine state: %s"),
4788 Global::stringifyMachineState(mData->mMachineState));
4789
4790 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4791 aControllerName,
4792 aControllerPort,
4793 aDevice);
4794 if (!pAttach)
4795 return setError(VBOX_E_OBJECT_NOT_FOUND,
4796 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4797 aDevice, aControllerPort, aControllerName);
4798
4799
4800 setModified(IsModified_Storage);
4801 mMediaData.backup();
4802
4803 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4804
4805 if (pAttach->getType() != DeviceType_HardDisk)
4806 return setError(E_INVALIDARG,
4807 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4808 aDevice, aControllerPort, aControllerName);
4809 pAttach->updateNonRotational(!!aNonRotational);
4810
4811 return S_OK;
4812}
4813
4814STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4815 LONG aDevice, BOOL aDiscard)
4816{
4817 CheckComArgStrNotEmptyOrNull(aControllerName);
4818
4819 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4820 aControllerName, aControllerPort, aDevice, aDiscard));
4821
4822 AutoCaller autoCaller(this);
4823 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4824
4825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4826
4827 HRESULT rc = checkStateDependency(MutableStateDep);
4828 if (FAILED(rc)) return rc;
4829
4830 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4831
4832 if (Global::IsOnlineOrTransient(mData->mMachineState))
4833 return setError(VBOX_E_INVALID_VM_STATE,
4834 tr("Invalid machine state: %s"),
4835 Global::stringifyMachineState(mData->mMachineState));
4836
4837 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4838 aControllerName,
4839 aControllerPort,
4840 aDevice);
4841 if (!pAttach)
4842 return setError(VBOX_E_OBJECT_NOT_FOUND,
4843 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4844 aDevice, aControllerPort, aControllerName);
4845
4846
4847 setModified(IsModified_Storage);
4848 mMediaData.backup();
4849
4850 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4851
4852 if (pAttach->getType() != DeviceType_HardDisk)
4853 return setError(E_INVALIDARG,
4854 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4855 aDevice, aControllerPort, aControllerName);
4856 pAttach->updateDiscard(!!aDiscard);
4857
4858 return S_OK;
4859}
4860
4861STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4862 LONG aDevice)
4863{
4864 int rc = S_OK;
4865 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4866 aControllerName, aControllerPort, aDevice));
4867
4868 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4869
4870 return rc;
4871}
4872
4873STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4874 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4875{
4876 CheckComArgStrNotEmptyOrNull(aControllerName);
4877
4878 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4879 aControllerName, aControllerPort, aDevice));
4880
4881 AutoCaller autoCaller(this);
4882 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4883
4884 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4885
4886 HRESULT rc = checkStateDependency(MutableStateDep);
4887 if (FAILED(rc)) return rc;
4888
4889 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4890
4891 if (Global::IsOnlineOrTransient(mData->mMachineState))
4892 return setError(VBOX_E_INVALID_VM_STATE,
4893 tr("Invalid machine state: %s"),
4894 Global::stringifyMachineState(mData->mMachineState));
4895
4896 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4897 aControllerName,
4898 aControllerPort,
4899 aDevice);
4900 if (!pAttach)
4901 return setError(VBOX_E_OBJECT_NOT_FOUND,
4902 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4903 aDevice, aControllerPort, aControllerName);
4904
4905
4906 setModified(IsModified_Storage);
4907 mMediaData.backup();
4908
4909 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4910 if (aBandwidthGroup && group.isNull())
4911 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4912
4913 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4914
4915 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4916 if (strBandwidthGroupOld.isNotEmpty())
4917 {
4918 /* Get the bandwidth group object and release it - this must not fail. */
4919 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4920 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4921 Assert(SUCCEEDED(rc));
4922
4923 pBandwidthGroupOld->release();
4924 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4925 }
4926
4927 if (!group.isNull())
4928 {
4929 group->reference();
4930 pAttach->updateBandwidthGroup(group->getName());
4931 }
4932
4933 return S_OK;
4934}
4935
4936STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4937 LONG aControllerPort,
4938 LONG aDevice,
4939 DeviceType_T aType)
4940{
4941 HRESULT rc = S_OK;
4942
4943 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4944 aControllerName, aControllerPort, aDevice, aType));
4945
4946 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4947
4948 return rc;
4949}
4950
4951
4952
4953STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4954 LONG aControllerPort,
4955 LONG aDevice,
4956 BOOL aForce)
4957{
4958 int rc = S_OK;
4959 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4960 aControllerName, aControllerPort, aForce));
4961
4962 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4963
4964 return rc;
4965}
4966
4967STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4968 LONG aControllerPort,
4969 LONG aDevice,
4970 IMedium *aMedium,
4971 BOOL aForce)
4972{
4973 int rc = S_OK;
4974 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4975 aControllerName, aControllerPort, aDevice, aForce));
4976
4977 CheckComArgStrNotEmptyOrNull(aControllerName);
4978
4979 AutoCaller autoCaller(this);
4980 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4981
4982 // request the host lock first, since might be calling Host methods for getting host drives;
4983 // next, protect the media tree all the while we're in here, as well as our member variables
4984 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4985 this->lockHandle(),
4986 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4987
4988 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4989 aControllerName,
4990 aControllerPort,
4991 aDevice);
4992 if (pAttach.isNull())
4993 return setError(VBOX_E_OBJECT_NOT_FOUND,
4994 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4995 aDevice, aControllerPort, aControllerName);
4996
4997 /* Remember previously mounted medium. The medium before taking the
4998 * backup is not necessarily the same thing. */
4999 ComObjPtr<Medium> oldmedium;
5000 oldmedium = pAttach->getMedium();
5001
5002 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
5003 if (aMedium && pMedium.isNull())
5004 return setError(E_INVALIDARG, "The given medium pointer is invalid");
5005
5006 AutoCaller mediumCaller(pMedium);
5007 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
5008
5009 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5010 if (pMedium)
5011 {
5012 DeviceType_T mediumType = pAttach->getType();
5013 switch (mediumType)
5014 {
5015 case DeviceType_DVD:
5016 case DeviceType_Floppy:
5017 break;
5018
5019 default:
5020 return setError(VBOX_E_INVALID_OBJECT_STATE,
5021 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
5022 aControllerPort,
5023 aDevice,
5024 aControllerName);
5025 }
5026 }
5027
5028 setModified(IsModified_Storage);
5029 mMediaData.backup();
5030
5031 {
5032 // The backup operation makes the pAttach reference point to the
5033 // old settings. Re-get the correct reference.
5034 pAttach = findAttachment(mMediaData->mAttachments,
5035 aControllerName,
5036 aControllerPort,
5037 aDevice);
5038 if (!oldmedium.isNull())
5039 oldmedium->removeBackReference(mData->mUuid);
5040 if (!pMedium.isNull())
5041 {
5042 pMedium->addBackReference(mData->mUuid);
5043
5044 mediumLock.release();
5045 multiLock.release();
5046 addMediumToRegistry(pMedium);
5047 multiLock.acquire();
5048 mediumLock.acquire();
5049 }
5050
5051 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5052 pAttach->updateMedium(pMedium);
5053 }
5054
5055 setModified(IsModified_Storage);
5056
5057 mediumLock.release();
5058 multiLock.release();
5059 rc = onMediumChange(pAttach, aForce);
5060 multiLock.acquire();
5061 mediumLock.acquire();
5062
5063 /* On error roll back this change only. */
5064 if (FAILED(rc))
5065 {
5066 if (!pMedium.isNull())
5067 pMedium->removeBackReference(mData->mUuid);
5068 pAttach = findAttachment(mMediaData->mAttachments,
5069 aControllerName,
5070 aControllerPort,
5071 aDevice);
5072 /* If the attachment is gone in the meantime, bail out. */
5073 if (pAttach.isNull())
5074 return rc;
5075 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5076 if (!oldmedium.isNull())
5077 oldmedium->addBackReference(mData->mUuid);
5078 pAttach->updateMedium(oldmedium);
5079 }
5080
5081 mediumLock.release();
5082 multiLock.release();
5083
5084 mParent->saveModifiedRegistries();
5085
5086 return rc;
5087}
5088
5089STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5090 LONG aControllerPort,
5091 LONG aDevice,
5092 IMedium **aMedium)
5093{
5094 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5095 aControllerName, aControllerPort, aDevice));
5096
5097 CheckComArgStrNotEmptyOrNull(aControllerName);
5098 CheckComArgOutPointerValid(aMedium);
5099
5100 AutoCaller autoCaller(this);
5101 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5102
5103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5104
5105 *aMedium = NULL;
5106
5107 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5108 aControllerName,
5109 aControllerPort,
5110 aDevice);
5111 if (pAttach.isNull())
5112 return setError(VBOX_E_OBJECT_NOT_FOUND,
5113 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5114 aDevice, aControllerPort, aControllerName);
5115
5116 pAttach->getMedium().queryInterfaceTo(aMedium);
5117
5118 return S_OK;
5119}
5120
5121STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5122{
5123 CheckComArgOutPointerValid(port);
5124 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5125
5126 AutoCaller autoCaller(this);
5127 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5128
5129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5130
5131 mSerialPorts[slot].queryInterfaceTo(port);
5132
5133 return S_OK;
5134}
5135
5136STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5137{
5138 CheckComArgOutPointerValid(port);
5139 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5140
5141 AutoCaller autoCaller(this);
5142 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5143
5144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5145
5146 mParallelPorts[slot].queryInterfaceTo(port);
5147
5148 return S_OK;
5149}
5150
5151STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5152{
5153 CheckComArgOutPointerValid(adapter);
5154 /* Do not assert if slot is out of range, just return the advertised
5155 status. testdriver/vbox.py triggers this in logVmInfo. */
5156 if (slot >= mNetworkAdapters.size())
5157 return setError(E_INVALIDARG,
5158 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5159 slot, mNetworkAdapters.size());
5160
5161 AutoCaller autoCaller(this);
5162 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5163
5164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5165
5166 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5167
5168 return S_OK;
5169}
5170
5171STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5172{
5173 CheckComArgOutSafeArrayPointerValid(aKeys);
5174
5175 AutoCaller autoCaller(this);
5176 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5177
5178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5179
5180 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5181 int i = 0;
5182 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5183 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5184 ++it, ++i)
5185 {
5186 const Utf8Str &strKey = it->first;
5187 strKey.cloneTo(&saKeys[i]);
5188 }
5189 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5190
5191 return S_OK;
5192 }
5193
5194 /**
5195 * @note Locks this object for reading.
5196 */
5197STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5198 BSTR *aValue)
5199{
5200 CheckComArgStrNotEmptyOrNull(aKey);
5201 CheckComArgOutPointerValid(aValue);
5202
5203 AutoCaller autoCaller(this);
5204 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5205
5206 /* start with nothing found */
5207 Bstr bstrResult("");
5208
5209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5210
5211 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5212 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5213 // found:
5214 bstrResult = it->second; // source is a Utf8Str
5215
5216 /* return the result to caller (may be empty) */
5217 bstrResult.cloneTo(aValue);
5218
5219 return S_OK;
5220}
5221
5222 /**
5223 * @note Locks mParent for writing + this object for writing.
5224 */
5225STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5226{
5227 CheckComArgStrNotEmptyOrNull(aKey);
5228
5229 AutoCaller autoCaller(this);
5230 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5231
5232 Utf8Str strKey(aKey);
5233 Utf8Str strValue(aValue);
5234 Utf8Str strOldValue; // empty
5235
5236 // locking note: we only hold the read lock briefly to look up the old value,
5237 // then release it and call the onExtraCanChange callbacks. There is a small
5238 // chance of a race insofar as the callback might be called twice if two callers
5239 // change the same key at the same time, but that's a much better solution
5240 // than the deadlock we had here before. The actual changing of the extradata
5241 // is then performed under the write lock and race-free.
5242
5243 // look up the old value first; if nothing has changed then we need not do anything
5244 {
5245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5246 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5247 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5248 strOldValue = it->second;
5249 }
5250
5251 bool fChanged;
5252 if ((fChanged = (strOldValue != strValue)))
5253 {
5254 // ask for permission from all listeners outside the locks;
5255 // onExtraDataCanChange() only briefly requests the VirtualBox
5256 // lock to copy the list of callbacks to invoke
5257 Bstr error;
5258 Bstr bstrValue(aValue);
5259
5260 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5261 {
5262 const char *sep = error.isEmpty() ? "" : ": ";
5263 CBSTR err = error.raw();
5264 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5265 sep, err));
5266 return setError(E_ACCESSDENIED,
5267 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5268 aKey,
5269 bstrValue.raw(),
5270 sep,
5271 err);
5272 }
5273
5274 // data is changing and change not vetoed: then write it out under the lock
5275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5276
5277 if (isSnapshotMachine())
5278 {
5279 HRESULT rc = checkStateDependency(MutableStateDep);
5280 if (FAILED(rc)) return rc;
5281 }
5282
5283 if (strValue.isEmpty())
5284 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5285 else
5286 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5287 // creates a new key if needed
5288
5289 bool fNeedsGlobalSaveSettings = false;
5290 saveSettings(&fNeedsGlobalSaveSettings);
5291
5292 if (fNeedsGlobalSaveSettings)
5293 {
5294 // save the global settings; for that we should hold only the VirtualBox lock
5295 alock.release();
5296 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5297 mParent->saveSettings();
5298 }
5299 }
5300
5301 // fire notification outside the lock
5302 if (fChanged)
5303 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5304
5305 return S_OK;
5306}
5307
5308STDMETHODIMP Machine::SaveSettings()
5309{
5310 AutoCaller autoCaller(this);
5311 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5312
5313 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5314
5315 /* when there was auto-conversion, we want to save the file even if
5316 * the VM is saved */
5317 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5318 if (FAILED(rc)) return rc;
5319
5320 /* the settings file path may never be null */
5321 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5322
5323 /* save all VM data excluding snapshots */
5324 bool fNeedsGlobalSaveSettings = false;
5325 rc = saveSettings(&fNeedsGlobalSaveSettings);
5326 mlock.release();
5327
5328 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5329 {
5330 // save the global settings; for that we should hold only the VirtualBox lock
5331 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5332 rc = mParent->saveSettings();
5333 }
5334
5335 return rc;
5336}
5337
5338STDMETHODIMP Machine::DiscardSettings()
5339{
5340 AutoCaller autoCaller(this);
5341 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5342
5343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5344
5345 HRESULT rc = checkStateDependency(MutableStateDep);
5346 if (FAILED(rc)) return rc;
5347
5348 /*
5349 * during this rollback, the session will be notified if data has
5350 * been actually changed
5351 */
5352 rollback(true /* aNotify */);
5353
5354 return S_OK;
5355}
5356
5357/** @note Locks objects! */
5358STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5359 ComSafeArrayOut(IMedium*, aMedia))
5360{
5361 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5362 AutoLimitedCaller autoCaller(this);
5363 AssertComRCReturnRC(autoCaller.rc());
5364
5365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5366
5367 Guid id(getId());
5368
5369 if (mData->mSession.mState != SessionState_Unlocked)
5370 return setError(VBOX_E_INVALID_OBJECT_STATE,
5371 tr("Cannot unregister the machine '%s' while it is locked"),
5372 mUserData->s.strName.c_str());
5373
5374 // wait for state dependents to drop to zero
5375 ensureNoStateDependencies();
5376
5377 if (!mData->mAccessible)
5378 {
5379 // inaccessible maschines can only be unregistered; uninitialize ourselves
5380 // here because currently there may be no unregistered that are inaccessible
5381 // (this state combination is not supported). Note releasing the caller and
5382 // leaving the lock before calling uninit()
5383 alock.release();
5384 autoCaller.release();
5385
5386 uninit();
5387
5388 mParent->unregisterMachine(this, id);
5389 // calls VirtualBox::saveSettings()
5390
5391 return S_OK;
5392 }
5393
5394 HRESULT rc = S_OK;
5395
5396 // discard saved state
5397 if (mData->mMachineState == MachineState_Saved)
5398 {
5399 // add the saved state file to the list of files the caller should delete
5400 Assert(!mSSData->strStateFilePath.isEmpty());
5401 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5402
5403 mSSData->strStateFilePath.setNull();
5404
5405 // unconditionally set the machine state to powered off, we now
5406 // know no session has locked the machine
5407 mData->mMachineState = MachineState_PoweredOff;
5408 }
5409
5410 size_t cSnapshots = 0;
5411 if (mData->mFirstSnapshot)
5412 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5413 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5414 // fail now before we start detaching media
5415 return setError(VBOX_E_INVALID_OBJECT_STATE,
5416 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5417 mUserData->s.strName.c_str(), cSnapshots);
5418
5419 // This list collects the medium objects from all medium attachments
5420 // which we will detach from the machine and its snapshots, in a specific
5421 // order which allows for closing all media without getting "media in use"
5422 // errors, simply by going through the list from the front to the back:
5423 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5424 // and must be closed before the parent media from the snapshots, or closing the parents
5425 // will fail because they still have children);
5426 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5427 // the root ("first") snapshot of the machine.
5428 MediaList llMedia;
5429
5430 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5431 && mMediaData->mAttachments.size()
5432 )
5433 {
5434 // we have media attachments: detach them all and add the Medium objects to our list
5435 if (cleanupMode != CleanupMode_UnregisterOnly)
5436 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5437 else
5438 return setError(VBOX_E_INVALID_OBJECT_STATE,
5439 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5440 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5441 }
5442
5443 if (cSnapshots)
5444 {
5445 // autoCleanup must be true here, or we would have failed above
5446
5447 // add the media from the medium attachments of the snapshots to llMedia
5448 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5449 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5450 // into the children first
5451
5452 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5453 MachineState_T oldState = mData->mMachineState;
5454 mData->mMachineState = MachineState_DeletingSnapshot;
5455
5456 // make a copy of the first snapshot so the refcount does not drop to 0
5457 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5458 // because of the AutoCaller voodoo)
5459 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5460
5461 // GO!
5462 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5463
5464 mData->mMachineState = oldState;
5465 }
5466
5467 if (FAILED(rc))
5468 {
5469 rollbackMedia();
5470 return rc;
5471 }
5472
5473 // commit all the media changes made above
5474 commitMedia();
5475
5476 mData->mRegistered = false;
5477
5478 // machine lock no longer needed
5479 alock.release();
5480
5481 // return media to caller
5482 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5483 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5484
5485 mParent->unregisterMachine(this, id);
5486 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5487
5488 return S_OK;
5489}
5490
5491struct Machine::DeleteTask
5492{
5493 ComObjPtr<Machine> pMachine;
5494 RTCList<ComPtr<IMedium> > llMediums;
5495 StringsList llFilesToDelete;
5496 ComObjPtr<Progress> pProgress;
5497};
5498
5499STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5500{
5501 LogFlowFuncEnter();
5502
5503 AutoCaller autoCaller(this);
5504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5505
5506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5507
5508 HRESULT rc = checkStateDependency(MutableStateDep);
5509 if (FAILED(rc)) return rc;
5510
5511 if (mData->mRegistered)
5512 return setError(VBOX_E_INVALID_VM_STATE,
5513 tr("Cannot delete settings of a registered machine"));
5514
5515 DeleteTask *pTask = new DeleteTask;
5516 pTask->pMachine = this;
5517 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5518
5519 // collect files to delete
5520 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5521
5522 for (size_t i = 0; i < sfaMedia.size(); ++i)
5523 {
5524 IMedium *pIMedium(sfaMedia[i]);
5525 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5526 if (pMedium.isNull())
5527 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5528 SafeArray<BSTR> ids;
5529 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5530 if (FAILED(rc)) return rc;
5531 /* At this point the medium should not have any back references
5532 * anymore. If it has it is attached to another VM and *must* not
5533 * deleted. */
5534 if (ids.size() < 1)
5535 pTask->llMediums.append(pMedium);
5536 }
5537 if (mData->pMachineConfigFile->fileExists())
5538 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5539
5540 pTask->pProgress.createObject();
5541 pTask->pProgress->init(getVirtualBox(),
5542 static_cast<IMachine*>(this) /* aInitiator */,
5543 Bstr(tr("Deleting files")).raw(),
5544 true /* fCancellable */,
5545 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5546 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5547
5548 int vrc = RTThreadCreate(NULL,
5549 Machine::deleteThread,
5550 (void*)pTask,
5551 0,
5552 RTTHREADTYPE_MAIN_WORKER,
5553 0,
5554 "MachineDelete");
5555
5556 pTask->pProgress.queryInterfaceTo(aProgress);
5557
5558 if (RT_FAILURE(vrc))
5559 {
5560 delete pTask;
5561 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5562 }
5563
5564 LogFlowFuncLeave();
5565
5566 return S_OK;
5567}
5568
5569/**
5570 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5571 * calls Machine::deleteTaskWorker() on the actual machine object.
5572 * @param Thread
5573 * @param pvUser
5574 * @return
5575 */
5576/*static*/
5577DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5578{
5579 LogFlowFuncEnter();
5580
5581 DeleteTask *pTask = (DeleteTask*)pvUser;
5582 Assert(pTask);
5583 Assert(pTask->pMachine);
5584 Assert(pTask->pProgress);
5585
5586 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5587 pTask->pProgress->notifyComplete(rc);
5588
5589 delete pTask;
5590
5591 LogFlowFuncLeave();
5592
5593 NOREF(Thread);
5594
5595 return VINF_SUCCESS;
5596}
5597
5598/**
5599 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5600 * @param task
5601 * @return
5602 */
5603HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5604{
5605 AutoCaller autoCaller(this);
5606 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5607
5608 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5609
5610 HRESULT rc = S_OK;
5611
5612 try
5613 {
5614 ULONG uLogHistoryCount = 3;
5615 ComPtr<ISystemProperties> systemProperties;
5616 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5617 if (FAILED(rc)) throw rc;
5618
5619 if (!systemProperties.isNull())
5620 {
5621 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5622 if (FAILED(rc)) throw rc;
5623 }
5624
5625 MachineState_T oldState = mData->mMachineState;
5626 setMachineState(MachineState_SettingUp);
5627 alock.release();
5628 for (size_t i = 0; i < task.llMediums.size(); ++i)
5629 {
5630 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5631 {
5632 AutoCaller mac(pMedium);
5633 if (FAILED(mac.rc())) throw mac.rc();
5634 Utf8Str strLocation = pMedium->getLocationFull();
5635 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5636 if (FAILED(rc)) throw rc;
5637 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5638 }
5639 ComPtr<IProgress> pProgress2;
5640 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5641 if (FAILED(rc)) throw rc;
5642 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5643 if (FAILED(rc)) throw rc;
5644 /* Check the result of the asynchrony process. */
5645 LONG iRc;
5646 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5647 if (FAILED(rc)) throw rc;
5648 /* If the thread of the progress object has an error, then
5649 * retrieve the error info from there, or it'll be lost. */
5650 if (FAILED(iRc))
5651 throw setError(ProgressErrorInfo(pProgress2));
5652 }
5653 setMachineState(oldState);
5654 alock.acquire();
5655
5656 // delete the files pushed on the task list by Machine::Delete()
5657 // (this includes saved states of the machine and snapshots and
5658 // medium storage files from the IMedium list passed in, and the
5659 // machine XML file)
5660 StringsList::const_iterator it = task.llFilesToDelete.begin();
5661 while (it != task.llFilesToDelete.end())
5662 {
5663 const Utf8Str &strFile = *it;
5664 LogFunc(("Deleting file %s\n", strFile.c_str()));
5665 int vrc = RTFileDelete(strFile.c_str());
5666 if (RT_FAILURE(vrc))
5667 throw setError(VBOX_E_IPRT_ERROR,
5668 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5669
5670 ++it;
5671 if (it == task.llFilesToDelete.end())
5672 {
5673 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5674 if (FAILED(rc)) throw rc;
5675 break;
5676 }
5677
5678 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5679 if (FAILED(rc)) throw rc;
5680 }
5681
5682 /* delete the settings only when the file actually exists */
5683 if (mData->pMachineConfigFile->fileExists())
5684 {
5685 /* Delete any backup or uncommitted XML files. Ignore failures.
5686 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5687 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5688 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5689 RTFileDelete(otherXml.c_str());
5690 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5691 RTFileDelete(otherXml.c_str());
5692
5693 /* delete the Logs folder, nothing important should be left
5694 * there (we don't check for errors because the user might have
5695 * some private files there that we don't want to delete) */
5696 Utf8Str logFolder;
5697 getLogFolder(logFolder);
5698 Assert(logFolder.length());
5699 if (RTDirExists(logFolder.c_str()))
5700 {
5701 /* Delete all VBox.log[.N] files from the Logs folder
5702 * (this must be in sync with the rotation logic in
5703 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5704 * files that may have been created by the GUI. */
5705 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5706 logFolder.c_str(), RTPATH_DELIMITER);
5707 RTFileDelete(log.c_str());
5708 log = Utf8StrFmt("%s%cVBox.png",
5709 logFolder.c_str(), RTPATH_DELIMITER);
5710 RTFileDelete(log.c_str());
5711 for (int i = uLogHistoryCount; i > 0; i--)
5712 {
5713 log = Utf8StrFmt("%s%cVBox.log.%d",
5714 logFolder.c_str(), RTPATH_DELIMITER, i);
5715 RTFileDelete(log.c_str());
5716 log = Utf8StrFmt("%s%cVBox.png.%d",
5717 logFolder.c_str(), RTPATH_DELIMITER, i);
5718 RTFileDelete(log.c_str());
5719 }
5720
5721 RTDirRemove(logFolder.c_str());
5722 }
5723
5724 /* delete the Snapshots folder, nothing important should be left
5725 * there (we don't check for errors because the user might have
5726 * some private files there that we don't want to delete) */
5727 Utf8Str strFullSnapshotFolder;
5728 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5729 Assert(!strFullSnapshotFolder.isEmpty());
5730 if (RTDirExists(strFullSnapshotFolder.c_str()))
5731 RTDirRemove(strFullSnapshotFolder.c_str());
5732
5733 // delete the directory that contains the settings file, but only
5734 // if it matches the VM name
5735 Utf8Str settingsDir;
5736 if (isInOwnDir(&settingsDir))
5737 RTDirRemove(settingsDir.c_str());
5738 }
5739
5740 alock.release();
5741
5742 mParent->saveModifiedRegistries();
5743 }
5744 catch (HRESULT aRC) { rc = aRC; }
5745
5746 return rc;
5747}
5748
5749STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5750{
5751 CheckComArgOutPointerValid(aSnapshot);
5752
5753 AutoCaller autoCaller(this);
5754 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5755
5756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5757
5758 ComObjPtr<Snapshot> pSnapshot;
5759 HRESULT rc;
5760
5761 if (!aNameOrId || !*aNameOrId)
5762 // null case (caller wants root snapshot): findSnapshotById() handles this
5763 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5764 else
5765 {
5766 Guid uuid(aNameOrId);
5767 if (uuid.isValid())
5768 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5769 else
5770 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5771 }
5772 pSnapshot.queryInterfaceTo(aSnapshot);
5773
5774 return rc;
5775}
5776
5777STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5778{
5779 CheckComArgStrNotEmptyOrNull(aName);
5780 CheckComArgStrNotEmptyOrNull(aHostPath);
5781
5782 AutoCaller autoCaller(this);
5783 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5784
5785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5786
5787 HRESULT rc = checkStateDependency(MutableStateDep);
5788 if (FAILED(rc)) return rc;
5789
5790 Utf8Str strName(aName);
5791
5792 ComObjPtr<SharedFolder> sharedFolder;
5793 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5794 if (SUCCEEDED(rc))
5795 return setError(VBOX_E_OBJECT_IN_USE,
5796 tr("Shared folder named '%s' already exists"),
5797 strName.c_str());
5798
5799 sharedFolder.createObject();
5800 rc = sharedFolder->init(getMachine(),
5801 strName,
5802 aHostPath,
5803 !!aWritable,
5804 !!aAutoMount,
5805 true /* fFailOnError */);
5806 if (FAILED(rc)) return rc;
5807
5808 setModified(IsModified_SharedFolders);
5809 mHWData.backup();
5810 mHWData->mSharedFolders.push_back(sharedFolder);
5811
5812 /* inform the direct session if any */
5813 alock.release();
5814 onSharedFolderChange();
5815
5816 return S_OK;
5817}
5818
5819STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5820{
5821 CheckComArgStrNotEmptyOrNull(aName);
5822
5823 AutoCaller autoCaller(this);
5824 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5825
5826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5827
5828 HRESULT rc = checkStateDependency(MutableStateDep);
5829 if (FAILED(rc)) return rc;
5830
5831 ComObjPtr<SharedFolder> sharedFolder;
5832 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5833 if (FAILED(rc)) return rc;
5834
5835 setModified(IsModified_SharedFolders);
5836 mHWData.backup();
5837 mHWData->mSharedFolders.remove(sharedFolder);
5838
5839 /* inform the direct session if any */
5840 alock.release();
5841 onSharedFolderChange();
5842
5843 return S_OK;
5844}
5845
5846STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5847{
5848 CheckComArgOutPointerValid(aCanShow);
5849
5850 /* start with No */
5851 *aCanShow = FALSE;
5852
5853 AutoCaller autoCaller(this);
5854 AssertComRCReturnRC(autoCaller.rc());
5855
5856 ComPtr<IInternalSessionControl> directControl;
5857 {
5858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5859
5860 if (mData->mSession.mState != SessionState_Locked)
5861 return setError(VBOX_E_INVALID_VM_STATE,
5862 tr("Machine is not locked for session (session state: %s)"),
5863 Global::stringifySessionState(mData->mSession.mState));
5864
5865 directControl = mData->mSession.mDirectControl;
5866 }
5867
5868 /* ignore calls made after #OnSessionEnd() is called */
5869 if (!directControl)
5870 return S_OK;
5871
5872 LONG64 dummy;
5873 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5874}
5875
5876STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5877{
5878 CheckComArgOutPointerValid(aWinId);
5879
5880 AutoCaller autoCaller(this);
5881 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5882
5883 ComPtr<IInternalSessionControl> directControl;
5884 {
5885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5886
5887 if (mData->mSession.mState != SessionState_Locked)
5888 return setError(E_FAIL,
5889 tr("Machine is not locked for session (session state: %s)"),
5890 Global::stringifySessionState(mData->mSession.mState));
5891
5892 directControl = mData->mSession.mDirectControl;
5893 }
5894
5895 /* ignore calls made after #OnSessionEnd() is called */
5896 if (!directControl)
5897 return S_OK;
5898
5899 BOOL dummy;
5900 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5901}
5902
5903#ifdef VBOX_WITH_GUEST_PROPS
5904/**
5905 * Look up a guest property in VBoxSVC's internal structures.
5906 */
5907HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5908 BSTR *aValue,
5909 LONG64 *aTimestamp,
5910 BSTR *aFlags) const
5911{
5912 using namespace guestProp;
5913
5914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5915 Utf8Str strName(aName);
5916 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5917
5918 if (it != mHWData->mGuestProperties.end())
5919 {
5920 char szFlags[MAX_FLAGS_LEN + 1];
5921 it->second.strValue.cloneTo(aValue);
5922 *aTimestamp = it->second.mTimestamp;
5923 writeFlags(it->second.mFlags, szFlags);
5924 Bstr(szFlags).cloneTo(aFlags);
5925 }
5926
5927 return S_OK;
5928}
5929
5930/**
5931 * Query the VM that a guest property belongs to for the property.
5932 * @returns E_ACCESSDENIED if the VM process is not available or not
5933 * currently handling queries and the lookup should then be done in
5934 * VBoxSVC.
5935 */
5936HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5937 BSTR *aValue,
5938 LONG64 *aTimestamp,
5939 BSTR *aFlags) const
5940{
5941 HRESULT rc;
5942 ComPtr<IInternalSessionControl> directControl;
5943 directControl = mData->mSession.mDirectControl;
5944
5945 /* fail if we were called after #OnSessionEnd() is called. This is a
5946 * silly race condition. */
5947
5948 if (!directControl)
5949 rc = E_ACCESSDENIED;
5950 else
5951 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5952 false /* isSetter */,
5953 aValue, aTimestamp, aFlags);
5954 return rc;
5955}
5956#endif // VBOX_WITH_GUEST_PROPS
5957
5958STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5959 BSTR *aValue,
5960 LONG64 *aTimestamp,
5961 BSTR *aFlags)
5962{
5963#ifndef VBOX_WITH_GUEST_PROPS
5964 ReturnComNotImplemented();
5965#else // VBOX_WITH_GUEST_PROPS
5966 CheckComArgStrNotEmptyOrNull(aName);
5967 CheckComArgOutPointerValid(aValue);
5968 CheckComArgOutPointerValid(aTimestamp);
5969 CheckComArgOutPointerValid(aFlags);
5970
5971 AutoCaller autoCaller(this);
5972 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5973
5974 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5975 if (rc == E_ACCESSDENIED)
5976 /* The VM is not running or the service is not (yet) accessible */
5977 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5978 return rc;
5979#endif // VBOX_WITH_GUEST_PROPS
5980}
5981
5982STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5983{
5984 LONG64 dummyTimestamp;
5985 Bstr dummyFlags;
5986 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5987}
5988
5989STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5990{
5991 Bstr dummyValue;
5992 Bstr dummyFlags;
5993 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5994}
5995
5996#ifdef VBOX_WITH_GUEST_PROPS
5997/**
5998 * Set a guest property in VBoxSVC's internal structures.
5999 */
6000HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
6001 IN_BSTR aFlags)
6002{
6003 using namespace guestProp;
6004
6005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6006 HRESULT rc = S_OK;
6007
6008 rc = checkStateDependency(MutableStateDep);
6009 if (FAILED(rc)) return rc;
6010
6011 try
6012 {
6013 Utf8Str utf8Name(aName);
6014 Utf8Str utf8Flags(aFlags);
6015 uint32_t fFlags = NILFLAG;
6016 if ( aFlags != NULL
6017 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
6018 return setError(E_INVALIDARG,
6019 tr("Invalid guest property flag values: '%ls'"),
6020 aFlags);
6021
6022 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
6023 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
6024 if (it == mHWData->mGuestProperties.end())
6025 {
6026 if (!fDelete)
6027 {
6028 setModified(IsModified_MachineData);
6029 mHWData.backupEx();
6030
6031 RTTIMESPEC time;
6032 HWData::GuestProperty prop;
6033 prop.strValue = aValue;
6034 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6035 prop.mFlags = fFlags;
6036 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
6037 }
6038 }
6039 else
6040 {
6041 if (it->second.mFlags & (RDONLYHOST))
6042 {
6043 rc = setError(E_ACCESSDENIED,
6044 tr("The property '%ls' cannot be changed by the host"),
6045 aName);
6046 }
6047 else
6048 {
6049 setModified(IsModified_MachineData);
6050 mHWData.backupEx();
6051
6052 /* The backupEx() operation invalidates our iterator,
6053 * so get a new one. */
6054 it = mHWData->mGuestProperties.find(utf8Name);
6055 Assert(it != mHWData->mGuestProperties.end());
6056
6057 if (!fDelete)
6058 {
6059 RTTIMESPEC time;
6060 it->second.strValue = aValue;
6061 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6062 it->second.mFlags = fFlags;
6063 }
6064 else
6065 mHWData->mGuestProperties.erase(it);
6066 }
6067 }
6068
6069 if ( SUCCEEDED(rc)
6070 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6071 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6072 RTSTR_MAX,
6073 utf8Name.c_str(),
6074 RTSTR_MAX,
6075 NULL)
6076 )
6077 )
6078 {
6079 alock.release();
6080
6081 mParent->onGuestPropertyChange(mData->mUuid, aName,
6082 aValue ? aValue : Bstr("").raw(),
6083 aFlags ? aFlags : Bstr("").raw());
6084 }
6085 }
6086 catch (std::bad_alloc &)
6087 {
6088 rc = E_OUTOFMEMORY;
6089 }
6090
6091 return rc;
6092}
6093
6094/**
6095 * Set a property on the VM that that property belongs to.
6096 * @returns E_ACCESSDENIED if the VM process is not available or not
6097 * currently handling queries and the setting should then be done in
6098 * VBoxSVC.
6099 */
6100HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6101 IN_BSTR aFlags)
6102{
6103 HRESULT rc;
6104
6105 try
6106 {
6107 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6108
6109 BSTR dummy = NULL; /* will not be changed (setter) */
6110 LONG64 dummy64;
6111 if (!directControl)
6112 rc = E_ACCESSDENIED;
6113 else
6114 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6115 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6116 true /* isSetter */,
6117 &dummy, &dummy64, &dummy);
6118 }
6119 catch (std::bad_alloc &)
6120 {
6121 rc = E_OUTOFMEMORY;
6122 }
6123
6124 return rc;
6125}
6126#endif // VBOX_WITH_GUEST_PROPS
6127
6128STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6129 IN_BSTR aFlags)
6130{
6131#ifndef VBOX_WITH_GUEST_PROPS
6132 ReturnComNotImplemented();
6133#else // VBOX_WITH_GUEST_PROPS
6134 CheckComArgStrNotEmptyOrNull(aName);
6135 CheckComArgMaybeNull(aFlags);
6136 CheckComArgMaybeNull(aValue);
6137
6138 AutoCaller autoCaller(this);
6139 if (FAILED(autoCaller.rc()))
6140 return autoCaller.rc();
6141
6142 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6143 if (rc == E_ACCESSDENIED)
6144 /* The VM is not running or the service is not (yet) accessible */
6145 rc = setGuestPropertyToService(aName, aValue, aFlags);
6146 return rc;
6147#endif // VBOX_WITH_GUEST_PROPS
6148}
6149
6150STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6151{
6152 return SetGuestProperty(aName, aValue, NULL);
6153}
6154
6155STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6156{
6157 return SetGuestProperty(aName, NULL, NULL);
6158}
6159
6160#ifdef VBOX_WITH_GUEST_PROPS
6161/**
6162 * Enumerate the guest properties in VBoxSVC's internal structures.
6163 */
6164HRESULT Machine::enumerateGuestPropertiesInService
6165 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6166 ComSafeArrayOut(BSTR, aValues),
6167 ComSafeArrayOut(LONG64, aTimestamps),
6168 ComSafeArrayOut(BSTR, aFlags))
6169{
6170 using namespace guestProp;
6171
6172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6173 Utf8Str strPatterns(aPatterns);
6174
6175 HWData::GuestPropertyMap propMap;
6176
6177 /*
6178 * Look for matching patterns and build up a list.
6179 */
6180 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6181 while (it != mHWData->mGuestProperties.end())
6182 {
6183 if ( strPatterns.isEmpty()
6184 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6185 RTSTR_MAX,
6186 it->first.c_str(),
6187 RTSTR_MAX,
6188 NULL)
6189 )
6190 {
6191 propMap.insert(*it);
6192 }
6193
6194 it++;
6195 }
6196
6197 alock.release();
6198
6199 /*
6200 * And build up the arrays for returning the property information.
6201 */
6202 size_t cEntries = propMap.size();
6203 SafeArray<BSTR> names(cEntries);
6204 SafeArray<BSTR> values(cEntries);
6205 SafeArray<LONG64> timestamps(cEntries);
6206 SafeArray<BSTR> flags(cEntries);
6207 size_t iProp = 0;
6208
6209 it = propMap.begin();
6210 while (it != propMap.end())
6211 {
6212 char szFlags[MAX_FLAGS_LEN + 1];
6213 it->first.cloneTo(&names[iProp]);
6214 it->second.strValue.cloneTo(&values[iProp]);
6215 timestamps[iProp] = it->second.mTimestamp;
6216 writeFlags(it->second.mFlags, szFlags);
6217 Bstr(szFlags).cloneTo(&flags[iProp++]);
6218 it++;
6219 }
6220 names.detachTo(ComSafeArrayOutArg(aNames));
6221 values.detachTo(ComSafeArrayOutArg(aValues));
6222 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6223 flags.detachTo(ComSafeArrayOutArg(aFlags));
6224 return S_OK;
6225}
6226
6227/**
6228 * Enumerate the properties managed by a VM.
6229 * @returns E_ACCESSDENIED if the VM process is not available or not
6230 * currently handling queries and the setting should then be done in
6231 * VBoxSVC.
6232 */
6233HRESULT Machine::enumerateGuestPropertiesOnVM
6234 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6235 ComSafeArrayOut(BSTR, aValues),
6236 ComSafeArrayOut(LONG64, aTimestamps),
6237 ComSafeArrayOut(BSTR, aFlags))
6238{
6239 HRESULT rc;
6240 ComPtr<IInternalSessionControl> directControl;
6241 directControl = mData->mSession.mDirectControl;
6242
6243 if (!directControl)
6244 rc = E_ACCESSDENIED;
6245 else
6246 rc = directControl->EnumerateGuestProperties
6247 (aPatterns, ComSafeArrayOutArg(aNames),
6248 ComSafeArrayOutArg(aValues),
6249 ComSafeArrayOutArg(aTimestamps),
6250 ComSafeArrayOutArg(aFlags));
6251 return rc;
6252}
6253#endif // VBOX_WITH_GUEST_PROPS
6254
6255STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6256 ComSafeArrayOut(BSTR, aNames),
6257 ComSafeArrayOut(BSTR, aValues),
6258 ComSafeArrayOut(LONG64, aTimestamps),
6259 ComSafeArrayOut(BSTR, aFlags))
6260{
6261#ifndef VBOX_WITH_GUEST_PROPS
6262 ReturnComNotImplemented();
6263#else // VBOX_WITH_GUEST_PROPS
6264 CheckComArgMaybeNull(aPatterns);
6265 CheckComArgOutSafeArrayPointerValid(aNames);
6266 CheckComArgOutSafeArrayPointerValid(aValues);
6267 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6268 CheckComArgOutSafeArrayPointerValid(aFlags);
6269
6270 AutoCaller autoCaller(this);
6271 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6272
6273 HRESULT rc = enumerateGuestPropertiesOnVM
6274 (aPatterns, ComSafeArrayOutArg(aNames),
6275 ComSafeArrayOutArg(aValues),
6276 ComSafeArrayOutArg(aTimestamps),
6277 ComSafeArrayOutArg(aFlags));
6278 if (rc == E_ACCESSDENIED)
6279 /* The VM is not running or the service is not (yet) accessible */
6280 rc = enumerateGuestPropertiesInService
6281 (aPatterns, ComSafeArrayOutArg(aNames),
6282 ComSafeArrayOutArg(aValues),
6283 ComSafeArrayOutArg(aTimestamps),
6284 ComSafeArrayOutArg(aFlags));
6285 return rc;
6286#endif // VBOX_WITH_GUEST_PROPS
6287}
6288
6289STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6290 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6291{
6292 MediaData::AttachmentList atts;
6293
6294 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6295 if (FAILED(rc)) return rc;
6296
6297 SafeIfaceArray<IMediumAttachment> attachments(atts);
6298 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6299
6300 return S_OK;
6301}
6302
6303STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6304 LONG aControllerPort,
6305 LONG aDevice,
6306 IMediumAttachment **aAttachment)
6307{
6308 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6309 aControllerName, aControllerPort, aDevice));
6310
6311 CheckComArgStrNotEmptyOrNull(aControllerName);
6312 CheckComArgOutPointerValid(aAttachment);
6313
6314 AutoCaller autoCaller(this);
6315 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6316
6317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6318
6319 *aAttachment = NULL;
6320
6321 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6322 aControllerName,
6323 aControllerPort,
6324 aDevice);
6325 if (pAttach.isNull())
6326 return setError(VBOX_E_OBJECT_NOT_FOUND,
6327 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6328 aDevice, aControllerPort, aControllerName);
6329
6330 pAttach.queryInterfaceTo(aAttachment);
6331
6332 return S_OK;
6333}
6334
6335STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6336 StorageBus_T aConnectionType,
6337 IStorageController **controller)
6338{
6339 CheckComArgStrNotEmptyOrNull(aName);
6340
6341 if ( (aConnectionType <= StorageBus_Null)
6342 || (aConnectionType > StorageBus_SAS))
6343 return setError(E_INVALIDARG,
6344 tr("Invalid connection type: %d"),
6345 aConnectionType);
6346
6347 AutoCaller autoCaller(this);
6348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6349
6350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6351
6352 HRESULT rc = checkStateDependency(MutableStateDep);
6353 if (FAILED(rc)) return rc;
6354
6355 /* try to find one with the name first. */
6356 ComObjPtr<StorageController> ctrl;
6357
6358 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6359 if (SUCCEEDED(rc))
6360 return setError(VBOX_E_OBJECT_IN_USE,
6361 tr("Storage controller named '%ls' already exists"),
6362 aName);
6363
6364 ctrl.createObject();
6365
6366 /* get a new instance number for the storage controller */
6367 ULONG ulInstance = 0;
6368 bool fBootable = true;
6369 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6370 it != mStorageControllers->end();
6371 ++it)
6372 {
6373 if ((*it)->getStorageBus() == aConnectionType)
6374 {
6375 ULONG ulCurInst = (*it)->getInstance();
6376
6377 if (ulCurInst >= ulInstance)
6378 ulInstance = ulCurInst + 1;
6379
6380 /* Only one controller of each type can be marked as bootable. */
6381 if ((*it)->getBootable())
6382 fBootable = false;
6383 }
6384 }
6385
6386 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6387 if (FAILED(rc)) return rc;
6388
6389 setModified(IsModified_Storage);
6390 mStorageControllers.backup();
6391 mStorageControllers->push_back(ctrl);
6392
6393 ctrl.queryInterfaceTo(controller);
6394
6395 /* inform the direct session if any */
6396 alock.release();
6397 onStorageControllerChange();
6398
6399 return S_OK;
6400}
6401
6402STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6403 IStorageController **aStorageController)
6404{
6405 CheckComArgStrNotEmptyOrNull(aName);
6406
6407 AutoCaller autoCaller(this);
6408 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6409
6410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6411
6412 ComObjPtr<StorageController> ctrl;
6413
6414 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6415 if (SUCCEEDED(rc))
6416 ctrl.queryInterfaceTo(aStorageController);
6417
6418 return rc;
6419}
6420
6421STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6422 IStorageController **aStorageController)
6423{
6424 AutoCaller autoCaller(this);
6425 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6426
6427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6428
6429 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6430 it != mStorageControllers->end();
6431 ++it)
6432 {
6433 if ((*it)->getInstance() == aInstance)
6434 {
6435 (*it).queryInterfaceTo(aStorageController);
6436 return S_OK;
6437 }
6438 }
6439
6440 return setError(VBOX_E_OBJECT_NOT_FOUND,
6441 tr("Could not find a storage controller with instance number '%lu'"),
6442 aInstance);
6443}
6444
6445STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6446{
6447 AutoCaller autoCaller(this);
6448 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6449
6450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6451
6452 HRESULT rc = checkStateDependency(MutableStateDep);
6453 if (FAILED(rc)) return rc;
6454
6455 ComObjPtr<StorageController> ctrl;
6456
6457 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6458 if (SUCCEEDED(rc))
6459 {
6460 /* Ensure that only one controller of each type is marked as bootable. */
6461 if (fBootable == TRUE)
6462 {
6463 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6464 it != mStorageControllers->end();
6465 ++it)
6466 {
6467 ComObjPtr<StorageController> aCtrl = (*it);
6468
6469 if ( (aCtrl->getName() != Utf8Str(aName))
6470 && aCtrl->getBootable() == TRUE
6471 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6472 && aCtrl->getControllerType() == ctrl->getControllerType())
6473 {
6474 aCtrl->setBootable(FALSE);
6475 break;
6476 }
6477 }
6478 }
6479
6480 if (SUCCEEDED(rc))
6481 {
6482 ctrl->setBootable(fBootable);
6483 setModified(IsModified_Storage);
6484 }
6485 }
6486
6487 if (SUCCEEDED(rc))
6488 {
6489 /* inform the direct session if any */
6490 alock.release();
6491 onStorageControllerChange();
6492 }
6493
6494 return rc;
6495}
6496
6497STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6498{
6499 CheckComArgStrNotEmptyOrNull(aName);
6500
6501 AutoCaller autoCaller(this);
6502 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6503
6504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6505
6506 HRESULT rc = checkStateDependency(MutableStateDep);
6507 if (FAILED(rc)) return rc;
6508
6509 ComObjPtr<StorageController> ctrl;
6510 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6511 if (FAILED(rc)) return rc;
6512
6513 {
6514 /* find all attached devices to the appropriate storage controller and detach them all */
6515 // make a temporary list because detachDevice invalidates iterators into
6516 // mMediaData->mAttachments
6517 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6518
6519 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6520 it != llAttachments2.end();
6521 ++it)
6522 {
6523 MediumAttachment *pAttachTemp = *it;
6524
6525 AutoCaller localAutoCaller(pAttachTemp);
6526 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6527
6528 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6529
6530 if (pAttachTemp->getControllerName() == aName)
6531 {
6532 rc = detachDevice(pAttachTemp, alock, NULL);
6533 if (FAILED(rc)) return rc;
6534 }
6535 }
6536 }
6537
6538 /* We can remove it now. */
6539 setModified(IsModified_Storage);
6540 mStorageControllers.backup();
6541
6542 ctrl->unshare();
6543
6544 mStorageControllers->remove(ctrl);
6545
6546 /* inform the direct session if any */
6547 alock.release();
6548 onStorageControllerChange();
6549
6550 return S_OK;
6551}
6552
6553STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType,
6554 IUSBController **controller)
6555{
6556 if ( (aType <= USBControllerType_Null)
6557 || (aType >= USBControllerType_Last))
6558 return setError(E_INVALIDARG,
6559 tr("Invalid USB controller type: %d"),
6560 aType);
6561
6562 AutoCaller autoCaller(this);
6563 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6564
6565 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6566
6567 HRESULT rc = checkStateDependency(MutableStateDep);
6568 if (FAILED(rc)) return rc;
6569
6570 /* try to find one with the same type first. */
6571 ComObjPtr<USBController> ctrl;
6572
6573 rc = getUSBControllerByName(aName, ctrl, false /* aSetError */);
6574 if (SUCCEEDED(rc))
6575 return setError(VBOX_E_OBJECT_IN_USE,
6576 tr("USB controller named '%ls' already exists"),
6577 aName);
6578
6579 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6580 ULONG maxInstances;
6581 rc = mParent->getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6582 if (FAILED(rc))
6583 return rc;
6584
6585 ULONG cInstances = getUSBControllerCountByType(aType);
6586 if (cInstances >= maxInstances)
6587 return setError(E_INVALIDARG,
6588 tr("Too many USB controllers of this type"));
6589
6590 ctrl.createObject();
6591
6592 rc = ctrl->init(this, aName, aType);
6593 if (FAILED(rc)) return rc;
6594
6595 setModified(IsModified_USB);
6596 mUSBControllers.backup();
6597 mUSBControllers->push_back(ctrl);
6598
6599 ctrl.queryInterfaceTo(controller);
6600
6601 /* inform the direct session if any */
6602 alock.release();
6603 onUSBControllerChange();
6604
6605 return S_OK;
6606}
6607
6608STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController)
6609{
6610 CheckComArgStrNotEmptyOrNull(aName);
6611
6612 AutoCaller autoCaller(this);
6613 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6614
6615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6616
6617 ComObjPtr<USBController> ctrl;
6618
6619 HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6620 if (SUCCEEDED(rc))
6621 ctrl.queryInterfaceTo(aUSBController);
6622
6623 return rc;
6624}
6625
6626STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType,
6627 ULONG *aControllers)
6628{
6629 CheckComArgOutPointerValid(aControllers);
6630
6631 if ( (aType <= USBControllerType_Null)
6632 || (aType >= USBControllerType_Last))
6633 return setError(E_INVALIDARG,
6634 tr("Invalid USB controller type: %d"),
6635 aType);
6636
6637 AutoCaller autoCaller(this);
6638 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6639
6640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6641
6642 ComObjPtr<USBController> ctrl;
6643
6644 *aControllers = getUSBControllerCountByType(aType);
6645
6646 return S_OK;
6647}
6648
6649STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName)
6650{
6651 CheckComArgStrNotEmptyOrNull(aName);
6652
6653 AutoCaller autoCaller(this);
6654 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6655
6656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6657
6658 HRESULT rc = checkStateDependency(MutableStateDep);
6659 if (FAILED(rc)) return rc;
6660
6661 ComObjPtr<USBController> ctrl;
6662 rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6663 if (FAILED(rc)) return rc;
6664
6665 setModified(IsModified_USB);
6666 mUSBControllers.backup();
6667
6668 ctrl->unshare();
6669
6670 mUSBControllers->remove(ctrl);
6671
6672 /* inform the direct session if any */
6673 alock.release();
6674 onUSBControllerChange();
6675
6676 return S_OK;
6677}
6678
6679STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6680 ULONG *puOriginX,
6681 ULONG *puOriginY,
6682 ULONG *puWidth,
6683 ULONG *puHeight,
6684 BOOL *pfEnabled)
6685{
6686 LogFlowThisFunc(("\n"));
6687
6688 CheckComArgNotNull(puOriginX);
6689 CheckComArgNotNull(puOriginY);
6690 CheckComArgNotNull(puWidth);
6691 CheckComArgNotNull(puHeight);
6692 CheckComArgNotNull(pfEnabled);
6693
6694 uint32_t u32OriginX= 0;
6695 uint32_t u32OriginY= 0;
6696 uint32_t u32Width = 0;
6697 uint32_t u32Height = 0;
6698 uint16_t u16Flags = 0;
6699
6700 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6701 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6702 if (RT_FAILURE(vrc))
6703 {
6704#ifdef RT_OS_WINDOWS
6705 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6706 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6707 * So just assign fEnable to TRUE again.
6708 * The right fix would be to change GUI API wrappers to make sure that parameters
6709 * are changed only if API succeeds.
6710 */
6711 *pfEnabled = TRUE;
6712#endif
6713 return setError(VBOX_E_IPRT_ERROR,
6714 tr("Saved guest size is not available (%Rrc)"),
6715 vrc);
6716 }
6717
6718 *puOriginX = u32OriginX;
6719 *puOriginY = u32OriginY;
6720 *puWidth = u32Width;
6721 *puHeight = u32Height;
6722 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6723
6724 return S_OK;
6725}
6726
6727STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6728{
6729 LogFlowThisFunc(("\n"));
6730
6731 CheckComArgNotNull(aSize);
6732 CheckComArgNotNull(aWidth);
6733 CheckComArgNotNull(aHeight);
6734
6735 if (aScreenId != 0)
6736 return E_NOTIMPL;
6737
6738 AutoCaller autoCaller(this);
6739 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6740
6741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6742
6743 uint8_t *pu8Data = NULL;
6744 uint32_t cbData = 0;
6745 uint32_t u32Width = 0;
6746 uint32_t u32Height = 0;
6747
6748 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6749
6750 if (RT_FAILURE(vrc))
6751 return setError(VBOX_E_IPRT_ERROR,
6752 tr("Saved screenshot data is not available (%Rrc)"),
6753 vrc);
6754
6755 *aSize = cbData;
6756 *aWidth = u32Width;
6757 *aHeight = u32Height;
6758
6759 freeSavedDisplayScreenshot(pu8Data);
6760
6761 return S_OK;
6762}
6763
6764STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6765{
6766 LogFlowThisFunc(("\n"));
6767
6768 CheckComArgNotNull(aWidth);
6769 CheckComArgNotNull(aHeight);
6770 CheckComArgOutSafeArrayPointerValid(aData);
6771
6772 if (aScreenId != 0)
6773 return E_NOTIMPL;
6774
6775 AutoCaller autoCaller(this);
6776 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6777
6778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6779
6780 uint8_t *pu8Data = NULL;
6781 uint32_t cbData = 0;
6782 uint32_t u32Width = 0;
6783 uint32_t u32Height = 0;
6784
6785 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6786
6787 if (RT_FAILURE(vrc))
6788 return setError(VBOX_E_IPRT_ERROR,
6789 tr("Saved screenshot data is not available (%Rrc)"),
6790 vrc);
6791
6792 *aWidth = u32Width;
6793 *aHeight = u32Height;
6794
6795 com::SafeArray<BYTE> bitmap(cbData);
6796 /* Convert pixels to format expected by the API caller. */
6797 if (aBGR)
6798 {
6799 /* [0] B, [1] G, [2] R, [3] A. */
6800 for (unsigned i = 0; i < cbData; i += 4)
6801 {
6802 bitmap[i] = pu8Data[i];
6803 bitmap[i + 1] = pu8Data[i + 1];
6804 bitmap[i + 2] = pu8Data[i + 2];
6805 bitmap[i + 3] = 0xff;
6806 }
6807 }
6808 else
6809 {
6810 /* [0] R, [1] G, [2] B, [3] A. */
6811 for (unsigned i = 0; i < cbData; i += 4)
6812 {
6813 bitmap[i] = pu8Data[i + 2];
6814 bitmap[i + 1] = pu8Data[i + 1];
6815 bitmap[i + 2] = pu8Data[i];
6816 bitmap[i + 3] = 0xff;
6817 }
6818 }
6819 bitmap.detachTo(ComSafeArrayOutArg(aData));
6820
6821 freeSavedDisplayScreenshot(pu8Data);
6822
6823 return S_OK;
6824}
6825
6826
6827STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6828{
6829 LogFlowThisFunc(("\n"));
6830
6831 CheckComArgNotNull(aWidth);
6832 CheckComArgNotNull(aHeight);
6833 CheckComArgOutSafeArrayPointerValid(aData);
6834
6835 if (aScreenId != 0)
6836 return E_NOTIMPL;
6837
6838 AutoCaller autoCaller(this);
6839 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6840
6841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6842
6843 uint8_t *pu8Data = NULL;
6844 uint32_t cbData = 0;
6845 uint32_t u32Width = 0;
6846 uint32_t u32Height = 0;
6847
6848 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6849
6850 if (RT_FAILURE(vrc))
6851 return setError(VBOX_E_IPRT_ERROR,
6852 tr("Saved screenshot data is not available (%Rrc)"),
6853 vrc);
6854
6855 *aWidth = u32Width;
6856 *aHeight = u32Height;
6857
6858 HRESULT rc = S_OK;
6859 uint8_t *pu8PNG = NULL;
6860 uint32_t cbPNG = 0;
6861 uint32_t cxPNG = 0;
6862 uint32_t cyPNG = 0;
6863
6864 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6865
6866 if (RT_SUCCESS(vrc))
6867 {
6868 com::SafeArray<BYTE> screenData(cbPNG);
6869 screenData.initFrom(pu8PNG, cbPNG);
6870 if (pu8PNG)
6871 RTMemFree(pu8PNG);
6872 screenData.detachTo(ComSafeArrayOutArg(aData));
6873 }
6874 else
6875 {
6876 if (pu8PNG)
6877 RTMemFree(pu8PNG);
6878 return setError(VBOX_E_IPRT_ERROR,
6879 tr("Could not convert screenshot to PNG (%Rrc)"),
6880 vrc);
6881 }
6882
6883 freeSavedDisplayScreenshot(pu8Data);
6884
6885 return rc;
6886}
6887
6888STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6889{
6890 LogFlowThisFunc(("\n"));
6891
6892 CheckComArgNotNull(aSize);
6893 CheckComArgNotNull(aWidth);
6894 CheckComArgNotNull(aHeight);
6895
6896 if (aScreenId != 0)
6897 return E_NOTIMPL;
6898
6899 AutoCaller autoCaller(this);
6900 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6901
6902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6903
6904 uint8_t *pu8Data = NULL;
6905 uint32_t cbData = 0;
6906 uint32_t u32Width = 0;
6907 uint32_t u32Height = 0;
6908
6909 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6910
6911 if (RT_FAILURE(vrc))
6912 return setError(VBOX_E_IPRT_ERROR,
6913 tr("Saved screenshot data is not available (%Rrc)"),
6914 vrc);
6915
6916 *aSize = cbData;
6917 *aWidth = u32Width;
6918 *aHeight = u32Height;
6919
6920 freeSavedDisplayScreenshot(pu8Data);
6921
6922 return S_OK;
6923}
6924
6925STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6926{
6927 LogFlowThisFunc(("\n"));
6928
6929 CheckComArgNotNull(aWidth);
6930 CheckComArgNotNull(aHeight);
6931 CheckComArgOutSafeArrayPointerValid(aData);
6932
6933 if (aScreenId != 0)
6934 return E_NOTIMPL;
6935
6936 AutoCaller autoCaller(this);
6937 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6938
6939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6940
6941 uint8_t *pu8Data = NULL;
6942 uint32_t cbData = 0;
6943 uint32_t u32Width = 0;
6944 uint32_t u32Height = 0;
6945
6946 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6947
6948 if (RT_FAILURE(vrc))
6949 return setError(VBOX_E_IPRT_ERROR,
6950 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6951 vrc);
6952
6953 *aWidth = u32Width;
6954 *aHeight = u32Height;
6955
6956 com::SafeArray<BYTE> png(cbData);
6957 png.initFrom(pu8Data, cbData);
6958 png.detachTo(ComSafeArrayOutArg(aData));
6959
6960 freeSavedDisplayScreenshot(pu8Data);
6961
6962 return S_OK;
6963}
6964
6965STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6966{
6967 HRESULT rc = S_OK;
6968 LogFlowThisFunc(("\n"));
6969
6970 AutoCaller autoCaller(this);
6971 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6972
6973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6974
6975 if (!mHWData->mCPUHotPlugEnabled)
6976 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6977
6978 if (aCpu >= mHWData->mCPUCount)
6979 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6980
6981 if (mHWData->mCPUAttached[aCpu])
6982 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6983
6984 alock.release();
6985 rc = onCPUChange(aCpu, false);
6986 alock.acquire();
6987 if (FAILED(rc)) return rc;
6988
6989 setModified(IsModified_MachineData);
6990 mHWData.backup();
6991 mHWData->mCPUAttached[aCpu] = true;
6992
6993 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6994 if (Global::IsOnline(mData->mMachineState))
6995 saveSettings(NULL);
6996
6997 return S_OK;
6998}
6999
7000STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
7001{
7002 HRESULT rc = S_OK;
7003 LogFlowThisFunc(("\n"));
7004
7005 AutoCaller autoCaller(this);
7006 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7007
7008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7009
7010 if (!mHWData->mCPUHotPlugEnabled)
7011 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7012
7013 if (aCpu >= SchemaDefs::MaxCPUCount)
7014 return setError(E_INVALIDARG,
7015 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
7016 SchemaDefs::MaxCPUCount);
7017
7018 if (!mHWData->mCPUAttached[aCpu])
7019 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
7020
7021 /* CPU 0 can't be detached */
7022 if (aCpu == 0)
7023 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
7024
7025 alock.release();
7026 rc = onCPUChange(aCpu, true);
7027 alock.acquire();
7028 if (FAILED(rc)) return rc;
7029
7030 setModified(IsModified_MachineData);
7031 mHWData.backup();
7032 mHWData->mCPUAttached[aCpu] = false;
7033
7034 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7035 if (Global::IsOnline(mData->mMachineState))
7036 saveSettings(NULL);
7037
7038 return S_OK;
7039}
7040
7041STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
7042{
7043 LogFlowThisFunc(("\n"));
7044
7045 CheckComArgNotNull(aCpuAttached);
7046
7047 *aCpuAttached = false;
7048
7049 AutoCaller autoCaller(this);
7050 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7051
7052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7053
7054 /* If hotplug is enabled the CPU is always enabled. */
7055 if (!mHWData->mCPUHotPlugEnabled)
7056 {
7057 if (aCpu < mHWData->mCPUCount)
7058 *aCpuAttached = true;
7059 }
7060 else
7061 {
7062 if (aCpu < SchemaDefs::MaxCPUCount)
7063 *aCpuAttached = mHWData->mCPUAttached[aCpu];
7064 }
7065
7066 return S_OK;
7067}
7068
7069STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
7070{
7071 CheckComArgOutPointerValid(aName);
7072
7073 AutoCaller autoCaller(this);
7074 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7075
7076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7077
7078 Utf8Str log = queryLogFilename(aIdx);
7079 if (!RTFileExists(log.c_str()))
7080 log.setNull();
7081 log.cloneTo(aName);
7082
7083 return S_OK;
7084}
7085
7086STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
7087{
7088 LogFlowThisFunc(("\n"));
7089 CheckComArgOutSafeArrayPointerValid(aData);
7090 if (aSize < 0)
7091 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
7092
7093 AutoCaller autoCaller(this);
7094 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7095
7096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7097
7098 HRESULT rc = S_OK;
7099 Utf8Str log = queryLogFilename(aIdx);
7100
7101 /* do not unnecessarily hold the lock while doing something which does
7102 * not need the lock and potentially takes a long time. */
7103 alock.release();
7104
7105 /* Limit the chunk size to 32K for now, as that gives better performance
7106 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
7107 * One byte expands to approx. 25 bytes of breathtaking XML. */
7108 size_t cbData = (size_t)RT_MIN(aSize, 32768);
7109 com::SafeArray<BYTE> logData(cbData);
7110
7111 RTFILE LogFile;
7112 int vrc = RTFileOpen(&LogFile, log.c_str(),
7113 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
7114 if (RT_SUCCESS(vrc))
7115 {
7116 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
7117 if (RT_SUCCESS(vrc))
7118 logData.resize(cbData);
7119 else
7120 rc = setError(VBOX_E_IPRT_ERROR,
7121 tr("Could not read log file '%s' (%Rrc)"),
7122 log.c_str(), vrc);
7123 RTFileClose(LogFile);
7124 }
7125 else
7126 rc = setError(VBOX_E_IPRT_ERROR,
7127 tr("Could not open log file '%s' (%Rrc)"),
7128 log.c_str(), vrc);
7129
7130 if (FAILED(rc))
7131 logData.resize(0);
7132 logData.detachTo(ComSafeArrayOutArg(aData));
7133
7134 return rc;
7135}
7136
7137
7138/**
7139 * Currently this method doesn't attach device to the running VM,
7140 * just makes sure it's plugged on next VM start.
7141 */
7142STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
7143{
7144 AutoCaller autoCaller(this);
7145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7146
7147 // lock scope
7148 {
7149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7150
7151 HRESULT rc = checkStateDependency(MutableStateDep);
7152 if (FAILED(rc)) return rc;
7153
7154 ChipsetType_T aChipset = ChipsetType_PIIX3;
7155 COMGETTER(ChipsetType)(&aChipset);
7156
7157 if (aChipset != ChipsetType_ICH9)
7158 {
7159 return setError(E_INVALIDARG,
7160 tr("Host PCI attachment only supported with ICH9 chipset"));
7161 }
7162
7163 // check if device with this host PCI address already attached
7164 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7165 it != mHWData->mPCIDeviceAssignments.end();
7166 ++it)
7167 {
7168 LONG iHostAddress = -1;
7169 ComPtr<PCIDeviceAttachment> pAttach;
7170 pAttach = *it;
7171 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7172 if (iHostAddress == hostAddress)
7173 return setError(E_INVALIDARG,
7174 tr("Device with host PCI address already attached to this VM"));
7175 }
7176
7177 ComObjPtr<PCIDeviceAttachment> pda;
7178 char name[32];
7179
7180 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7181 Bstr bname(name);
7182 pda.createObject();
7183 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7184 setModified(IsModified_MachineData);
7185 mHWData.backup();
7186 mHWData->mPCIDeviceAssignments.push_back(pda);
7187 }
7188
7189 return S_OK;
7190}
7191
7192/**
7193 * Currently this method doesn't detach device from the running VM,
7194 * just makes sure it's not plugged on next VM start.
7195 */
7196STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7197{
7198 AutoCaller autoCaller(this);
7199 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7200
7201 ComObjPtr<PCIDeviceAttachment> pAttach;
7202 bool fRemoved = false;
7203 HRESULT rc;
7204
7205 // lock scope
7206 {
7207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7208
7209 rc = checkStateDependency(MutableStateDep);
7210 if (FAILED(rc)) return rc;
7211
7212 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7213 it != mHWData->mPCIDeviceAssignments.end();
7214 ++it)
7215 {
7216 LONG iHostAddress = -1;
7217 pAttach = *it;
7218 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7219 if (iHostAddress != -1 && iHostAddress == hostAddress)
7220 {
7221 setModified(IsModified_MachineData);
7222 mHWData.backup();
7223 mHWData->mPCIDeviceAssignments.remove(pAttach);
7224 fRemoved = true;
7225 break;
7226 }
7227 }
7228 }
7229
7230
7231 /* Fire event outside of the lock */
7232 if (fRemoved)
7233 {
7234 Assert(!pAttach.isNull());
7235 ComPtr<IEventSource> es;
7236 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7237 Assert(SUCCEEDED(rc));
7238 Bstr mid;
7239 rc = this->COMGETTER(Id)(mid.asOutParam());
7240 Assert(SUCCEEDED(rc));
7241 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7242 }
7243
7244 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7245 tr("No host PCI device %08x attached"),
7246 hostAddress
7247 );
7248}
7249
7250STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7251{
7252 CheckComArgOutSafeArrayPointerValid(aAssignments);
7253
7254 AutoCaller autoCaller(this);
7255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7256
7257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7258
7259 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7260 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7261
7262 return S_OK;
7263}
7264
7265STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7266{
7267 CheckComArgOutPointerValid(aBandwidthControl);
7268
7269 AutoCaller autoCaller(this);
7270 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7271
7272 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7273
7274 return S_OK;
7275}
7276
7277STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7278{
7279 CheckComArgOutPointerValid(pfEnabled);
7280 AutoCaller autoCaller(this);
7281 HRESULT hrc = autoCaller.rc();
7282 if (SUCCEEDED(hrc))
7283 {
7284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7285 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7286 }
7287 return hrc;
7288}
7289
7290STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7291{
7292 AutoCaller autoCaller(this);
7293 HRESULT hrc = autoCaller.rc();
7294 if (SUCCEEDED(hrc))
7295 {
7296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7297 hrc = checkStateDependency(MutableStateDep);
7298 if (SUCCEEDED(hrc))
7299 {
7300 hrc = mHWData.backupEx();
7301 if (SUCCEEDED(hrc))
7302 {
7303 setModified(IsModified_MachineData);
7304 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7305 }
7306 }
7307 }
7308 return hrc;
7309}
7310
7311STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7312{
7313 CheckComArgOutPointerValid(pbstrConfig);
7314 AutoCaller autoCaller(this);
7315 HRESULT hrc = autoCaller.rc();
7316 if (SUCCEEDED(hrc))
7317 {
7318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7319 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7320 }
7321 return hrc;
7322}
7323
7324STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7325{
7326 CheckComArgStr(bstrConfig);
7327 AutoCaller autoCaller(this);
7328 HRESULT hrc = autoCaller.rc();
7329 if (SUCCEEDED(hrc))
7330 {
7331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7332 hrc = checkStateDependency(MutableStateDep);
7333 if (SUCCEEDED(hrc))
7334 {
7335 hrc = mHWData.backupEx();
7336 if (SUCCEEDED(hrc))
7337 {
7338 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7339 if (SUCCEEDED(hrc))
7340 setModified(IsModified_MachineData);
7341 }
7342 }
7343 }
7344 return hrc;
7345
7346}
7347
7348STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7349{
7350 CheckComArgOutPointerValid(pfAllow);
7351 AutoCaller autoCaller(this);
7352 HRESULT hrc = autoCaller.rc();
7353 if (SUCCEEDED(hrc))
7354 {
7355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7356 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7357 }
7358 return hrc;
7359}
7360
7361STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7362{
7363 AutoCaller autoCaller(this);
7364 HRESULT hrc = autoCaller.rc();
7365 if (SUCCEEDED(hrc))
7366 {
7367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7368 hrc = checkStateDependency(MutableStateDep);
7369 if (SUCCEEDED(hrc))
7370 {
7371 hrc = mHWData.backupEx();
7372 if (SUCCEEDED(hrc))
7373 {
7374 setModified(IsModified_MachineData);
7375 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7376 }
7377 }
7378 }
7379 return hrc;
7380}
7381
7382STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7383{
7384 CheckComArgOutPointerValid(pfEnabled);
7385 AutoCaller autoCaller(this);
7386 HRESULT hrc = autoCaller.rc();
7387 if (SUCCEEDED(hrc))
7388 {
7389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7390 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7391 }
7392 return hrc;
7393}
7394
7395STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7396{
7397 AutoCaller autoCaller(this);
7398 HRESULT hrc = autoCaller.rc();
7399 if (SUCCEEDED(hrc))
7400 {
7401 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7402 hrc = checkStateDependency(MutableStateDep);
7403 if ( SUCCEEDED(hrc)
7404 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7405 {
7406 AutostartDb *autostartDb = mParent->getAutostartDb();
7407 int vrc;
7408
7409 if (fEnabled)
7410 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7411 else
7412 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7413
7414 if (RT_SUCCESS(vrc))
7415 {
7416 hrc = mHWData.backupEx();
7417 if (SUCCEEDED(hrc))
7418 {
7419 setModified(IsModified_MachineData);
7420 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7421 }
7422 }
7423 else if (vrc == VERR_NOT_SUPPORTED)
7424 hrc = setError(VBOX_E_NOT_SUPPORTED,
7425 tr("The VM autostart feature is not supported on this platform"));
7426 else if (vrc == VERR_PATH_NOT_FOUND)
7427 hrc = setError(E_FAIL,
7428 tr("The path to the autostart database is not set"));
7429 else
7430 hrc = setError(E_UNEXPECTED,
7431 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7432 fEnabled ? "Adding" : "Removing",
7433 mUserData->s.strName.c_str(), vrc);
7434 }
7435 }
7436 return hrc;
7437}
7438
7439STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7440{
7441 CheckComArgOutPointerValid(puDelay);
7442 AutoCaller autoCaller(this);
7443 HRESULT hrc = autoCaller.rc();
7444 if (SUCCEEDED(hrc))
7445 {
7446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7447 *puDelay = mHWData->mAutostart.uAutostartDelay;
7448 }
7449 return hrc;
7450}
7451
7452STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7453{
7454 AutoCaller autoCaller(this);
7455 HRESULT hrc = autoCaller.rc();
7456 if (SUCCEEDED(hrc))
7457 {
7458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7459 hrc = checkStateDependency(MutableStateDep);
7460 if (SUCCEEDED(hrc))
7461 {
7462 hrc = mHWData.backupEx();
7463 if (SUCCEEDED(hrc))
7464 {
7465 setModified(IsModified_MachineData);
7466 mHWData->mAutostart.uAutostartDelay = uDelay;
7467 }
7468 }
7469 }
7470 return hrc;
7471}
7472
7473STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7474{
7475 CheckComArgOutPointerValid(penmAutostopType);
7476 AutoCaller autoCaller(this);
7477 HRESULT hrc = autoCaller.rc();
7478 if (SUCCEEDED(hrc))
7479 {
7480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7481 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7482 }
7483 return hrc;
7484}
7485
7486STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7487{
7488 AutoCaller autoCaller(this);
7489 HRESULT hrc = autoCaller.rc();
7490 if (SUCCEEDED(hrc))
7491 {
7492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7493 hrc = checkStateDependency(MutableStateDep);
7494 if ( SUCCEEDED(hrc)
7495 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7496 {
7497 AutostartDb *autostartDb = mParent->getAutostartDb();
7498 int vrc;
7499
7500 if (enmAutostopType != AutostopType_Disabled)
7501 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7502 else
7503 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7504
7505 if (RT_SUCCESS(vrc))
7506 {
7507 hrc = mHWData.backupEx();
7508 if (SUCCEEDED(hrc))
7509 {
7510 setModified(IsModified_MachineData);
7511 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7512 }
7513 }
7514 else if (vrc == VERR_NOT_SUPPORTED)
7515 hrc = setError(VBOX_E_NOT_SUPPORTED,
7516 tr("The VM autostop feature is not supported on this platform"));
7517 else if (vrc == VERR_PATH_NOT_FOUND)
7518 hrc = setError(E_FAIL,
7519 tr("The path to the autostart database is not set"));
7520 else
7521 hrc = setError(E_UNEXPECTED,
7522 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7523 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7524 mUserData->s.strName.c_str(), vrc);
7525 }
7526 }
7527 return hrc;
7528}
7529
7530STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7531{
7532 CheckComArgOutPointerValid(aDefaultFrontend);
7533 AutoCaller autoCaller(this);
7534 HRESULT hrc = autoCaller.rc();
7535 if (SUCCEEDED(hrc))
7536 {
7537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7538 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7539 }
7540 return hrc;
7541}
7542
7543STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7544{
7545 CheckComArgStr(aDefaultFrontend);
7546 AutoCaller autoCaller(this);
7547 HRESULT hrc = autoCaller.rc();
7548 if (SUCCEEDED(hrc))
7549 {
7550 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7551 hrc = checkStateDependency(MutableOrSavedStateDep);
7552 if (SUCCEEDED(hrc))
7553 {
7554 hrc = mHWData.backupEx();
7555 if (SUCCEEDED(hrc))
7556 {
7557 setModified(IsModified_MachineData);
7558 mHWData->mDefaultFrontend = aDefaultFrontend;
7559 }
7560 }
7561 }
7562 return hrc;
7563}
7564
7565STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7566{
7567 CheckComArgSafeArrayNotNull(aIcon);
7568 CheckComArgOutSafeArrayPointerValid(aIcon);
7569 AutoCaller autoCaller(this);
7570 HRESULT hrc = autoCaller.rc();
7571 if (SUCCEEDED(hrc))
7572 {
7573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7574 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7575 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7576 icon.detachTo(ComSafeArrayOutArg(aIcon));
7577 }
7578 return hrc;
7579}
7580
7581STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7582{
7583 CheckComArgSafeArrayNotNull(aIcon);
7584 AutoCaller autoCaller(this);
7585 HRESULT hrc = autoCaller.rc();
7586 if (SUCCEEDED(hrc))
7587 {
7588 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7589 hrc = checkStateDependency(MutableOrSavedStateDep);
7590 if (SUCCEEDED(hrc))
7591 {
7592 setModified(IsModified_MachineData);
7593 mUserData.backup();
7594 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7595 mUserData->mIcon.resize(icon.size());
7596 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7597 }
7598 }
7599 return hrc;
7600}
7601
7602STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable)
7603{
7604 CheckComArgOutPointerValid(aAvailable);
7605
7606 AutoCaller autoCaller(this);
7607 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7608
7609#ifdef VBOX_WITH_USB
7610 *aAvailable = true;
7611#else
7612 *aAvailable = false;
7613#endif
7614 return S_OK;
7615}
7616
7617STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7618{
7619 LogFlowFuncEnter();
7620
7621 CheckComArgNotNull(pTarget);
7622 CheckComArgOutPointerValid(pProgress);
7623
7624 /* Convert the options. */
7625 RTCList<CloneOptions_T> optList;
7626 if (options != NULL)
7627 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7628
7629 if (optList.contains(CloneOptions_Link))
7630 {
7631 if (!isSnapshotMachine())
7632 return setError(E_INVALIDARG,
7633 tr("Linked clone can only be created from a snapshot"));
7634 if (mode != CloneMode_MachineState)
7635 return setError(E_INVALIDARG,
7636 tr("Linked clone can only be created for a single machine state"));
7637 }
7638 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7639
7640 AutoCaller autoCaller(this);
7641 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7642
7643
7644 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7645
7646 HRESULT rc = pWorker->start(pProgress);
7647
7648 LogFlowFuncLeave();
7649
7650 return rc;
7651}
7652
7653// public methods for internal purposes
7654/////////////////////////////////////////////////////////////////////////////
7655
7656/**
7657 * Adds the given IsModified_* flag to the dirty flags of the machine.
7658 * This must be called either during loadSettings or under the machine write lock.
7659 * @param fl
7660 */
7661void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7662{
7663 mData->flModifications |= fl;
7664 if (fAllowStateModification && isStateModificationAllowed())
7665 mData->mCurrentStateModified = true;
7666}
7667
7668/**
7669 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7670 * care of the write locking.
7671 *
7672 * @param fModifications The flag to add.
7673 */
7674void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7675{
7676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7677 setModified(fModification, fAllowStateModification);
7678}
7679
7680/**
7681 * Saves the registry entry of this machine to the given configuration node.
7682 *
7683 * @param aEntryNode Node to save the registry entry to.
7684 *
7685 * @note locks this object for reading.
7686 */
7687HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7688{
7689 AutoLimitedCaller autoCaller(this);
7690 AssertComRCReturnRC(autoCaller.rc());
7691
7692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7693
7694 data.uuid = mData->mUuid;
7695 data.strSettingsFile = mData->m_strConfigFile;
7696
7697 return S_OK;
7698}
7699
7700/**
7701 * Calculates the absolute path of the given path taking the directory of the
7702 * machine settings file as the current directory.
7703 *
7704 * @param aPath Path to calculate the absolute path for.
7705 * @param aResult Where to put the result (used only on success, can be the
7706 * same Utf8Str instance as passed in @a aPath).
7707 * @return IPRT result.
7708 *
7709 * @note Locks this object for reading.
7710 */
7711int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7712{
7713 AutoCaller autoCaller(this);
7714 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7715
7716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7717
7718 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7719
7720 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7721
7722 strSettingsDir.stripFilename();
7723 char folder[RTPATH_MAX];
7724 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7725 if (RT_SUCCESS(vrc))
7726 aResult = folder;
7727
7728 return vrc;
7729}
7730
7731/**
7732 * Copies strSource to strTarget, making it relative to the machine folder
7733 * if it is a subdirectory thereof, or simply copying it otherwise.
7734 *
7735 * @param strSource Path to evaluate and copy.
7736 * @param strTarget Buffer to receive target path.
7737 *
7738 * @note Locks this object for reading.
7739 */
7740void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7741 Utf8Str &strTarget)
7742{
7743 AutoCaller autoCaller(this);
7744 AssertComRCReturn(autoCaller.rc(), (void)0);
7745
7746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7747
7748 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7749 // use strTarget as a temporary buffer to hold the machine settings dir
7750 strTarget = mData->m_strConfigFileFull;
7751 strTarget.stripFilename();
7752 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7753 {
7754 // is relative: then append what's left
7755 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7756 // for empty paths (only possible for subdirs) use "." to avoid
7757 // triggering default settings for not present config attributes.
7758 if (strTarget.isEmpty())
7759 strTarget = ".";
7760 }
7761 else
7762 // is not relative: then overwrite
7763 strTarget = strSource;
7764}
7765
7766/**
7767 * Returns the full path to the machine's log folder in the
7768 * \a aLogFolder argument.
7769 */
7770void Machine::getLogFolder(Utf8Str &aLogFolder)
7771{
7772 AutoCaller autoCaller(this);
7773 AssertComRCReturnVoid(autoCaller.rc());
7774
7775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7776
7777 char szTmp[RTPATH_MAX];
7778 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7779 if (RT_SUCCESS(vrc))
7780 {
7781 if (szTmp[0] && !mUserData.isNull())
7782 {
7783 char szTmp2[RTPATH_MAX];
7784 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7785 if (RT_SUCCESS(vrc))
7786 aLogFolder = BstrFmt("%s%c%s",
7787 szTmp2,
7788 RTPATH_DELIMITER,
7789 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7790 }
7791 else
7792 vrc = VERR_PATH_IS_RELATIVE;
7793 }
7794
7795 if (RT_FAILURE(vrc))
7796 {
7797 // fallback if VBOX_USER_LOGHOME is not set or invalid
7798 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7799 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7800 aLogFolder.append(RTPATH_DELIMITER);
7801 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7802 }
7803}
7804
7805/**
7806 * Returns the full path to the machine's log file for an given index.
7807 */
7808Utf8Str Machine::queryLogFilename(ULONG idx)
7809{
7810 Utf8Str logFolder;
7811 getLogFolder(logFolder);
7812 Assert(logFolder.length());
7813 Utf8Str log;
7814 if (idx == 0)
7815 log = Utf8StrFmt("%s%cVBox.log",
7816 logFolder.c_str(), RTPATH_DELIMITER);
7817 else
7818 log = Utf8StrFmt("%s%cVBox.log.%d",
7819 logFolder.c_str(), RTPATH_DELIMITER, idx);
7820 return log;
7821}
7822
7823/**
7824 * Composes a unique saved state filename based on the current system time. The filename is
7825 * granular to the second so this will work so long as no more than one snapshot is taken on
7826 * a machine per second.
7827 *
7828 * Before version 4.1, we used this formula for saved state files:
7829 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7830 * which no longer works because saved state files can now be shared between the saved state of the
7831 * "saved" machine and an online snapshot, and the following would cause problems:
7832 * 1) save machine
7833 * 2) create online snapshot from that machine state --> reusing saved state file
7834 * 3) save machine again --> filename would be reused, breaking the online snapshot
7835 *
7836 * So instead we now use a timestamp.
7837 *
7838 * @param str
7839 */
7840void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7841{
7842 AutoCaller autoCaller(this);
7843 AssertComRCReturnVoid(autoCaller.rc());
7844
7845 {
7846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7847 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7848 }
7849
7850 RTTIMESPEC ts;
7851 RTTimeNow(&ts);
7852 RTTIME time;
7853 RTTimeExplode(&time, &ts);
7854
7855 strStateFilePath += RTPATH_DELIMITER;
7856 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7857 time.i32Year, time.u8Month, time.u8MonthDay,
7858 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7859}
7860
7861/**
7862 * Returns the full path to the default video capture file.
7863 */
7864void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7865{
7866 AutoCaller autoCaller(this);
7867 AssertComRCReturnVoid(autoCaller.rc());
7868
7869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7870
7871 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7872 strFile.stripExt(); // path/to/machinesfolder/vmname/vmname
7873 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7874}
7875
7876/**
7877 * Returns whether at least one USB controller is present for the VM.
7878 */
7879bool Machine::isUSBControllerPresent()
7880{
7881 AutoCaller autoCaller(this);
7882 AssertComRCReturn(autoCaller.rc(), false);
7883
7884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7885
7886 return (mUSBControllers->size() > 0);
7887}
7888
7889/**
7890 * @note Locks this object for writing, calls the client process
7891 * (inside the lock).
7892 */
7893HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7894 const Utf8Str &strFrontend,
7895 const Utf8Str &strEnvironment,
7896 ProgressProxy *aProgress)
7897{
7898 LogFlowThisFuncEnter();
7899
7900 AssertReturn(aControl, E_FAIL);
7901 AssertReturn(aProgress, E_FAIL);
7902 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7903
7904 AutoCaller autoCaller(this);
7905 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7906
7907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7908
7909 if (!mData->mRegistered)
7910 return setError(E_UNEXPECTED,
7911 tr("The machine '%s' is not registered"),
7912 mUserData->s.strName.c_str());
7913
7914 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7915
7916 if ( mData->mSession.mState == SessionState_Locked
7917 || mData->mSession.mState == SessionState_Spawning
7918 || mData->mSession.mState == SessionState_Unlocking)
7919 return setError(VBOX_E_INVALID_OBJECT_STATE,
7920 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7921 mUserData->s.strName.c_str());
7922
7923 /* may not be busy */
7924 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7925
7926 /* get the path to the executable */
7927 char szPath[RTPATH_MAX];
7928 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7929 size_t sz = strlen(szPath);
7930 szPath[sz++] = RTPATH_DELIMITER;
7931 szPath[sz] = 0;
7932 char *cmd = szPath + sz;
7933 sz = sizeof(szPath) - sz;
7934
7935 int vrc = VINF_SUCCESS;
7936 RTPROCESS pid = NIL_RTPROCESS;
7937
7938 RTENV env = RTENV_DEFAULT;
7939
7940 if (!strEnvironment.isEmpty())
7941 {
7942 char *newEnvStr = NULL;
7943
7944 do
7945 {
7946 /* clone the current environment */
7947 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7948 AssertRCBreakStmt(vrc2, vrc = vrc2);
7949
7950 newEnvStr = RTStrDup(strEnvironment.c_str());
7951 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7952
7953 /* put new variables to the environment
7954 * (ignore empty variable names here since RTEnv API
7955 * intentionally doesn't do that) */
7956 char *var = newEnvStr;
7957 for (char *p = newEnvStr; *p; ++p)
7958 {
7959 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7960 {
7961 *p = '\0';
7962 if (*var)
7963 {
7964 char *val = strchr(var, '=');
7965 if (val)
7966 {
7967 *val++ = '\0';
7968 vrc2 = RTEnvSetEx(env, var, val);
7969 }
7970 else
7971 vrc2 = RTEnvUnsetEx(env, var);
7972 if (RT_FAILURE(vrc2))
7973 break;
7974 }
7975 var = p + 1;
7976 }
7977 }
7978 if (RT_SUCCESS(vrc2) && *var)
7979 vrc2 = RTEnvPutEx(env, var);
7980
7981 AssertRCBreakStmt(vrc2, vrc = vrc2);
7982 }
7983 while (0);
7984
7985 if (newEnvStr != NULL)
7986 RTStrFree(newEnvStr);
7987 }
7988
7989#ifdef VBOX_WITH_QTGUI
7990 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7991 {
7992# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7993 /* Modify the base path so that we don't need to use ".." below. */
7994 RTPathStripTrailingSlash(szPath);
7995 RTPathStripFilename(szPath);
7996 sz = strlen(szPath);
7997 cmd = szPath + sz;
7998 sz = sizeof(szPath) - sz;
7999
8000#define OSX_APP_NAME "VirtualBoxVM"
8001#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
8002
8003 Utf8Str strAppOverride = getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
8004 if ( strAppOverride.contains(".")
8005 || strAppOverride.contains("/")
8006 || strAppOverride.contains("\\")
8007 || strAppOverride.contains(":"))
8008 strAppOverride.setNull();
8009 Utf8Str strAppPath;
8010 if (!strAppOverride.isEmpty())
8011 {
8012 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
8013 Utf8Str strFullPath(szPath);
8014 strFullPath.append(strAppPath);
8015 /* there is a race, but people using this deserve the failure */
8016 if (!RTFileExists(strFullPath.c_str()))
8017 strAppOverride.setNull();
8018 }
8019 if (strAppOverride.isEmpty())
8020 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
8021 const char *VirtualBox_exe = strAppPath.c_str();
8022 AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED);
8023# else
8024 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
8025 Assert(sz >= sizeof(VirtualBox_exe));
8026# endif
8027 strcpy(cmd, VirtualBox_exe);
8028
8029 Utf8Str idStr = mData->mUuid.toString();
8030 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
8031 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8032 }
8033#else /* !VBOX_WITH_QTGUI */
8034 if (0)
8035 ;
8036#endif /* VBOX_WITH_QTGUI */
8037
8038 else
8039
8040#ifdef VBOX_WITH_VBOXSDL
8041 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
8042 {
8043 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
8044 Assert(sz >= sizeof(VBoxSDL_exe));
8045 strcpy(cmd, VBoxSDL_exe);
8046
8047 Utf8Str idStr = mData->mUuid.toString();
8048 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
8049 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8050 }
8051#else /* !VBOX_WITH_VBOXSDL */
8052 if (0)
8053 ;
8054#endif /* !VBOX_WITH_VBOXSDL */
8055
8056 else
8057
8058#ifdef VBOX_WITH_HEADLESS
8059 if ( strFrontend == "headless"
8060 || strFrontend == "capture"
8061 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
8062 )
8063 {
8064 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
8065 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
8066 * and a VM works even if the server has not been installed.
8067 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
8068 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
8069 * differently in 4.0 and 3.x.
8070 */
8071 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
8072 Assert(sz >= sizeof(VBoxHeadless_exe));
8073 strcpy(cmd, VBoxHeadless_exe);
8074
8075 Utf8Str idStr = mData->mUuid.toString();
8076 /* Leave space for "--capture" arg. */
8077 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
8078 "--startvm", idStr.c_str(),
8079 "--vrde", "config",
8080 0, /* For "--capture". */
8081 0 };
8082 if (strFrontend == "capture")
8083 {
8084 unsigned pos = RT_ELEMENTS(args) - 2;
8085 args[pos] = "--capture";
8086 }
8087 vrc = RTProcCreate(szPath, args, env,
8088#ifdef RT_OS_WINDOWS
8089 RTPROC_FLAGS_NO_WINDOW
8090#else
8091 0
8092#endif
8093 , &pid);
8094 }
8095#else /* !VBOX_WITH_HEADLESS */
8096 if (0)
8097 ;
8098#endif /* !VBOX_WITH_HEADLESS */
8099 else
8100 {
8101 RTEnvDestroy(env);
8102 return setError(E_INVALIDARG,
8103 tr("Invalid frontend name: '%s'"),
8104 strFrontend.c_str());
8105 }
8106
8107 RTEnvDestroy(env);
8108
8109 if (RT_FAILURE(vrc))
8110 return setError(VBOX_E_IPRT_ERROR,
8111 tr("Could not launch a process for the machine '%s' (%Rrc)"),
8112 mUserData->s.strName.c_str(), vrc);
8113
8114 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
8115
8116 /*
8117 * Note that we don't release the lock here before calling the client,
8118 * because it doesn't need to call us back if called with a NULL argument.
8119 * Releasing the lock here is dangerous because we didn't prepare the
8120 * launch data yet, but the client we've just started may happen to be
8121 * too fast and call LockMachine() that will fail (because of PID, etc.),
8122 * so that the Machine will never get out of the Spawning session state.
8123 */
8124
8125 /* inform the session that it will be a remote one */
8126 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8127 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8128 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8129
8130 if (FAILED(rc))
8131 {
8132 /* restore the session state */
8133 mData->mSession.mState = SessionState_Unlocked;
8134 /* The failure may occur w/o any error info (from RPC), so provide one */
8135 return setError(VBOX_E_VM_ERROR,
8136 tr("Failed to assign the machine to the session (%Rrc)"), rc);
8137 }
8138
8139 /* attach launch data to the machine */
8140 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8141 mData->mSession.mRemoteControls.push_back(aControl);
8142 mData->mSession.mProgress = aProgress;
8143 mData->mSession.mPID = pid;
8144 mData->mSession.mState = SessionState_Spawning;
8145 mData->mSession.mType = strFrontend;
8146
8147 LogFlowThisFuncLeave();
8148 return S_OK;
8149}
8150
8151/**
8152 * Returns @c true if the given session machine instance has an open direct
8153 * session (and optionally also for direct sessions which are closing) and
8154 * returns the session control machine instance if so.
8155 *
8156 * Note that when the method returns @c false, the arguments remain unchanged.
8157 *
8158 * @param aMachine Session machine object.
8159 * @param aControl Direct session control object (optional).
8160 * @param aAllowClosing If true then additionally a session which is currently
8161 * being closed will also be allowed.
8162 *
8163 * @note locks this object for reading.
8164 */
8165bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8166 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8167 bool aAllowClosing /*= false*/)
8168{
8169 AutoLimitedCaller autoCaller(this);
8170 AssertComRCReturn(autoCaller.rc(), false);
8171
8172 /* just return false for inaccessible machines */
8173 if (autoCaller.state() != Ready)
8174 return false;
8175
8176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8177
8178 if ( mData->mSession.mState == SessionState_Locked
8179 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8180 )
8181 {
8182 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8183
8184 aMachine = mData->mSession.mMachine;
8185
8186 if (aControl != NULL)
8187 *aControl = mData->mSession.mDirectControl;
8188
8189 return true;
8190 }
8191
8192 return false;
8193}
8194
8195/**
8196 * Returns @c true if the given machine has an spawning direct session.
8197 *
8198 * @note locks this object for reading.
8199 */
8200bool Machine::isSessionSpawning()
8201{
8202 AutoLimitedCaller autoCaller(this);
8203 AssertComRCReturn(autoCaller.rc(), false);
8204
8205 /* just return false for inaccessible machines */
8206 if (autoCaller.state() != Ready)
8207 return false;
8208
8209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8210
8211 if (mData->mSession.mState == SessionState_Spawning)
8212 return true;
8213
8214 return false;
8215}
8216
8217/**
8218 * Called from the client watcher thread to check for unexpected client process
8219 * death during Session_Spawning state (e.g. before it successfully opened a
8220 * direct session).
8221 *
8222 * On Win32 and on OS/2, this method is called only when we've got the
8223 * direct client's process termination notification, so it always returns @c
8224 * true.
8225 *
8226 * On other platforms, this method returns @c true if the client process is
8227 * terminated and @c false if it's still alive.
8228 *
8229 * @note Locks this object for writing.
8230 */
8231bool Machine::checkForSpawnFailure()
8232{
8233 AutoCaller autoCaller(this);
8234 if (!autoCaller.isOk())
8235 {
8236 /* nothing to do */
8237 LogFlowThisFunc(("Already uninitialized!\n"));
8238 return true;
8239 }
8240
8241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8242
8243 if (mData->mSession.mState != SessionState_Spawning)
8244 {
8245 /* nothing to do */
8246 LogFlowThisFunc(("Not spawning any more!\n"));
8247 return true;
8248 }
8249
8250 HRESULT rc = S_OK;
8251
8252 /* PID not yet initialized, skip check. */
8253 if (mData->mSession.mPID == NIL_RTPROCESS)
8254 return false;
8255
8256 RTPROCSTATUS status;
8257 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8258
8259 if (vrc != VERR_PROCESS_RUNNING)
8260 {
8261 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8262 rc = setError(E_FAIL,
8263 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8264 getName().c_str(), status.iStatus);
8265 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8266 rc = setError(E_FAIL,
8267 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8268 getName().c_str(), status.iStatus);
8269 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8270 rc = setError(E_FAIL,
8271 tr("The virtual machine '%s' has terminated abnormally"),
8272 getName().c_str(), status.iStatus);
8273 else
8274 rc = setError(E_FAIL,
8275 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8276 getName().c_str(), rc);
8277 }
8278
8279 if (FAILED(rc))
8280 {
8281 /* Close the remote session, remove the remote control from the list
8282 * and reset session state to Closed (@note keep the code in sync with
8283 * the relevant part in LockMachine()). */
8284
8285 Assert(mData->mSession.mRemoteControls.size() == 1);
8286 if (mData->mSession.mRemoteControls.size() == 1)
8287 {
8288 ErrorInfoKeeper eik;
8289 mData->mSession.mRemoteControls.front()->Uninitialize();
8290 }
8291
8292 mData->mSession.mRemoteControls.clear();
8293 mData->mSession.mState = SessionState_Unlocked;
8294
8295 /* finalize the progress after setting the state */
8296 if (!mData->mSession.mProgress.isNull())
8297 {
8298 mData->mSession.mProgress->notifyComplete(rc);
8299 mData->mSession.mProgress.setNull();
8300 }
8301
8302 mData->mSession.mPID = NIL_RTPROCESS;
8303
8304 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8305 return true;
8306 }
8307
8308 return false;
8309}
8310
8311/**
8312 * Checks whether the machine can be registered. If so, commits and saves
8313 * all settings.
8314 *
8315 * @note Must be called from mParent's write lock. Locks this object and
8316 * children for writing.
8317 */
8318HRESULT Machine::prepareRegister()
8319{
8320 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8321
8322 AutoLimitedCaller autoCaller(this);
8323 AssertComRCReturnRC(autoCaller.rc());
8324
8325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8326
8327 /* wait for state dependents to drop to zero */
8328 ensureNoStateDependencies();
8329
8330 if (!mData->mAccessible)
8331 return setError(VBOX_E_INVALID_OBJECT_STATE,
8332 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8333 mUserData->s.strName.c_str(),
8334 mData->mUuid.toString().c_str());
8335
8336 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8337
8338 if (mData->mRegistered)
8339 return setError(VBOX_E_INVALID_OBJECT_STATE,
8340 tr("The machine '%s' with UUID {%s} is already registered"),
8341 mUserData->s.strName.c_str(),
8342 mData->mUuid.toString().c_str());
8343
8344 HRESULT rc = S_OK;
8345
8346 // Ensure the settings are saved. If we are going to be registered and
8347 // no config file exists yet, create it by calling saveSettings() too.
8348 if ( (mData->flModifications)
8349 || (!mData->pMachineConfigFile->fileExists())
8350 )
8351 {
8352 rc = saveSettings(NULL);
8353 // no need to check whether VirtualBox.xml needs saving too since
8354 // we can't have a machine XML file rename pending
8355 if (FAILED(rc)) return rc;
8356 }
8357
8358 /* more config checking goes here */
8359
8360 if (SUCCEEDED(rc))
8361 {
8362 /* we may have had implicit modifications we want to fix on success */
8363 commit();
8364
8365 mData->mRegistered = true;
8366 }
8367 else
8368 {
8369 /* we may have had implicit modifications we want to cancel on failure*/
8370 rollback(false /* aNotify */);
8371 }
8372
8373 return rc;
8374}
8375
8376/**
8377 * Increases the number of objects dependent on the machine state or on the
8378 * registered state. Guarantees that these two states will not change at least
8379 * until #releaseStateDependency() is called.
8380 *
8381 * Depending on the @a aDepType value, additional state checks may be made.
8382 * These checks will set extended error info on failure. See
8383 * #checkStateDependency() for more info.
8384 *
8385 * If this method returns a failure, the dependency is not added and the caller
8386 * is not allowed to rely on any particular machine state or registration state
8387 * value and may return the failed result code to the upper level.
8388 *
8389 * @param aDepType Dependency type to add.
8390 * @param aState Current machine state (NULL if not interested).
8391 * @param aRegistered Current registered state (NULL if not interested).
8392 *
8393 * @note Locks this object for writing.
8394 */
8395HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8396 MachineState_T *aState /* = NULL */,
8397 BOOL *aRegistered /* = NULL */)
8398{
8399 AutoCaller autoCaller(this);
8400 AssertComRCReturnRC(autoCaller.rc());
8401
8402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8403
8404 HRESULT rc = checkStateDependency(aDepType);
8405 if (FAILED(rc)) return rc;
8406
8407 {
8408 if (mData->mMachineStateChangePending != 0)
8409 {
8410 /* ensureNoStateDependencies() is waiting for state dependencies to
8411 * drop to zero so don't add more. It may make sense to wait a bit
8412 * and retry before reporting an error (since the pending state
8413 * transition should be really quick) but let's just assert for
8414 * now to see if it ever happens on practice. */
8415
8416 AssertFailed();
8417
8418 return setError(E_ACCESSDENIED,
8419 tr("Machine state change is in progress. Please retry the operation later."));
8420 }
8421
8422 ++mData->mMachineStateDeps;
8423 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8424 }
8425
8426 if (aState)
8427 *aState = mData->mMachineState;
8428 if (aRegistered)
8429 *aRegistered = mData->mRegistered;
8430
8431 return S_OK;
8432}
8433
8434/**
8435 * Decreases the number of objects dependent on the machine state.
8436 * Must always complete the #addStateDependency() call after the state
8437 * dependency is no more necessary.
8438 */
8439void Machine::releaseStateDependency()
8440{
8441 AutoCaller autoCaller(this);
8442 AssertComRCReturnVoid(autoCaller.rc());
8443
8444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8445
8446 /* releaseStateDependency() w/o addStateDependency()? */
8447 AssertReturnVoid(mData->mMachineStateDeps != 0);
8448 -- mData->mMachineStateDeps;
8449
8450 if (mData->mMachineStateDeps == 0)
8451 {
8452 /* inform ensureNoStateDependencies() that there are no more deps */
8453 if (mData->mMachineStateChangePending != 0)
8454 {
8455 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8456 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8457 }
8458 }
8459}
8460
8461Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8462{
8463 /* start with nothing found */
8464 Utf8Str strResult("");
8465
8466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8467
8468 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8469 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8470 // found:
8471 strResult = it->second; // source is a Utf8Str
8472
8473 return strResult;
8474}
8475
8476// protected methods
8477/////////////////////////////////////////////////////////////////////////////
8478
8479/**
8480 * Performs machine state checks based on the @a aDepType value. If a check
8481 * fails, this method will set extended error info, otherwise it will return
8482 * S_OK. It is supposed, that on failure, the caller will immediately return
8483 * the return value of this method to the upper level.
8484 *
8485 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8486 *
8487 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8488 * current state of this machine object allows to change settings of the
8489 * machine (i.e. the machine is not registered, or registered but not running
8490 * and not saved). It is useful to call this method from Machine setters
8491 * before performing any change.
8492 *
8493 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8494 * as for MutableStateDep except that if the machine is saved, S_OK is also
8495 * returned. This is useful in setters which allow changing machine
8496 * properties when it is in the saved state.
8497 *
8498 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8499 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8500 * Aborted).
8501 *
8502 * @param aDepType Dependency type to check.
8503 *
8504 * @note Non Machine based classes should use #addStateDependency() and
8505 * #releaseStateDependency() methods or the smart AutoStateDependency
8506 * template.
8507 *
8508 * @note This method must be called from under this object's read or write
8509 * lock.
8510 */
8511HRESULT Machine::checkStateDependency(StateDependency aDepType)
8512{
8513 switch (aDepType)
8514 {
8515 case AnyStateDep:
8516 {
8517 break;
8518 }
8519 case MutableStateDep:
8520 {
8521 if ( mData->mRegistered
8522 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8523 || ( mData->mMachineState != MachineState_Paused
8524 && mData->mMachineState != MachineState_Running
8525 && mData->mMachineState != MachineState_Aborted
8526 && mData->mMachineState != MachineState_Teleported
8527 && mData->mMachineState != MachineState_PoweredOff
8528 )
8529 )
8530 )
8531 return setError(VBOX_E_INVALID_VM_STATE,
8532 tr("The machine is not mutable (state is %s)"),
8533 Global::stringifyMachineState(mData->mMachineState));
8534 break;
8535 }
8536 case MutableOrSavedStateDep:
8537 {
8538 if ( mData->mRegistered
8539 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8540 || ( mData->mMachineState != MachineState_Paused
8541 && mData->mMachineState != MachineState_Running
8542 && mData->mMachineState != MachineState_Aborted
8543 && mData->mMachineState != MachineState_Teleported
8544 && mData->mMachineState != MachineState_Saved
8545 && mData->mMachineState != MachineState_PoweredOff
8546 )
8547 )
8548 )
8549 return setError(VBOX_E_INVALID_VM_STATE,
8550 tr("The machine is not mutable (state is %s)"),
8551 Global::stringifyMachineState(mData->mMachineState));
8552 break;
8553 }
8554 case OfflineStateDep:
8555 {
8556 if ( mData->mRegistered
8557 && ( !isSessionMachine()
8558 || ( mData->mMachineState != MachineState_PoweredOff
8559 && mData->mMachineState != MachineState_Saved
8560 && mData->mMachineState != MachineState_Aborted
8561 && mData->mMachineState != MachineState_Teleported
8562 )
8563 )
8564 )
8565 return setError(VBOX_E_INVALID_VM_STATE,
8566 tr("The machine is not offline (state is %s)"),
8567 Global::stringifyMachineState(mData->mMachineState));
8568 break;
8569 }
8570 }
8571
8572 return S_OK;
8573}
8574
8575/**
8576 * Helper to initialize all associated child objects and allocate data
8577 * structures.
8578 *
8579 * This method must be called as a part of the object's initialization procedure
8580 * (usually done in the #init() method).
8581 *
8582 * @note Must be called only from #init() or from #registeredInit().
8583 */
8584HRESULT Machine::initDataAndChildObjects()
8585{
8586 AutoCaller autoCaller(this);
8587 AssertComRCReturnRC(autoCaller.rc());
8588 AssertComRCReturn(autoCaller.state() == InInit ||
8589 autoCaller.state() == Limited, E_FAIL);
8590
8591 AssertReturn(!mData->mAccessible, E_FAIL);
8592
8593 /* allocate data structures */
8594 mSSData.allocate();
8595 mUserData.allocate();
8596 mHWData.allocate();
8597 mMediaData.allocate();
8598 mStorageControllers.allocate();
8599 mUSBControllers.allocate();
8600
8601 /* initialize mOSTypeId */
8602 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8603
8604 /* create associated BIOS settings object */
8605 unconst(mBIOSSettings).createObject();
8606 mBIOSSettings->init(this);
8607
8608 /* create an associated VRDE object (default is disabled) */
8609 unconst(mVRDEServer).createObject();
8610 mVRDEServer->init(this);
8611
8612 /* create associated serial port objects */
8613 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8614 {
8615 unconst(mSerialPorts[slot]).createObject();
8616 mSerialPorts[slot]->init(this, slot);
8617 }
8618
8619 /* create associated parallel port objects */
8620 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8621 {
8622 unconst(mParallelPorts[slot]).createObject();
8623 mParallelPorts[slot]->init(this, slot);
8624 }
8625
8626 /* create the audio adapter object (always present, default is disabled) */
8627 unconst(mAudioAdapter).createObject();
8628 mAudioAdapter->init(this);
8629
8630 /* create the USB device filters object (always present) */
8631 unconst(mUSBDeviceFilters).createObject();
8632 mUSBDeviceFilters->init(this);
8633
8634 /* create associated network adapter objects */
8635 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8636 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8637 {
8638 unconst(mNetworkAdapters[slot]).createObject();
8639 mNetworkAdapters[slot]->init(this, slot);
8640 }
8641
8642 /* create the bandwidth control */
8643 unconst(mBandwidthControl).createObject();
8644 mBandwidthControl->init(this);
8645
8646 return S_OK;
8647}
8648
8649/**
8650 * Helper to uninitialize all associated child objects and to free all data
8651 * structures.
8652 *
8653 * This method must be called as a part of the object's uninitialization
8654 * procedure (usually done in the #uninit() method).
8655 *
8656 * @note Must be called only from #uninit() or from #registeredInit().
8657 */
8658void Machine::uninitDataAndChildObjects()
8659{
8660 AutoCaller autoCaller(this);
8661 AssertComRCReturnVoid(autoCaller.rc());
8662 AssertComRCReturnVoid( autoCaller.state() == InUninit
8663 || autoCaller.state() == Limited);
8664
8665 /* tell all our other child objects we've been uninitialized */
8666 if (mBandwidthControl)
8667 {
8668 mBandwidthControl->uninit();
8669 unconst(mBandwidthControl).setNull();
8670 }
8671
8672 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8673 {
8674 if (mNetworkAdapters[slot])
8675 {
8676 mNetworkAdapters[slot]->uninit();
8677 unconst(mNetworkAdapters[slot]).setNull();
8678 }
8679 }
8680
8681 if (mUSBDeviceFilters)
8682 {
8683 mUSBDeviceFilters->uninit();
8684 unconst(mUSBDeviceFilters).setNull();
8685 }
8686
8687 if (mAudioAdapter)
8688 {
8689 mAudioAdapter->uninit();
8690 unconst(mAudioAdapter).setNull();
8691 }
8692
8693 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8694 {
8695 if (mParallelPorts[slot])
8696 {
8697 mParallelPorts[slot]->uninit();
8698 unconst(mParallelPorts[slot]).setNull();
8699 }
8700 }
8701
8702 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8703 {
8704 if (mSerialPorts[slot])
8705 {
8706 mSerialPorts[slot]->uninit();
8707 unconst(mSerialPorts[slot]).setNull();
8708 }
8709 }
8710
8711 if (mVRDEServer)
8712 {
8713 mVRDEServer->uninit();
8714 unconst(mVRDEServer).setNull();
8715 }
8716
8717 if (mBIOSSettings)
8718 {
8719 mBIOSSettings->uninit();
8720 unconst(mBIOSSettings).setNull();
8721 }
8722
8723 /* Deassociate media (only when a real Machine or a SnapshotMachine
8724 * instance is uninitialized; SessionMachine instances refer to real
8725 * Machine media). This is necessary for a clean re-initialization of
8726 * the VM after successfully re-checking the accessibility state. Note
8727 * that in case of normal Machine or SnapshotMachine uninitialization (as
8728 * a result of unregistering or deleting the snapshot), outdated media
8729 * attachments will already be uninitialized and deleted, so this
8730 * code will not affect them. */
8731 if ( !!mMediaData
8732 && (!isSessionMachine())
8733 )
8734 {
8735 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8736 it != mMediaData->mAttachments.end();
8737 ++it)
8738 {
8739 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8740 if (pMedium.isNull())
8741 continue;
8742 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8743 AssertComRC(rc);
8744 }
8745 }
8746
8747 if (!isSessionMachine() && !isSnapshotMachine())
8748 {
8749 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8750 if (mData->mFirstSnapshot)
8751 {
8752 // snapshots tree is protected by machine write lock; strictly
8753 // this isn't necessary here since we're deleting the entire
8754 // machine, but otherwise we assert in Snapshot::uninit()
8755 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8756 mData->mFirstSnapshot->uninit();
8757 mData->mFirstSnapshot.setNull();
8758 }
8759
8760 mData->mCurrentSnapshot.setNull();
8761 }
8762
8763 /* free data structures (the essential mData structure is not freed here
8764 * since it may be still in use) */
8765 mMediaData.free();
8766 mStorageControllers.free();
8767 mUSBControllers.free();
8768 mHWData.free();
8769 mUserData.free();
8770 mSSData.free();
8771}
8772
8773/**
8774 * Returns a pointer to the Machine object for this machine that acts like a
8775 * parent for complex machine data objects such as shared folders, etc.
8776 *
8777 * For primary Machine objects and for SnapshotMachine objects, returns this
8778 * object's pointer itself. For SessionMachine objects, returns the peer
8779 * (primary) machine pointer.
8780 */
8781Machine* Machine::getMachine()
8782{
8783 if (isSessionMachine())
8784 return (Machine*)mPeer;
8785 return this;
8786}
8787
8788/**
8789 * Makes sure that there are no machine state dependents. If necessary, waits
8790 * for the number of dependents to drop to zero.
8791 *
8792 * Make sure this method is called from under this object's write lock to
8793 * guarantee that no new dependents may be added when this method returns
8794 * control to the caller.
8795 *
8796 * @note Locks this object for writing. The lock will be released while waiting
8797 * (if necessary).
8798 *
8799 * @warning To be used only in methods that change the machine state!
8800 */
8801void Machine::ensureNoStateDependencies()
8802{
8803 AssertReturnVoid(isWriteLockOnCurrentThread());
8804
8805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8806
8807 /* Wait for all state dependents if necessary */
8808 if (mData->mMachineStateDeps != 0)
8809 {
8810 /* lazy semaphore creation */
8811 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8812 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8813
8814 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8815 mData->mMachineStateDeps));
8816
8817 ++mData->mMachineStateChangePending;
8818
8819 /* reset the semaphore before waiting, the last dependent will signal
8820 * it */
8821 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8822
8823 alock.release();
8824
8825 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8826
8827 alock.acquire();
8828
8829 -- mData->mMachineStateChangePending;
8830 }
8831}
8832
8833/**
8834 * Changes the machine state and informs callbacks.
8835 *
8836 * This method is not intended to fail so it either returns S_OK or asserts (and
8837 * returns a failure).
8838 *
8839 * @note Locks this object for writing.
8840 */
8841HRESULT Machine::setMachineState(MachineState_T aMachineState)
8842{
8843 LogFlowThisFuncEnter();
8844 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8845
8846 AutoCaller autoCaller(this);
8847 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8848
8849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8850
8851 /* wait for state dependents to drop to zero */
8852 ensureNoStateDependencies();
8853
8854 if (mData->mMachineState != aMachineState)
8855 {
8856 mData->mMachineState = aMachineState;
8857
8858 RTTimeNow(&mData->mLastStateChange);
8859
8860 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8861 }
8862
8863 LogFlowThisFuncLeave();
8864 return S_OK;
8865}
8866
8867/**
8868 * Searches for a shared folder with the given logical name
8869 * in the collection of shared folders.
8870 *
8871 * @param aName logical name of the shared folder
8872 * @param aSharedFolder where to return the found object
8873 * @param aSetError whether to set the error info if the folder is
8874 * not found
8875 * @return
8876 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8877 *
8878 * @note
8879 * must be called from under the object's lock!
8880 */
8881HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8882 ComObjPtr<SharedFolder> &aSharedFolder,
8883 bool aSetError /* = false */)
8884{
8885 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8886 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8887 it != mHWData->mSharedFolders.end();
8888 ++it)
8889 {
8890 SharedFolder *pSF = *it;
8891 AutoCaller autoCaller(pSF);
8892 if (pSF->getName() == aName)
8893 {
8894 aSharedFolder = pSF;
8895 rc = S_OK;
8896 break;
8897 }
8898 }
8899
8900 if (aSetError && FAILED(rc))
8901 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8902
8903 return rc;
8904}
8905
8906/**
8907 * Initializes all machine instance data from the given settings structures
8908 * from XML. The exception is the machine UUID which needs special handling
8909 * depending on the caller's use case, so the caller needs to set that herself.
8910 *
8911 * This gets called in several contexts during machine initialization:
8912 *
8913 * -- When machine XML exists on disk already and needs to be loaded into memory,
8914 * for example, from registeredInit() to load all registered machines on
8915 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8916 * attached to the machine should be part of some media registry already.
8917 *
8918 * -- During OVF import, when a machine config has been constructed from an
8919 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8920 * ensure that the media listed as attachments in the config (which have
8921 * been imported from the OVF) receive the correct registry ID.
8922 *
8923 * -- During VM cloning.
8924 *
8925 * @param config Machine settings from XML.
8926 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8927 * @return
8928 */
8929HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8930 const Guid *puuidRegistry)
8931{
8932 // copy name, description, OS type, teleporter, UTC etc.
8933 mUserData->s = config.machineUserData;
8934
8935 // Decode the Icon overide data from config userdata and set onto Machine.
8936 #define DECODE_STR_MAX _1M
8937 const char* pszStr = config.machineUserData.ovIcon.c_str();
8938 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8939 if (cbOut > DECODE_STR_MAX)
8940 return setError(E_FAIL,
8941 tr("Icon Data too long.'%d' > '%d'"),
8942 cbOut,
8943 DECODE_STR_MAX);
8944 com::SafeArray<BYTE> iconByte(cbOut);
8945 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8946 if (FAILED(rc))
8947 return setError(E_FAIL,
8948 tr("Failure to Decode Icon Data. '%s' (%d)"),
8949 pszStr,
8950 rc);
8951 mUserData->mIcon.resize(iconByte.size());
8952 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
8953
8954 // look up the object by Id to check it is valid
8955 ComPtr<IGuestOSType> guestOSType;
8956 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8957 guestOSType.asOutParam());
8958 if (FAILED(rc)) return rc;
8959
8960 // stateFile (optional)
8961 if (config.strStateFile.isEmpty())
8962 mSSData->strStateFilePath.setNull();
8963 else
8964 {
8965 Utf8Str stateFilePathFull(config.strStateFile);
8966 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8967 if (RT_FAILURE(vrc))
8968 return setError(E_FAIL,
8969 tr("Invalid saved state file path '%s' (%Rrc)"),
8970 config.strStateFile.c_str(),
8971 vrc);
8972 mSSData->strStateFilePath = stateFilePathFull;
8973 }
8974
8975 // snapshot folder needs special processing so set it again
8976 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8977 if (FAILED(rc)) return rc;
8978
8979 /* Copy the extra data items (Not in any case config is already the same as
8980 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8981 * make sure the extra data map is copied). */
8982 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8983
8984 /* currentStateModified (optional, default is true) */
8985 mData->mCurrentStateModified = config.fCurrentStateModified;
8986
8987 mData->mLastStateChange = config.timeLastStateChange;
8988
8989 /*
8990 * note: all mUserData members must be assigned prior this point because
8991 * we need to commit changes in order to let mUserData be shared by all
8992 * snapshot machine instances.
8993 */
8994 mUserData.commitCopy();
8995
8996 // machine registry, if present (must be loaded before snapshots)
8997 if (config.canHaveOwnMediaRegistry())
8998 {
8999 // determine machine folder
9000 Utf8Str strMachineFolder = getSettingsFileFull();
9001 strMachineFolder.stripFilename();
9002 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
9003 config.mediaRegistry,
9004 strMachineFolder);
9005 if (FAILED(rc)) return rc;
9006 }
9007
9008 /* Snapshot node (optional) */
9009 size_t cRootSnapshots;
9010 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9011 {
9012 // there must be only one root snapshot
9013 Assert(cRootSnapshots == 1);
9014
9015 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9016
9017 rc = loadSnapshot(snap,
9018 config.uuidCurrentSnapshot,
9019 NULL); // no parent == first snapshot
9020 if (FAILED(rc)) return rc;
9021 }
9022
9023 // hardware data
9024 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9025 if (FAILED(rc)) return rc;
9026
9027 // load storage controllers
9028 rc = loadStorageControllers(config.storageMachine,
9029 puuidRegistry,
9030 NULL /* puuidSnapshot */);
9031 if (FAILED(rc)) return rc;
9032
9033 /*
9034 * NOTE: the assignment below must be the last thing to do,
9035 * otherwise it will be not possible to change the settings
9036 * somewhere in the code above because all setters will be
9037 * blocked by checkStateDependency(MutableStateDep).
9038 */
9039
9040 /* set the machine state to Aborted or Saved when appropriate */
9041 if (config.fAborted)
9042 {
9043 mSSData->strStateFilePath.setNull();
9044
9045 /* no need to use setMachineState() during init() */
9046 mData->mMachineState = MachineState_Aborted;
9047 }
9048 else if (!mSSData->strStateFilePath.isEmpty())
9049 {
9050 /* no need to use setMachineState() during init() */
9051 mData->mMachineState = MachineState_Saved;
9052 }
9053
9054 // after loading settings, we are no longer different from the XML on disk
9055 mData->flModifications = 0;
9056
9057 return S_OK;
9058}
9059
9060/**
9061 * Recursively loads all snapshots starting from the given.
9062 *
9063 * @param aNode <Snapshot> node.
9064 * @param aCurSnapshotId Current snapshot ID from the settings file.
9065 * @param aParentSnapshot Parent snapshot.
9066 */
9067HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
9068 const Guid &aCurSnapshotId,
9069 Snapshot *aParentSnapshot)
9070{
9071 AssertReturn(!isSnapshotMachine(), E_FAIL);
9072 AssertReturn(!isSessionMachine(), E_FAIL);
9073
9074 HRESULT rc = S_OK;
9075
9076 Utf8Str strStateFile;
9077 if (!data.strStateFile.isEmpty())
9078 {
9079 /* optional */
9080 strStateFile = data.strStateFile;
9081 int vrc = calculateFullPath(strStateFile, strStateFile);
9082 if (RT_FAILURE(vrc))
9083 return setError(E_FAIL,
9084 tr("Invalid saved state file path '%s' (%Rrc)"),
9085 strStateFile.c_str(),
9086 vrc);
9087 }
9088
9089 /* create a snapshot machine object */
9090 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9091 pSnapshotMachine.createObject();
9092 rc = pSnapshotMachine->initFromSettings(this,
9093 data.hardware,
9094 &data.debugging,
9095 &data.autostart,
9096 data.storage,
9097 data.uuid.ref(),
9098 strStateFile);
9099 if (FAILED(rc)) return rc;
9100
9101 /* create a snapshot object */
9102 ComObjPtr<Snapshot> pSnapshot;
9103 pSnapshot.createObject();
9104 /* initialize the snapshot */
9105 rc = pSnapshot->init(mParent, // VirtualBox object
9106 data.uuid,
9107 data.strName,
9108 data.strDescription,
9109 data.timestamp,
9110 pSnapshotMachine,
9111 aParentSnapshot);
9112 if (FAILED(rc)) return rc;
9113
9114 /* memorize the first snapshot if necessary */
9115 if (!mData->mFirstSnapshot)
9116 mData->mFirstSnapshot = pSnapshot;
9117
9118 /* memorize the current snapshot when appropriate */
9119 if ( !mData->mCurrentSnapshot
9120 && pSnapshot->getId() == aCurSnapshotId
9121 )
9122 mData->mCurrentSnapshot = pSnapshot;
9123
9124 // now create the children
9125 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
9126 it != data.llChildSnapshots.end();
9127 ++it)
9128 {
9129 const settings::Snapshot &childData = *it;
9130 // recurse
9131 rc = loadSnapshot(childData,
9132 aCurSnapshotId,
9133 pSnapshot); // parent = the one we created above
9134 if (FAILED(rc)) return rc;
9135 }
9136
9137 return rc;
9138}
9139
9140/**
9141 * Loads settings into mHWData.
9142 *
9143 * @param data Reference to the hardware settings.
9144 * @param pDbg Pointer to the debugging settings.
9145 * @param pAutostart Pointer to the autostart settings.
9146 */
9147HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
9148 const settings::Autostart *pAutostart)
9149{
9150 AssertReturn(!isSessionMachine(), E_FAIL);
9151
9152 HRESULT rc = S_OK;
9153
9154 try
9155 {
9156 /* The hardware version attribute (optional). */
9157 mHWData->mHWVersion = data.strVersion;
9158 mHWData->mHardwareUUID = data.uuid;
9159
9160 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9161 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
9162 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9163 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9164 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9165 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9166 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9167 mHWData->mPAEEnabled = data.fPAE;
9168 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9169 mHWData->mLongMode = data.enmLongMode;
9170 mHWData->mCPUCount = data.cCPUs;
9171 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9172 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9173
9174 // cpu
9175 if (mHWData->mCPUHotPlugEnabled)
9176 {
9177 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9178 it != data.llCpus.end();
9179 ++it)
9180 {
9181 const settings::Cpu &cpu = *it;
9182
9183 mHWData->mCPUAttached[cpu.ulId] = true;
9184 }
9185 }
9186
9187 // cpuid leafs
9188 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9189 it != data.llCpuIdLeafs.end();
9190 ++it)
9191 {
9192 const settings::CpuIdLeaf &leaf = *it;
9193
9194 switch (leaf.ulId)
9195 {
9196 case 0x0:
9197 case 0x1:
9198 case 0x2:
9199 case 0x3:
9200 case 0x4:
9201 case 0x5:
9202 case 0x6:
9203 case 0x7:
9204 case 0x8:
9205 case 0x9:
9206 case 0xA:
9207 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9208 break;
9209
9210 case 0x80000000:
9211 case 0x80000001:
9212 case 0x80000002:
9213 case 0x80000003:
9214 case 0x80000004:
9215 case 0x80000005:
9216 case 0x80000006:
9217 case 0x80000007:
9218 case 0x80000008:
9219 case 0x80000009:
9220 case 0x8000000A:
9221 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9222 break;
9223
9224 default:
9225 /* just ignore */
9226 break;
9227 }
9228 }
9229
9230 mHWData->mMemorySize = data.ulMemorySizeMB;
9231 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9232
9233 // boot order
9234 for (size_t i = 0;
9235 i < RT_ELEMENTS(mHWData->mBootOrder);
9236 i++)
9237 {
9238 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9239 if (it == data.mapBootOrder.end())
9240 mHWData->mBootOrder[i] = DeviceType_Null;
9241 else
9242 mHWData->mBootOrder[i] = it->second;
9243 }
9244
9245 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9246 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9247 mHWData->mMonitorCount = data.cMonitors;
9248 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9249 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9250 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9251 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9252 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9253 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9254 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9255 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9256 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9257 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9258 if (!data.strVideoCaptureFile.isEmpty())
9259 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9260 else
9261 mHWData->mVideoCaptureFile.setNull();
9262 mHWData->mFirmwareType = data.firmwareType;
9263 mHWData->mPointingHIDType = data.pointingHIDType;
9264 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9265 mHWData->mChipsetType = data.chipsetType;
9266 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
9267 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9268 mHWData->mHPETEnabled = data.fHPETEnabled;
9269
9270 /* VRDEServer */
9271 rc = mVRDEServer->loadSettings(data.vrdeSettings);
9272 if (FAILED(rc)) return rc;
9273
9274 /* BIOS */
9275 rc = mBIOSSettings->loadSettings(data.biosSettings);
9276 if (FAILED(rc)) return rc;
9277
9278 // Bandwidth control (must come before network adapters)
9279 rc = mBandwidthControl->loadSettings(data.ioSettings);
9280 if (FAILED(rc)) return rc;
9281
9282 /* Shared folders */
9283 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9284 it != data.usbSettings.llUSBControllers.end();
9285 ++it)
9286 {
9287 const settings::USBController &settingsCtrl = *it;
9288 ComObjPtr<USBController> newCtrl;
9289
9290 newCtrl.createObject();
9291 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9292 mUSBControllers->push_back(newCtrl);
9293 }
9294
9295 /* USB device filters */
9296 rc = mUSBDeviceFilters->loadSettings(data.usbSettings);
9297 if (FAILED(rc)) return rc;
9298
9299 // network adapters
9300 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9301 uint32_t oldCount = mNetworkAdapters.size();
9302 if (newCount > oldCount)
9303 {
9304 mNetworkAdapters.resize(newCount);
9305 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9306 {
9307 unconst(mNetworkAdapters[slot]).createObject();
9308 mNetworkAdapters[slot]->init(this, slot);
9309 }
9310 }
9311 else if (newCount < oldCount)
9312 mNetworkAdapters.resize(newCount);
9313 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9314 it != data.llNetworkAdapters.end();
9315 ++it)
9316 {
9317 const settings::NetworkAdapter &nic = *it;
9318
9319 /* slot unicity is guaranteed by XML Schema */
9320 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9321 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9322 if (FAILED(rc)) return rc;
9323 }
9324
9325 // serial ports
9326 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9327 it != data.llSerialPorts.end();
9328 ++it)
9329 {
9330 const settings::SerialPort &s = *it;
9331
9332 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9333 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
9334 if (FAILED(rc)) return rc;
9335 }
9336
9337 // parallel ports (optional)
9338 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9339 it != data.llParallelPorts.end();
9340 ++it)
9341 {
9342 const settings::ParallelPort &p = *it;
9343
9344 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9345 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9346 if (FAILED(rc)) return rc;
9347 }
9348
9349 /* AudioAdapter */
9350 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9351 if (FAILED(rc)) return rc;
9352
9353 /* Shared folders */
9354 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9355 it != data.llSharedFolders.end();
9356 ++it)
9357 {
9358 const settings::SharedFolder &sf = *it;
9359
9360 ComObjPtr<SharedFolder> sharedFolder;
9361 /* Check for double entries. Not allowed! */
9362 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9363 if (SUCCEEDED(rc))
9364 return setError(VBOX_E_OBJECT_IN_USE,
9365 tr("Shared folder named '%s' already exists"),
9366 sf.strName.c_str());
9367
9368 /* Create the new shared folder. Don't break on error. This will be
9369 * reported when the machine starts. */
9370 sharedFolder.createObject();
9371 rc = sharedFolder->init(getMachine(),
9372 sf.strName,
9373 sf.strHostPath,
9374 RT_BOOL(sf.fWritable),
9375 RT_BOOL(sf.fAutoMount),
9376 false /* fFailOnError */);
9377 if (FAILED(rc)) return rc;
9378 mHWData->mSharedFolders.push_back(sharedFolder);
9379 }
9380
9381 // Clipboard
9382 mHWData->mClipboardMode = data.clipboardMode;
9383
9384 // drag'n'drop
9385 mHWData->mDragAndDropMode = data.dragAndDropMode;
9386
9387 // guest settings
9388 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9389
9390 // IO settings
9391 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9392 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9393
9394 // Host PCI devices
9395 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9396 it != data.pciAttachments.end();
9397 ++it)
9398 {
9399 const settings::HostPCIDeviceAttachment &hpda = *it;
9400 ComObjPtr<PCIDeviceAttachment> pda;
9401
9402 pda.createObject();
9403 pda->loadSettings(this, hpda);
9404 mHWData->mPCIDeviceAssignments.push_back(pda);
9405 }
9406
9407 /*
9408 * (The following isn't really real hardware, but it lives in HWData
9409 * for reasons of convenience.)
9410 */
9411
9412#ifdef VBOX_WITH_GUEST_PROPS
9413 /* Guest properties (optional) */
9414 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9415 it != data.llGuestProperties.end();
9416 ++it)
9417 {
9418 const settings::GuestProperty &prop = *it;
9419 uint32_t fFlags = guestProp::NILFLAG;
9420 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9421 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9422 mHWData->mGuestProperties[prop.strName] = property;
9423 }
9424
9425 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9426#endif /* VBOX_WITH_GUEST_PROPS defined */
9427
9428 rc = loadDebugging(pDbg);
9429 if (FAILED(rc))
9430 return rc;
9431
9432 mHWData->mAutostart = *pAutostart;
9433
9434 /* default frontend */
9435 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9436 }
9437 catch(std::bad_alloc &)
9438 {
9439 return E_OUTOFMEMORY;
9440 }
9441
9442 AssertComRC(rc);
9443 return rc;
9444}
9445
9446/**
9447 * Called from Machine::loadHardware() to load the debugging settings of the
9448 * machine.
9449 *
9450 * @param pDbg Pointer to the settings.
9451 */
9452HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9453{
9454 mHWData->mDebugging = *pDbg;
9455 /* no more processing currently required, this will probably change. */
9456 return S_OK;
9457}
9458
9459/**
9460 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9461 *
9462 * @param data
9463 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9464 * @param puuidSnapshot
9465 * @return
9466 */
9467HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9468 const Guid *puuidRegistry,
9469 const Guid *puuidSnapshot)
9470{
9471 AssertReturn(!isSessionMachine(), E_FAIL);
9472
9473 HRESULT rc = S_OK;
9474
9475 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9476 it != data.llStorageControllers.end();
9477 ++it)
9478 {
9479 const settings::StorageController &ctlData = *it;
9480
9481 ComObjPtr<StorageController> pCtl;
9482 /* Try to find one with the name first. */
9483 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9484 if (SUCCEEDED(rc))
9485 return setError(VBOX_E_OBJECT_IN_USE,
9486 tr("Storage controller named '%s' already exists"),
9487 ctlData.strName.c_str());
9488
9489 pCtl.createObject();
9490 rc = pCtl->init(this,
9491 ctlData.strName,
9492 ctlData.storageBus,
9493 ctlData.ulInstance,
9494 ctlData.fBootable);
9495 if (FAILED(rc)) return rc;
9496
9497 mStorageControllers->push_back(pCtl);
9498
9499 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9500 if (FAILED(rc)) return rc;
9501
9502 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9503 if (FAILED(rc)) return rc;
9504
9505 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9506 if (FAILED(rc)) return rc;
9507
9508 /* Set IDE emulation settings (only for AHCI controller). */
9509 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9510 {
9511 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9512 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9513 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9514 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9515 )
9516 return rc;
9517 }
9518
9519 /* Load the attached devices now. */
9520 rc = loadStorageDevices(pCtl,
9521 ctlData,
9522 puuidRegistry,
9523 puuidSnapshot);
9524 if (FAILED(rc)) return rc;
9525 }
9526
9527 return S_OK;
9528}
9529
9530/**
9531 * Called from loadStorageControllers for a controller's devices.
9532 *
9533 * @param aStorageController
9534 * @param data
9535 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9536 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9537 * @return
9538 */
9539HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9540 const settings::StorageController &data,
9541 const Guid *puuidRegistry,
9542 const Guid *puuidSnapshot)
9543{
9544 HRESULT rc = S_OK;
9545
9546 /* paranoia: detect duplicate attachments */
9547 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9548 it != data.llAttachedDevices.end();
9549 ++it)
9550 {
9551 const settings::AttachedDevice &ad = *it;
9552
9553 for (settings::AttachedDevicesList::const_iterator it2 = it;
9554 it2 != data.llAttachedDevices.end();
9555 ++it2)
9556 {
9557 if (it == it2)
9558 continue;
9559
9560 const settings::AttachedDevice &ad2 = *it2;
9561
9562 if ( ad.lPort == ad2.lPort
9563 && ad.lDevice == ad2.lDevice)
9564 {
9565 return setError(E_FAIL,
9566 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9567 aStorageController->getName().c_str(),
9568 ad.lPort,
9569 ad.lDevice,
9570 mUserData->s.strName.c_str());
9571 }
9572 }
9573 }
9574
9575 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9576 it != data.llAttachedDevices.end();
9577 ++it)
9578 {
9579 const settings::AttachedDevice &dev = *it;
9580 ComObjPtr<Medium> medium;
9581
9582 switch (dev.deviceType)
9583 {
9584 case DeviceType_Floppy:
9585 case DeviceType_DVD:
9586 if (dev.strHostDriveSrc.isNotEmpty())
9587 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9588 else
9589 rc = mParent->findRemoveableMedium(dev.deviceType,
9590 dev.uuid,
9591 false /* fRefresh */,
9592 false /* aSetError */,
9593 medium);
9594 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9595 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9596 rc = S_OK;
9597 break;
9598
9599 case DeviceType_HardDisk:
9600 {
9601 /* find a hard disk by UUID */
9602 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9603 if (FAILED(rc))
9604 {
9605 if (isSnapshotMachine())
9606 {
9607 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9608 // so the user knows that the bad disk is in a snapshot somewhere
9609 com::ErrorInfo info;
9610 return setError(E_FAIL,
9611 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9612 puuidSnapshot->raw(),
9613 info.getText().raw());
9614 }
9615 else
9616 return rc;
9617 }
9618
9619 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9620
9621 if (medium->getType() == MediumType_Immutable)
9622 {
9623 if (isSnapshotMachine())
9624 return setError(E_FAIL,
9625 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9626 "of the virtual machine '%s' ('%s')"),
9627 medium->getLocationFull().c_str(),
9628 dev.uuid.raw(),
9629 puuidSnapshot->raw(),
9630 mUserData->s.strName.c_str(),
9631 mData->m_strConfigFileFull.c_str());
9632
9633 return setError(E_FAIL,
9634 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9635 medium->getLocationFull().c_str(),
9636 dev.uuid.raw(),
9637 mUserData->s.strName.c_str(),
9638 mData->m_strConfigFileFull.c_str());
9639 }
9640
9641 if (medium->getType() == MediumType_MultiAttach)
9642 {
9643 if (isSnapshotMachine())
9644 return setError(E_FAIL,
9645 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9646 "of the virtual machine '%s' ('%s')"),
9647 medium->getLocationFull().c_str(),
9648 dev.uuid.raw(),
9649 puuidSnapshot->raw(),
9650 mUserData->s.strName.c_str(),
9651 mData->m_strConfigFileFull.c_str());
9652
9653 return setError(E_FAIL,
9654 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9655 medium->getLocationFull().c_str(),
9656 dev.uuid.raw(),
9657 mUserData->s.strName.c_str(),
9658 mData->m_strConfigFileFull.c_str());
9659 }
9660
9661 if ( !isSnapshotMachine()
9662 && medium->getChildren().size() != 0
9663 )
9664 return setError(E_FAIL,
9665 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9666 "because it has %d differencing child hard disks"),
9667 medium->getLocationFull().c_str(),
9668 dev.uuid.raw(),
9669 mUserData->s.strName.c_str(),
9670 mData->m_strConfigFileFull.c_str(),
9671 medium->getChildren().size());
9672
9673 if (findAttachment(mMediaData->mAttachments,
9674 medium))
9675 return setError(E_FAIL,
9676 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9677 medium->getLocationFull().c_str(),
9678 dev.uuid.raw(),
9679 mUserData->s.strName.c_str(),
9680 mData->m_strConfigFileFull.c_str());
9681
9682 break;
9683 }
9684
9685 default:
9686 return setError(E_FAIL,
9687 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9688 medium->getLocationFull().c_str(),
9689 mUserData->s.strName.c_str(),
9690 mData->m_strConfigFileFull.c_str());
9691 }
9692
9693 if (FAILED(rc))
9694 break;
9695
9696 /* Bandwidth groups are loaded at this point. */
9697 ComObjPtr<BandwidthGroup> pBwGroup;
9698
9699 if (!dev.strBwGroup.isEmpty())
9700 {
9701 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9702 if (FAILED(rc))
9703 return setError(E_FAIL,
9704 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9705 medium->getLocationFull().c_str(),
9706 dev.strBwGroup.c_str(),
9707 mUserData->s.strName.c_str(),
9708 mData->m_strConfigFileFull.c_str());
9709 pBwGroup->reference();
9710 }
9711
9712 const Bstr controllerName = aStorageController->getName();
9713 ComObjPtr<MediumAttachment> pAttachment;
9714 pAttachment.createObject();
9715 rc = pAttachment->init(this,
9716 medium,
9717 controllerName,
9718 dev.lPort,
9719 dev.lDevice,
9720 dev.deviceType,
9721 false,
9722 dev.fPassThrough,
9723 dev.fTempEject,
9724 dev.fNonRotational,
9725 dev.fDiscard,
9726 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9727 if (FAILED(rc)) break;
9728
9729 /* associate the medium with this machine and snapshot */
9730 if (!medium.isNull())
9731 {
9732 AutoCaller medCaller(medium);
9733 if (FAILED(medCaller.rc())) return medCaller.rc();
9734 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9735
9736 if (isSnapshotMachine())
9737 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9738 else
9739 rc = medium->addBackReference(mData->mUuid);
9740 /* If the medium->addBackReference fails it sets an appropriate
9741 * error message, so no need to do any guesswork here. */
9742
9743 if (puuidRegistry)
9744 // caller wants registry ID to be set on all attached media (OVF import case)
9745 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9746 }
9747
9748 if (FAILED(rc))
9749 break;
9750
9751 /* back up mMediaData to let registeredInit() properly rollback on failure
9752 * (= limited accessibility) */
9753 setModified(IsModified_Storage);
9754 mMediaData.backup();
9755 mMediaData->mAttachments.push_back(pAttachment);
9756 }
9757
9758 return rc;
9759}
9760
9761/**
9762 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9763 *
9764 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9765 * @param aSnapshot where to return the found snapshot
9766 * @param aSetError true to set extended error info on failure
9767 */
9768HRESULT Machine::findSnapshotById(const Guid &aId,
9769 ComObjPtr<Snapshot> &aSnapshot,
9770 bool aSetError /* = false */)
9771{
9772 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9773
9774 if (!mData->mFirstSnapshot)
9775 {
9776 if (aSetError)
9777 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9778 return E_FAIL;
9779 }
9780
9781 if (aId.isZero())
9782 aSnapshot = mData->mFirstSnapshot;
9783 else
9784 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9785
9786 if (!aSnapshot)
9787 {
9788 if (aSetError)
9789 return setError(E_FAIL,
9790 tr("Could not find a snapshot with UUID {%s}"),
9791 aId.toString().c_str());
9792 return E_FAIL;
9793 }
9794
9795 return S_OK;
9796}
9797
9798/**
9799 * Returns the snapshot with the given name or fails of no such snapshot.
9800 *
9801 * @param aName snapshot name to find
9802 * @param aSnapshot where to return the found snapshot
9803 * @param aSetError true to set extended error info on failure
9804 */
9805HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9806 ComObjPtr<Snapshot> &aSnapshot,
9807 bool aSetError /* = false */)
9808{
9809 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9810
9811 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9812
9813 if (!mData->mFirstSnapshot)
9814 {
9815 if (aSetError)
9816 return setError(VBOX_E_OBJECT_NOT_FOUND,
9817 tr("This machine does not have any snapshots"));
9818 return VBOX_E_OBJECT_NOT_FOUND;
9819 }
9820
9821 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9822
9823 if (!aSnapshot)
9824 {
9825 if (aSetError)
9826 return setError(VBOX_E_OBJECT_NOT_FOUND,
9827 tr("Could not find a snapshot named '%s'"), strName.c_str());
9828 return VBOX_E_OBJECT_NOT_FOUND;
9829 }
9830
9831 return S_OK;
9832}
9833
9834/**
9835 * Returns a storage controller object with the given name.
9836 *
9837 * @param aName storage controller name to find
9838 * @param aStorageController where to return the found storage controller
9839 * @param aSetError true to set extended error info on failure
9840 */
9841HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9842 ComObjPtr<StorageController> &aStorageController,
9843 bool aSetError /* = false */)
9844{
9845 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9846
9847 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9848 it != mStorageControllers->end();
9849 ++it)
9850 {
9851 if ((*it)->getName() == aName)
9852 {
9853 aStorageController = (*it);
9854 return S_OK;
9855 }
9856 }
9857
9858 if (aSetError)
9859 return setError(VBOX_E_OBJECT_NOT_FOUND,
9860 tr("Could not find a storage controller named '%s'"),
9861 aName.c_str());
9862 return VBOX_E_OBJECT_NOT_FOUND;
9863}
9864
9865/**
9866 * Returns a USB controller object with the given name.
9867 *
9868 * @param aName USB controller name to find
9869 * @param aUSBController where to return the found USB controller
9870 * @param aSetError true to set extended error info on failure
9871 */
9872HRESULT Machine::getUSBControllerByName(const Utf8Str &aName,
9873 ComObjPtr<USBController> &aUSBController,
9874 bool aSetError /* = false */)
9875{
9876 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9877
9878 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9879 it != mUSBControllers->end();
9880 ++it)
9881 {
9882 if ((*it)->getName() == aName)
9883 {
9884 aUSBController = (*it);
9885 return S_OK;
9886 }
9887 }
9888
9889 if (aSetError)
9890 return setError(VBOX_E_OBJECT_NOT_FOUND,
9891 tr("Could not find a storage controller named '%s'"),
9892 aName.c_str());
9893 return VBOX_E_OBJECT_NOT_FOUND;
9894}
9895
9896/**
9897 * Returns the number of USB controller instance of the given type.
9898 *
9899 * @param enmType USB controller type.
9900 */
9901ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType)
9902{
9903 ULONG cCtrls = 0;
9904
9905 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9906 it != mUSBControllers->end();
9907 ++it)
9908 {
9909 if ((*it)->getControllerType() == enmType)
9910 cCtrls++;
9911 }
9912
9913 return cCtrls;
9914}
9915
9916HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9917 MediaData::AttachmentList &atts)
9918{
9919 AutoCaller autoCaller(this);
9920 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9921
9922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9923
9924 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9925 it != mMediaData->mAttachments.end();
9926 ++it)
9927 {
9928 const ComObjPtr<MediumAttachment> &pAtt = *it;
9929
9930 // should never happen, but deal with NULL pointers in the list.
9931 AssertStmt(!pAtt.isNull(), continue);
9932
9933 // getControllerName() needs caller+read lock
9934 AutoCaller autoAttCaller(pAtt);
9935 if (FAILED(autoAttCaller.rc()))
9936 {
9937 atts.clear();
9938 return autoAttCaller.rc();
9939 }
9940 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9941
9942 if (pAtt->getControllerName() == aName)
9943 atts.push_back(pAtt);
9944 }
9945
9946 return S_OK;
9947}
9948
9949/**
9950 * Helper for #saveSettings. Cares about renaming the settings directory and
9951 * file if the machine name was changed and about creating a new settings file
9952 * if this is a new machine.
9953 *
9954 * @note Must be never called directly but only from #saveSettings().
9955 */
9956HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9957{
9958 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9959
9960 HRESULT rc = S_OK;
9961
9962 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9963
9964 /// @todo need to handle primary group change, too
9965
9966 /* attempt to rename the settings file if machine name is changed */
9967 if ( mUserData->s.fNameSync
9968 && mUserData.isBackedUp()
9969 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9970 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9971 )
9972 {
9973 bool dirRenamed = false;
9974 bool fileRenamed = false;
9975
9976 Utf8Str configFile, newConfigFile;
9977 Utf8Str configFilePrev, newConfigFilePrev;
9978 Utf8Str configDir, newConfigDir;
9979
9980 do
9981 {
9982 int vrc = VINF_SUCCESS;
9983
9984 Utf8Str name = mUserData.backedUpData()->s.strName;
9985 Utf8Str newName = mUserData->s.strName;
9986 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9987 if (group == "/")
9988 group.setNull();
9989 Utf8Str newGroup = mUserData->s.llGroups.front();
9990 if (newGroup == "/")
9991 newGroup.setNull();
9992
9993 configFile = mData->m_strConfigFileFull;
9994
9995 /* first, rename the directory if it matches the group and machine name */
9996 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9997 group.c_str(), RTPATH_DELIMITER, name.c_str());
9998 /** @todo hack, make somehow use of ComposeMachineFilename */
9999 if (mUserData->s.fDirectoryIncludesUUID)
10000 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10001 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
10002 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10003 /** @todo hack, make somehow use of ComposeMachineFilename */
10004 if (mUserData->s.fDirectoryIncludesUUID)
10005 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10006 configDir = configFile;
10007 configDir.stripFilename();
10008 newConfigDir = configDir;
10009 if ( configDir.length() >= groupPlusName.length()
10010 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
10011 {
10012 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10013 Utf8Str newConfigBaseDir(newConfigDir);
10014 newConfigDir.append(newGroupPlusName);
10015 /* consistency: use \ if appropriate on the platform */
10016 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10017 /* new dir and old dir cannot be equal here because of 'if'
10018 * above and because name != newName */
10019 Assert(configDir != newConfigDir);
10020 if (!fSettingsFileIsNew)
10021 {
10022 /* perform real rename only if the machine is not new */
10023 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10024 if ( vrc == VERR_FILE_NOT_FOUND
10025 || vrc == VERR_PATH_NOT_FOUND)
10026 {
10027 /* create the parent directory, then retry renaming */
10028 Utf8Str parent(newConfigDir);
10029 parent.stripFilename();
10030 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10031 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10032 }
10033 if (RT_FAILURE(vrc))
10034 {
10035 rc = setError(E_FAIL,
10036 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10037 configDir.c_str(),
10038 newConfigDir.c_str(),
10039 vrc);
10040 break;
10041 }
10042 /* delete subdirectories which are no longer needed */
10043 Utf8Str dir(configDir);
10044 dir.stripFilename();
10045 while (dir != newConfigBaseDir && dir != ".")
10046 {
10047 vrc = RTDirRemove(dir.c_str());
10048 if (RT_FAILURE(vrc))
10049 break;
10050 dir.stripFilename();
10051 }
10052 dirRenamed = true;
10053 }
10054 }
10055
10056 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10057 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10058
10059 /* then try to rename the settings file itself */
10060 if (newConfigFile != configFile)
10061 {
10062 /* get the path to old settings file in renamed directory */
10063 configFile = Utf8StrFmt("%s%c%s",
10064 newConfigDir.c_str(),
10065 RTPATH_DELIMITER,
10066 RTPathFilename(configFile.c_str()));
10067 if (!fSettingsFileIsNew)
10068 {
10069 /* perform real rename only if the machine is not new */
10070 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10071 if (RT_FAILURE(vrc))
10072 {
10073 rc = setError(E_FAIL,
10074 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10075 configFile.c_str(),
10076 newConfigFile.c_str(),
10077 vrc);
10078 break;
10079 }
10080 fileRenamed = true;
10081 configFilePrev = configFile;
10082 configFilePrev += "-prev";
10083 newConfigFilePrev = newConfigFile;
10084 newConfigFilePrev += "-prev";
10085 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10086 }
10087 }
10088
10089 // update m_strConfigFileFull amd mConfigFile
10090 mData->m_strConfigFileFull = newConfigFile;
10091 // compute the relative path too
10092 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10093
10094 // store the old and new so that VirtualBox::saveSettings() can update
10095 // the media registry
10096 if ( mData->mRegistered
10097 && configDir != newConfigDir)
10098 {
10099 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
10100
10101 if (pfNeedsGlobalSaveSettings)
10102 *pfNeedsGlobalSaveSettings = true;
10103 }
10104
10105 // in the saved state file path, replace the old directory with the new directory
10106 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10107 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
10108
10109 // and do the same thing for the saved state file paths of all the online snapshots
10110 if (mData->mFirstSnapshot)
10111 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
10112 newConfigDir.c_str());
10113 }
10114 while (0);
10115
10116 if (FAILED(rc))
10117 {
10118 /* silently try to rename everything back */
10119 if (fileRenamed)
10120 {
10121 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10122 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10123 }
10124 if (dirRenamed)
10125 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10126 }
10127
10128 if (FAILED(rc)) return rc;
10129 }
10130
10131 if (fSettingsFileIsNew)
10132 {
10133 /* create a virgin config file */
10134 int vrc = VINF_SUCCESS;
10135
10136 /* ensure the settings directory exists */
10137 Utf8Str path(mData->m_strConfigFileFull);
10138 path.stripFilename();
10139 if (!RTDirExists(path.c_str()))
10140 {
10141 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10142 if (RT_FAILURE(vrc))
10143 {
10144 return setError(E_FAIL,
10145 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10146 path.c_str(),
10147 vrc);
10148 }
10149 }
10150
10151 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10152 path = Utf8Str(mData->m_strConfigFileFull);
10153 RTFILE f = NIL_RTFILE;
10154 vrc = RTFileOpen(&f, path.c_str(),
10155 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10156 if (RT_FAILURE(vrc))
10157 return setError(E_FAIL,
10158 tr("Could not create the settings file '%s' (%Rrc)"),
10159 path.c_str(),
10160 vrc);
10161 RTFileClose(f);
10162 }
10163
10164 return rc;
10165}
10166
10167/**
10168 * Saves and commits machine data, user data and hardware data.
10169 *
10170 * Note that on failure, the data remains uncommitted.
10171 *
10172 * @a aFlags may combine the following flags:
10173 *
10174 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10175 * Used when saving settings after an operation that makes them 100%
10176 * correspond to the settings from the current snapshot.
10177 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
10178 * #isReallyModified() returns false. This is necessary for cases when we
10179 * change machine data directly, not through the backup()/commit() mechanism.
10180 * - SaveS_Force: settings will be saved without doing a deep compare of the
10181 * settings structures. This is used when this is called because snapshots
10182 * have changed to avoid the overhead of the deep compare.
10183 *
10184 * @note Must be called from under this object's write lock. Locks children for
10185 * writing.
10186 *
10187 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10188 * initialized to false and that will be set to true by this function if
10189 * the caller must invoke VirtualBox::saveSettings() because the global
10190 * settings have changed. This will happen if a machine rename has been
10191 * saved and the global machine and media registries will therefore need
10192 * updating.
10193 */
10194HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
10195 int aFlags /*= 0*/)
10196{
10197 LogFlowThisFuncEnter();
10198
10199 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10200
10201 /* make sure child objects are unable to modify the settings while we are
10202 * saving them */
10203 ensureNoStateDependencies();
10204
10205 AssertReturn(!isSnapshotMachine(),
10206 E_FAIL);
10207
10208 HRESULT rc = S_OK;
10209 bool fNeedsWrite = false;
10210
10211 /* First, prepare to save settings. It will care about renaming the
10212 * settings directory and file if the machine name was changed and about
10213 * creating a new settings file if this is a new machine. */
10214 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
10215 if (FAILED(rc)) return rc;
10216
10217 // keep a pointer to the current settings structures
10218 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10219 settings::MachineConfigFile *pNewConfig = NULL;
10220
10221 try
10222 {
10223 // make a fresh one to have everyone write stuff into
10224 pNewConfig = new settings::MachineConfigFile(NULL);
10225 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10226
10227 // now go and copy all the settings data from COM to the settings structures
10228 // (this calles saveSettings() on all the COM objects in the machine)
10229 copyMachineDataToSettings(*pNewConfig);
10230
10231 if (aFlags & SaveS_ResetCurStateModified)
10232 {
10233 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10234 mData->mCurrentStateModified = FALSE;
10235 fNeedsWrite = true; // always, no need to compare
10236 }
10237 else if (aFlags & SaveS_Force)
10238 {
10239 fNeedsWrite = true; // always, no need to compare
10240 }
10241 else
10242 {
10243 if (!mData->mCurrentStateModified)
10244 {
10245 // do a deep compare of the settings that we just saved with the settings
10246 // previously stored in the config file; this invokes MachineConfigFile::operator==
10247 // which does a deep compare of all the settings, which is expensive but less expensive
10248 // than writing out XML in vain
10249 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10250
10251 // could still be modified if any settings changed
10252 mData->mCurrentStateModified = fAnySettingsChanged;
10253
10254 fNeedsWrite = fAnySettingsChanged;
10255 }
10256 else
10257 fNeedsWrite = true;
10258 }
10259
10260 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10261
10262 if (fNeedsWrite)
10263 // now spit it all out!
10264 pNewConfig->write(mData->m_strConfigFileFull);
10265
10266 mData->pMachineConfigFile = pNewConfig;
10267 delete pOldConfig;
10268 commit();
10269
10270 // after saving settings, we are no longer different from the XML on disk
10271 mData->flModifications = 0;
10272 }
10273 catch (HRESULT err)
10274 {
10275 // we assume that error info is set by the thrower
10276 rc = err;
10277
10278 // restore old config
10279 delete pNewConfig;
10280 mData->pMachineConfigFile = pOldConfig;
10281 }
10282 catch (...)
10283 {
10284 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10285 }
10286
10287 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10288 {
10289 /* Fire the data change event, even on failure (since we've already
10290 * committed all data). This is done only for SessionMachines because
10291 * mutable Machine instances are always not registered (i.e. private
10292 * to the client process that creates them) and thus don't need to
10293 * inform callbacks. */
10294 if (isSessionMachine())
10295 mParent->onMachineDataChange(mData->mUuid);
10296 }
10297
10298 LogFlowThisFunc(("rc=%08X\n", rc));
10299 LogFlowThisFuncLeave();
10300 return rc;
10301}
10302
10303/**
10304 * Implementation for saving the machine settings into the given
10305 * settings::MachineConfigFile instance. This copies machine extradata
10306 * from the previous machine config file in the instance data, if any.
10307 *
10308 * This gets called from two locations:
10309 *
10310 * -- Machine::saveSettings(), during the regular XML writing;
10311 *
10312 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10313 * exported to OVF and we write the VirtualBox proprietary XML
10314 * into a <vbox:Machine> tag.
10315 *
10316 * This routine fills all the fields in there, including snapshots, *except*
10317 * for the following:
10318 *
10319 * -- fCurrentStateModified. There is some special logic associated with that.
10320 *
10321 * The caller can then call MachineConfigFile::write() or do something else
10322 * with it.
10323 *
10324 * Caller must hold the machine lock!
10325 *
10326 * This throws XML errors and HRESULT, so the caller must have a catch block!
10327 */
10328void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10329{
10330 // deep copy extradata
10331 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10332
10333 config.uuid = mData->mUuid;
10334
10335 // copy name, description, OS type, teleport, UTC etc.
10336 config.machineUserData = mUserData->s;
10337
10338 // Encode the Icon Override data from Machine and store on config userdata.
10339 com::SafeArray<BYTE> iconByte;
10340 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10341 ssize_t cbData = iconByte.size();
10342 if (cbData > 0)
10343 {
10344 ssize_t cchOut = RTBase64EncodedLength(cbData);
10345 Utf8Str strIconData;
10346 strIconData.reserve(cchOut+1);
10347 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10348 strIconData.mutableRaw(), strIconData.capacity(),
10349 NULL);
10350 if (RT_FAILURE(vrc))
10351 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10352 strIconData.jolt();
10353 config.machineUserData.ovIcon = strIconData;
10354 }
10355 else
10356 config.machineUserData.ovIcon.setNull();
10357
10358 if ( mData->mMachineState == MachineState_Saved
10359 || mData->mMachineState == MachineState_Restoring
10360 // when deleting a snapshot we may or may not have a saved state in the current state,
10361 // so let's not assert here please
10362 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10363 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10364 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10365 && (!mSSData->strStateFilePath.isEmpty())
10366 )
10367 )
10368 {
10369 Assert(!mSSData->strStateFilePath.isEmpty());
10370 /* try to make the file name relative to the settings file dir */
10371 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10372 }
10373 else
10374 {
10375 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10376 config.strStateFile.setNull();
10377 }
10378
10379 if (mData->mCurrentSnapshot)
10380 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10381 else
10382 config.uuidCurrentSnapshot.clear();
10383
10384 config.timeLastStateChange = mData->mLastStateChange;
10385 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10386 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10387
10388 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10389 if (FAILED(rc)) throw rc;
10390
10391 rc = saveStorageControllers(config.storageMachine);
10392 if (FAILED(rc)) throw rc;
10393
10394 // save machine's media registry if this is VirtualBox 4.0 or later
10395 if (config.canHaveOwnMediaRegistry())
10396 {
10397 // determine machine folder
10398 Utf8Str strMachineFolder = getSettingsFileFull();
10399 strMachineFolder.stripFilename();
10400 mParent->saveMediaRegistry(config.mediaRegistry,
10401 getId(), // only media with registry ID == machine UUID
10402 strMachineFolder);
10403 // this throws HRESULT
10404 }
10405
10406 // save snapshots
10407 rc = saveAllSnapshots(config);
10408 if (FAILED(rc)) throw rc;
10409}
10410
10411/**
10412 * Saves all snapshots of the machine into the given machine config file. Called
10413 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10414 * @param config
10415 * @return
10416 */
10417HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10418{
10419 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10420
10421 HRESULT rc = S_OK;
10422
10423 try
10424 {
10425 config.llFirstSnapshot.clear();
10426
10427 if (mData->mFirstSnapshot)
10428 {
10429 settings::Snapshot snapNew;
10430 config.llFirstSnapshot.push_back(snapNew);
10431
10432 // get reference to the fresh copy of the snapshot on the list and
10433 // work on that copy directly to avoid excessive copying later
10434 settings::Snapshot &snap = config.llFirstSnapshot.front();
10435
10436 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10437 if (FAILED(rc)) throw rc;
10438 }
10439
10440// if (mType == IsSessionMachine)
10441// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10442
10443 }
10444 catch (HRESULT err)
10445 {
10446 /* we assume that error info is set by the thrower */
10447 rc = err;
10448 }
10449 catch (...)
10450 {
10451 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10452 }
10453
10454 return rc;
10455}
10456
10457/**
10458 * Saves the VM hardware configuration. It is assumed that the
10459 * given node is empty.
10460 *
10461 * @param data Reference to the settings object for the hardware config.
10462 * @param pDbg Pointer to the settings object for the debugging config
10463 * which happens to live in mHWData.
10464 * @param pAutostart Pointer to the settings object for the autostart config
10465 * which happens to live in mHWData.
10466 */
10467HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10468 settings::Autostart *pAutostart)
10469{
10470 HRESULT rc = S_OK;
10471
10472 try
10473 {
10474 /* The hardware version attribute (optional).
10475 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10476 if ( mHWData->mHWVersion == "1"
10477 && mSSData->strStateFilePath.isEmpty()
10478 )
10479 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some other point needs to be found where this can be done. */
10480
10481 data.strVersion = mHWData->mHWVersion;
10482 data.uuid = mHWData->mHardwareUUID;
10483
10484 // CPU
10485 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10486 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
10487 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10488 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10489 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10490 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10491 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10492 data.fPAE = !!mHWData->mPAEEnabled;
10493 data.enmLongMode = mHWData->mLongMode;
10494 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10495
10496 /* Standard and Extended CPUID leafs. */
10497 data.llCpuIdLeafs.clear();
10498 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10499 {
10500 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10501 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10502 }
10503 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10504 {
10505 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10506 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10507 }
10508
10509 data.cCPUs = mHWData->mCPUCount;
10510 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10511 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10512
10513 data.llCpus.clear();
10514 if (data.fCpuHotPlug)
10515 {
10516 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10517 {
10518 if (mHWData->mCPUAttached[idx])
10519 {
10520 settings::Cpu cpu;
10521 cpu.ulId = idx;
10522 data.llCpus.push_back(cpu);
10523 }
10524 }
10525 }
10526
10527 // memory
10528 data.ulMemorySizeMB = mHWData->mMemorySize;
10529 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10530
10531 // firmware
10532 data.firmwareType = mHWData->mFirmwareType;
10533
10534 // HID
10535 data.pointingHIDType = mHWData->mPointingHIDType;
10536 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10537
10538 // chipset
10539 data.chipsetType = mHWData->mChipsetType;
10540
10541 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10542 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10543
10544 // HPET
10545 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10546
10547 // boot order
10548 data.mapBootOrder.clear();
10549 for (size_t i = 0;
10550 i < RT_ELEMENTS(mHWData->mBootOrder);
10551 ++i)
10552 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10553
10554 // display
10555 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10556 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10557 data.cMonitors = mHWData->mMonitorCount;
10558 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10559 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10560 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10561 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10562 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10563 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10564 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10565 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10566 {
10567 if (mHWData->maVideoCaptureScreens[i])
10568 ASMBitSet(&data.u64VideoCaptureScreens, i);
10569 else
10570 ASMBitClear(&data.u64VideoCaptureScreens, i);
10571 }
10572 /* store relative video capture file if possible */
10573 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10574
10575 /* VRDEServer settings (optional) */
10576 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10577 if (FAILED(rc)) throw rc;
10578
10579 /* BIOS (required) */
10580 rc = mBIOSSettings->saveSettings(data.biosSettings);
10581 if (FAILED(rc)) throw rc;
10582
10583 /* USB Controller (required) */
10584 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10585 it != mUSBControllers->end();
10586 ++it)
10587 {
10588 ComObjPtr<USBController> ctrl = *it;
10589 settings::USBController settingsCtrl;
10590
10591 settingsCtrl.strName = ctrl->getName();
10592 settingsCtrl.enmType = ctrl->getControllerType();
10593
10594 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10595 }
10596
10597 /* USB device filters (required) */
10598 rc = mUSBDeviceFilters->saveSettings(data.usbSettings);
10599 if (FAILED(rc)) throw rc;
10600
10601 /* Network adapters (required) */
10602 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10603 data.llNetworkAdapters.clear();
10604 /* Write out only the nominal number of network adapters for this
10605 * chipset type. Since Machine::commit() hasn't been called there
10606 * may be extra NIC settings in the vector. */
10607 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10608 {
10609 settings::NetworkAdapter nic;
10610 nic.ulSlot = slot;
10611 /* paranoia check... must not be NULL, but must not crash either. */
10612 if (mNetworkAdapters[slot])
10613 {
10614 rc = mNetworkAdapters[slot]->saveSettings(nic);
10615 if (FAILED(rc)) throw rc;
10616
10617 data.llNetworkAdapters.push_back(nic);
10618 }
10619 }
10620
10621 /* Serial ports */
10622 data.llSerialPorts.clear();
10623 for (ULONG slot = 0;
10624 slot < RT_ELEMENTS(mSerialPorts);
10625 ++slot)
10626 {
10627 settings::SerialPort s;
10628 s.ulSlot = slot;
10629 rc = mSerialPorts[slot]->saveSettings(s);
10630 if (FAILED(rc)) return rc;
10631
10632 data.llSerialPorts.push_back(s);
10633 }
10634
10635 /* Parallel ports */
10636 data.llParallelPorts.clear();
10637 for (ULONG slot = 0;
10638 slot < RT_ELEMENTS(mParallelPorts);
10639 ++slot)
10640 {
10641 settings::ParallelPort p;
10642 p.ulSlot = slot;
10643 rc = mParallelPorts[slot]->saveSettings(p);
10644 if (FAILED(rc)) return rc;
10645
10646 data.llParallelPorts.push_back(p);
10647 }
10648
10649 /* Audio adapter */
10650 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10651 if (FAILED(rc)) return rc;
10652
10653 /* Shared folders */
10654 data.llSharedFolders.clear();
10655 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10656 it != mHWData->mSharedFolders.end();
10657 ++it)
10658 {
10659 SharedFolder *pSF = *it;
10660 AutoCaller sfCaller(pSF);
10661 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10662 settings::SharedFolder sf;
10663 sf.strName = pSF->getName();
10664 sf.strHostPath = pSF->getHostPath();
10665 sf.fWritable = !!pSF->isWritable();
10666 sf.fAutoMount = !!pSF->isAutoMounted();
10667
10668 data.llSharedFolders.push_back(sf);
10669 }
10670
10671 // clipboard
10672 data.clipboardMode = mHWData->mClipboardMode;
10673
10674 // drag'n'drop
10675 data.dragAndDropMode = mHWData->mDragAndDropMode;
10676
10677 /* Guest */
10678 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10679
10680 // IO settings
10681 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10682 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10683
10684 /* BandwidthControl (required) */
10685 rc = mBandwidthControl->saveSettings(data.ioSettings);
10686 if (FAILED(rc)) throw rc;
10687
10688 /* Host PCI devices */
10689 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10690 it != mHWData->mPCIDeviceAssignments.end();
10691 ++it)
10692 {
10693 ComObjPtr<PCIDeviceAttachment> pda = *it;
10694 settings::HostPCIDeviceAttachment hpda;
10695
10696 rc = pda->saveSettings(hpda);
10697 if (FAILED(rc)) throw rc;
10698
10699 data.pciAttachments.push_back(hpda);
10700 }
10701
10702
10703 // guest properties
10704 data.llGuestProperties.clear();
10705#ifdef VBOX_WITH_GUEST_PROPS
10706 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10707 it != mHWData->mGuestProperties.end();
10708 ++it)
10709 {
10710 HWData::GuestProperty property = it->second;
10711
10712 /* Remove transient guest properties at shutdown unless we
10713 * are saving state */
10714 if ( ( mData->mMachineState == MachineState_PoweredOff
10715 || mData->mMachineState == MachineState_Aborted
10716 || mData->mMachineState == MachineState_Teleported)
10717 && ( property.mFlags & guestProp::TRANSIENT
10718 || property.mFlags & guestProp::TRANSRESET))
10719 continue;
10720 settings::GuestProperty prop;
10721 prop.strName = it->first;
10722 prop.strValue = property.strValue;
10723 prop.timestamp = property.mTimestamp;
10724 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10725 guestProp::writeFlags(property.mFlags, szFlags);
10726 prop.strFlags = szFlags;
10727
10728 data.llGuestProperties.push_back(prop);
10729 }
10730
10731 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10732 /* I presume this doesn't require a backup(). */
10733 mData->mGuestPropertiesModified = FALSE;
10734#endif /* VBOX_WITH_GUEST_PROPS defined */
10735
10736 *pDbg = mHWData->mDebugging;
10737 *pAutostart = mHWData->mAutostart;
10738
10739 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10740 }
10741 catch(std::bad_alloc &)
10742 {
10743 return E_OUTOFMEMORY;
10744 }
10745
10746 AssertComRC(rc);
10747 return rc;
10748}
10749
10750/**
10751 * Saves the storage controller configuration.
10752 *
10753 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10754 */
10755HRESULT Machine::saveStorageControllers(settings::Storage &data)
10756{
10757 data.llStorageControllers.clear();
10758
10759 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10760 it != mStorageControllers->end();
10761 ++it)
10762 {
10763 HRESULT rc;
10764 ComObjPtr<StorageController> pCtl = *it;
10765
10766 settings::StorageController ctl;
10767 ctl.strName = pCtl->getName();
10768 ctl.controllerType = pCtl->getControllerType();
10769 ctl.storageBus = pCtl->getStorageBus();
10770 ctl.ulInstance = pCtl->getInstance();
10771 ctl.fBootable = pCtl->getBootable();
10772
10773 /* Save the port count. */
10774 ULONG portCount;
10775 rc = pCtl->COMGETTER(PortCount)(&portCount);
10776 ComAssertComRCRet(rc, rc);
10777 ctl.ulPortCount = portCount;
10778
10779 /* Save fUseHostIOCache */
10780 BOOL fUseHostIOCache;
10781 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10782 ComAssertComRCRet(rc, rc);
10783 ctl.fUseHostIOCache = !!fUseHostIOCache;
10784
10785 /* Save IDE emulation settings. */
10786 if (ctl.controllerType == StorageControllerType_IntelAhci)
10787 {
10788 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10789 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10790 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10791 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10792 )
10793 ComAssertComRCRet(rc, rc);
10794 }
10795
10796 /* save the devices now. */
10797 rc = saveStorageDevices(pCtl, ctl);
10798 ComAssertComRCRet(rc, rc);
10799
10800 data.llStorageControllers.push_back(ctl);
10801 }
10802
10803 return S_OK;
10804}
10805
10806/**
10807 * Saves the hard disk configuration.
10808 */
10809HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10810 settings::StorageController &data)
10811{
10812 MediaData::AttachmentList atts;
10813
10814 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10815 if (FAILED(rc)) return rc;
10816
10817 data.llAttachedDevices.clear();
10818 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10819 it != atts.end();
10820 ++it)
10821 {
10822 settings::AttachedDevice dev;
10823
10824 MediumAttachment *pAttach = *it;
10825 Medium *pMedium = pAttach->getMedium();
10826
10827 dev.deviceType = pAttach->getType();
10828 dev.lPort = pAttach->getPort();
10829 dev.lDevice = pAttach->getDevice();
10830 if (pMedium)
10831 {
10832 if (pMedium->isHostDrive())
10833 dev.strHostDriveSrc = pMedium->getLocationFull();
10834 else
10835 dev.uuid = pMedium->getId();
10836 dev.fPassThrough = pAttach->getPassthrough();
10837 dev.fTempEject = pAttach->getTempEject();
10838 dev.fNonRotational = pAttach->getNonRotational();
10839 dev.fDiscard = pAttach->getDiscard();
10840 }
10841
10842 dev.strBwGroup = pAttach->getBandwidthGroup();
10843
10844 data.llAttachedDevices.push_back(dev);
10845 }
10846
10847 return S_OK;
10848}
10849
10850/**
10851 * Saves machine state settings as defined by aFlags
10852 * (SaveSTS_* values).
10853 *
10854 * @param aFlags Combination of SaveSTS_* flags.
10855 *
10856 * @note Locks objects for writing.
10857 */
10858HRESULT Machine::saveStateSettings(int aFlags)
10859{
10860 if (aFlags == 0)
10861 return S_OK;
10862
10863 AutoCaller autoCaller(this);
10864 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10865
10866 /* This object's write lock is also necessary to serialize file access
10867 * (prevent concurrent reads and writes) */
10868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10869
10870 HRESULT rc = S_OK;
10871
10872 Assert(mData->pMachineConfigFile);
10873
10874 try
10875 {
10876 if (aFlags & SaveSTS_CurStateModified)
10877 mData->pMachineConfigFile->fCurrentStateModified = true;
10878
10879 if (aFlags & SaveSTS_StateFilePath)
10880 {
10881 if (!mSSData->strStateFilePath.isEmpty())
10882 /* try to make the file name relative to the settings file dir */
10883 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10884 else
10885 mData->pMachineConfigFile->strStateFile.setNull();
10886 }
10887
10888 if (aFlags & SaveSTS_StateTimeStamp)
10889 {
10890 Assert( mData->mMachineState != MachineState_Aborted
10891 || mSSData->strStateFilePath.isEmpty());
10892
10893 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10894
10895 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10896//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10897 }
10898
10899 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10900 }
10901 catch (...)
10902 {
10903 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10904 }
10905
10906 return rc;
10907}
10908
10909/**
10910 * Ensures that the given medium is added to a media registry. If this machine
10911 * was created with 4.0 or later, then the machine registry is used. Otherwise
10912 * the global VirtualBox media registry is used.
10913 *
10914 * Caller must NOT hold machine lock, media tree or any medium locks!
10915 *
10916 * @param pMedium
10917 */
10918void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10919{
10920 /* Paranoia checks: do not hold machine or media tree locks. */
10921 AssertReturnVoid(!isWriteLockOnCurrentThread());
10922 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10923
10924 ComObjPtr<Medium> pBase;
10925 {
10926 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10927 pBase = pMedium->getBase();
10928 }
10929
10930 /* Paranoia checks: do not hold medium locks. */
10931 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10932 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10933
10934 // decide which medium registry to use now that the medium is attached:
10935 Guid uuid;
10936 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10937 // machine XML is VirtualBox 4.0 or higher:
10938 uuid = getId(); // machine UUID
10939 else
10940 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10941
10942 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10943 mParent->markRegistryModified(uuid);
10944
10945 /* For more complex hard disk structures it can happen that the base
10946 * medium isn't yet associated with any medium registry. Do that now. */
10947 if (pMedium != pBase)
10948 {
10949 if (pBase->addRegistry(uuid, true /* fRecurse */))
10950 mParent->markRegistryModified(uuid);
10951 }
10952}
10953
10954/**
10955 * Creates differencing hard disks for all normal hard disks attached to this
10956 * machine and a new set of attachments to refer to created disks.
10957 *
10958 * Used when taking a snapshot or when deleting the current state. Gets called
10959 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10960 *
10961 * This method assumes that mMediaData contains the original hard disk attachments
10962 * it needs to create diffs for. On success, these attachments will be replaced
10963 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10964 * called to delete created diffs which will also rollback mMediaData and restore
10965 * whatever was backed up before calling this method.
10966 *
10967 * Attachments with non-normal hard disks are left as is.
10968 *
10969 * If @a aOnline is @c false then the original hard disks that require implicit
10970 * diffs will be locked for reading. Otherwise it is assumed that they are
10971 * already locked for writing (when the VM was started). Note that in the latter
10972 * case it is responsibility of the caller to lock the newly created diffs for
10973 * writing if this method succeeds.
10974 *
10975 * @param aProgress Progress object to run (must contain at least as
10976 * many operations left as the number of hard disks
10977 * attached).
10978 * @param aOnline Whether the VM was online prior to this operation.
10979 *
10980 * @note The progress object is not marked as completed, neither on success nor
10981 * on failure. This is a responsibility of the caller.
10982 *
10983 * @note Locks this object and the media tree for writing.
10984 */
10985HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10986 ULONG aWeight,
10987 bool aOnline)
10988{
10989 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10990
10991 AutoCaller autoCaller(this);
10992 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10993
10994 AutoMultiWriteLock2 alock(this->lockHandle(),
10995 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10996
10997 /* must be in a protective state because we release the lock below */
10998 AssertReturn( mData->mMachineState == MachineState_Saving
10999 || mData->mMachineState == MachineState_LiveSnapshotting
11000 || mData->mMachineState == MachineState_RestoringSnapshot
11001 || mData->mMachineState == MachineState_DeletingSnapshot
11002 , E_FAIL);
11003
11004 HRESULT rc = S_OK;
11005
11006 // use appropriate locked media map (online or offline)
11007 MediumLockListMap lockedMediaOffline;
11008 MediumLockListMap *lockedMediaMap;
11009 if (aOnline)
11010 lockedMediaMap = &mData->mSession.mLockedMedia;
11011 else
11012 lockedMediaMap = &lockedMediaOffline;
11013
11014 try
11015 {
11016 if (!aOnline)
11017 {
11018 /* lock all attached hard disks early to detect "in use"
11019 * situations before creating actual diffs */
11020 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11021 it != mMediaData->mAttachments.end();
11022 ++it)
11023 {
11024 MediumAttachment* pAtt = *it;
11025 if (pAtt->getType() == DeviceType_HardDisk)
11026 {
11027 Medium* pMedium = pAtt->getMedium();
11028 Assert(pMedium);
11029
11030 MediumLockList *pMediumLockList(new MediumLockList());
11031 alock.release();
11032 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11033 false /* fMediumLockWrite */,
11034 NULL,
11035 *pMediumLockList);
11036 alock.acquire();
11037 if (FAILED(rc))
11038 {
11039 delete pMediumLockList;
11040 throw rc;
11041 }
11042 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11043 if (FAILED(rc))
11044 {
11045 throw setError(rc,
11046 tr("Collecting locking information for all attached media failed"));
11047 }
11048 }
11049 }
11050
11051 /* Now lock all media. If this fails, nothing is locked. */
11052 alock.release();
11053 rc = lockedMediaMap->Lock();
11054 alock.acquire();
11055 if (FAILED(rc))
11056 {
11057 throw setError(rc,
11058 tr("Locking of attached media failed"));
11059 }
11060 }
11061
11062 /* remember the current list (note that we don't use backup() since
11063 * mMediaData may be already backed up) */
11064 MediaData::AttachmentList atts = mMediaData->mAttachments;
11065
11066 /* start from scratch */
11067 mMediaData->mAttachments.clear();
11068
11069 /* go through remembered attachments and create diffs for normal hard
11070 * disks and attach them */
11071 for (MediaData::AttachmentList::const_iterator it = atts.begin();
11072 it != atts.end();
11073 ++it)
11074 {
11075 MediumAttachment* pAtt = *it;
11076
11077 DeviceType_T devType = pAtt->getType();
11078 Medium* pMedium = pAtt->getMedium();
11079
11080 if ( devType != DeviceType_HardDisk
11081 || pMedium == NULL
11082 || pMedium->getType() != MediumType_Normal)
11083 {
11084 /* copy the attachment as is */
11085
11086 /** @todo the progress object created in Console::TakeSnaphot
11087 * only expects operations for hard disks. Later other
11088 * device types need to show up in the progress as well. */
11089 if (devType == DeviceType_HardDisk)
11090 {
11091 if (pMedium == NULL)
11092 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11093 aWeight); // weight
11094 else
11095 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11096 pMedium->getBase()->getName().c_str()).raw(),
11097 aWeight); // weight
11098 }
11099
11100 mMediaData->mAttachments.push_back(pAtt);
11101 continue;
11102 }
11103
11104 /* need a diff */
11105 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11106 pMedium->getBase()->getName().c_str()).raw(),
11107 aWeight); // weight
11108
11109 Utf8Str strFullSnapshotFolder;
11110 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11111
11112 ComObjPtr<Medium> diff;
11113 diff.createObject();
11114 // store the diff in the same registry as the parent
11115 // (this cannot fail here because we can't create implicit diffs for
11116 // unregistered images)
11117 Guid uuidRegistryParent;
11118 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
11119 Assert(fInRegistry); NOREF(fInRegistry);
11120 rc = diff->init(mParent,
11121 pMedium->getPreferredDiffFormat(),
11122 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11123 uuidRegistryParent);
11124 if (FAILED(rc)) throw rc;
11125
11126 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11127 * the push_back? Looks like we're going to release medium with the
11128 * wrong kind of lock (general issue with if we fail anywhere at all)
11129 * and an orphaned VDI in the snapshots folder. */
11130
11131 /* update the appropriate lock list */
11132 MediumLockList *pMediumLockList;
11133 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11134 AssertComRCThrowRC(rc);
11135 if (aOnline)
11136 {
11137 alock.release();
11138 /* The currently attached medium will be read-only, change
11139 * the lock type to read. */
11140 rc = pMediumLockList->Update(pMedium, false);
11141 alock.acquire();
11142 AssertComRCThrowRC(rc);
11143 }
11144
11145 /* release the locks before the potentially lengthy operation */
11146 alock.release();
11147 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
11148 pMediumLockList,
11149 NULL /* aProgress */,
11150 true /* aWait */);
11151 alock.acquire();
11152 if (FAILED(rc)) throw rc;
11153
11154 /* actual lock list update is done in Medium::commitMedia */
11155
11156 rc = diff->addBackReference(mData->mUuid);
11157 AssertComRCThrowRC(rc);
11158
11159 /* add a new attachment */
11160 ComObjPtr<MediumAttachment> attachment;
11161 attachment.createObject();
11162 rc = attachment->init(this,
11163 diff,
11164 pAtt->getControllerName(),
11165 pAtt->getPort(),
11166 pAtt->getDevice(),
11167 DeviceType_HardDisk,
11168 true /* aImplicit */,
11169 false /* aPassthrough */,
11170 false /* aTempEject */,
11171 pAtt->getNonRotational(),
11172 pAtt->getDiscard(),
11173 pAtt->getBandwidthGroup());
11174 if (FAILED(rc)) throw rc;
11175
11176 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11177 AssertComRCThrowRC(rc);
11178 mMediaData->mAttachments.push_back(attachment);
11179 }
11180 }
11181 catch (HRESULT aRC) { rc = aRC; }
11182
11183 /* unlock all hard disks we locked when there is no VM */
11184 if (!aOnline)
11185 {
11186 ErrorInfoKeeper eik;
11187
11188 HRESULT rc1 = lockedMediaMap->Clear();
11189 AssertComRC(rc1);
11190 }
11191
11192 return rc;
11193}
11194
11195/**
11196 * Deletes implicit differencing hard disks created either by
11197 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
11198 *
11199 * Note that to delete hard disks created by #AttachDevice() this method is
11200 * called from #fixupMedia() when the changes are rolled back.
11201 *
11202 * @note Locks this object and the media tree for writing.
11203 */
11204HRESULT Machine::deleteImplicitDiffs(bool aOnline)
11205{
11206 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11207
11208 AutoCaller autoCaller(this);
11209 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11210
11211 AutoMultiWriteLock2 alock(this->lockHandle(),
11212 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11213
11214 /* We absolutely must have backed up state. */
11215 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
11216
11217 /* Check if there are any implicitly created diff images. */
11218 bool fImplicitDiffs = false;
11219 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11220 it != mMediaData->mAttachments.end();
11221 ++it)
11222 {
11223 const ComObjPtr<MediumAttachment> &pAtt = *it;
11224 if (pAtt->isImplicit())
11225 {
11226 fImplicitDiffs = true;
11227 break;
11228 }
11229 }
11230 /* If there is nothing to do, leave early. This saves lots of image locking
11231 * effort. It also avoids a MachineStateChanged event without real reason.
11232 * This is important e.g. when loading a VM config, because there should be
11233 * no events. Otherwise API clients can become thoroughly confused for
11234 * inaccessible VMs (the code for loading VM configs uses this method for
11235 * cleanup if the config makes no sense), as they take such events as an
11236 * indication that the VM is alive, and they would force the VM config to
11237 * be reread, leading to an endless loop. */
11238 if (!fImplicitDiffs)
11239 return S_OK;
11240
11241 HRESULT rc = S_OK;
11242 MachineState_T oldState = mData->mMachineState;
11243
11244 /* will release the lock before the potentially lengthy operation,
11245 * so protect with the special state (unless already protected) */
11246 if ( oldState != MachineState_Saving
11247 && oldState != MachineState_LiveSnapshotting
11248 && oldState != MachineState_RestoringSnapshot
11249 && oldState != MachineState_DeletingSnapshot
11250 && oldState != MachineState_DeletingSnapshotOnline
11251 && oldState != MachineState_DeletingSnapshotPaused
11252 )
11253 setMachineState(MachineState_SettingUp);
11254
11255 // use appropriate locked media map (online or offline)
11256 MediumLockListMap lockedMediaOffline;
11257 MediumLockListMap *lockedMediaMap;
11258 if (aOnline)
11259 lockedMediaMap = &mData->mSession.mLockedMedia;
11260 else
11261 lockedMediaMap = &lockedMediaOffline;
11262
11263 try
11264 {
11265 if (!aOnline)
11266 {
11267 /* lock all attached hard disks early to detect "in use"
11268 * situations before deleting actual diffs */
11269 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11270 it != mMediaData->mAttachments.end();
11271 ++it)
11272 {
11273 MediumAttachment* pAtt = *it;
11274 if (pAtt->getType() == DeviceType_HardDisk)
11275 {
11276 Medium* pMedium = pAtt->getMedium();
11277 Assert(pMedium);
11278
11279 MediumLockList *pMediumLockList(new MediumLockList());
11280 alock.release();
11281 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11282 false /* fMediumLockWrite */,
11283 NULL,
11284 *pMediumLockList);
11285 alock.acquire();
11286
11287 if (FAILED(rc))
11288 {
11289 delete pMediumLockList;
11290 throw rc;
11291 }
11292
11293 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11294 if (FAILED(rc))
11295 throw rc;
11296 }
11297 }
11298
11299 if (FAILED(rc))
11300 throw rc;
11301 } // end of offline
11302
11303 /* Lock lists are now up to date and include implicitly created media */
11304
11305 /* Go through remembered attachments and delete all implicitly created
11306 * diffs and fix up the attachment information */
11307 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11308 MediaData::AttachmentList implicitAtts;
11309 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11310 it != mMediaData->mAttachments.end();
11311 ++it)
11312 {
11313 ComObjPtr<MediumAttachment> pAtt = *it;
11314 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11315 if (pMedium.isNull())
11316 continue;
11317
11318 // Implicit attachments go on the list for deletion and back references are removed.
11319 if (pAtt->isImplicit())
11320 {
11321 /* Deassociate and mark for deletion */
11322 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
11323 rc = pMedium->removeBackReference(mData->mUuid);
11324 if (FAILED(rc))
11325 throw rc;
11326 implicitAtts.push_back(pAtt);
11327 continue;
11328 }
11329
11330 /* Was this medium attached before? */
11331 if (!findAttachment(oldAtts, pMedium))
11332 {
11333 /* no: de-associate */
11334 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
11335 rc = pMedium->removeBackReference(mData->mUuid);
11336 if (FAILED(rc))
11337 throw rc;
11338 continue;
11339 }
11340 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
11341 }
11342
11343 /* If there are implicit attachments to delete, throw away the lock
11344 * map contents (which will unlock all media) since the medium
11345 * attachments will be rolled back. Below we need to completely
11346 * recreate the lock map anyway since it is infinitely complex to
11347 * do this incrementally (would need reconstructing each attachment
11348 * change, which would be extremely hairy). */
11349 if (implicitAtts.size() != 0)
11350 {
11351 ErrorInfoKeeper eik;
11352
11353 HRESULT rc1 = lockedMediaMap->Clear();
11354 AssertComRC(rc1);
11355 }
11356
11357 /* rollback hard disk changes */
11358 mMediaData.rollback();
11359
11360 MultiResult mrc(S_OK);
11361
11362 // Delete unused implicit diffs.
11363 if (implicitAtts.size() != 0)
11364 {
11365 alock.release();
11366
11367 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11368 it != implicitAtts.end();
11369 ++it)
11370 {
11371 // Remove medium associated with this attachment.
11372 ComObjPtr<MediumAttachment> pAtt = *it;
11373 Assert(pAtt);
11374 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11375 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11376 Assert(pMedium);
11377
11378 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11379 // continue on delete failure, just collect error messages
11380 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11381 mrc = rc;
11382 }
11383
11384 alock.acquire();
11385
11386 /* if there is a VM recreate media lock map as mentioned above,
11387 * otherwise it is a waste of time and we leave things unlocked */
11388 if (aOnline)
11389 {
11390 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11391 /* must never be NULL, but better safe than sorry */
11392 if (!pMachine.isNull())
11393 {
11394 alock.release();
11395 rc = mData->mSession.mMachine->lockMedia();
11396 alock.acquire();
11397 if (FAILED(rc))
11398 throw rc;
11399 }
11400 }
11401 }
11402 }
11403 catch (HRESULT aRC) {rc = aRC;}
11404
11405 if (mData->mMachineState == MachineState_SettingUp)
11406 setMachineState(oldState);
11407
11408 /* unlock all hard disks we locked when there is no VM */
11409 if (!aOnline)
11410 {
11411 ErrorInfoKeeper eik;
11412
11413 HRESULT rc1 = lockedMediaMap->Clear();
11414 AssertComRC(rc1);
11415 }
11416
11417 return rc;
11418}
11419
11420
11421/**
11422 * Looks through the given list of media attachments for one with the given parameters
11423 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11424 * can be searched as well if needed.
11425 *
11426 * @param list
11427 * @param aControllerName
11428 * @param aControllerPort
11429 * @param aDevice
11430 * @return
11431 */
11432MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11433 IN_BSTR aControllerName,
11434 LONG aControllerPort,
11435 LONG aDevice)
11436{
11437 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11438 it != ll.end();
11439 ++it)
11440 {
11441 MediumAttachment *pAttach = *it;
11442 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11443 return pAttach;
11444 }
11445
11446 return NULL;
11447}
11448
11449/**
11450 * Looks through the given list of media attachments for one with the given parameters
11451 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11452 * can be searched as well if needed.
11453 *
11454 * @param list
11455 * @param aControllerName
11456 * @param aControllerPort
11457 * @param aDevice
11458 * @return
11459 */
11460MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11461 ComObjPtr<Medium> pMedium)
11462{
11463 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11464 it != ll.end();
11465 ++it)
11466 {
11467 MediumAttachment *pAttach = *it;
11468 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11469 if (pMediumThis == pMedium)
11470 return pAttach;
11471 }
11472
11473 return NULL;
11474}
11475
11476/**
11477 * Looks through the given list of media attachments for one with the given parameters
11478 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11479 * can be searched as well if needed.
11480 *
11481 * @param list
11482 * @param aControllerName
11483 * @param aControllerPort
11484 * @param aDevice
11485 * @return
11486 */
11487MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11488 Guid &id)
11489{
11490 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11491 it != ll.end();
11492 ++it)
11493 {
11494 MediumAttachment *pAttach = *it;
11495 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11496 if (pMediumThis->getId() == id)
11497 return pAttach;
11498 }
11499
11500 return NULL;
11501}
11502
11503/**
11504 * Main implementation for Machine::DetachDevice. This also gets called
11505 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11506 *
11507 * @param pAttach Medium attachment to detach.
11508 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11509 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11510 * @return
11511 */
11512HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11513 AutoWriteLock &writeLock,
11514 Snapshot *pSnapshot)
11515{
11516 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11517 DeviceType_T mediumType = pAttach->getType();
11518
11519 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11520
11521 if (pAttach->isImplicit())
11522 {
11523 /* attempt to implicitly delete the implicitly created diff */
11524
11525 /// @todo move the implicit flag from MediumAttachment to Medium
11526 /// and forbid any hard disk operation when it is implicit. Or maybe
11527 /// a special media state for it to make it even more simple.
11528
11529 Assert(mMediaData.isBackedUp());
11530
11531 /* will release the lock before the potentially lengthy operation, so
11532 * protect with the special state */
11533 MachineState_T oldState = mData->mMachineState;
11534 setMachineState(MachineState_SettingUp);
11535
11536 writeLock.release();
11537
11538 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11539 true /*aWait*/);
11540
11541 writeLock.acquire();
11542
11543 setMachineState(oldState);
11544
11545 if (FAILED(rc)) return rc;
11546 }
11547
11548 setModified(IsModified_Storage);
11549 mMediaData.backup();
11550 mMediaData->mAttachments.remove(pAttach);
11551
11552 if (!oldmedium.isNull())
11553 {
11554 // if this is from a snapshot, do not defer detachment to commitMedia()
11555 if (pSnapshot)
11556 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11557 // else if non-hard disk media, do not defer detachment to commitMedia() either
11558 else if (mediumType != DeviceType_HardDisk)
11559 oldmedium->removeBackReference(mData->mUuid);
11560 }
11561
11562 return S_OK;
11563}
11564
11565/**
11566 * Goes thru all media of the given list and
11567 *
11568 * 1) calls detachDevice() on each of them for this machine and
11569 * 2) adds all Medium objects found in the process to the given list,
11570 * depending on cleanupMode.
11571 *
11572 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11573 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11574 * media to the list.
11575 *
11576 * This gets called from Machine::Unregister, both for the actual Machine and
11577 * the SnapshotMachine objects that might be found in the snapshots.
11578 *
11579 * Requires caller and locking. The machine lock must be passed in because it
11580 * will be passed on to detachDevice which needs it for temporary unlocking.
11581 *
11582 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11583 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11584 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11585 * otherwise no media get added.
11586 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11587 * @return
11588 */
11589HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11590 Snapshot *pSnapshot,
11591 CleanupMode_T cleanupMode,
11592 MediaList &llMedia)
11593{
11594 Assert(isWriteLockOnCurrentThread());
11595
11596 HRESULT rc;
11597
11598 // make a temporary list because detachDevice invalidates iterators into
11599 // mMediaData->mAttachments
11600 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11601
11602 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11603 it != llAttachments2.end();
11604 ++it)
11605 {
11606 ComObjPtr<MediumAttachment> &pAttach = *it;
11607 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11608
11609 if (!pMedium.isNull())
11610 {
11611 AutoCaller mac(pMedium);
11612 if (FAILED(mac.rc())) return mac.rc();
11613 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11614 DeviceType_T devType = pMedium->getDeviceType();
11615 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11616 && devType == DeviceType_HardDisk)
11617 || (cleanupMode == CleanupMode_Full)
11618 )
11619 {
11620 llMedia.push_back(pMedium);
11621 ComObjPtr<Medium> pParent = pMedium->getParent();
11622 /*
11623 * Search for medias which are not attached to any machine, but
11624 * in the chain to an attached disk. Mediums are only consided
11625 * if they are:
11626 * - have only one child
11627 * - no references to any machines
11628 * - are of normal medium type
11629 */
11630 while (!pParent.isNull())
11631 {
11632 AutoCaller mac1(pParent);
11633 if (FAILED(mac1.rc())) return mac1.rc();
11634 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11635 if (pParent->getChildren().size() == 1)
11636 {
11637 if ( pParent->getMachineBackRefCount() == 0
11638 && pParent->getType() == MediumType_Normal
11639 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11640 llMedia.push_back(pParent);
11641 }
11642 else
11643 break;
11644 pParent = pParent->getParent();
11645 }
11646 }
11647 }
11648
11649 // real machine: then we need to use the proper method
11650 rc = detachDevice(pAttach, writeLock, pSnapshot);
11651
11652 if (FAILED(rc))
11653 return rc;
11654 }
11655
11656 return S_OK;
11657}
11658
11659/**
11660 * Perform deferred hard disk detachments.
11661 *
11662 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11663 * backed up).
11664 *
11665 * If @a aOnline is @c true then this method will also unlock the old hard disks
11666 * for which the new implicit diffs were created and will lock these new diffs for
11667 * writing.
11668 *
11669 * @param aOnline Whether the VM was online prior to this operation.
11670 *
11671 * @note Locks this object for writing!
11672 */
11673void Machine::commitMedia(bool aOnline /*= false*/)
11674{
11675 AutoCaller autoCaller(this);
11676 AssertComRCReturnVoid(autoCaller.rc());
11677
11678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11679
11680 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11681
11682 HRESULT rc = S_OK;
11683
11684 /* no attach/detach operations -- nothing to do */
11685 if (!mMediaData.isBackedUp())
11686 return;
11687
11688 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11689 bool fMediaNeedsLocking = false;
11690
11691 /* enumerate new attachments */
11692 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11693 it != mMediaData->mAttachments.end();
11694 ++it)
11695 {
11696 MediumAttachment *pAttach = *it;
11697
11698 pAttach->commit();
11699
11700 Medium* pMedium = pAttach->getMedium();
11701 bool fImplicit = pAttach->isImplicit();
11702
11703 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11704 (pMedium) ? pMedium->getName().c_str() : "NULL",
11705 fImplicit));
11706
11707 /** @todo convert all this Machine-based voodoo to MediumAttachment
11708 * based commit logic. */
11709 if (fImplicit)
11710 {
11711 /* convert implicit attachment to normal */
11712 pAttach->setImplicit(false);
11713
11714 if ( aOnline
11715 && pMedium
11716 && pAttach->getType() == DeviceType_HardDisk
11717 )
11718 {
11719 ComObjPtr<Medium> parent = pMedium->getParent();
11720 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11721
11722 /* update the appropriate lock list */
11723 MediumLockList *pMediumLockList;
11724 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11725 AssertComRC(rc);
11726 if (pMediumLockList)
11727 {
11728 /* unlock if there's a need to change the locking */
11729 if (!fMediaNeedsLocking)
11730 {
11731 rc = mData->mSession.mLockedMedia.Unlock();
11732 AssertComRC(rc);
11733 fMediaNeedsLocking = true;
11734 }
11735 rc = pMediumLockList->Update(parent, false);
11736 AssertComRC(rc);
11737 rc = pMediumLockList->Append(pMedium, true);
11738 AssertComRC(rc);
11739 }
11740 }
11741
11742 continue;
11743 }
11744
11745 if (pMedium)
11746 {
11747 /* was this medium attached before? */
11748 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11749 oldIt != oldAtts.end();
11750 ++oldIt)
11751 {
11752 MediumAttachment *pOldAttach = *oldIt;
11753 if (pOldAttach->getMedium() == pMedium)
11754 {
11755 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11756
11757 /* yes: remove from old to avoid de-association */
11758 oldAtts.erase(oldIt);
11759 break;
11760 }
11761 }
11762 }
11763 }
11764
11765 /* enumerate remaining old attachments and de-associate from the
11766 * current machine state */
11767 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11768 it != oldAtts.end();
11769 ++it)
11770 {
11771 MediumAttachment *pAttach = *it;
11772 Medium* pMedium = pAttach->getMedium();
11773
11774 /* Detach only hard disks, since DVD/floppy media is detached
11775 * instantly in MountMedium. */
11776 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11777 {
11778 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11779
11780 /* now de-associate from the current machine state */
11781 rc = pMedium->removeBackReference(mData->mUuid);
11782 AssertComRC(rc);
11783
11784 if (aOnline)
11785 {
11786 /* unlock since medium is not used anymore */
11787 MediumLockList *pMediumLockList;
11788 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11789 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11790 {
11791 /* this happens for online snapshots, there the attachment
11792 * is changing, but only to a diff image created under
11793 * the old one, so there is no separate lock list */
11794 Assert(!pMediumLockList);
11795 }
11796 else
11797 {
11798 AssertComRC(rc);
11799 if (pMediumLockList)
11800 {
11801 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11802 AssertComRC(rc);
11803 }
11804 }
11805 }
11806 }
11807 }
11808
11809 /* take media locks again so that the locking state is consistent */
11810 if (fMediaNeedsLocking)
11811 {
11812 Assert(aOnline);
11813 rc = mData->mSession.mLockedMedia.Lock();
11814 AssertComRC(rc);
11815 }
11816
11817 /* commit the hard disk changes */
11818 mMediaData.commit();
11819
11820 if (isSessionMachine())
11821 {
11822 /*
11823 * Update the parent machine to point to the new owner.
11824 * This is necessary because the stored parent will point to the
11825 * session machine otherwise and cause crashes or errors later
11826 * when the session machine gets invalid.
11827 */
11828 /** @todo Change the MediumAttachment class to behave like any other
11829 * class in this regard by creating peer MediumAttachment
11830 * objects for session machines and share the data with the peer
11831 * machine.
11832 */
11833 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11834 it != mMediaData->mAttachments.end();
11835 ++it)
11836 {
11837 (*it)->updateParentMachine(mPeer);
11838 }
11839
11840 /* attach new data to the primary machine and reshare it */
11841 mPeer->mMediaData.attach(mMediaData);
11842 }
11843
11844 return;
11845}
11846
11847/**
11848 * Perform deferred deletion of implicitly created diffs.
11849 *
11850 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11851 * backed up).
11852 *
11853 * @note Locks this object for writing!
11854 */
11855void Machine::rollbackMedia()
11856{
11857 AutoCaller autoCaller(this);
11858 AssertComRCReturnVoid(autoCaller.rc());
11859
11860 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11861 LogFlowThisFunc(("Entering rollbackMedia\n"));
11862
11863 HRESULT rc = S_OK;
11864
11865 /* no attach/detach operations -- nothing to do */
11866 if (!mMediaData.isBackedUp())
11867 return;
11868
11869 /* enumerate new attachments */
11870 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11871 it != mMediaData->mAttachments.end();
11872 ++it)
11873 {
11874 MediumAttachment *pAttach = *it;
11875 /* Fix up the backrefs for DVD/floppy media. */
11876 if (pAttach->getType() != DeviceType_HardDisk)
11877 {
11878 Medium* pMedium = pAttach->getMedium();
11879 if (pMedium)
11880 {
11881 rc = pMedium->removeBackReference(mData->mUuid);
11882 AssertComRC(rc);
11883 }
11884 }
11885
11886 (*it)->rollback();
11887
11888 pAttach = *it;
11889 /* Fix up the backrefs for DVD/floppy media. */
11890 if (pAttach->getType() != DeviceType_HardDisk)
11891 {
11892 Medium* pMedium = pAttach->getMedium();
11893 if (pMedium)
11894 {
11895 rc = pMedium->addBackReference(mData->mUuid);
11896 AssertComRC(rc);
11897 }
11898 }
11899 }
11900
11901 /** @todo convert all this Machine-based voodoo to MediumAttachment
11902 * based rollback logic. */
11903 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11904
11905 return;
11906}
11907
11908/**
11909 * Returns true if the settings file is located in the directory named exactly
11910 * as the machine; this means, among other things, that the machine directory
11911 * should be auto-renamed.
11912 *
11913 * @param aSettingsDir if not NULL, the full machine settings file directory
11914 * name will be assigned there.
11915 *
11916 * @note Doesn't lock anything.
11917 * @note Not thread safe (must be called from this object's lock).
11918 */
11919bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11920{
11921 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11922 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11923 if (aSettingsDir)
11924 *aSettingsDir = strMachineDirName;
11925 strMachineDirName.stripPath(); // vmname
11926 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11927 strConfigFileOnly.stripPath() // vmname.vbox
11928 .stripExt(); // vmname
11929 /** @todo hack, make somehow use of ComposeMachineFilename */
11930 if (mUserData->s.fDirectoryIncludesUUID)
11931 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11932
11933 AssertReturn(!strMachineDirName.isEmpty(), false);
11934 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11935
11936 return strMachineDirName == strConfigFileOnly;
11937}
11938
11939/**
11940 * Discards all changes to machine settings.
11941 *
11942 * @param aNotify Whether to notify the direct session about changes or not.
11943 *
11944 * @note Locks objects for writing!
11945 */
11946void Machine::rollback(bool aNotify)
11947{
11948 AutoCaller autoCaller(this);
11949 AssertComRCReturn(autoCaller.rc(), (void)0);
11950
11951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11952
11953 if (!mStorageControllers.isNull())
11954 {
11955 if (mStorageControllers.isBackedUp())
11956 {
11957 /* unitialize all new devices (absent in the backed up list). */
11958 StorageControllerList::const_iterator it = mStorageControllers->begin();
11959 StorageControllerList *backedList = mStorageControllers.backedUpData();
11960 while (it != mStorageControllers->end())
11961 {
11962 if ( std::find(backedList->begin(), backedList->end(), *it)
11963 == backedList->end()
11964 )
11965 {
11966 (*it)->uninit();
11967 }
11968 ++it;
11969 }
11970
11971 /* restore the list */
11972 mStorageControllers.rollback();
11973 }
11974
11975 /* rollback any changes to devices after restoring the list */
11976 if (mData->flModifications & IsModified_Storage)
11977 {
11978 StorageControllerList::const_iterator it = mStorageControllers->begin();
11979 while (it != mStorageControllers->end())
11980 {
11981 (*it)->rollback();
11982 ++it;
11983 }
11984 }
11985 }
11986
11987 if (!mUSBControllers.isNull())
11988 {
11989 if (mUSBControllers.isBackedUp())
11990 {
11991 /* unitialize all new devices (absent in the backed up list). */
11992 USBControllerList::const_iterator it = mUSBControllers->begin();
11993 USBControllerList *backedList = mUSBControllers.backedUpData();
11994 while (it != mUSBControllers->end())
11995 {
11996 if ( std::find(backedList->begin(), backedList->end(), *it)
11997 == backedList->end()
11998 )
11999 {
12000 (*it)->uninit();
12001 }
12002 ++it;
12003 }
12004
12005 /* restore the list */
12006 mUSBControllers.rollback();
12007 }
12008
12009 /* rollback any changes to devices after restoring the list */
12010 if (mData->flModifications & IsModified_USB)
12011 {
12012 USBControllerList::const_iterator it = mUSBControllers->begin();
12013 while (it != mUSBControllers->end())
12014 {
12015 (*it)->rollback();
12016 ++it;
12017 }
12018 }
12019 }
12020
12021 mUserData.rollback();
12022
12023 mHWData.rollback();
12024
12025 if (mData->flModifications & IsModified_Storage)
12026 rollbackMedia();
12027
12028 if (mBIOSSettings)
12029 mBIOSSettings->rollback();
12030
12031 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12032 mVRDEServer->rollback();
12033
12034 if (mAudioAdapter)
12035 mAudioAdapter->rollback();
12036
12037 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12038 mUSBDeviceFilters->rollback();
12039
12040 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12041 mBandwidthControl->rollback();
12042
12043 if (!mHWData.isNull())
12044 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12045 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12046 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12047 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12048
12049 if (mData->flModifications & IsModified_NetworkAdapters)
12050 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12051 if ( mNetworkAdapters[slot]
12052 && mNetworkAdapters[slot]->isModified())
12053 {
12054 mNetworkAdapters[slot]->rollback();
12055 networkAdapters[slot] = mNetworkAdapters[slot];
12056 }
12057
12058 if (mData->flModifications & IsModified_SerialPorts)
12059 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12060 if ( mSerialPorts[slot]
12061 && mSerialPorts[slot]->isModified())
12062 {
12063 mSerialPorts[slot]->rollback();
12064 serialPorts[slot] = mSerialPorts[slot];
12065 }
12066
12067 if (mData->flModifications & IsModified_ParallelPorts)
12068 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12069 if ( mParallelPorts[slot]
12070 && mParallelPorts[slot]->isModified())
12071 {
12072 mParallelPorts[slot]->rollback();
12073 parallelPorts[slot] = mParallelPorts[slot];
12074 }
12075
12076 if (aNotify)
12077 {
12078 /* inform the direct session about changes */
12079
12080 ComObjPtr<Machine> that = this;
12081 uint32_t flModifications = mData->flModifications;
12082 alock.release();
12083
12084 if (flModifications & IsModified_SharedFolders)
12085 that->onSharedFolderChange();
12086
12087 if (flModifications & IsModified_VRDEServer)
12088 that->onVRDEServerChange(/* aRestart */ TRUE);
12089 if (flModifications & IsModified_USB)
12090 that->onUSBControllerChange();
12091
12092 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
12093 if (networkAdapters[slot])
12094 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
12095 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
12096 if (serialPorts[slot])
12097 that->onSerialPortChange(serialPorts[slot]);
12098 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
12099 if (parallelPorts[slot])
12100 that->onParallelPortChange(parallelPorts[slot]);
12101
12102 if (flModifications & IsModified_Storage)
12103 that->onStorageControllerChange();
12104
12105#if 0
12106 if (flModifications & IsModified_BandwidthControl)
12107 that->onBandwidthControlChange();
12108#endif
12109 }
12110}
12111
12112/**
12113 * Commits all the changes to machine settings.
12114 *
12115 * Note that this operation is supposed to never fail.
12116 *
12117 * @note Locks this object and children for writing.
12118 */
12119void Machine::commit()
12120{
12121 AutoCaller autoCaller(this);
12122 AssertComRCReturnVoid(autoCaller.rc());
12123
12124 AutoCaller peerCaller(mPeer);
12125 AssertComRCReturnVoid(peerCaller.rc());
12126
12127 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12128
12129 /*
12130 * use safe commit to ensure Snapshot machines (that share mUserData)
12131 * will still refer to a valid memory location
12132 */
12133 mUserData.commitCopy();
12134
12135 mHWData.commit();
12136
12137 if (mMediaData.isBackedUp())
12138 commitMedia(Global::IsOnline(mData->mMachineState));
12139
12140 mBIOSSettings->commit();
12141 mVRDEServer->commit();
12142 mAudioAdapter->commit();
12143 mUSBDeviceFilters->commit();
12144 mBandwidthControl->commit();
12145
12146 /* Since mNetworkAdapters is a list which might have been changed (resized)
12147 * without using the Backupable<> template we need to handle the copying
12148 * of the list entries manually, including the creation of peers for the
12149 * new objects. */
12150 bool commitNetworkAdapters = false;
12151 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12152 if (mPeer)
12153 {
12154 /* commit everything, even the ones which will go away */
12155 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12156 mNetworkAdapters[slot]->commit();
12157 /* copy over the new entries, creating a peer and uninit the original */
12158 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12159 for (size_t slot = 0; slot < newSize; slot++)
12160 {
12161 /* look if this adapter has a peer device */
12162 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
12163 if (!peer)
12164 {
12165 /* no peer means the adapter is a newly created one;
12166 * create a peer owning data this data share it with */
12167 peer.createObject();
12168 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12169 }
12170 mPeer->mNetworkAdapters[slot] = peer;
12171 }
12172 /* uninit any no longer needed network adapters */
12173 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
12174 mNetworkAdapters[slot]->uninit();
12175 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
12176 {
12177 if (mPeer->mNetworkAdapters[slot])
12178 mPeer->mNetworkAdapters[slot]->uninit();
12179 }
12180 /* Keep the original network adapter count until this point, so that
12181 * discarding a chipset type change will not lose settings. */
12182 mNetworkAdapters.resize(newSize);
12183 mPeer->mNetworkAdapters.resize(newSize);
12184 }
12185 else
12186 {
12187 /* we have no peer (our parent is the newly created machine);
12188 * just commit changes to the network adapters */
12189 commitNetworkAdapters = true;
12190 }
12191 if (commitNetworkAdapters)
12192 {
12193 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12194 mNetworkAdapters[slot]->commit();
12195 }
12196
12197 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12198 mSerialPorts[slot]->commit();
12199 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12200 mParallelPorts[slot]->commit();
12201
12202 bool commitStorageControllers = false;
12203
12204 if (mStorageControllers.isBackedUp())
12205 {
12206 mStorageControllers.commit();
12207
12208 if (mPeer)
12209 {
12210 /* Commit all changes to new controllers (this will reshare data with
12211 * peers for those who have peers) */
12212 StorageControllerList *newList = new StorageControllerList();
12213 StorageControllerList::const_iterator it = mStorageControllers->begin();
12214 while (it != mStorageControllers->end())
12215 {
12216 (*it)->commit();
12217
12218 /* look if this controller has a peer device */
12219 ComObjPtr<StorageController> peer = (*it)->getPeer();
12220 if (!peer)
12221 {
12222 /* no peer means the device is a newly created one;
12223 * create a peer owning data this device share it with */
12224 peer.createObject();
12225 peer->init(mPeer, *it, true /* aReshare */);
12226 }
12227 else
12228 {
12229 /* remove peer from the old list */
12230 mPeer->mStorageControllers->remove(peer);
12231 }
12232 /* and add it to the new list */
12233 newList->push_back(peer);
12234
12235 ++it;
12236 }
12237
12238 /* uninit old peer's controllers that are left */
12239 it = mPeer->mStorageControllers->begin();
12240 while (it != mPeer->mStorageControllers->end())
12241 {
12242 (*it)->uninit();
12243 ++it;
12244 }
12245
12246 /* attach new list of controllers to our peer */
12247 mPeer->mStorageControllers.attach(newList);
12248 }
12249 else
12250 {
12251 /* we have no peer (our parent is the newly created machine);
12252 * just commit changes to devices */
12253 commitStorageControllers = true;
12254 }
12255 }
12256 else
12257 {
12258 /* the list of controllers itself is not changed,
12259 * just commit changes to controllers themselves */
12260 commitStorageControllers = true;
12261 }
12262
12263 if (commitStorageControllers)
12264 {
12265 StorageControllerList::const_iterator it = mStorageControllers->begin();
12266 while (it != mStorageControllers->end())
12267 {
12268 (*it)->commit();
12269 ++it;
12270 }
12271 }
12272
12273 bool commitUSBControllers = false;
12274
12275 if (mUSBControllers.isBackedUp())
12276 {
12277 mUSBControllers.commit();
12278
12279 if (mPeer)
12280 {
12281 /* Commit all changes to new controllers (this will reshare data with
12282 * peers for those who have peers) */
12283 USBControllerList *newList = new USBControllerList();
12284 USBControllerList::const_iterator it = mUSBControllers->begin();
12285 while (it != mUSBControllers->end())
12286 {
12287 (*it)->commit();
12288
12289 /* look if this controller has a peer device */
12290 ComObjPtr<USBController> peer = (*it)->getPeer();
12291 if (!peer)
12292 {
12293 /* no peer means the device is a newly created one;
12294 * create a peer owning data this device share it with */
12295 peer.createObject();
12296 peer->init(mPeer, *it, true /* aReshare */);
12297 }
12298 else
12299 {
12300 /* remove peer from the old list */
12301 mPeer->mUSBControllers->remove(peer);
12302 }
12303 /* and add it to the new list */
12304 newList->push_back(peer);
12305
12306 ++it;
12307 }
12308
12309 /* uninit old peer's controllers that are left */
12310 it = mPeer->mUSBControllers->begin();
12311 while (it != mPeer->mUSBControllers->end())
12312 {
12313 (*it)->uninit();
12314 ++it;
12315 }
12316
12317 /* attach new list of controllers to our peer */
12318 mPeer->mUSBControllers.attach(newList);
12319 }
12320 else
12321 {
12322 /* we have no peer (our parent is the newly created machine);
12323 * just commit changes to devices */
12324 commitUSBControllers = true;
12325 }
12326 }
12327 else
12328 {
12329 /* the list of controllers itself is not changed,
12330 * just commit changes to controllers themselves */
12331 commitUSBControllers = true;
12332 }
12333
12334 if (commitUSBControllers)
12335 {
12336 USBControllerList::const_iterator it = mUSBControllers->begin();
12337 while (it != mUSBControllers->end())
12338 {
12339 (*it)->commit();
12340 ++it;
12341 }
12342 }
12343
12344 if (isSessionMachine())
12345 {
12346 /* attach new data to the primary machine and reshare it */
12347 mPeer->mUserData.attach(mUserData);
12348 mPeer->mHWData.attach(mHWData);
12349 /* mMediaData is reshared by fixupMedia */
12350 // mPeer->mMediaData.attach(mMediaData);
12351 Assert(mPeer->mMediaData.data() == mMediaData.data());
12352 }
12353}
12354
12355/**
12356 * Copies all the hardware data from the given machine.
12357 *
12358 * Currently, only called when the VM is being restored from a snapshot. In
12359 * particular, this implies that the VM is not running during this method's
12360 * call.
12361 *
12362 * @note This method must be called from under this object's lock.
12363 *
12364 * @note This method doesn't call #commit(), so all data remains backed up and
12365 * unsaved.
12366 */
12367void Machine::copyFrom(Machine *aThat)
12368{
12369 AssertReturnVoid(!isSnapshotMachine());
12370 AssertReturnVoid(aThat->isSnapshotMachine());
12371
12372 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12373
12374 mHWData.assignCopy(aThat->mHWData);
12375
12376 // create copies of all shared folders (mHWData after attaching a copy
12377 // contains just references to original objects)
12378 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12379 it != mHWData->mSharedFolders.end();
12380 ++it)
12381 {
12382 ComObjPtr<SharedFolder> folder;
12383 folder.createObject();
12384 HRESULT rc = folder->initCopy(getMachine(), *it);
12385 AssertComRC(rc);
12386 *it = folder;
12387 }
12388
12389 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
12390 mVRDEServer->copyFrom(aThat->mVRDEServer);
12391 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
12392 mUSBDeviceFilters->copyFrom(aThat->mUSBDeviceFilters);
12393 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
12394
12395 /* create private copies of all controllers */
12396 mStorageControllers.backup();
12397 mStorageControllers->clear();
12398 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12399 it != aThat->mStorageControllers->end();
12400 ++it)
12401 {
12402 ComObjPtr<StorageController> ctrl;
12403 ctrl.createObject();
12404 ctrl->initCopy(this, *it);
12405 mStorageControllers->push_back(ctrl);
12406 }
12407
12408 /* create private copies of all USB controllers */
12409 mUSBControllers.backup();
12410 mUSBControllers->clear();
12411 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12412 it != aThat->mUSBControllers->end();
12413 ++it)
12414 {
12415 ComObjPtr<USBController> ctrl;
12416 ctrl.createObject();
12417 ctrl->initCopy(this, *it);
12418 mUSBControllers->push_back(ctrl);
12419 }
12420
12421 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12422 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12423 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
12424 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12425 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
12426 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12427 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
12428}
12429
12430/**
12431 * Returns whether the given storage controller is hotplug capable.
12432 *
12433 * @returns true if the controller supports hotplugging
12434 * false otherwise.
12435 * @param enmCtrlType The controller type to check for.
12436 */
12437bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12438{
12439 switch (enmCtrlType)
12440 {
12441 case StorageControllerType_IntelAhci:
12442 return true;
12443 case StorageControllerType_LsiLogic:
12444 case StorageControllerType_LsiLogicSas:
12445 case StorageControllerType_BusLogic:
12446 case StorageControllerType_PIIX3:
12447 case StorageControllerType_PIIX4:
12448 case StorageControllerType_ICH6:
12449 case StorageControllerType_I82078:
12450 default:
12451 return false;
12452 }
12453}
12454
12455#ifdef VBOX_WITH_RESOURCE_USAGE_API
12456
12457void Machine::getDiskList(MediaList &list)
12458{
12459 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12460 it != mMediaData->mAttachments.end();
12461 ++it)
12462 {
12463 MediumAttachment* pAttach = *it;
12464 /* just in case */
12465 AssertStmt(pAttach, continue);
12466
12467 AutoCaller localAutoCallerA(pAttach);
12468 if (FAILED(localAutoCallerA.rc())) continue;
12469
12470 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12471
12472 if (pAttach->getType() == DeviceType_HardDisk)
12473 list.push_back(pAttach->getMedium());
12474 }
12475}
12476
12477void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12478{
12479 AssertReturnVoid(isWriteLockOnCurrentThread());
12480 AssertPtrReturnVoid(aCollector);
12481
12482 pm::CollectorHAL *hal = aCollector->getHAL();
12483 /* Create sub metrics */
12484 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12485 "Percentage of processor time spent in user mode by the VM process.");
12486 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12487 "Percentage of processor time spent in kernel mode by the VM process.");
12488 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12489 "Size of resident portion of VM process in memory.");
12490 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12491 "Actual size of all VM disks combined.");
12492 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12493 "Network receive rate.");
12494 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12495 "Network transmit rate.");
12496 /* Create and register base metrics */
12497 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12498 cpuLoadUser, cpuLoadKernel);
12499 aCollector->registerBaseMetric(cpuLoad);
12500 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12501 ramUsageUsed);
12502 aCollector->registerBaseMetric(ramUsage);
12503 MediaList disks;
12504 getDiskList(disks);
12505 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12506 diskUsageUsed);
12507 aCollector->registerBaseMetric(diskUsage);
12508
12509 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12510 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12511 new pm::AggregateAvg()));
12512 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12513 new pm::AggregateMin()));
12514 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12515 new pm::AggregateMax()));
12516 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12517 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12518 new pm::AggregateAvg()));
12519 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12520 new pm::AggregateMin()));
12521 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12522 new pm::AggregateMax()));
12523
12524 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12525 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12526 new pm::AggregateAvg()));
12527 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12528 new pm::AggregateMin()));
12529 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12530 new pm::AggregateMax()));
12531
12532 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12533 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12534 new pm::AggregateAvg()));
12535 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12536 new pm::AggregateMin()));
12537 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12538 new pm::AggregateMax()));
12539
12540
12541 /* Guest metrics collector */
12542 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12543 aCollector->registerGuest(mCollectorGuest);
12544 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12545 this, __PRETTY_FUNCTION__, mCollectorGuest));
12546
12547 /* Create sub metrics */
12548 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12549 "Percentage of processor time spent in user mode as seen by the guest.");
12550 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12551 "Percentage of processor time spent in kernel mode as seen by the guest.");
12552 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12553 "Percentage of processor time spent idling as seen by the guest.");
12554
12555 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12556 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12557 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12558 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12559 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12560 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12561
12562 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12563
12564 /* Create and register base metrics */
12565 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12566 machineNetRx, machineNetTx);
12567 aCollector->registerBaseMetric(machineNetRate);
12568
12569 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12570 guestLoadUser, guestLoadKernel, guestLoadIdle);
12571 aCollector->registerBaseMetric(guestCpuLoad);
12572
12573 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12574 guestMemTotal, guestMemFree,
12575 guestMemBalloon, guestMemShared,
12576 guestMemCache, guestPagedTotal);
12577 aCollector->registerBaseMetric(guestCpuMem);
12578
12579 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12580 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12581 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12582 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12583
12584 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12585 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12586 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12587 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12588
12589 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12590 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12591 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12592 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12593
12594 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12595 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12596 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12597 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12598
12599 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12600 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12601 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12602 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12603
12604 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12605 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12606 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12607 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12608
12609 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12610 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12611 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12612 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12613
12614 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12615 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12616 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12617 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12618
12619 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12620 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12621 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12622 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12623
12624 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12625 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12626 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12627 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12628
12629 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12630 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12631 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12632 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12633}
12634
12635void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12636{
12637 AssertReturnVoid(isWriteLockOnCurrentThread());
12638
12639 if (aCollector)
12640 {
12641 aCollector->unregisterMetricsFor(aMachine);
12642 aCollector->unregisterBaseMetricsFor(aMachine);
12643 }
12644}
12645
12646#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12647
12648
12649////////////////////////////////////////////////////////////////////////////////
12650
12651DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12652
12653HRESULT SessionMachine::FinalConstruct()
12654{
12655 LogFlowThisFunc(("\n"));
12656
12657 mClientToken = NULL;
12658
12659 return BaseFinalConstruct();
12660}
12661
12662void SessionMachine::FinalRelease()
12663{
12664 LogFlowThisFunc(("\n"));
12665
12666 Assert(!mClientToken);
12667 /* paranoia, should not hang around any more */
12668 if (mClientToken)
12669 {
12670 delete mClientToken;
12671 mClientToken = NULL;
12672 }
12673
12674 uninit(Uninit::Unexpected);
12675
12676 BaseFinalRelease();
12677}
12678
12679/**
12680 * @note Must be called only by Machine::LockMachine() from its own write lock.
12681 */
12682HRESULT SessionMachine::init(Machine *aMachine)
12683{
12684 LogFlowThisFuncEnter();
12685 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12686
12687 AssertReturn(aMachine, E_INVALIDARG);
12688
12689 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12690
12691 /* Enclose the state transition NotReady->InInit->Ready */
12692 AutoInitSpan autoInitSpan(this);
12693 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12694
12695 HRESULT rc = S_OK;
12696
12697 /* create the machine client token */
12698 try
12699 {
12700 mClientToken = new ClientToken(aMachine);
12701 if (!mClientToken->isReady())
12702 {
12703 delete mClientToken;
12704 mClientToken = NULL;
12705 rc = E_FAIL;
12706 }
12707 }
12708 catch (std::bad_alloc &)
12709 {
12710 rc = E_OUTOFMEMORY;
12711 }
12712 if (FAILED(rc))
12713 return rc;
12714
12715 /* memorize the peer Machine */
12716 unconst(mPeer) = aMachine;
12717 /* share the parent pointer */
12718 unconst(mParent) = aMachine->mParent;
12719
12720 /* take the pointers to data to share */
12721 mData.share(aMachine->mData);
12722 mSSData.share(aMachine->mSSData);
12723
12724 mUserData.share(aMachine->mUserData);
12725 mHWData.share(aMachine->mHWData);
12726 mMediaData.share(aMachine->mMediaData);
12727
12728 mStorageControllers.allocate();
12729 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12730 it != aMachine->mStorageControllers->end();
12731 ++it)
12732 {
12733 ComObjPtr<StorageController> ctl;
12734 ctl.createObject();
12735 ctl->init(this, *it);
12736 mStorageControllers->push_back(ctl);
12737 }
12738
12739 mUSBControllers.allocate();
12740 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12741 it != aMachine->mUSBControllers->end();
12742 ++it)
12743 {
12744 ComObjPtr<USBController> ctl;
12745 ctl.createObject();
12746 ctl->init(this, *it);
12747 mUSBControllers->push_back(ctl);
12748 }
12749
12750 unconst(mBIOSSettings).createObject();
12751 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12752 /* create another VRDEServer object that will be mutable */
12753 unconst(mVRDEServer).createObject();
12754 mVRDEServer->init(this, aMachine->mVRDEServer);
12755 /* create another audio adapter object that will be mutable */
12756 unconst(mAudioAdapter).createObject();
12757 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12758 /* create a list of serial ports that will be mutable */
12759 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12760 {
12761 unconst(mSerialPorts[slot]).createObject();
12762 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12763 }
12764 /* create a list of parallel ports that will be mutable */
12765 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12766 {
12767 unconst(mParallelPorts[slot]).createObject();
12768 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12769 }
12770
12771 /* create another USB device filters object that will be mutable */
12772 unconst(mUSBDeviceFilters).createObject();
12773 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12774
12775 /* create a list of network adapters that will be mutable */
12776 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12777 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12778 {
12779 unconst(mNetworkAdapters[slot]).createObject();
12780 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12781 }
12782
12783 /* create another bandwidth control object that will be mutable */
12784 unconst(mBandwidthControl).createObject();
12785 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12786
12787 /* default is to delete saved state on Saved -> PoweredOff transition */
12788 mRemoveSavedState = true;
12789
12790 /* Confirm a successful initialization when it's the case */
12791 autoInitSpan.setSucceeded();
12792
12793 LogFlowThisFuncLeave();
12794 return rc;
12795}
12796
12797/**
12798 * Uninitializes this session object. If the reason is other than
12799 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12800 *
12801 * @param aReason uninitialization reason
12802 *
12803 * @note Locks mParent + this object for writing.
12804 */
12805void SessionMachine::uninit(Uninit::Reason aReason)
12806{
12807 LogFlowThisFuncEnter();
12808 LogFlowThisFunc(("reason=%d\n", aReason));
12809
12810 /*
12811 * Strongly reference ourselves to prevent this object deletion after
12812 * mData->mSession.mMachine.setNull() below (which can release the last
12813 * reference and call the destructor). Important: this must be done before
12814 * accessing any members (and before AutoUninitSpan that does it as well).
12815 * This self reference will be released as the very last step on return.
12816 */
12817 ComObjPtr<SessionMachine> selfRef = this;
12818
12819 /* Enclose the state transition Ready->InUninit->NotReady */
12820 AutoUninitSpan autoUninitSpan(this);
12821 if (autoUninitSpan.uninitDone())
12822 {
12823 LogFlowThisFunc(("Already uninitialized\n"));
12824 LogFlowThisFuncLeave();
12825 return;
12826 }
12827
12828 if (autoUninitSpan.initFailed())
12829 {
12830 /* We've been called by init() because it's failed. It's not really
12831 * necessary (nor it's safe) to perform the regular uninit sequence
12832 * below, the following is enough.
12833 */
12834 LogFlowThisFunc(("Initialization failed.\n"));
12835 /* destroy the machine client token */
12836 if (mClientToken)
12837 {
12838 delete mClientToken;
12839 mClientToken = NULL;
12840 }
12841 uninitDataAndChildObjects();
12842 mData.free();
12843 unconst(mParent) = NULL;
12844 unconst(mPeer) = NULL;
12845 LogFlowThisFuncLeave();
12846 return;
12847 }
12848
12849 MachineState_T lastState;
12850 {
12851 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12852 lastState = mData->mMachineState;
12853 }
12854 NOREF(lastState);
12855
12856#ifdef VBOX_WITH_USB
12857 // release all captured USB devices, but do this before requesting the locks below
12858 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12859 {
12860 /* Console::captureUSBDevices() is called in the VM process only after
12861 * setting the machine state to Starting or Restoring.
12862 * Console::detachAllUSBDevices() will be called upon successful
12863 * termination. So, we need to release USB devices only if there was
12864 * an abnormal termination of a running VM.
12865 *
12866 * This is identical to SessionMachine::DetachAllUSBDevices except
12867 * for the aAbnormal argument. */
12868 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
12869 AssertComRC(rc);
12870 NOREF(rc);
12871
12872 USBProxyService *service = mParent->host()->usbProxyService();
12873 if (service)
12874 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12875 }
12876#endif /* VBOX_WITH_USB */
12877
12878 // we need to lock this object in uninit() because the lock is shared
12879 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12880 // and others need mParent lock, and USB needs host lock.
12881 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12882
12883#ifdef VBOX_WITH_RESOURCE_USAGE_API
12884 /*
12885 * It is safe to call Machine::unregisterMetrics() here because
12886 * PerformanceCollector::samplerCallback no longer accesses guest methods
12887 * holding the lock.
12888 */
12889 unregisterMetrics(mParent->performanceCollector(), mPeer);
12890 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12891 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12892 this, __PRETTY_FUNCTION__, mCollectorGuest));
12893 if (mCollectorGuest)
12894 {
12895 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12896 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12897 mCollectorGuest = NULL;
12898 }
12899#endif
12900
12901 if (aReason == Uninit::Abnormal)
12902 {
12903 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12904 Global::IsOnlineOrTransient(lastState)));
12905
12906 /* reset the state to Aborted */
12907 if (mData->mMachineState != MachineState_Aborted)
12908 setMachineState(MachineState_Aborted);
12909 }
12910
12911 // any machine settings modified?
12912 if (mData->flModifications)
12913 {
12914 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12915 rollback(false /* aNotify */);
12916 }
12917
12918 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12919 || !mConsoleTaskData.mSnapshot);
12920 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12921 {
12922 LogWarningThisFunc(("canceling failed save state request!\n"));
12923 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12924 }
12925 else if (!mConsoleTaskData.mSnapshot.isNull())
12926 {
12927 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12928
12929 /* delete all differencing hard disks created (this will also attach
12930 * their parents back by rolling back mMediaData) */
12931 rollbackMedia();
12932
12933 // delete the saved state file (it might have been already created)
12934 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12935 // think it's still in use
12936 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12937 mConsoleTaskData.mSnapshot->uninit();
12938 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12939 }
12940
12941 if (!mData->mSession.mType.isEmpty())
12942 {
12943 /* mType is not null when this machine's process has been started by
12944 * Machine::LaunchVMProcess(), therefore it is our child. We
12945 * need to queue the PID to reap the process (and avoid zombies on
12946 * Linux). */
12947 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12948 mParent->addProcessToReap(mData->mSession.mPID);
12949 }
12950
12951 mData->mSession.mPID = NIL_RTPROCESS;
12952
12953 if (aReason == Uninit::Unexpected)
12954 {
12955 /* Uninitialization didn't come from #checkForDeath(), so tell the
12956 * client watcher thread to update the set of machines that have open
12957 * sessions. */
12958 mParent->updateClientWatcher();
12959 }
12960
12961 /* uninitialize all remote controls */
12962 if (mData->mSession.mRemoteControls.size())
12963 {
12964 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12965 mData->mSession.mRemoteControls.size()));
12966
12967 Data::Session::RemoteControlList::iterator it =
12968 mData->mSession.mRemoteControls.begin();
12969 while (it != mData->mSession.mRemoteControls.end())
12970 {
12971 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12972 HRESULT rc = (*it)->Uninitialize();
12973 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12974 if (FAILED(rc))
12975 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12976 ++it;
12977 }
12978 mData->mSession.mRemoteControls.clear();
12979 }
12980
12981 /*
12982 * An expected uninitialization can come only from #checkForDeath().
12983 * Otherwise it means that something's gone really wrong (for example,
12984 * the Session implementation has released the VirtualBox reference
12985 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12986 * etc). However, it's also possible, that the client releases the IPC
12987 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12988 * but the VirtualBox release event comes first to the server process.
12989 * This case is practically possible, so we should not assert on an
12990 * unexpected uninit, just log a warning.
12991 */
12992
12993 if ((aReason == Uninit::Unexpected))
12994 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12995
12996 if (aReason != Uninit::Normal)
12997 {
12998 mData->mSession.mDirectControl.setNull();
12999 }
13000 else
13001 {
13002 /* this must be null here (see #OnSessionEnd()) */
13003 Assert(mData->mSession.mDirectControl.isNull());
13004 Assert(mData->mSession.mState == SessionState_Unlocking);
13005 Assert(!mData->mSession.mProgress.isNull());
13006 }
13007 if (mData->mSession.mProgress)
13008 {
13009 if (aReason == Uninit::Normal)
13010 mData->mSession.mProgress->notifyComplete(S_OK);
13011 else
13012 mData->mSession.mProgress->notifyComplete(E_FAIL,
13013 COM_IIDOF(ISession),
13014 getComponentName(),
13015 tr("The VM session was aborted"));
13016 mData->mSession.mProgress.setNull();
13017 }
13018
13019 /* remove the association between the peer machine and this session machine */
13020 Assert( (SessionMachine*)mData->mSession.mMachine == this
13021 || aReason == Uninit::Unexpected);
13022
13023 /* reset the rest of session data */
13024 mData->mSession.mMachine.setNull();
13025 mData->mSession.mState = SessionState_Unlocked;
13026 mData->mSession.mType.setNull();
13027
13028 /* destroy the machine client token before leaving the exclusive lock */
13029 if (mClientToken)
13030 {
13031 delete mClientToken;
13032 mClientToken = NULL;
13033 }
13034
13035 /* fire an event */
13036 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13037
13038 uninitDataAndChildObjects();
13039
13040 /* free the essential data structure last */
13041 mData.free();
13042
13043 /* release the exclusive lock before setting the below two to NULL */
13044 multilock.release();
13045
13046 RTThreadSleep(500);
13047 mParent->AddRef();
13048 LONG c = mParent->Release();
13049 LogFlowThisFunc(("vbox ref=%d\n", c)); NOREF(c);
13050 unconst(mParent) = NULL;
13051 unconst(mPeer) = NULL;
13052
13053 LogFlowThisFuncLeave();
13054}
13055
13056// util::Lockable interface
13057////////////////////////////////////////////////////////////////////////////////
13058
13059/**
13060 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13061 * with the primary Machine instance (mPeer).
13062 */
13063RWLockHandle *SessionMachine::lockHandle() const
13064{
13065 AssertReturn(mPeer != NULL, NULL);
13066 return mPeer->lockHandle();
13067}
13068
13069// IInternalMachineControl methods
13070////////////////////////////////////////////////////////////////////////////////
13071
13072/**
13073 * Passes collected guest statistics to performance collector object
13074 */
13075STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13076 ULONG aCpuKernel, ULONG aCpuIdle,
13077 ULONG aMemTotal, ULONG aMemFree,
13078 ULONG aMemBalloon, ULONG aMemShared,
13079 ULONG aMemCache, ULONG aPageTotal,
13080 ULONG aAllocVMM, ULONG aFreeVMM,
13081 ULONG aBalloonedVMM, ULONG aSharedVMM,
13082 ULONG aVmNetRx, ULONG aVmNetTx)
13083{
13084#ifdef VBOX_WITH_RESOURCE_USAGE_API
13085 if (mCollectorGuest)
13086 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13087 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13088 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13089 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13090
13091 return S_OK;
13092#else
13093 NOREF(aValidStats);
13094 NOREF(aCpuUser);
13095 NOREF(aCpuKernel);
13096 NOREF(aCpuIdle);
13097 NOREF(aMemTotal);
13098 NOREF(aMemFree);
13099 NOREF(aMemBalloon);
13100 NOREF(aMemShared);
13101 NOREF(aMemCache);
13102 NOREF(aPageTotal);
13103 NOREF(aAllocVMM);
13104 NOREF(aFreeVMM);
13105 NOREF(aBalloonedVMM);
13106 NOREF(aSharedVMM);
13107 NOREF(aVmNetRx);
13108 NOREF(aVmNetTx);
13109 return E_NOTIMPL;
13110#endif
13111}
13112
13113/**
13114 * @note Locks this object for writing.
13115 */
13116STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
13117{
13118 AutoCaller autoCaller(this);
13119 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13120
13121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13122
13123 mRemoveSavedState = aRemove;
13124
13125 return S_OK;
13126}
13127
13128/**
13129 * @note Locks the same as #setMachineState() does.
13130 */
13131STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
13132{
13133 return setMachineState(aMachineState);
13134}
13135
13136/**
13137 * @note Locks this object for writing.
13138 */
13139STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
13140{
13141 LogFlowThisFunc(("aProgress=%p\n", aProgress));
13142 AutoCaller autoCaller(this);
13143 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13144
13145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13146
13147 if (mData->mSession.mState != SessionState_Locked)
13148 return VBOX_E_INVALID_OBJECT_STATE;
13149
13150 if (!mData->mSession.mProgress.isNull())
13151 mData->mSession.mProgress->setOtherProgressObject(aProgress);
13152
13153 LogFlowThisFunc(("returns S_OK.\n"));
13154 return S_OK;
13155}
13156
13157/**
13158 * @note Locks this object for writing.
13159 */
13160STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
13161{
13162 AutoCaller autoCaller(this);
13163 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13164
13165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13166
13167 if (mData->mSession.mState != SessionState_Locked)
13168 return VBOX_E_INVALID_OBJECT_STATE;
13169
13170 /* Finalize the LaunchVMProcess progress object. */
13171 if (mData->mSession.mProgress)
13172 {
13173 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
13174 mData->mSession.mProgress.setNull();
13175 }
13176
13177 if (SUCCEEDED((HRESULT)iResult))
13178 {
13179#ifdef VBOX_WITH_RESOURCE_USAGE_API
13180 /* The VM has been powered up successfully, so it makes sense
13181 * now to offer the performance metrics for a running machine
13182 * object. Doing it earlier wouldn't be safe. */
13183 registerMetrics(mParent->performanceCollector(), mPeer,
13184 mData->mSession.mPID);
13185#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13186 }
13187
13188 return S_OK;
13189}
13190
13191/**
13192 * @note Locks this object for writing.
13193 */
13194STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
13195{
13196 LogFlowThisFuncEnter();
13197
13198 CheckComArgOutPointerValid(aProgress);
13199
13200 AutoCaller autoCaller(this);
13201 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13202
13203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13204
13205 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13206 E_FAIL);
13207
13208 /* create a progress object to track operation completion */
13209 ComObjPtr<Progress> pProgress;
13210 pProgress.createObject();
13211 pProgress->init(getVirtualBox(),
13212 static_cast<IMachine *>(this) /* aInitiator */,
13213 Bstr(tr("Stopping the virtual machine")).raw(),
13214 FALSE /* aCancelable */);
13215
13216 /* fill in the console task data */
13217 mConsoleTaskData.mLastState = mData->mMachineState;
13218 mConsoleTaskData.mProgress = pProgress;
13219
13220 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13221 setMachineState(MachineState_Stopping);
13222
13223 pProgress.queryInterfaceTo(aProgress);
13224
13225 return S_OK;
13226}
13227
13228/**
13229 * @note Locks this object for writing.
13230 */
13231STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
13232{
13233 LogFlowThisFuncEnter();
13234
13235 AutoCaller autoCaller(this);
13236 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13237
13238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13239
13240 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
13241 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
13242 && mConsoleTaskData.mLastState != MachineState_Null,
13243 E_FAIL);
13244
13245 /*
13246 * On failure, set the state to the state we had when BeginPoweringDown()
13247 * was called (this is expected by Console::PowerDown() and the associated
13248 * task). On success the VM process already changed the state to
13249 * MachineState_PoweredOff, so no need to do anything.
13250 */
13251 if (FAILED(iResult))
13252 setMachineState(mConsoleTaskData.mLastState);
13253
13254 /* notify the progress object about operation completion */
13255 Assert(mConsoleTaskData.mProgress);
13256 if (SUCCEEDED(iResult))
13257 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13258 else
13259 {
13260 Utf8Str strErrMsg(aErrMsg);
13261 if (strErrMsg.length())
13262 mConsoleTaskData.mProgress->notifyComplete(iResult,
13263 COM_IIDOF(ISession),
13264 getComponentName(),
13265 strErrMsg.c_str());
13266 else
13267 mConsoleTaskData.mProgress->notifyComplete(iResult);
13268 }
13269
13270 /* clear out the temporary saved state data */
13271 mConsoleTaskData.mLastState = MachineState_Null;
13272 mConsoleTaskData.mProgress.setNull();
13273
13274 LogFlowThisFuncLeave();
13275 return S_OK;
13276}
13277
13278
13279/**
13280 * Goes through the USB filters of the given machine to see if the given
13281 * device matches any filter or not.
13282 *
13283 * @note Locks the same as USBController::hasMatchingFilter() does.
13284 */
13285STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13286 BOOL *aMatched,
13287 ULONG *aMaskedIfs)
13288{
13289 LogFlowThisFunc(("\n"));
13290
13291 CheckComArgNotNull(aUSBDevice);
13292 CheckComArgOutPointerValid(aMatched);
13293
13294 AutoCaller autoCaller(this);
13295 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13296
13297#ifdef VBOX_WITH_USB
13298 *aMatched = mUSBDeviceFilters->hasMatchingFilter(aUSBDevice, aMaskedIfs);
13299#else
13300 NOREF(aUSBDevice);
13301 NOREF(aMaskedIfs);
13302 *aMatched = FALSE;
13303#endif
13304
13305 return S_OK;
13306}
13307
13308/**
13309 * @note Locks the same as Host::captureUSBDevice() does.
13310 */
13311STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13312{
13313 LogFlowThisFunc(("\n"));
13314
13315 AutoCaller autoCaller(this);
13316 AssertComRCReturnRC(autoCaller.rc());
13317
13318#ifdef VBOX_WITH_USB
13319 /* if captureDeviceForVM() fails, it must have set extended error info */
13320 clearError();
13321 MultiResult rc = mParent->host()->checkUSBProxyService();
13322 if (FAILED(rc)) return rc;
13323
13324 USBProxyService *service = mParent->host()->usbProxyService();
13325 AssertReturn(service, E_FAIL);
13326 return service->captureDeviceForVM(this, Guid(aId).ref());
13327#else
13328 NOREF(aId);
13329 return E_NOTIMPL;
13330#endif
13331}
13332
13333/**
13334 * @note Locks the same as Host::detachUSBDevice() does.
13335 */
13336STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13337{
13338 LogFlowThisFunc(("\n"));
13339
13340 AutoCaller autoCaller(this);
13341 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13342
13343#ifdef VBOX_WITH_USB
13344 USBProxyService *service = mParent->host()->usbProxyService();
13345 AssertReturn(service, E_FAIL);
13346 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13347#else
13348 NOREF(aId);
13349 NOREF(aDone);
13350 return E_NOTIMPL;
13351#endif
13352}
13353
13354/**
13355 * Inserts all machine filters to the USB proxy service and then calls
13356 * Host::autoCaptureUSBDevices().
13357 *
13358 * Called by Console from the VM process upon VM startup.
13359 *
13360 * @note Locks what called methods lock.
13361 */
13362STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13363{
13364 LogFlowThisFunc(("\n"));
13365
13366 AutoCaller autoCaller(this);
13367 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13368
13369#ifdef VBOX_WITH_USB
13370 HRESULT rc = mUSBDeviceFilters->notifyProxy(true /* aInsertFilters */);
13371 AssertComRC(rc);
13372 NOREF(rc);
13373
13374 USBProxyService *service = mParent->host()->usbProxyService();
13375 AssertReturn(service, E_FAIL);
13376 return service->autoCaptureDevicesForVM(this);
13377#else
13378 return S_OK;
13379#endif
13380}
13381
13382/**
13383 * Removes all machine filters from the USB proxy service and then calls
13384 * Host::detachAllUSBDevices().
13385 *
13386 * Called by Console from the VM process upon normal VM termination or by
13387 * SessionMachine::uninit() upon abnormal VM termination (from under the
13388 * Machine/SessionMachine lock).
13389 *
13390 * @note Locks what called methods lock.
13391 */
13392STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13393{
13394 LogFlowThisFunc(("\n"));
13395
13396 AutoCaller autoCaller(this);
13397 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13398
13399#ifdef VBOX_WITH_USB
13400 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
13401 AssertComRC(rc);
13402 NOREF(rc);
13403
13404 USBProxyService *service = mParent->host()->usbProxyService();
13405 AssertReturn(service, E_FAIL);
13406 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13407#else
13408 NOREF(aDone);
13409 return S_OK;
13410#endif
13411}
13412
13413/**
13414 * @note Locks this object for writing.
13415 */
13416STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13417 IProgress **aProgress)
13418{
13419 LogFlowThisFuncEnter();
13420
13421 AssertReturn(aSession, E_INVALIDARG);
13422 AssertReturn(aProgress, E_INVALIDARG);
13423
13424 AutoCaller autoCaller(this);
13425
13426 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13427 /*
13428 * We don't assert below because it might happen that a non-direct session
13429 * informs us it is closed right after we've been uninitialized -- it's ok.
13430 */
13431 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13432
13433 /* get IInternalSessionControl interface */
13434 ComPtr<IInternalSessionControl> control(aSession);
13435
13436 ComAssertRet(!control.isNull(), E_INVALIDARG);
13437
13438 /* Creating a Progress object requires the VirtualBox lock, and
13439 * thus locking it here is required by the lock order rules. */
13440 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13441
13442 if (control == mData->mSession.mDirectControl)
13443 {
13444 ComAssertRet(aProgress, E_POINTER);
13445
13446 /* The direct session is being normally closed by the client process
13447 * ----------------------------------------------------------------- */
13448
13449 /* go to the closing state (essential for all open*Session() calls and
13450 * for #checkForDeath()) */
13451 Assert(mData->mSession.mState == SessionState_Locked);
13452 mData->mSession.mState = SessionState_Unlocking;
13453
13454 /* set direct control to NULL to release the remote instance */
13455 mData->mSession.mDirectControl.setNull();
13456 LogFlowThisFunc(("Direct control is set to NULL\n"));
13457
13458 if (mData->mSession.mProgress)
13459 {
13460 /* finalize the progress, someone might wait if a frontend
13461 * closes the session before powering on the VM. */
13462 mData->mSession.mProgress->notifyComplete(E_FAIL,
13463 COM_IIDOF(ISession),
13464 getComponentName(),
13465 tr("The VM session was closed before any attempt to power it on"));
13466 mData->mSession.mProgress.setNull();
13467 }
13468
13469 /* Create the progress object the client will use to wait until
13470 * #checkForDeath() is called to uninitialize this session object after
13471 * it releases the IPC semaphore.
13472 * Note! Because we're "reusing" mProgress here, this must be a proxy
13473 * object just like for LaunchVMProcess. */
13474 Assert(mData->mSession.mProgress.isNull());
13475 ComObjPtr<ProgressProxy> progress;
13476 progress.createObject();
13477 ComPtr<IUnknown> pPeer(mPeer);
13478 progress->init(mParent, pPeer,
13479 Bstr(tr("Closing session")).raw(),
13480 FALSE /* aCancelable */);
13481 progress.queryInterfaceTo(aProgress);
13482 mData->mSession.mProgress = progress;
13483 }
13484 else
13485 {
13486 /* the remote session is being normally closed */
13487 Data::Session::RemoteControlList::iterator it =
13488 mData->mSession.mRemoteControls.begin();
13489 while (it != mData->mSession.mRemoteControls.end())
13490 {
13491 if (control == *it)
13492 break;
13493 ++it;
13494 }
13495 BOOL found = it != mData->mSession.mRemoteControls.end();
13496 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13497 E_INVALIDARG);
13498 // This MUST be erase(it), not remove(*it) as the latter triggers a
13499 // very nasty use after free due to the place where the value "lives".
13500 mData->mSession.mRemoteControls.erase(it);
13501 }
13502
13503 /* signal the client watcher thread, because the client is going away */
13504 mParent->updateClientWatcher();
13505
13506 LogFlowThisFuncLeave();
13507 return S_OK;
13508}
13509
13510/**
13511 * @note Locks this object for writing.
13512 */
13513STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13514{
13515 LogFlowThisFuncEnter();
13516
13517 CheckComArgOutPointerValid(aProgress);
13518 CheckComArgOutPointerValid(aStateFilePath);
13519
13520 AutoCaller autoCaller(this);
13521 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13522
13523 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13524
13525 AssertReturn( mData->mMachineState == MachineState_Paused
13526 && mConsoleTaskData.mLastState == MachineState_Null
13527 && mConsoleTaskData.strStateFilePath.isEmpty(),
13528 E_FAIL);
13529
13530 /* create a progress object to track operation completion */
13531 ComObjPtr<Progress> pProgress;
13532 pProgress.createObject();
13533 pProgress->init(getVirtualBox(),
13534 static_cast<IMachine *>(this) /* aInitiator */,
13535 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13536 FALSE /* aCancelable */);
13537
13538 Utf8Str strStateFilePath;
13539 /* stateFilePath is null when the machine is not running */
13540 if (mData->mMachineState == MachineState_Paused)
13541 composeSavedStateFilename(strStateFilePath);
13542
13543 /* fill in the console task data */
13544 mConsoleTaskData.mLastState = mData->mMachineState;
13545 mConsoleTaskData.strStateFilePath = strStateFilePath;
13546 mConsoleTaskData.mProgress = pProgress;
13547
13548 /* set the state to Saving (this is expected by Console::SaveState()) */
13549 setMachineState(MachineState_Saving);
13550
13551 strStateFilePath.cloneTo(aStateFilePath);
13552 pProgress.queryInterfaceTo(aProgress);
13553
13554 return S_OK;
13555}
13556
13557/**
13558 * @note Locks mParent + this object for writing.
13559 */
13560STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13561{
13562 LogFlowThisFunc(("\n"));
13563
13564 AutoCaller autoCaller(this);
13565 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13566
13567 /* endSavingState() need mParent lock */
13568 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13569
13570 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13571 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13572 && mConsoleTaskData.mLastState != MachineState_Null
13573 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13574 E_FAIL);
13575
13576 /*
13577 * On failure, set the state to the state we had when BeginSavingState()
13578 * was called (this is expected by Console::SaveState() and the associated
13579 * task). On success the VM process already changed the state to
13580 * MachineState_Saved, so no need to do anything.
13581 */
13582 if (FAILED(iResult))
13583 setMachineState(mConsoleTaskData.mLastState);
13584
13585 return endSavingState(iResult, aErrMsg);
13586}
13587
13588/**
13589 * @note Locks this object for writing.
13590 */
13591STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13592{
13593 LogFlowThisFunc(("\n"));
13594
13595 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13596
13597 AutoCaller autoCaller(this);
13598 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13599
13600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13601
13602 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13603 || mData->mMachineState == MachineState_Teleported
13604 || mData->mMachineState == MachineState_Aborted
13605 , E_FAIL); /** @todo setError. */
13606
13607 Utf8Str stateFilePathFull = aSavedStateFile;
13608 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13609 if (RT_FAILURE(vrc))
13610 return setError(VBOX_E_FILE_ERROR,
13611 tr("Invalid saved state file path '%ls' (%Rrc)"),
13612 aSavedStateFile,
13613 vrc);
13614
13615 mSSData->strStateFilePath = stateFilePathFull;
13616
13617 /* The below setMachineState() will detect the state transition and will
13618 * update the settings file */
13619
13620 return setMachineState(MachineState_Saved);
13621}
13622
13623STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13624 ComSafeArrayOut(BSTR, aValues),
13625 ComSafeArrayOut(LONG64, aTimestamps),
13626 ComSafeArrayOut(BSTR, aFlags))
13627{
13628 LogFlowThisFunc(("\n"));
13629
13630#ifdef VBOX_WITH_GUEST_PROPS
13631 using namespace guestProp;
13632
13633 AutoCaller autoCaller(this);
13634 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13635
13636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13637
13638 CheckComArgOutSafeArrayPointerValid(aNames);
13639 CheckComArgOutSafeArrayPointerValid(aValues);
13640 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13641 CheckComArgOutSafeArrayPointerValid(aFlags);
13642
13643 size_t cEntries = mHWData->mGuestProperties.size();
13644 com::SafeArray<BSTR> names(cEntries);
13645 com::SafeArray<BSTR> values(cEntries);
13646 com::SafeArray<LONG64> timestamps(cEntries);
13647 com::SafeArray<BSTR> flags(cEntries);
13648 unsigned i = 0;
13649 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13650 it != mHWData->mGuestProperties.end();
13651 ++it)
13652 {
13653 char szFlags[MAX_FLAGS_LEN + 1];
13654 it->first.cloneTo(&names[i]);
13655 it->second.strValue.cloneTo(&values[i]);
13656 timestamps[i] = it->second.mTimestamp;
13657 /* If it is NULL, keep it NULL. */
13658 if (it->second.mFlags)
13659 {
13660 writeFlags(it->second.mFlags, szFlags);
13661 Bstr(szFlags).cloneTo(&flags[i]);
13662 }
13663 else
13664 flags[i] = NULL;
13665 ++i;
13666 }
13667 names.detachTo(ComSafeArrayOutArg(aNames));
13668 values.detachTo(ComSafeArrayOutArg(aValues));
13669 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13670 flags.detachTo(ComSafeArrayOutArg(aFlags));
13671 return S_OK;
13672#else
13673 ReturnComNotImplemented();
13674#endif
13675}
13676
13677STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13678 IN_BSTR aValue,
13679 LONG64 aTimestamp,
13680 IN_BSTR aFlags)
13681{
13682 LogFlowThisFunc(("\n"));
13683
13684#ifdef VBOX_WITH_GUEST_PROPS
13685 using namespace guestProp;
13686
13687 CheckComArgStrNotEmptyOrNull(aName);
13688 CheckComArgNotNull(aValue);
13689 CheckComArgNotNull(aFlags);
13690
13691 try
13692 {
13693 /*
13694 * Convert input up front.
13695 */
13696 Utf8Str utf8Name(aName);
13697 uint32_t fFlags = NILFLAG;
13698 if (aFlags)
13699 {
13700 Utf8Str utf8Flags(aFlags);
13701 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13702 AssertRCReturn(vrc, E_INVALIDARG);
13703 }
13704
13705 /*
13706 * Now grab the object lock, validate the state and do the update.
13707 */
13708 AutoCaller autoCaller(this);
13709 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13710
13711 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13712
13713 switch (mData->mMachineState)
13714 {
13715 case MachineState_Paused:
13716 case MachineState_Running:
13717 case MachineState_Teleporting:
13718 case MachineState_TeleportingPausedVM:
13719 case MachineState_LiveSnapshotting:
13720 case MachineState_DeletingSnapshotOnline:
13721 case MachineState_DeletingSnapshotPaused:
13722 case MachineState_Saving:
13723 case MachineState_Stopping:
13724 break;
13725
13726 default:
13727 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13728 VBOX_E_INVALID_VM_STATE);
13729 }
13730
13731 setModified(IsModified_MachineData);
13732 mHWData.backup();
13733
13734 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13735 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13736 if (it != mHWData->mGuestProperties.end())
13737 {
13738 if (!fDelete)
13739 {
13740 it->second.strValue = aValue;
13741 it->second.mTimestamp = aTimestamp;
13742 it->second.mFlags = fFlags;
13743 }
13744 else
13745 mHWData->mGuestProperties.erase(it);
13746
13747 mData->mGuestPropertiesModified = TRUE;
13748 }
13749 else if (!fDelete)
13750 {
13751 HWData::GuestProperty prop;
13752 prop.strValue = aValue;
13753 prop.mTimestamp = aTimestamp;
13754 prop.mFlags = fFlags;
13755
13756 mHWData->mGuestProperties[utf8Name] = prop;
13757 mData->mGuestPropertiesModified = TRUE;
13758 }
13759
13760 /*
13761 * Send a callback notification if appropriate
13762 */
13763 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13764 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13765 RTSTR_MAX,
13766 utf8Name.c_str(),
13767 RTSTR_MAX, NULL)
13768 )
13769 {
13770 alock.release();
13771
13772 mParent->onGuestPropertyChange(mData->mUuid,
13773 aName,
13774 aValue,
13775 aFlags);
13776 }
13777 }
13778 catch (...)
13779 {
13780 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13781 }
13782 return S_OK;
13783#else
13784 ReturnComNotImplemented();
13785#endif
13786}
13787
13788STDMETHODIMP SessionMachine::LockMedia()
13789{
13790 AutoCaller autoCaller(this);
13791 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13792
13793 AutoMultiWriteLock2 alock(this->lockHandle(),
13794 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13795
13796 AssertReturn( mData->mMachineState == MachineState_Starting
13797 || mData->mMachineState == MachineState_Restoring
13798 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13799
13800 clearError();
13801 alock.release();
13802 return lockMedia();
13803}
13804
13805STDMETHODIMP SessionMachine::UnlockMedia()
13806{
13807 unlockMedia();
13808 return S_OK;
13809}
13810
13811STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13812 IMediumAttachment **aNewAttachment)
13813{
13814 CheckComArgNotNull(aAttachment);
13815 CheckComArgOutPointerValid(aNewAttachment);
13816
13817 AutoCaller autoCaller(this);
13818 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13819
13820 // request the host lock first, since might be calling Host methods for getting host drives;
13821 // next, protect the media tree all the while we're in here, as well as our member variables
13822 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13823 this->lockHandle(),
13824 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13825
13826 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13827
13828 Bstr ctrlName;
13829 LONG lPort;
13830 LONG lDevice;
13831 bool fTempEject;
13832 {
13833 AutoCaller autoAttachCaller(this);
13834 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13835
13836 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13837
13838 /* Need to query the details first, as the IMediumAttachment reference
13839 * might be to the original settings, which we are going to change. */
13840 ctrlName = pAttach->getControllerName();
13841 lPort = pAttach->getPort();
13842 lDevice = pAttach->getDevice();
13843 fTempEject = pAttach->getTempEject();
13844 }
13845
13846 if (!fTempEject)
13847 {
13848 /* Remember previously mounted medium. The medium before taking the
13849 * backup is not necessarily the same thing. */
13850 ComObjPtr<Medium> oldmedium;
13851 oldmedium = pAttach->getMedium();
13852
13853 setModified(IsModified_Storage);
13854 mMediaData.backup();
13855
13856 // The backup operation makes the pAttach reference point to the
13857 // old settings. Re-get the correct reference.
13858 pAttach = findAttachment(mMediaData->mAttachments,
13859 ctrlName.raw(),
13860 lPort,
13861 lDevice);
13862
13863 {
13864 AutoCaller autoAttachCaller(this);
13865 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13866
13867 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13868 if (!oldmedium.isNull())
13869 oldmedium->removeBackReference(mData->mUuid);
13870
13871 pAttach->updateMedium(NULL);
13872 pAttach->updateEjected();
13873 }
13874
13875 setModified(IsModified_Storage);
13876 }
13877 else
13878 {
13879 {
13880 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13881 pAttach->updateEjected();
13882 }
13883 }
13884
13885 pAttach.queryInterfaceTo(aNewAttachment);
13886
13887 return S_OK;
13888}
13889
13890// public methods only for internal purposes
13891/////////////////////////////////////////////////////////////////////////////
13892
13893/**
13894 * Called from the client watcher thread to check for expected or unexpected
13895 * death of the client process that has a direct session to this machine.
13896 *
13897 * On Win32 and on OS/2, this method is called only when we've got the
13898 * mutex (i.e. the client has either died or terminated normally) so it always
13899 * returns @c true (the client is terminated, the session machine is
13900 * uninitialized).
13901 *
13902 * On other platforms, the method returns @c true if the client process has
13903 * terminated normally or abnormally and the session machine was uninitialized,
13904 * and @c false if the client process is still alive.
13905 *
13906 * @note Locks this object for writing.
13907 */
13908bool SessionMachine::checkForDeath()
13909{
13910 Uninit::Reason reason;
13911 bool terminated = false;
13912
13913 /* Enclose autoCaller with a block because calling uninit() from under it
13914 * will deadlock. */
13915 {
13916 AutoCaller autoCaller(this);
13917 if (!autoCaller.isOk())
13918 {
13919 /* return true if not ready, to cause the client watcher to exclude
13920 * the corresponding session from watching */
13921 LogFlowThisFunc(("Already uninitialized!\n"));
13922 return true;
13923 }
13924
13925 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13926
13927 /* Determine the reason of death: if the session state is Closing here,
13928 * everything is fine. Otherwise it means that the client did not call
13929 * OnSessionEnd() before it released the IPC semaphore. This may happen
13930 * either because the client process has abnormally terminated, or
13931 * because it simply forgot to call ISession::Close() before exiting. We
13932 * threat the latter also as an abnormal termination (see
13933 * Session::uninit() for details). */
13934 reason = mData->mSession.mState == SessionState_Unlocking ?
13935 Uninit::Normal :
13936 Uninit::Abnormal;
13937
13938 if (mClientToken)
13939 terminated = mClientToken->release();
13940 } /* AutoCaller block */
13941
13942 if (terminated)
13943 uninit(reason);
13944
13945 return terminated;
13946}
13947
13948void SessionMachine::getTokenId(Utf8Str &strTokenId)
13949{
13950 LogFlowThisFunc(("\n"));
13951
13952 strTokenId.setNull();
13953
13954 AutoCaller autoCaller(this);
13955 AssertComRCReturnVoid(autoCaller.rc());
13956
13957 Assert(mClientToken);
13958 if (mClientToken)
13959 mClientToken->getId(strTokenId);
13960}
13961
13962Machine::ClientToken *SessionMachine::getClientToken()
13963{
13964 LogFlowThisFunc(("\n"));
13965
13966 AutoCaller autoCaller(this);
13967 AssertComRCReturn(autoCaller.rc(), NULL);
13968
13969 return mClientToken;
13970}
13971
13972
13973/**
13974 * @note Locks this object for reading.
13975 */
13976HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13977{
13978 LogFlowThisFunc(("\n"));
13979
13980 AutoCaller autoCaller(this);
13981 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13982
13983 ComPtr<IInternalSessionControl> directControl;
13984 {
13985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13986 directControl = mData->mSession.mDirectControl;
13987 }
13988
13989 /* ignore notifications sent after #OnSessionEnd() is called */
13990 if (!directControl)
13991 return S_OK;
13992
13993 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13994}
13995
13996/**
13997 * @note Locks this object for reading.
13998 */
13999HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14000 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
14001{
14002 LogFlowThisFunc(("\n"));
14003
14004 AutoCaller autoCaller(this);
14005 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14006
14007 ComPtr<IInternalSessionControl> directControl;
14008 {
14009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14010 directControl = mData->mSession.mDirectControl;
14011 }
14012
14013 /* ignore notifications sent after #OnSessionEnd() is called */
14014 if (!directControl)
14015 return S_OK;
14016 /*
14017 * instead acting like callback we ask IVirtualBox deliver corresponding event
14018 */
14019
14020 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14021 return S_OK;
14022}
14023
14024/**
14025 * @note Locks this object for reading.
14026 */
14027HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
14028{
14029 LogFlowThisFunc(("\n"));
14030
14031 AutoCaller autoCaller(this);
14032 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14033
14034 ComPtr<IInternalSessionControl> directControl;
14035 {
14036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14037 directControl = mData->mSession.mDirectControl;
14038 }
14039
14040 /* ignore notifications sent after #OnSessionEnd() is called */
14041 if (!directControl)
14042 return S_OK;
14043
14044 return directControl->OnSerialPortChange(serialPort);
14045}
14046
14047/**
14048 * @note Locks this object for reading.
14049 */
14050HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
14051{
14052 LogFlowThisFunc(("\n"));
14053
14054 AutoCaller autoCaller(this);
14055 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14056
14057 ComPtr<IInternalSessionControl> directControl;
14058 {
14059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14060 directControl = mData->mSession.mDirectControl;
14061 }
14062
14063 /* ignore notifications sent after #OnSessionEnd() is called */
14064 if (!directControl)
14065 return S_OK;
14066
14067 return directControl->OnParallelPortChange(parallelPort);
14068}
14069
14070/**
14071 * @note Locks this object for reading.
14072 */
14073HRESULT SessionMachine::onStorageControllerChange()
14074{
14075 LogFlowThisFunc(("\n"));
14076
14077 AutoCaller autoCaller(this);
14078 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14079
14080 ComPtr<IInternalSessionControl> directControl;
14081 {
14082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14083 directControl = mData->mSession.mDirectControl;
14084 }
14085
14086 /* ignore notifications sent after #OnSessionEnd() is called */
14087 if (!directControl)
14088 return S_OK;
14089
14090 return directControl->OnStorageControllerChange();
14091}
14092
14093/**
14094 * @note Locks this object for reading.
14095 */
14096HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14097{
14098 LogFlowThisFunc(("\n"));
14099
14100 AutoCaller autoCaller(this);
14101 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14102
14103 ComPtr<IInternalSessionControl> directControl;
14104 {
14105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14106 directControl = mData->mSession.mDirectControl;
14107 }
14108
14109 /* ignore notifications sent after #OnSessionEnd() is called */
14110 if (!directControl)
14111 return S_OK;
14112
14113 return directControl->OnMediumChange(aAttachment, aForce);
14114}
14115
14116/**
14117 * @note Locks this object for reading.
14118 */
14119HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
14120{
14121 LogFlowThisFunc(("\n"));
14122
14123 AutoCaller autoCaller(this);
14124 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14125
14126 ComPtr<IInternalSessionControl> directControl;
14127 {
14128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14129 directControl = mData->mSession.mDirectControl;
14130 }
14131
14132 /* ignore notifications sent after #OnSessionEnd() is called */
14133 if (!directControl)
14134 return S_OK;
14135
14136 return directControl->OnCPUChange(aCPU, aRemove);
14137}
14138
14139HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
14140{
14141 LogFlowThisFunc(("\n"));
14142
14143 AutoCaller autoCaller(this);
14144 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14145
14146 ComPtr<IInternalSessionControl> directControl;
14147 {
14148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14149 directControl = mData->mSession.mDirectControl;
14150 }
14151
14152 /* ignore notifications sent after #OnSessionEnd() is called */
14153 if (!directControl)
14154 return S_OK;
14155
14156 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14157}
14158
14159/**
14160 * @note Locks this object for reading.
14161 */
14162HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
14163{
14164 LogFlowThisFunc(("\n"));
14165
14166 AutoCaller autoCaller(this);
14167 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14168
14169 ComPtr<IInternalSessionControl> directControl;
14170 {
14171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14172 directControl = mData->mSession.mDirectControl;
14173 }
14174
14175 /* ignore notifications sent after #OnSessionEnd() is called */
14176 if (!directControl)
14177 return S_OK;
14178
14179 return directControl->OnVRDEServerChange(aRestart);
14180}
14181
14182/**
14183 * @note Locks this object for reading.
14184 */
14185HRESULT SessionMachine::onVideoCaptureChange()
14186{
14187 LogFlowThisFunc(("\n"));
14188
14189 AutoCaller autoCaller(this);
14190 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14191
14192 ComPtr<IInternalSessionControl> directControl;
14193 {
14194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14195 directControl = mData->mSession.mDirectControl;
14196 }
14197
14198 /* ignore notifications sent after #OnSessionEnd() is called */
14199 if (!directControl)
14200 return S_OK;
14201
14202 return directControl->OnVideoCaptureChange();
14203}
14204
14205/**
14206 * @note Locks this object for reading.
14207 */
14208HRESULT SessionMachine::onUSBControllerChange()
14209{
14210 LogFlowThisFunc(("\n"));
14211
14212 AutoCaller autoCaller(this);
14213 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14214
14215 ComPtr<IInternalSessionControl> directControl;
14216 {
14217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14218 directControl = mData->mSession.mDirectControl;
14219 }
14220
14221 /* ignore notifications sent after #OnSessionEnd() is called */
14222 if (!directControl)
14223 return S_OK;
14224
14225 return directControl->OnUSBControllerChange();
14226}
14227
14228/**
14229 * @note Locks this object for reading.
14230 */
14231HRESULT SessionMachine::onSharedFolderChange()
14232{
14233 LogFlowThisFunc(("\n"));
14234
14235 AutoCaller autoCaller(this);
14236 AssertComRCReturnRC(autoCaller.rc());
14237
14238 ComPtr<IInternalSessionControl> directControl;
14239 {
14240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14241 directControl = mData->mSession.mDirectControl;
14242 }
14243
14244 /* ignore notifications sent after #OnSessionEnd() is called */
14245 if (!directControl)
14246 return S_OK;
14247
14248 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14249}
14250
14251/**
14252 * @note Locks this object for reading.
14253 */
14254HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14255{
14256 LogFlowThisFunc(("\n"));
14257
14258 AutoCaller autoCaller(this);
14259 AssertComRCReturnRC(autoCaller.rc());
14260
14261 ComPtr<IInternalSessionControl> directControl;
14262 {
14263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14264 directControl = mData->mSession.mDirectControl;
14265 }
14266
14267 /* ignore notifications sent after #OnSessionEnd() is called */
14268 if (!directControl)
14269 return S_OK;
14270
14271 return directControl->OnClipboardModeChange(aClipboardMode);
14272}
14273
14274/**
14275 * @note Locks this object for reading.
14276 */
14277HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14278{
14279 LogFlowThisFunc(("\n"));
14280
14281 AutoCaller autoCaller(this);
14282 AssertComRCReturnRC(autoCaller.rc());
14283
14284 ComPtr<IInternalSessionControl> directControl;
14285 {
14286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14287 directControl = mData->mSession.mDirectControl;
14288 }
14289
14290 /* ignore notifications sent after #OnSessionEnd() is called */
14291 if (!directControl)
14292 return S_OK;
14293
14294 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14295}
14296
14297/**
14298 * @note Locks this object for reading.
14299 */
14300HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14301{
14302 LogFlowThisFunc(("\n"));
14303
14304 AutoCaller autoCaller(this);
14305 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14306
14307 ComPtr<IInternalSessionControl> directControl;
14308 {
14309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14310 directControl = mData->mSession.mDirectControl;
14311 }
14312
14313 /* ignore notifications sent after #OnSessionEnd() is called */
14314 if (!directControl)
14315 return S_OK;
14316
14317 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14318}
14319
14320/**
14321 * @note Locks this object for reading.
14322 */
14323HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14324{
14325 LogFlowThisFunc(("\n"));
14326
14327 AutoCaller autoCaller(this);
14328 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14329
14330 ComPtr<IInternalSessionControl> directControl;
14331 {
14332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14333 directControl = mData->mSession.mDirectControl;
14334 }
14335
14336 /* ignore notifications sent after #OnSessionEnd() is called */
14337 if (!directControl)
14338 return S_OK;
14339
14340 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14341}
14342
14343/**
14344 * Returns @c true if this machine's USB controller reports it has a matching
14345 * filter for the given USB device and @c false otherwise.
14346 *
14347 * @note locks this object for reading.
14348 */
14349bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14350{
14351 AutoCaller autoCaller(this);
14352 /* silently return if not ready -- this method may be called after the
14353 * direct machine session has been called */
14354 if (!autoCaller.isOk())
14355 return false;
14356
14357#ifdef VBOX_WITH_USB
14358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14359
14360 switch (mData->mMachineState)
14361 {
14362 case MachineState_Starting:
14363 case MachineState_Restoring:
14364 case MachineState_TeleportingIn:
14365 case MachineState_Paused:
14366 case MachineState_Running:
14367 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14368 * elsewhere... */
14369 alock.release();
14370 return mUSBDeviceFilters->hasMatchingFilter(aDevice, aMaskedIfs);
14371 default: break;
14372 }
14373#else
14374 NOREF(aDevice);
14375 NOREF(aMaskedIfs);
14376#endif
14377 return false;
14378}
14379
14380/**
14381 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14382 */
14383HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14384 IVirtualBoxErrorInfo *aError,
14385 ULONG aMaskedIfs)
14386{
14387 LogFlowThisFunc(("\n"));
14388
14389 AutoCaller autoCaller(this);
14390
14391 /* This notification may happen after the machine object has been
14392 * uninitialized (the session was closed), so don't assert. */
14393 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14394
14395 ComPtr<IInternalSessionControl> directControl;
14396 {
14397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14398 directControl = mData->mSession.mDirectControl;
14399 }
14400
14401 /* fail on notifications sent after #OnSessionEnd() is called, it is
14402 * expected by the caller */
14403 if (!directControl)
14404 return E_FAIL;
14405
14406 /* No locks should be held at this point. */
14407 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14408 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14409
14410 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14411}
14412
14413/**
14414 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14415 */
14416HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14417 IVirtualBoxErrorInfo *aError)
14418{
14419 LogFlowThisFunc(("\n"));
14420
14421 AutoCaller autoCaller(this);
14422
14423 /* This notification may happen after the machine object has been
14424 * uninitialized (the session was closed), so don't assert. */
14425 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14426
14427 ComPtr<IInternalSessionControl> directControl;
14428 {
14429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14430 directControl = mData->mSession.mDirectControl;
14431 }
14432
14433 /* fail on notifications sent after #OnSessionEnd() is called, it is
14434 * expected by the caller */
14435 if (!directControl)
14436 return E_FAIL;
14437
14438 /* No locks should be held at this point. */
14439 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14440 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14441
14442 return directControl->OnUSBDeviceDetach(aId, aError);
14443}
14444
14445// protected methods
14446/////////////////////////////////////////////////////////////////////////////
14447
14448/**
14449 * Helper method to finalize saving the state.
14450 *
14451 * @note Must be called from under this object's lock.
14452 *
14453 * @param aRc S_OK if the snapshot has been taken successfully
14454 * @param aErrMsg human readable error message for failure
14455 *
14456 * @note Locks mParent + this objects for writing.
14457 */
14458HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14459{
14460 LogFlowThisFuncEnter();
14461
14462 AutoCaller autoCaller(this);
14463 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14464
14465 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14466
14467 HRESULT rc = S_OK;
14468
14469 if (SUCCEEDED(aRc))
14470 {
14471 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14472
14473 /* save all VM settings */
14474 rc = saveSettings(NULL);
14475 // no need to check whether VirtualBox.xml needs saving also since
14476 // we can't have a name change pending at this point
14477 }
14478 else
14479 {
14480 // delete the saved state file (it might have been already created);
14481 // we need not check whether this is shared with a snapshot here because
14482 // we certainly created this saved state file here anew
14483 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14484 }
14485
14486 /* notify the progress object about operation completion */
14487 Assert(mConsoleTaskData.mProgress);
14488 if (SUCCEEDED(aRc))
14489 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14490 else
14491 {
14492 if (aErrMsg.length())
14493 mConsoleTaskData.mProgress->notifyComplete(aRc,
14494 COM_IIDOF(ISession),
14495 getComponentName(),
14496 aErrMsg.c_str());
14497 else
14498 mConsoleTaskData.mProgress->notifyComplete(aRc);
14499 }
14500
14501 /* clear out the temporary saved state data */
14502 mConsoleTaskData.mLastState = MachineState_Null;
14503 mConsoleTaskData.strStateFilePath.setNull();
14504 mConsoleTaskData.mProgress.setNull();
14505
14506 LogFlowThisFuncLeave();
14507 return rc;
14508}
14509
14510/**
14511 * Deletes the given file if it is no longer in use by either the current machine state
14512 * (if the machine is "saved") or any of the machine's snapshots.
14513 *
14514 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14515 * but is different for each SnapshotMachine. When calling this, the order of calling this
14516 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14517 * is therefore critical. I know, it's all rather messy.
14518 *
14519 * @param strStateFile
14520 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14521 */
14522void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14523 Snapshot *pSnapshotToIgnore)
14524{
14525 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14526 if ( (strStateFile.isNotEmpty())
14527 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14528 )
14529 // ... and it must also not be shared with other snapshots
14530 if ( !mData->mFirstSnapshot
14531 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14532 // this checks the SnapshotMachine's state file paths
14533 )
14534 RTFileDelete(strStateFile.c_str());
14535}
14536
14537/**
14538 * Locks the attached media.
14539 *
14540 * All attached hard disks are locked for writing and DVD/floppy are locked for
14541 * reading. Parents of attached hard disks (if any) are locked for reading.
14542 *
14543 * This method also performs accessibility check of all media it locks: if some
14544 * media is inaccessible, the method will return a failure and a bunch of
14545 * extended error info objects per each inaccessible medium.
14546 *
14547 * Note that this method is atomic: if it returns a success, all media are
14548 * locked as described above; on failure no media is locked at all (all
14549 * succeeded individual locks will be undone).
14550 *
14551 * The caller is responsible for doing the necessary state sanity checks.
14552 *
14553 * The locks made by this method must be undone by calling #unlockMedia() when
14554 * no more needed.
14555 */
14556HRESULT SessionMachine::lockMedia()
14557{
14558 AutoCaller autoCaller(this);
14559 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14560
14561 AutoMultiWriteLock2 alock(this->lockHandle(),
14562 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14563
14564 /* bail out if trying to lock things with already set up locking */
14565 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14566
14567 MultiResult mrc(S_OK);
14568
14569 /* Collect locking information for all medium objects attached to the VM. */
14570 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14571 it != mMediaData->mAttachments.end();
14572 ++it)
14573 {
14574 MediumAttachment* pAtt = *it;
14575 DeviceType_T devType = pAtt->getType();
14576 Medium *pMedium = pAtt->getMedium();
14577
14578 MediumLockList *pMediumLockList(new MediumLockList());
14579 // There can be attachments without a medium (floppy/dvd), and thus
14580 // it's impossible to create a medium lock list. It still makes sense
14581 // to have the empty medium lock list in the map in case a medium is
14582 // attached later.
14583 if (pMedium != NULL)
14584 {
14585 MediumType_T mediumType = pMedium->getType();
14586 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14587 || mediumType == MediumType_Shareable;
14588 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14589
14590 alock.release();
14591 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14592 !fIsReadOnlyLock /* fMediumLockWrite */,
14593 NULL,
14594 *pMediumLockList);
14595 alock.acquire();
14596 if (FAILED(mrc))
14597 {
14598 delete pMediumLockList;
14599 mData->mSession.mLockedMedia.Clear();
14600 break;
14601 }
14602 }
14603
14604 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14605 if (FAILED(rc))
14606 {
14607 mData->mSession.mLockedMedia.Clear();
14608 mrc = setError(rc,
14609 tr("Collecting locking information for all attached media failed"));
14610 break;
14611 }
14612 }
14613
14614 if (SUCCEEDED(mrc))
14615 {
14616 /* Now lock all media. If this fails, nothing is locked. */
14617 alock.release();
14618 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14619 alock.acquire();
14620 if (FAILED(rc))
14621 {
14622 mrc = setError(rc,
14623 tr("Locking of attached media failed"));
14624 }
14625 }
14626
14627 return mrc;
14628}
14629
14630/**
14631 * Undoes the locks made by by #lockMedia().
14632 */
14633void SessionMachine::unlockMedia()
14634{
14635 AutoCaller autoCaller(this);
14636 AssertComRCReturnVoid(autoCaller.rc());
14637
14638 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14639
14640 /* we may be holding important error info on the current thread;
14641 * preserve it */
14642 ErrorInfoKeeper eik;
14643
14644 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14645 AssertComRC(rc);
14646}
14647
14648/**
14649 * Helper to change the machine state (reimplementation).
14650 *
14651 * @note Locks this object for writing.
14652 * @note This method must not call saveSettings or SaveSettings, otherwise
14653 * it can cause crashes in random places due to unexpectedly committing
14654 * the current settings. The caller is responsible for that. The call
14655 * to saveStateSettings is fine, because this method does not commit.
14656 */
14657HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14658{
14659 LogFlowThisFuncEnter();
14660 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14661
14662 AutoCaller autoCaller(this);
14663 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14664
14665 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14666
14667 MachineState_T oldMachineState = mData->mMachineState;
14668
14669 AssertMsgReturn(oldMachineState != aMachineState,
14670 ("oldMachineState=%s, aMachineState=%s\n",
14671 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14672 E_FAIL);
14673
14674 HRESULT rc = S_OK;
14675
14676 int stsFlags = 0;
14677 bool deleteSavedState = false;
14678
14679 /* detect some state transitions */
14680
14681 if ( ( oldMachineState == MachineState_Saved
14682 && aMachineState == MachineState_Restoring)
14683 || ( ( oldMachineState == MachineState_PoweredOff
14684 || oldMachineState == MachineState_Teleported
14685 || oldMachineState == MachineState_Aborted
14686 )
14687 && ( aMachineState == MachineState_TeleportingIn
14688 || aMachineState == MachineState_Starting
14689 )
14690 )
14691 )
14692 {
14693 /* The EMT thread is about to start */
14694
14695 /* Nothing to do here for now... */
14696
14697 /// @todo NEWMEDIA don't let mDVDDrive and other children
14698 /// change anything when in the Starting/Restoring state
14699 }
14700 else if ( ( oldMachineState == MachineState_Running
14701 || oldMachineState == MachineState_Paused
14702 || oldMachineState == MachineState_Teleporting
14703 || oldMachineState == MachineState_LiveSnapshotting
14704 || oldMachineState == MachineState_Stuck
14705 || oldMachineState == MachineState_Starting
14706 || oldMachineState == MachineState_Stopping
14707 || oldMachineState == MachineState_Saving
14708 || oldMachineState == MachineState_Restoring
14709 || oldMachineState == MachineState_TeleportingPausedVM
14710 || oldMachineState == MachineState_TeleportingIn
14711 )
14712 && ( aMachineState == MachineState_PoweredOff
14713 || aMachineState == MachineState_Saved
14714 || aMachineState == MachineState_Teleported
14715 || aMachineState == MachineState_Aborted
14716 )
14717 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14718 * snapshot */
14719 && ( mConsoleTaskData.mSnapshot.isNull()
14720 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14721 )
14722 )
14723 {
14724 /* The EMT thread has just stopped, unlock attached media. Note that as
14725 * opposed to locking that is done from Console, we do unlocking here
14726 * because the VM process may have aborted before having a chance to
14727 * properly unlock all media it locked. */
14728
14729 unlockMedia();
14730 }
14731
14732 if (oldMachineState == MachineState_Restoring)
14733 {
14734 if (aMachineState != MachineState_Saved)
14735 {
14736 /*
14737 * delete the saved state file once the machine has finished
14738 * restoring from it (note that Console sets the state from
14739 * Restoring to Saved if the VM couldn't restore successfully,
14740 * to give the user an ability to fix an error and retry --
14741 * we keep the saved state file in this case)
14742 */
14743 deleteSavedState = true;
14744 }
14745 }
14746 else if ( oldMachineState == MachineState_Saved
14747 && ( aMachineState == MachineState_PoweredOff
14748 || aMachineState == MachineState_Aborted
14749 || aMachineState == MachineState_Teleported
14750 )
14751 )
14752 {
14753 /*
14754 * delete the saved state after Console::ForgetSavedState() is called
14755 * or if the VM process (owning a direct VM session) crashed while the
14756 * VM was Saved
14757 */
14758
14759 /// @todo (dmik)
14760 // Not sure that deleting the saved state file just because of the
14761 // client death before it attempted to restore the VM is a good
14762 // thing. But when it crashes we need to go to the Aborted state
14763 // which cannot have the saved state file associated... The only
14764 // way to fix this is to make the Aborted condition not a VM state
14765 // but a bool flag: i.e., when a crash occurs, set it to true and
14766 // change the state to PoweredOff or Saved depending on the
14767 // saved state presence.
14768
14769 deleteSavedState = true;
14770 mData->mCurrentStateModified = TRUE;
14771 stsFlags |= SaveSTS_CurStateModified;
14772 }
14773
14774 if ( aMachineState == MachineState_Starting
14775 || aMachineState == MachineState_Restoring
14776 || aMachineState == MachineState_TeleportingIn
14777 )
14778 {
14779 /* set the current state modified flag to indicate that the current
14780 * state is no more identical to the state in the
14781 * current snapshot */
14782 if (!mData->mCurrentSnapshot.isNull())
14783 {
14784 mData->mCurrentStateModified = TRUE;
14785 stsFlags |= SaveSTS_CurStateModified;
14786 }
14787 }
14788
14789 if (deleteSavedState)
14790 {
14791 if (mRemoveSavedState)
14792 {
14793 Assert(!mSSData->strStateFilePath.isEmpty());
14794
14795 // it is safe to delete the saved state file if ...
14796 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14797 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14798 // ... none of the snapshots share the saved state file
14799 )
14800 RTFileDelete(mSSData->strStateFilePath.c_str());
14801 }
14802
14803 mSSData->strStateFilePath.setNull();
14804 stsFlags |= SaveSTS_StateFilePath;
14805 }
14806
14807 /* redirect to the underlying peer machine */
14808 mPeer->setMachineState(aMachineState);
14809
14810 if ( aMachineState == MachineState_PoweredOff
14811 || aMachineState == MachineState_Teleported
14812 || aMachineState == MachineState_Aborted
14813 || aMachineState == MachineState_Saved)
14814 {
14815 /* the machine has stopped execution
14816 * (or the saved state file was adopted) */
14817 stsFlags |= SaveSTS_StateTimeStamp;
14818 }
14819
14820 if ( ( oldMachineState == MachineState_PoweredOff
14821 || oldMachineState == MachineState_Aborted
14822 || oldMachineState == MachineState_Teleported
14823 )
14824 && aMachineState == MachineState_Saved)
14825 {
14826 /* the saved state file was adopted */
14827 Assert(!mSSData->strStateFilePath.isEmpty());
14828 stsFlags |= SaveSTS_StateFilePath;
14829 }
14830
14831#ifdef VBOX_WITH_GUEST_PROPS
14832 if ( aMachineState == MachineState_PoweredOff
14833 || aMachineState == MachineState_Aborted
14834 || aMachineState == MachineState_Teleported)
14835 {
14836 /* Make sure any transient guest properties get removed from the
14837 * property store on shutdown. */
14838
14839 HWData::GuestPropertyMap::const_iterator it;
14840 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14841 if (!fNeedsSaving)
14842 for (it = mHWData->mGuestProperties.begin();
14843 it != mHWData->mGuestProperties.end(); ++it)
14844 if ( (it->second.mFlags & guestProp::TRANSIENT)
14845 || (it->second.mFlags & guestProp::TRANSRESET))
14846 {
14847 fNeedsSaving = true;
14848 break;
14849 }
14850 if (fNeedsSaving)
14851 {
14852 mData->mCurrentStateModified = TRUE;
14853 stsFlags |= SaveSTS_CurStateModified;
14854 }
14855 }
14856#endif
14857
14858 rc = saveStateSettings(stsFlags);
14859
14860 if ( ( oldMachineState != MachineState_PoweredOff
14861 && oldMachineState != MachineState_Aborted
14862 && oldMachineState != MachineState_Teleported
14863 )
14864 && ( aMachineState == MachineState_PoweredOff
14865 || aMachineState == MachineState_Aborted
14866 || aMachineState == MachineState_Teleported
14867 )
14868 )
14869 {
14870 /* we've been shut down for any reason */
14871 /* no special action so far */
14872 }
14873
14874 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14875 LogFlowThisFuncLeave();
14876 return rc;
14877}
14878
14879/**
14880 * Sends the current machine state value to the VM process.
14881 *
14882 * @note Locks this object for reading, then calls a client process.
14883 */
14884HRESULT SessionMachine::updateMachineStateOnClient()
14885{
14886 AutoCaller autoCaller(this);
14887 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14888
14889 ComPtr<IInternalSessionControl> directControl;
14890 {
14891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14892 AssertReturn(!!mData, E_FAIL);
14893 directControl = mData->mSession.mDirectControl;
14894
14895 /* directControl may be already set to NULL here in #OnSessionEnd()
14896 * called too early by the direct session process while there is still
14897 * some operation (like deleting the snapshot) in progress. The client
14898 * process in this case is waiting inside Session::close() for the
14899 * "end session" process object to complete, while #uninit() called by
14900 * #checkForDeath() on the Watcher thread is waiting for the pending
14901 * operation to complete. For now, we accept this inconsistent behavior
14902 * and simply do nothing here. */
14903
14904 if (mData->mSession.mState == SessionState_Unlocking)
14905 return S_OK;
14906
14907 AssertReturn(!directControl.isNull(), E_FAIL);
14908 }
14909
14910 return directControl->UpdateMachineState(mData->mMachineState);
14911}
Note: See TracBrowser for help on using the repository browser.

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