VirtualBox

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

Last change on this file since 47915 was 47915, checked in by vboxsync, 12 years ago

Main/Machine: add a way to safely override the VM app, OSX specific

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 500.1 KB
Line 
1/* $Id: MachineImpl.cpp 47915 2013-08-20 14:00:58Z 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.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "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.clear();
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 #define DECODE_STR_MAX _1M
8934 mUserData->s = config.machineUserData;
8935
8936 // Decode the Icon overide data from config userdata and set onto Machine.
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 COMSETTER(Icon)(ComSafeArrayAsInParam(iconByte));
8952
8953 // look up the object by Id to check it is valid
8954 ComPtr<IGuestOSType> guestOSType;
8955 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8956 guestOSType.asOutParam());
8957 if (FAILED(rc)) return rc;
8958
8959 // stateFile (optional)
8960 if (config.strStateFile.isEmpty())
8961 mSSData->strStateFilePath.setNull();
8962 else
8963 {
8964 Utf8Str stateFilePathFull(config.strStateFile);
8965 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8966 if (RT_FAILURE(vrc))
8967 return setError(E_FAIL,
8968 tr("Invalid saved state file path '%s' (%Rrc)"),
8969 config.strStateFile.c_str(),
8970 vrc);
8971 mSSData->strStateFilePath = stateFilePathFull;
8972 }
8973
8974 // snapshot folder needs special processing so set it again
8975 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8976 if (FAILED(rc)) return rc;
8977
8978 /* Copy the extra data items (Not in any case config is already the same as
8979 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8980 * make sure the extra data map is copied). */
8981 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8982
8983 /* currentStateModified (optional, default is true) */
8984 mData->mCurrentStateModified = config.fCurrentStateModified;
8985
8986 mData->mLastStateChange = config.timeLastStateChange;
8987
8988 /*
8989 * note: all mUserData members must be assigned prior this point because
8990 * we need to commit changes in order to let mUserData be shared by all
8991 * snapshot machine instances.
8992 */
8993 mUserData.commitCopy();
8994
8995 // machine registry, if present (must be loaded before snapshots)
8996 if (config.canHaveOwnMediaRegistry())
8997 {
8998 // determine machine folder
8999 Utf8Str strMachineFolder = getSettingsFileFull();
9000 strMachineFolder.stripFilename();
9001 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
9002 config.mediaRegistry,
9003 strMachineFolder);
9004 if (FAILED(rc)) return rc;
9005 }
9006
9007 /* Snapshot node (optional) */
9008 size_t cRootSnapshots;
9009 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9010 {
9011 // there must be only one root snapshot
9012 Assert(cRootSnapshots == 1);
9013
9014 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9015
9016 rc = loadSnapshot(snap,
9017 config.uuidCurrentSnapshot,
9018 NULL); // no parent == first snapshot
9019 if (FAILED(rc)) return rc;
9020 }
9021
9022 // hardware data
9023 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9024 if (FAILED(rc)) return rc;
9025
9026 // load storage controllers
9027 rc = loadStorageControllers(config.storageMachine,
9028 puuidRegistry,
9029 NULL /* puuidSnapshot */);
9030 if (FAILED(rc)) return rc;
9031
9032 /*
9033 * NOTE: the assignment below must be the last thing to do,
9034 * otherwise it will be not possible to change the settings
9035 * somewhere in the code above because all setters will be
9036 * blocked by checkStateDependency(MutableStateDep).
9037 */
9038
9039 /* set the machine state to Aborted or Saved when appropriate */
9040 if (config.fAborted)
9041 {
9042 mSSData->strStateFilePath.setNull();
9043
9044 /* no need to use setMachineState() during init() */
9045 mData->mMachineState = MachineState_Aborted;
9046 }
9047 else if (!mSSData->strStateFilePath.isEmpty())
9048 {
9049 /* no need to use setMachineState() during init() */
9050 mData->mMachineState = MachineState_Saved;
9051 }
9052
9053 // after loading settings, we are no longer different from the XML on disk
9054 mData->flModifications = 0;
9055
9056 return S_OK;
9057}
9058
9059/**
9060 * Recursively loads all snapshots starting from the given.
9061 *
9062 * @param aNode <Snapshot> node.
9063 * @param aCurSnapshotId Current snapshot ID from the settings file.
9064 * @param aParentSnapshot Parent snapshot.
9065 */
9066HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
9067 const Guid &aCurSnapshotId,
9068 Snapshot *aParentSnapshot)
9069{
9070 AssertReturn(!isSnapshotMachine(), E_FAIL);
9071 AssertReturn(!isSessionMachine(), E_FAIL);
9072
9073 HRESULT rc = S_OK;
9074
9075 Utf8Str strStateFile;
9076 if (!data.strStateFile.isEmpty())
9077 {
9078 /* optional */
9079 strStateFile = data.strStateFile;
9080 int vrc = calculateFullPath(strStateFile, strStateFile);
9081 if (RT_FAILURE(vrc))
9082 return setError(E_FAIL,
9083 tr("Invalid saved state file path '%s' (%Rrc)"),
9084 strStateFile.c_str(),
9085 vrc);
9086 }
9087
9088 /* create a snapshot machine object */
9089 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9090 pSnapshotMachine.createObject();
9091 rc = pSnapshotMachine->initFromSettings(this,
9092 data.hardware,
9093 &data.debugging,
9094 &data.autostart,
9095 data.storage,
9096 data.uuid.ref(),
9097 strStateFile);
9098 if (FAILED(rc)) return rc;
9099
9100 /* create a snapshot object */
9101 ComObjPtr<Snapshot> pSnapshot;
9102 pSnapshot.createObject();
9103 /* initialize the snapshot */
9104 rc = pSnapshot->init(mParent, // VirtualBox object
9105 data.uuid,
9106 data.strName,
9107 data.strDescription,
9108 data.timestamp,
9109 pSnapshotMachine,
9110 aParentSnapshot);
9111 if (FAILED(rc)) return rc;
9112
9113 /* memorize the first snapshot if necessary */
9114 if (!mData->mFirstSnapshot)
9115 mData->mFirstSnapshot = pSnapshot;
9116
9117 /* memorize the current snapshot when appropriate */
9118 if ( !mData->mCurrentSnapshot
9119 && pSnapshot->getId() == aCurSnapshotId
9120 )
9121 mData->mCurrentSnapshot = pSnapshot;
9122
9123 // now create the children
9124 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
9125 it != data.llChildSnapshots.end();
9126 ++it)
9127 {
9128 const settings::Snapshot &childData = *it;
9129 // recurse
9130 rc = loadSnapshot(childData,
9131 aCurSnapshotId,
9132 pSnapshot); // parent = the one we created above
9133 if (FAILED(rc)) return rc;
9134 }
9135
9136 return rc;
9137}
9138
9139/**
9140 * Loads settings into mHWData.
9141 *
9142 * @param data Reference to the hardware settings.
9143 * @param pDbg Pointer to the debugging settings.
9144 * @param pAutostart Pointer to the autostart settings.
9145 */
9146HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
9147 const settings::Autostart *pAutostart)
9148{
9149 AssertReturn(!isSessionMachine(), E_FAIL);
9150
9151 HRESULT rc = S_OK;
9152
9153 try
9154 {
9155 /* The hardware version attribute (optional). */
9156 mHWData->mHWVersion = data.strVersion;
9157 mHWData->mHardwareUUID = data.uuid;
9158
9159 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9160 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
9161 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9162 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9163 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9164 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9165 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9166 mHWData->mPAEEnabled = data.fPAE;
9167 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9168 mHWData->mLongMode = data.enmLongMode;
9169 mHWData->mCPUCount = data.cCPUs;
9170 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9171 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9172
9173 // cpu
9174 if (mHWData->mCPUHotPlugEnabled)
9175 {
9176 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9177 it != data.llCpus.end();
9178 ++it)
9179 {
9180 const settings::Cpu &cpu = *it;
9181
9182 mHWData->mCPUAttached[cpu.ulId] = true;
9183 }
9184 }
9185
9186 // cpuid leafs
9187 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9188 it != data.llCpuIdLeafs.end();
9189 ++it)
9190 {
9191 const settings::CpuIdLeaf &leaf = *it;
9192
9193 switch (leaf.ulId)
9194 {
9195 case 0x0:
9196 case 0x1:
9197 case 0x2:
9198 case 0x3:
9199 case 0x4:
9200 case 0x5:
9201 case 0x6:
9202 case 0x7:
9203 case 0x8:
9204 case 0x9:
9205 case 0xA:
9206 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9207 break;
9208
9209 case 0x80000000:
9210 case 0x80000001:
9211 case 0x80000002:
9212 case 0x80000003:
9213 case 0x80000004:
9214 case 0x80000005:
9215 case 0x80000006:
9216 case 0x80000007:
9217 case 0x80000008:
9218 case 0x80000009:
9219 case 0x8000000A:
9220 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9221 break;
9222
9223 default:
9224 /* just ignore */
9225 break;
9226 }
9227 }
9228
9229 mHWData->mMemorySize = data.ulMemorySizeMB;
9230 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9231
9232 // boot order
9233 for (size_t i = 0;
9234 i < RT_ELEMENTS(mHWData->mBootOrder);
9235 i++)
9236 {
9237 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9238 if (it == data.mapBootOrder.end())
9239 mHWData->mBootOrder[i] = DeviceType_Null;
9240 else
9241 mHWData->mBootOrder[i] = it->second;
9242 }
9243
9244 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9245 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9246 mHWData->mMonitorCount = data.cMonitors;
9247 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9248 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9249 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9250 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9251 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9252 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9253 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9254 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9255 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9256 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9257 if (!data.strVideoCaptureFile.isEmpty())
9258 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9259 else
9260 mHWData->mVideoCaptureFile.setNull();
9261 mHWData->mFirmwareType = data.firmwareType;
9262 mHWData->mPointingHIDType = data.pointingHIDType;
9263 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9264 mHWData->mChipsetType = data.chipsetType;
9265 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
9266 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9267 mHWData->mHPETEnabled = data.fHPETEnabled;
9268
9269 /* VRDEServer */
9270 rc = mVRDEServer->loadSettings(data.vrdeSettings);
9271 if (FAILED(rc)) return rc;
9272
9273 /* BIOS */
9274 rc = mBIOSSettings->loadSettings(data.biosSettings);
9275 if (FAILED(rc)) return rc;
9276
9277 // Bandwidth control (must come before network adapters)
9278 rc = mBandwidthControl->loadSettings(data.ioSettings);
9279 if (FAILED(rc)) return rc;
9280
9281 /* Shared folders */
9282 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9283 it != data.usbSettings.llUSBControllers.end();
9284 ++it)
9285 {
9286 const settings::USBController &settingsCtrl = *it;
9287 ComObjPtr<USBController> newCtrl;
9288
9289 newCtrl.createObject();
9290 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9291 mUSBControllers->push_back(newCtrl);
9292 }
9293
9294 /* USB device filters */
9295 rc = mUSBDeviceFilters->loadSettings(data.usbSettings);
9296 if (FAILED(rc)) return rc;
9297
9298 // network adapters
9299 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9300 uint32_t oldCount = mNetworkAdapters.size();
9301 if (newCount > oldCount)
9302 {
9303 mNetworkAdapters.resize(newCount);
9304 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9305 {
9306 unconst(mNetworkAdapters[slot]).createObject();
9307 mNetworkAdapters[slot]->init(this, slot);
9308 }
9309 }
9310 else if (newCount < oldCount)
9311 mNetworkAdapters.resize(newCount);
9312 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9313 it != data.llNetworkAdapters.end();
9314 ++it)
9315 {
9316 const settings::NetworkAdapter &nic = *it;
9317
9318 /* slot unicity is guaranteed by XML Schema */
9319 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9320 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9321 if (FAILED(rc)) return rc;
9322 }
9323
9324 // serial ports
9325 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9326 it != data.llSerialPorts.end();
9327 ++it)
9328 {
9329 const settings::SerialPort &s = *it;
9330
9331 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9332 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
9333 if (FAILED(rc)) return rc;
9334 }
9335
9336 // parallel ports (optional)
9337 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9338 it != data.llParallelPorts.end();
9339 ++it)
9340 {
9341 const settings::ParallelPort &p = *it;
9342
9343 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9344 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9345 if (FAILED(rc)) return rc;
9346 }
9347
9348 /* AudioAdapter */
9349 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9350 if (FAILED(rc)) return rc;
9351
9352 /* Shared folders */
9353 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9354 it != data.llSharedFolders.end();
9355 ++it)
9356 {
9357 const settings::SharedFolder &sf = *it;
9358
9359 ComObjPtr<SharedFolder> sharedFolder;
9360 /* Check for double entries. Not allowed! */
9361 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9362 if (SUCCEEDED(rc))
9363 return setError(VBOX_E_OBJECT_IN_USE,
9364 tr("Shared folder named '%s' already exists"),
9365 sf.strName.c_str());
9366
9367 /* Create the new shared folder. Don't break on error. This will be
9368 * reported when the machine starts. */
9369 sharedFolder.createObject();
9370 rc = sharedFolder->init(getMachine(),
9371 sf.strName,
9372 sf.strHostPath,
9373 RT_BOOL(sf.fWritable),
9374 RT_BOOL(sf.fAutoMount),
9375 false /* fFailOnError */);
9376 if (FAILED(rc)) return rc;
9377 mHWData->mSharedFolders.push_back(sharedFolder);
9378 }
9379
9380 // Clipboard
9381 mHWData->mClipboardMode = data.clipboardMode;
9382
9383 // drag'n'drop
9384 mHWData->mDragAndDropMode = data.dragAndDropMode;
9385
9386 // guest settings
9387 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9388
9389 // IO settings
9390 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9391 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9392
9393 // Host PCI devices
9394 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9395 it != data.pciAttachments.end();
9396 ++it)
9397 {
9398 const settings::HostPCIDeviceAttachment &hpda = *it;
9399 ComObjPtr<PCIDeviceAttachment> pda;
9400
9401 pda.createObject();
9402 pda->loadSettings(this, hpda);
9403 mHWData->mPCIDeviceAssignments.push_back(pda);
9404 }
9405
9406 /*
9407 * (The following isn't really real hardware, but it lives in HWData
9408 * for reasons of convenience.)
9409 */
9410
9411#ifdef VBOX_WITH_GUEST_PROPS
9412 /* Guest properties (optional) */
9413 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9414 it != data.llGuestProperties.end();
9415 ++it)
9416 {
9417 const settings::GuestProperty &prop = *it;
9418 uint32_t fFlags = guestProp::NILFLAG;
9419 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9420 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9421 mHWData->mGuestProperties[prop.strName] = property;
9422 }
9423
9424 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9425#endif /* VBOX_WITH_GUEST_PROPS defined */
9426
9427 rc = loadDebugging(pDbg);
9428 if (FAILED(rc))
9429 return rc;
9430
9431 mHWData->mAutostart = *pAutostart;
9432
9433 /* default frontend */
9434 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9435 }
9436 catch(std::bad_alloc &)
9437 {
9438 return E_OUTOFMEMORY;
9439 }
9440
9441 AssertComRC(rc);
9442 return rc;
9443}
9444
9445/**
9446 * Called from Machine::loadHardware() to load the debugging settings of the
9447 * machine.
9448 *
9449 * @param pDbg Pointer to the settings.
9450 */
9451HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9452{
9453 mHWData->mDebugging = *pDbg;
9454 /* no more processing currently required, this will probably change. */
9455 return S_OK;
9456}
9457
9458/**
9459 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9460 *
9461 * @param data
9462 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9463 * @param puuidSnapshot
9464 * @return
9465 */
9466HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9467 const Guid *puuidRegistry,
9468 const Guid *puuidSnapshot)
9469{
9470 AssertReturn(!isSessionMachine(), E_FAIL);
9471
9472 HRESULT rc = S_OK;
9473
9474 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9475 it != data.llStorageControllers.end();
9476 ++it)
9477 {
9478 const settings::StorageController &ctlData = *it;
9479
9480 ComObjPtr<StorageController> pCtl;
9481 /* Try to find one with the name first. */
9482 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9483 if (SUCCEEDED(rc))
9484 return setError(VBOX_E_OBJECT_IN_USE,
9485 tr("Storage controller named '%s' already exists"),
9486 ctlData.strName.c_str());
9487
9488 pCtl.createObject();
9489 rc = pCtl->init(this,
9490 ctlData.strName,
9491 ctlData.storageBus,
9492 ctlData.ulInstance,
9493 ctlData.fBootable);
9494 if (FAILED(rc)) return rc;
9495
9496 mStorageControllers->push_back(pCtl);
9497
9498 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9499 if (FAILED(rc)) return rc;
9500
9501 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9502 if (FAILED(rc)) return rc;
9503
9504 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9505 if (FAILED(rc)) return rc;
9506
9507 /* Set IDE emulation settings (only for AHCI controller). */
9508 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9509 {
9510 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9511 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9512 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9513 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9514 )
9515 return rc;
9516 }
9517
9518 /* Load the attached devices now. */
9519 rc = loadStorageDevices(pCtl,
9520 ctlData,
9521 puuidRegistry,
9522 puuidSnapshot);
9523 if (FAILED(rc)) return rc;
9524 }
9525
9526 return S_OK;
9527}
9528
9529/**
9530 * Called from loadStorageControllers for a controller's devices.
9531 *
9532 * @param aStorageController
9533 * @param data
9534 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9535 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9536 * @return
9537 */
9538HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9539 const settings::StorageController &data,
9540 const Guid *puuidRegistry,
9541 const Guid *puuidSnapshot)
9542{
9543 HRESULT rc = S_OK;
9544
9545 /* paranoia: detect duplicate attachments */
9546 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9547 it != data.llAttachedDevices.end();
9548 ++it)
9549 {
9550 const settings::AttachedDevice &ad = *it;
9551
9552 for (settings::AttachedDevicesList::const_iterator it2 = it;
9553 it2 != data.llAttachedDevices.end();
9554 ++it2)
9555 {
9556 if (it == it2)
9557 continue;
9558
9559 const settings::AttachedDevice &ad2 = *it2;
9560
9561 if ( ad.lPort == ad2.lPort
9562 && ad.lDevice == ad2.lDevice)
9563 {
9564 return setError(E_FAIL,
9565 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9566 aStorageController->getName().c_str(),
9567 ad.lPort,
9568 ad.lDevice,
9569 mUserData->s.strName.c_str());
9570 }
9571 }
9572 }
9573
9574 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9575 it != data.llAttachedDevices.end();
9576 ++it)
9577 {
9578 const settings::AttachedDevice &dev = *it;
9579 ComObjPtr<Medium> medium;
9580
9581 switch (dev.deviceType)
9582 {
9583 case DeviceType_Floppy:
9584 case DeviceType_DVD:
9585 if (dev.strHostDriveSrc.isNotEmpty())
9586 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9587 else
9588 rc = mParent->findRemoveableMedium(dev.deviceType,
9589 dev.uuid,
9590 false /* fRefresh */,
9591 false /* aSetError */,
9592 medium);
9593 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9594 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9595 rc = S_OK;
9596 break;
9597
9598 case DeviceType_HardDisk:
9599 {
9600 /* find a hard disk by UUID */
9601 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9602 if (FAILED(rc))
9603 {
9604 if (isSnapshotMachine())
9605 {
9606 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9607 // so the user knows that the bad disk is in a snapshot somewhere
9608 com::ErrorInfo info;
9609 return setError(E_FAIL,
9610 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9611 puuidSnapshot->raw(),
9612 info.getText().raw());
9613 }
9614 else
9615 return rc;
9616 }
9617
9618 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9619
9620 if (medium->getType() == MediumType_Immutable)
9621 {
9622 if (isSnapshotMachine())
9623 return setError(E_FAIL,
9624 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9625 "of the virtual machine '%s' ('%s')"),
9626 medium->getLocationFull().c_str(),
9627 dev.uuid.raw(),
9628 puuidSnapshot->raw(),
9629 mUserData->s.strName.c_str(),
9630 mData->m_strConfigFileFull.c_str());
9631
9632 return setError(E_FAIL,
9633 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9634 medium->getLocationFull().c_str(),
9635 dev.uuid.raw(),
9636 mUserData->s.strName.c_str(),
9637 mData->m_strConfigFileFull.c_str());
9638 }
9639
9640 if (medium->getType() == MediumType_MultiAttach)
9641 {
9642 if (isSnapshotMachine())
9643 return setError(E_FAIL,
9644 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9645 "of the virtual machine '%s' ('%s')"),
9646 medium->getLocationFull().c_str(),
9647 dev.uuid.raw(),
9648 puuidSnapshot->raw(),
9649 mUserData->s.strName.c_str(),
9650 mData->m_strConfigFileFull.c_str());
9651
9652 return setError(E_FAIL,
9653 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9654 medium->getLocationFull().c_str(),
9655 dev.uuid.raw(),
9656 mUserData->s.strName.c_str(),
9657 mData->m_strConfigFileFull.c_str());
9658 }
9659
9660 if ( !isSnapshotMachine()
9661 && medium->getChildren().size() != 0
9662 )
9663 return setError(E_FAIL,
9664 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9665 "because it has %d differencing child hard disks"),
9666 medium->getLocationFull().c_str(),
9667 dev.uuid.raw(),
9668 mUserData->s.strName.c_str(),
9669 mData->m_strConfigFileFull.c_str(),
9670 medium->getChildren().size());
9671
9672 if (findAttachment(mMediaData->mAttachments,
9673 medium))
9674 return setError(E_FAIL,
9675 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9676 medium->getLocationFull().c_str(),
9677 dev.uuid.raw(),
9678 mUserData->s.strName.c_str(),
9679 mData->m_strConfigFileFull.c_str());
9680
9681 break;
9682 }
9683
9684 default:
9685 return setError(E_FAIL,
9686 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9687 medium->getLocationFull().c_str(),
9688 mUserData->s.strName.c_str(),
9689 mData->m_strConfigFileFull.c_str());
9690 }
9691
9692 if (FAILED(rc))
9693 break;
9694
9695 /* Bandwidth groups are loaded at this point. */
9696 ComObjPtr<BandwidthGroup> pBwGroup;
9697
9698 if (!dev.strBwGroup.isEmpty())
9699 {
9700 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9701 if (FAILED(rc))
9702 return setError(E_FAIL,
9703 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9704 medium->getLocationFull().c_str(),
9705 dev.strBwGroup.c_str(),
9706 mUserData->s.strName.c_str(),
9707 mData->m_strConfigFileFull.c_str());
9708 pBwGroup->reference();
9709 }
9710
9711 const Bstr controllerName = aStorageController->getName();
9712 ComObjPtr<MediumAttachment> pAttachment;
9713 pAttachment.createObject();
9714 rc = pAttachment->init(this,
9715 medium,
9716 controllerName,
9717 dev.lPort,
9718 dev.lDevice,
9719 dev.deviceType,
9720 false,
9721 dev.fPassThrough,
9722 dev.fTempEject,
9723 dev.fNonRotational,
9724 dev.fDiscard,
9725 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9726 if (FAILED(rc)) break;
9727
9728 /* associate the medium with this machine and snapshot */
9729 if (!medium.isNull())
9730 {
9731 AutoCaller medCaller(medium);
9732 if (FAILED(medCaller.rc())) return medCaller.rc();
9733 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9734
9735 if (isSnapshotMachine())
9736 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9737 else
9738 rc = medium->addBackReference(mData->mUuid);
9739 /* If the medium->addBackReference fails it sets an appropriate
9740 * error message, so no need to do any guesswork here. */
9741
9742 if (puuidRegistry)
9743 // caller wants registry ID to be set on all attached media (OVF import case)
9744 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9745 }
9746
9747 if (FAILED(rc))
9748 break;
9749
9750 /* back up mMediaData to let registeredInit() properly rollback on failure
9751 * (= limited accessibility) */
9752 setModified(IsModified_Storage);
9753 mMediaData.backup();
9754 mMediaData->mAttachments.push_back(pAttachment);
9755 }
9756
9757 return rc;
9758}
9759
9760/**
9761 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9762 *
9763 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9764 * @param aSnapshot where to return the found snapshot
9765 * @param aSetError true to set extended error info on failure
9766 */
9767HRESULT Machine::findSnapshotById(const Guid &aId,
9768 ComObjPtr<Snapshot> &aSnapshot,
9769 bool aSetError /* = false */)
9770{
9771 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9772
9773 if (!mData->mFirstSnapshot)
9774 {
9775 if (aSetError)
9776 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9777 return E_FAIL;
9778 }
9779
9780 if (aId.isZero())
9781 aSnapshot = mData->mFirstSnapshot;
9782 else
9783 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9784
9785 if (!aSnapshot)
9786 {
9787 if (aSetError)
9788 return setError(E_FAIL,
9789 tr("Could not find a snapshot with UUID {%s}"),
9790 aId.toString().c_str());
9791 return E_FAIL;
9792 }
9793
9794 return S_OK;
9795}
9796
9797/**
9798 * Returns the snapshot with the given name or fails of no such snapshot.
9799 *
9800 * @param aName snapshot name to find
9801 * @param aSnapshot where to return the found snapshot
9802 * @param aSetError true to set extended error info on failure
9803 */
9804HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9805 ComObjPtr<Snapshot> &aSnapshot,
9806 bool aSetError /* = false */)
9807{
9808 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9809
9810 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9811
9812 if (!mData->mFirstSnapshot)
9813 {
9814 if (aSetError)
9815 return setError(VBOX_E_OBJECT_NOT_FOUND,
9816 tr("This machine does not have any snapshots"));
9817 return VBOX_E_OBJECT_NOT_FOUND;
9818 }
9819
9820 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9821
9822 if (!aSnapshot)
9823 {
9824 if (aSetError)
9825 return setError(VBOX_E_OBJECT_NOT_FOUND,
9826 tr("Could not find a snapshot named '%s'"), strName.c_str());
9827 return VBOX_E_OBJECT_NOT_FOUND;
9828 }
9829
9830 return S_OK;
9831}
9832
9833/**
9834 * Returns a storage controller object with the given name.
9835 *
9836 * @param aName storage controller name to find
9837 * @param aStorageController where to return the found storage controller
9838 * @param aSetError true to set extended error info on failure
9839 */
9840HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9841 ComObjPtr<StorageController> &aStorageController,
9842 bool aSetError /* = false */)
9843{
9844 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9845
9846 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9847 it != mStorageControllers->end();
9848 ++it)
9849 {
9850 if ((*it)->getName() == aName)
9851 {
9852 aStorageController = (*it);
9853 return S_OK;
9854 }
9855 }
9856
9857 if (aSetError)
9858 return setError(VBOX_E_OBJECT_NOT_FOUND,
9859 tr("Could not find a storage controller named '%s'"),
9860 aName.c_str());
9861 return VBOX_E_OBJECT_NOT_FOUND;
9862}
9863
9864/**
9865 * Returns a USB controller object with the given name.
9866 *
9867 * @param aName USB controller name to find
9868 * @param aUSBController where to return the found USB controller
9869 * @param aSetError true to set extended error info on failure
9870 */
9871HRESULT Machine::getUSBControllerByName(const Utf8Str &aName,
9872 ComObjPtr<USBController> &aUSBController,
9873 bool aSetError /* = false */)
9874{
9875 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9876
9877 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9878 it != mUSBControllers->end();
9879 ++it)
9880 {
9881 if ((*it)->getName() == aName)
9882 {
9883 aUSBController = (*it);
9884 return S_OK;
9885 }
9886 }
9887
9888 if (aSetError)
9889 return setError(VBOX_E_OBJECT_NOT_FOUND,
9890 tr("Could not find a storage controller named '%s'"),
9891 aName.c_str());
9892 return VBOX_E_OBJECT_NOT_FOUND;
9893}
9894
9895/**
9896 * Returns the number of USB controller instance of the given type.
9897 *
9898 * @param enmType USB controller type.
9899 */
9900ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType)
9901{
9902 ULONG cCtrls = 0;
9903
9904 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9905 it != mUSBControllers->end();
9906 ++it)
9907 {
9908 if ((*it)->getControllerType() == enmType)
9909 cCtrls++;
9910 }
9911
9912 return cCtrls;
9913}
9914
9915HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9916 MediaData::AttachmentList &atts)
9917{
9918 AutoCaller autoCaller(this);
9919 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9920
9921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9922
9923 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9924 it != mMediaData->mAttachments.end();
9925 ++it)
9926 {
9927 const ComObjPtr<MediumAttachment> &pAtt = *it;
9928
9929 // should never happen, but deal with NULL pointers in the list.
9930 AssertStmt(!pAtt.isNull(), continue);
9931
9932 // getControllerName() needs caller+read lock
9933 AutoCaller autoAttCaller(pAtt);
9934 if (FAILED(autoAttCaller.rc()))
9935 {
9936 atts.clear();
9937 return autoAttCaller.rc();
9938 }
9939 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9940
9941 if (pAtt->getControllerName() == aName)
9942 atts.push_back(pAtt);
9943 }
9944
9945 return S_OK;
9946}
9947
9948/**
9949 * Helper for #saveSettings. Cares about renaming the settings directory and
9950 * file if the machine name was changed and about creating a new settings file
9951 * if this is a new machine.
9952 *
9953 * @note Must be never called directly but only from #saveSettings().
9954 */
9955HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9956{
9957 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9958
9959 HRESULT rc = S_OK;
9960
9961 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9962
9963 /// @todo need to handle primary group change, too
9964
9965 /* attempt to rename the settings file if machine name is changed */
9966 if ( mUserData->s.fNameSync
9967 && mUserData.isBackedUp()
9968 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9969 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9970 )
9971 {
9972 bool dirRenamed = false;
9973 bool fileRenamed = false;
9974
9975 Utf8Str configFile, newConfigFile;
9976 Utf8Str configFilePrev, newConfigFilePrev;
9977 Utf8Str configDir, newConfigDir;
9978
9979 do
9980 {
9981 int vrc = VINF_SUCCESS;
9982
9983 Utf8Str name = mUserData.backedUpData()->s.strName;
9984 Utf8Str newName = mUserData->s.strName;
9985 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9986 if (group == "/")
9987 group.setNull();
9988 Utf8Str newGroup = mUserData->s.llGroups.front();
9989 if (newGroup == "/")
9990 newGroup.setNull();
9991
9992 configFile = mData->m_strConfigFileFull;
9993
9994 /* first, rename the directory if it matches the group and machine name */
9995 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9996 group.c_str(), RTPATH_DELIMITER, name.c_str());
9997 /** @todo hack, make somehow use of ComposeMachineFilename */
9998 if (mUserData->s.fDirectoryIncludesUUID)
9999 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10000 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
10001 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10002 /** @todo hack, make somehow use of ComposeMachineFilename */
10003 if (mUserData->s.fDirectoryIncludesUUID)
10004 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10005 configDir = configFile;
10006 configDir.stripFilename();
10007 newConfigDir = configDir;
10008 if ( configDir.length() >= groupPlusName.length()
10009 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
10010 {
10011 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10012 Utf8Str newConfigBaseDir(newConfigDir);
10013 newConfigDir.append(newGroupPlusName);
10014 /* consistency: use \ if appropriate on the platform */
10015 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10016 /* new dir and old dir cannot be equal here because of 'if'
10017 * above and because name != newName */
10018 Assert(configDir != newConfigDir);
10019 if (!fSettingsFileIsNew)
10020 {
10021 /* perform real rename only if the machine is not new */
10022 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10023 if ( vrc == VERR_FILE_NOT_FOUND
10024 || vrc == VERR_PATH_NOT_FOUND)
10025 {
10026 /* create the parent directory, then retry renaming */
10027 Utf8Str parent(newConfigDir);
10028 parent.stripFilename();
10029 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10030 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10031 }
10032 if (RT_FAILURE(vrc))
10033 {
10034 rc = setError(E_FAIL,
10035 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10036 configDir.c_str(),
10037 newConfigDir.c_str(),
10038 vrc);
10039 break;
10040 }
10041 /* delete subdirectories which are no longer needed */
10042 Utf8Str dir(configDir);
10043 dir.stripFilename();
10044 while (dir != newConfigBaseDir && dir != ".")
10045 {
10046 vrc = RTDirRemove(dir.c_str());
10047 if (RT_FAILURE(vrc))
10048 break;
10049 dir.stripFilename();
10050 }
10051 dirRenamed = true;
10052 }
10053 }
10054
10055 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10056 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10057
10058 /* then try to rename the settings file itself */
10059 if (newConfigFile != configFile)
10060 {
10061 /* get the path to old settings file in renamed directory */
10062 configFile = Utf8StrFmt("%s%c%s",
10063 newConfigDir.c_str(),
10064 RTPATH_DELIMITER,
10065 RTPathFilename(configFile.c_str()));
10066 if (!fSettingsFileIsNew)
10067 {
10068 /* perform real rename only if the machine is not new */
10069 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10070 if (RT_FAILURE(vrc))
10071 {
10072 rc = setError(E_FAIL,
10073 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10074 configFile.c_str(),
10075 newConfigFile.c_str(),
10076 vrc);
10077 break;
10078 }
10079 fileRenamed = true;
10080 configFilePrev = configFile;
10081 configFilePrev += "-prev";
10082 newConfigFilePrev = newConfigFile;
10083 newConfigFilePrev += "-prev";
10084 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10085 }
10086 }
10087
10088 // update m_strConfigFileFull amd mConfigFile
10089 mData->m_strConfigFileFull = newConfigFile;
10090 // compute the relative path too
10091 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10092
10093 // store the old and new so that VirtualBox::saveSettings() can update
10094 // the media registry
10095 if ( mData->mRegistered
10096 && configDir != newConfigDir)
10097 {
10098 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
10099
10100 if (pfNeedsGlobalSaveSettings)
10101 *pfNeedsGlobalSaveSettings = true;
10102 }
10103
10104 // in the saved state file path, replace the old directory with the new directory
10105 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10106 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
10107
10108 // and do the same thing for the saved state file paths of all the online snapshots
10109 if (mData->mFirstSnapshot)
10110 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
10111 newConfigDir.c_str());
10112 }
10113 while (0);
10114
10115 if (FAILED(rc))
10116 {
10117 /* silently try to rename everything back */
10118 if (fileRenamed)
10119 {
10120 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10121 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10122 }
10123 if (dirRenamed)
10124 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10125 }
10126
10127 if (FAILED(rc)) return rc;
10128 }
10129
10130 if (fSettingsFileIsNew)
10131 {
10132 /* create a virgin config file */
10133 int vrc = VINF_SUCCESS;
10134
10135 /* ensure the settings directory exists */
10136 Utf8Str path(mData->m_strConfigFileFull);
10137 path.stripFilename();
10138 if (!RTDirExists(path.c_str()))
10139 {
10140 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10141 if (RT_FAILURE(vrc))
10142 {
10143 return setError(E_FAIL,
10144 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10145 path.c_str(),
10146 vrc);
10147 }
10148 }
10149
10150 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10151 path = Utf8Str(mData->m_strConfigFileFull);
10152 RTFILE f = NIL_RTFILE;
10153 vrc = RTFileOpen(&f, path.c_str(),
10154 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10155 if (RT_FAILURE(vrc))
10156 return setError(E_FAIL,
10157 tr("Could not create the settings file '%s' (%Rrc)"),
10158 path.c_str(),
10159 vrc);
10160 RTFileClose(f);
10161 }
10162
10163 return rc;
10164}
10165
10166/**
10167 * Saves and commits machine data, user data and hardware data.
10168 *
10169 * Note that on failure, the data remains uncommitted.
10170 *
10171 * @a aFlags may combine the following flags:
10172 *
10173 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10174 * Used when saving settings after an operation that makes them 100%
10175 * correspond to the settings from the current snapshot.
10176 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
10177 * #isReallyModified() returns false. This is necessary for cases when we
10178 * change machine data directly, not through the backup()/commit() mechanism.
10179 * - SaveS_Force: settings will be saved without doing a deep compare of the
10180 * settings structures. This is used when this is called because snapshots
10181 * have changed to avoid the overhead of the deep compare.
10182 *
10183 * @note Must be called from under this object's write lock. Locks children for
10184 * writing.
10185 *
10186 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10187 * initialized to false and that will be set to true by this function if
10188 * the caller must invoke VirtualBox::saveSettings() because the global
10189 * settings have changed. This will happen if a machine rename has been
10190 * saved and the global machine and media registries will therefore need
10191 * updating.
10192 */
10193HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
10194 int aFlags /*= 0*/)
10195{
10196 LogFlowThisFuncEnter();
10197
10198 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10199
10200 /* make sure child objects are unable to modify the settings while we are
10201 * saving them */
10202 ensureNoStateDependencies();
10203
10204 AssertReturn(!isSnapshotMachine(),
10205 E_FAIL);
10206
10207 HRESULT rc = S_OK;
10208 bool fNeedsWrite = false;
10209
10210 /* First, prepare to save settings. It will care about renaming the
10211 * settings directory and file if the machine name was changed and about
10212 * creating a new settings file if this is a new machine. */
10213 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
10214 if (FAILED(rc)) return rc;
10215
10216 // keep a pointer to the current settings structures
10217 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10218 settings::MachineConfigFile *pNewConfig = NULL;
10219
10220 try
10221 {
10222 // make a fresh one to have everyone write stuff into
10223 pNewConfig = new settings::MachineConfigFile(NULL);
10224 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10225
10226 // now go and copy all the settings data from COM to the settings structures
10227 // (this calles saveSettings() on all the COM objects in the machine)
10228 copyMachineDataToSettings(*pNewConfig);
10229
10230 if (aFlags & SaveS_ResetCurStateModified)
10231 {
10232 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10233 mData->mCurrentStateModified = FALSE;
10234 fNeedsWrite = true; // always, no need to compare
10235 }
10236 else if (aFlags & SaveS_Force)
10237 {
10238 fNeedsWrite = true; // always, no need to compare
10239 }
10240 else
10241 {
10242 if (!mData->mCurrentStateModified)
10243 {
10244 // do a deep compare of the settings that we just saved with the settings
10245 // previously stored in the config file; this invokes MachineConfigFile::operator==
10246 // which does a deep compare of all the settings, which is expensive but less expensive
10247 // than writing out XML in vain
10248 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10249
10250 // could still be modified if any settings changed
10251 mData->mCurrentStateModified = fAnySettingsChanged;
10252
10253 fNeedsWrite = fAnySettingsChanged;
10254 }
10255 else
10256 fNeedsWrite = true;
10257 }
10258
10259 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10260
10261 if (fNeedsWrite)
10262 // now spit it all out!
10263 pNewConfig->write(mData->m_strConfigFileFull);
10264
10265 mData->pMachineConfigFile = pNewConfig;
10266 delete pOldConfig;
10267 commit();
10268
10269 // after saving settings, we are no longer different from the XML on disk
10270 mData->flModifications = 0;
10271 }
10272 catch (HRESULT err)
10273 {
10274 // we assume that error info is set by the thrower
10275 rc = err;
10276
10277 // restore old config
10278 delete pNewConfig;
10279 mData->pMachineConfigFile = pOldConfig;
10280 }
10281 catch (...)
10282 {
10283 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10284 }
10285
10286 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10287 {
10288 /* Fire the data change event, even on failure (since we've already
10289 * committed all data). This is done only for SessionMachines because
10290 * mutable Machine instances are always not registered (i.e. private
10291 * to the client process that creates them) and thus don't need to
10292 * inform callbacks. */
10293 if (isSessionMachine())
10294 mParent->onMachineDataChange(mData->mUuid);
10295 }
10296
10297 LogFlowThisFunc(("rc=%08X\n", rc));
10298 LogFlowThisFuncLeave();
10299 return rc;
10300}
10301
10302/**
10303 * Implementation for saving the machine settings into the given
10304 * settings::MachineConfigFile instance. This copies machine extradata
10305 * from the previous machine config file in the instance data, if any.
10306 *
10307 * This gets called from two locations:
10308 *
10309 * -- Machine::saveSettings(), during the regular XML writing;
10310 *
10311 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10312 * exported to OVF and we write the VirtualBox proprietary XML
10313 * into a <vbox:Machine> tag.
10314 *
10315 * This routine fills all the fields in there, including snapshots, *except*
10316 * for the following:
10317 *
10318 * -- fCurrentStateModified. There is some special logic associated with that.
10319 *
10320 * The caller can then call MachineConfigFile::write() or do something else
10321 * with it.
10322 *
10323 * Caller must hold the machine lock!
10324 *
10325 * This throws XML errors and HRESULT, so the caller must have a catch block!
10326 */
10327void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10328{
10329 // deep copy extradata
10330 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10331
10332 config.uuid = mData->mUuid;
10333
10334 // copy name, description, OS type, teleport, UTC etc.
10335 config.machineUserData = mUserData->s;
10336
10337 // Encode the Icon Override data from Machine and store on config userdata.
10338 com::SafeArray<BYTE> iconByte;
10339 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10340 ssize_t cbData = iconByte.size();
10341 if (cbData > 0)
10342 {
10343 ssize_t cchOut = RTBase64EncodedLength(cbData);
10344 Utf8Str strIconData;
10345 strIconData.reserve(cchOut+1);
10346 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10347 strIconData.mutableRaw(), strIconData.capacity(),
10348 NULL);
10349 if (RT_FAILURE(vrc))
10350 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10351 strIconData.jolt();
10352 config.machineUserData.ovIcon = strIconData;
10353 }
10354 else
10355 config.machineUserData.ovIcon.setNull();
10356
10357 if ( mData->mMachineState == MachineState_Saved
10358 || mData->mMachineState == MachineState_Restoring
10359 // when deleting a snapshot we may or may not have a saved state in the current state,
10360 // so let's not assert here please
10361 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10362 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10363 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10364 && (!mSSData->strStateFilePath.isEmpty())
10365 )
10366 )
10367 {
10368 Assert(!mSSData->strStateFilePath.isEmpty());
10369 /* try to make the file name relative to the settings file dir */
10370 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10371 }
10372 else
10373 {
10374 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10375 config.strStateFile.setNull();
10376 }
10377
10378 if (mData->mCurrentSnapshot)
10379 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10380 else
10381 config.uuidCurrentSnapshot.clear();
10382
10383 config.timeLastStateChange = mData->mLastStateChange;
10384 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10385 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10386
10387 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10388 if (FAILED(rc)) throw rc;
10389
10390 rc = saveStorageControllers(config.storageMachine);
10391 if (FAILED(rc)) throw rc;
10392
10393 // save machine's media registry if this is VirtualBox 4.0 or later
10394 if (config.canHaveOwnMediaRegistry())
10395 {
10396 // determine machine folder
10397 Utf8Str strMachineFolder = getSettingsFileFull();
10398 strMachineFolder.stripFilename();
10399 mParent->saveMediaRegistry(config.mediaRegistry,
10400 getId(), // only media with registry ID == machine UUID
10401 strMachineFolder);
10402 // this throws HRESULT
10403 }
10404
10405 // save snapshots
10406 rc = saveAllSnapshots(config);
10407 if (FAILED(rc)) throw rc;
10408}
10409
10410/**
10411 * Saves all snapshots of the machine into the given machine config file. Called
10412 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10413 * @param config
10414 * @return
10415 */
10416HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10417{
10418 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10419
10420 HRESULT rc = S_OK;
10421
10422 try
10423 {
10424 config.llFirstSnapshot.clear();
10425
10426 if (mData->mFirstSnapshot)
10427 {
10428 settings::Snapshot snapNew;
10429 config.llFirstSnapshot.push_back(snapNew);
10430
10431 // get reference to the fresh copy of the snapshot on the list and
10432 // work on that copy directly to avoid excessive copying later
10433 settings::Snapshot &snap = config.llFirstSnapshot.front();
10434
10435 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10436 if (FAILED(rc)) throw rc;
10437 }
10438
10439// if (mType == IsSessionMachine)
10440// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10441
10442 }
10443 catch (HRESULT err)
10444 {
10445 /* we assume that error info is set by the thrower */
10446 rc = err;
10447 }
10448 catch (...)
10449 {
10450 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10451 }
10452
10453 return rc;
10454}
10455
10456/**
10457 * Saves the VM hardware configuration. It is assumed that the
10458 * given node is empty.
10459 *
10460 * @param data Reference to the settings object for the hardware config.
10461 * @param pDbg Pointer to the settings object for the debugging config
10462 * which happens to live in mHWData.
10463 * @param pAutostart Pointer to the settings object for the autostart config
10464 * which happens to live in mHWData.
10465 */
10466HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10467 settings::Autostart *pAutostart)
10468{
10469 HRESULT rc = S_OK;
10470
10471 try
10472 {
10473 /* The hardware version attribute (optional).
10474 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10475 if ( mHWData->mHWVersion == "1"
10476 && mSSData->strStateFilePath.isEmpty()
10477 )
10478 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. */
10479
10480 data.strVersion = mHWData->mHWVersion;
10481 data.uuid = mHWData->mHardwareUUID;
10482
10483 // CPU
10484 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10485 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
10486 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10487 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10488 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10489 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10490 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10491 data.fPAE = !!mHWData->mPAEEnabled;
10492 data.enmLongMode = mHWData->mLongMode;
10493 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10494
10495 /* Standard and Extended CPUID leafs. */
10496 data.llCpuIdLeafs.clear();
10497 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10498 {
10499 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10500 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10501 }
10502 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10503 {
10504 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10505 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10506 }
10507
10508 data.cCPUs = mHWData->mCPUCount;
10509 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10510 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10511
10512 data.llCpus.clear();
10513 if (data.fCpuHotPlug)
10514 {
10515 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10516 {
10517 if (mHWData->mCPUAttached[idx])
10518 {
10519 settings::Cpu cpu;
10520 cpu.ulId = idx;
10521 data.llCpus.push_back(cpu);
10522 }
10523 }
10524 }
10525
10526 // memory
10527 data.ulMemorySizeMB = mHWData->mMemorySize;
10528 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10529
10530 // firmware
10531 data.firmwareType = mHWData->mFirmwareType;
10532
10533 // HID
10534 data.pointingHIDType = mHWData->mPointingHIDType;
10535 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10536
10537 // chipset
10538 data.chipsetType = mHWData->mChipsetType;
10539
10540 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10541 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10542
10543 // HPET
10544 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10545
10546 // boot order
10547 data.mapBootOrder.clear();
10548 for (size_t i = 0;
10549 i < RT_ELEMENTS(mHWData->mBootOrder);
10550 ++i)
10551 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10552
10553 // display
10554 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10555 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10556 data.cMonitors = mHWData->mMonitorCount;
10557 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10558 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10559 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10560 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10561 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10562 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10563 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10564 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10565 {
10566 if (mHWData->maVideoCaptureScreens[i])
10567 ASMBitSet(&data.u64VideoCaptureScreens, i);
10568 else
10569 ASMBitClear(&data.u64VideoCaptureScreens, i);
10570 }
10571 /* store relative video capture file if possible */
10572 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10573
10574 /* VRDEServer settings (optional) */
10575 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10576 if (FAILED(rc)) throw rc;
10577
10578 /* BIOS (required) */
10579 rc = mBIOSSettings->saveSettings(data.biosSettings);
10580 if (FAILED(rc)) throw rc;
10581
10582 /* USB Controller (required) */
10583 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10584 it != mUSBControllers->end();
10585 ++it)
10586 {
10587 ComObjPtr<USBController> ctrl = *it;
10588 settings::USBController settingsCtrl;
10589
10590 settingsCtrl.strName = ctrl->getName();
10591 settingsCtrl.enmType = ctrl->getControllerType();
10592
10593 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10594 }
10595
10596 /* USB device filters (required) */
10597 rc = mUSBDeviceFilters->saveSettings(data.usbSettings);
10598 if (FAILED(rc)) throw rc;
10599
10600 /* Network adapters (required) */
10601 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10602 data.llNetworkAdapters.clear();
10603 /* Write out only the nominal number of network adapters for this
10604 * chipset type. Since Machine::commit() hasn't been called there
10605 * may be extra NIC settings in the vector. */
10606 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10607 {
10608 settings::NetworkAdapter nic;
10609 nic.ulSlot = slot;
10610 /* paranoia check... must not be NULL, but must not crash either. */
10611 if (mNetworkAdapters[slot])
10612 {
10613 rc = mNetworkAdapters[slot]->saveSettings(nic);
10614 if (FAILED(rc)) throw rc;
10615
10616 data.llNetworkAdapters.push_back(nic);
10617 }
10618 }
10619
10620 /* Serial ports */
10621 data.llSerialPorts.clear();
10622 for (ULONG slot = 0;
10623 slot < RT_ELEMENTS(mSerialPorts);
10624 ++slot)
10625 {
10626 settings::SerialPort s;
10627 s.ulSlot = slot;
10628 rc = mSerialPorts[slot]->saveSettings(s);
10629 if (FAILED(rc)) return rc;
10630
10631 data.llSerialPorts.push_back(s);
10632 }
10633
10634 /* Parallel ports */
10635 data.llParallelPorts.clear();
10636 for (ULONG slot = 0;
10637 slot < RT_ELEMENTS(mParallelPorts);
10638 ++slot)
10639 {
10640 settings::ParallelPort p;
10641 p.ulSlot = slot;
10642 rc = mParallelPorts[slot]->saveSettings(p);
10643 if (FAILED(rc)) return rc;
10644
10645 data.llParallelPorts.push_back(p);
10646 }
10647
10648 /* Audio adapter */
10649 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10650 if (FAILED(rc)) return rc;
10651
10652 /* Shared folders */
10653 data.llSharedFolders.clear();
10654 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10655 it != mHWData->mSharedFolders.end();
10656 ++it)
10657 {
10658 SharedFolder *pSF = *it;
10659 AutoCaller sfCaller(pSF);
10660 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10661 settings::SharedFolder sf;
10662 sf.strName = pSF->getName();
10663 sf.strHostPath = pSF->getHostPath();
10664 sf.fWritable = !!pSF->isWritable();
10665 sf.fAutoMount = !!pSF->isAutoMounted();
10666
10667 data.llSharedFolders.push_back(sf);
10668 }
10669
10670 // clipboard
10671 data.clipboardMode = mHWData->mClipboardMode;
10672
10673 // drag'n'drop
10674 data.dragAndDropMode = mHWData->mDragAndDropMode;
10675
10676 /* Guest */
10677 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10678
10679 // IO settings
10680 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10681 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10682
10683 /* BandwidthControl (required) */
10684 rc = mBandwidthControl->saveSettings(data.ioSettings);
10685 if (FAILED(rc)) throw rc;
10686
10687 /* Host PCI devices */
10688 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10689 it != mHWData->mPCIDeviceAssignments.end();
10690 ++it)
10691 {
10692 ComObjPtr<PCIDeviceAttachment> pda = *it;
10693 settings::HostPCIDeviceAttachment hpda;
10694
10695 rc = pda->saveSettings(hpda);
10696 if (FAILED(rc)) throw rc;
10697
10698 data.pciAttachments.push_back(hpda);
10699 }
10700
10701
10702 // guest properties
10703 data.llGuestProperties.clear();
10704#ifdef VBOX_WITH_GUEST_PROPS
10705 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10706 it != mHWData->mGuestProperties.end();
10707 ++it)
10708 {
10709 HWData::GuestProperty property = it->second;
10710
10711 /* Remove transient guest properties at shutdown unless we
10712 * are saving state */
10713 if ( ( mData->mMachineState == MachineState_PoweredOff
10714 || mData->mMachineState == MachineState_Aborted
10715 || mData->mMachineState == MachineState_Teleported)
10716 && ( property.mFlags & guestProp::TRANSIENT
10717 || property.mFlags & guestProp::TRANSRESET))
10718 continue;
10719 settings::GuestProperty prop;
10720 prop.strName = it->first;
10721 prop.strValue = property.strValue;
10722 prop.timestamp = property.mTimestamp;
10723 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10724 guestProp::writeFlags(property.mFlags, szFlags);
10725 prop.strFlags = szFlags;
10726
10727 data.llGuestProperties.push_back(prop);
10728 }
10729
10730 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10731 /* I presume this doesn't require a backup(). */
10732 mData->mGuestPropertiesModified = FALSE;
10733#endif /* VBOX_WITH_GUEST_PROPS defined */
10734
10735 *pDbg = mHWData->mDebugging;
10736 *pAutostart = mHWData->mAutostart;
10737
10738 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10739 }
10740 catch(std::bad_alloc &)
10741 {
10742 return E_OUTOFMEMORY;
10743 }
10744
10745 AssertComRC(rc);
10746 return rc;
10747}
10748
10749/**
10750 * Saves the storage controller configuration.
10751 *
10752 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10753 */
10754HRESULT Machine::saveStorageControllers(settings::Storage &data)
10755{
10756 data.llStorageControllers.clear();
10757
10758 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10759 it != mStorageControllers->end();
10760 ++it)
10761 {
10762 HRESULT rc;
10763 ComObjPtr<StorageController> pCtl = *it;
10764
10765 settings::StorageController ctl;
10766 ctl.strName = pCtl->getName();
10767 ctl.controllerType = pCtl->getControllerType();
10768 ctl.storageBus = pCtl->getStorageBus();
10769 ctl.ulInstance = pCtl->getInstance();
10770 ctl.fBootable = pCtl->getBootable();
10771
10772 /* Save the port count. */
10773 ULONG portCount;
10774 rc = pCtl->COMGETTER(PortCount)(&portCount);
10775 ComAssertComRCRet(rc, rc);
10776 ctl.ulPortCount = portCount;
10777
10778 /* Save fUseHostIOCache */
10779 BOOL fUseHostIOCache;
10780 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10781 ComAssertComRCRet(rc, rc);
10782 ctl.fUseHostIOCache = !!fUseHostIOCache;
10783
10784 /* Save IDE emulation settings. */
10785 if (ctl.controllerType == StorageControllerType_IntelAhci)
10786 {
10787 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10788 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10789 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10790 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10791 )
10792 ComAssertComRCRet(rc, rc);
10793 }
10794
10795 /* save the devices now. */
10796 rc = saveStorageDevices(pCtl, ctl);
10797 ComAssertComRCRet(rc, rc);
10798
10799 data.llStorageControllers.push_back(ctl);
10800 }
10801
10802 return S_OK;
10803}
10804
10805/**
10806 * Saves the hard disk configuration.
10807 */
10808HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10809 settings::StorageController &data)
10810{
10811 MediaData::AttachmentList atts;
10812
10813 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10814 if (FAILED(rc)) return rc;
10815
10816 data.llAttachedDevices.clear();
10817 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10818 it != atts.end();
10819 ++it)
10820 {
10821 settings::AttachedDevice dev;
10822
10823 MediumAttachment *pAttach = *it;
10824 Medium *pMedium = pAttach->getMedium();
10825
10826 dev.deviceType = pAttach->getType();
10827 dev.lPort = pAttach->getPort();
10828 dev.lDevice = pAttach->getDevice();
10829 if (pMedium)
10830 {
10831 if (pMedium->isHostDrive())
10832 dev.strHostDriveSrc = pMedium->getLocationFull();
10833 else
10834 dev.uuid = pMedium->getId();
10835 dev.fPassThrough = pAttach->getPassthrough();
10836 dev.fTempEject = pAttach->getTempEject();
10837 dev.fNonRotational = pAttach->getNonRotational();
10838 dev.fDiscard = pAttach->getDiscard();
10839 }
10840
10841 dev.strBwGroup = pAttach->getBandwidthGroup();
10842
10843 data.llAttachedDevices.push_back(dev);
10844 }
10845
10846 return S_OK;
10847}
10848
10849/**
10850 * Saves machine state settings as defined by aFlags
10851 * (SaveSTS_* values).
10852 *
10853 * @param aFlags Combination of SaveSTS_* flags.
10854 *
10855 * @note Locks objects for writing.
10856 */
10857HRESULT Machine::saveStateSettings(int aFlags)
10858{
10859 if (aFlags == 0)
10860 return S_OK;
10861
10862 AutoCaller autoCaller(this);
10863 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10864
10865 /* This object's write lock is also necessary to serialize file access
10866 * (prevent concurrent reads and writes) */
10867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10868
10869 HRESULT rc = S_OK;
10870
10871 Assert(mData->pMachineConfigFile);
10872
10873 try
10874 {
10875 if (aFlags & SaveSTS_CurStateModified)
10876 mData->pMachineConfigFile->fCurrentStateModified = true;
10877
10878 if (aFlags & SaveSTS_StateFilePath)
10879 {
10880 if (!mSSData->strStateFilePath.isEmpty())
10881 /* try to make the file name relative to the settings file dir */
10882 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10883 else
10884 mData->pMachineConfigFile->strStateFile.setNull();
10885 }
10886
10887 if (aFlags & SaveSTS_StateTimeStamp)
10888 {
10889 Assert( mData->mMachineState != MachineState_Aborted
10890 || mSSData->strStateFilePath.isEmpty());
10891
10892 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10893
10894 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10895//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10896 }
10897
10898 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10899 }
10900 catch (...)
10901 {
10902 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10903 }
10904
10905 return rc;
10906}
10907
10908/**
10909 * Ensures that the given medium is added to a media registry. If this machine
10910 * was created with 4.0 or later, then the machine registry is used. Otherwise
10911 * the global VirtualBox media registry is used.
10912 *
10913 * Caller must NOT hold machine lock, media tree or any medium locks!
10914 *
10915 * @param pMedium
10916 */
10917void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10918{
10919 /* Paranoia checks: do not hold machine or media tree locks. */
10920 AssertReturnVoid(!isWriteLockOnCurrentThread());
10921 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10922
10923 ComObjPtr<Medium> pBase;
10924 {
10925 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10926 pBase = pMedium->getBase();
10927 }
10928
10929 /* Paranoia checks: do not hold medium locks. */
10930 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10931 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10932
10933 // decide which medium registry to use now that the medium is attached:
10934 Guid uuid;
10935 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10936 // machine XML is VirtualBox 4.0 or higher:
10937 uuid = getId(); // machine UUID
10938 else
10939 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10940
10941 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10942 mParent->markRegistryModified(uuid);
10943
10944 /* For more complex hard disk structures it can happen that the base
10945 * medium isn't yet associated with any medium registry. Do that now. */
10946 if (pMedium != pBase)
10947 {
10948 if (pBase->addRegistry(uuid, true /* fRecurse */))
10949 mParent->markRegistryModified(uuid);
10950 }
10951}
10952
10953/**
10954 * Creates differencing hard disks for all normal hard disks attached to this
10955 * machine and a new set of attachments to refer to created disks.
10956 *
10957 * Used when taking a snapshot or when deleting the current state. Gets called
10958 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10959 *
10960 * This method assumes that mMediaData contains the original hard disk attachments
10961 * it needs to create diffs for. On success, these attachments will be replaced
10962 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10963 * called to delete created diffs which will also rollback mMediaData and restore
10964 * whatever was backed up before calling this method.
10965 *
10966 * Attachments with non-normal hard disks are left as is.
10967 *
10968 * If @a aOnline is @c false then the original hard disks that require implicit
10969 * diffs will be locked for reading. Otherwise it is assumed that they are
10970 * already locked for writing (when the VM was started). Note that in the latter
10971 * case it is responsibility of the caller to lock the newly created diffs for
10972 * writing if this method succeeds.
10973 *
10974 * @param aProgress Progress object to run (must contain at least as
10975 * many operations left as the number of hard disks
10976 * attached).
10977 * @param aOnline Whether the VM was online prior to this operation.
10978 *
10979 * @note The progress object is not marked as completed, neither on success nor
10980 * on failure. This is a responsibility of the caller.
10981 *
10982 * @note Locks this object and the media tree for writing.
10983 */
10984HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10985 ULONG aWeight,
10986 bool aOnline)
10987{
10988 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10989
10990 AutoCaller autoCaller(this);
10991 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10992
10993 AutoMultiWriteLock2 alock(this->lockHandle(),
10994 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10995
10996 /* must be in a protective state because we release the lock below */
10997 AssertReturn( mData->mMachineState == MachineState_Saving
10998 || mData->mMachineState == MachineState_LiveSnapshotting
10999 || mData->mMachineState == MachineState_RestoringSnapshot
11000 || mData->mMachineState == MachineState_DeletingSnapshot
11001 , E_FAIL);
11002
11003 HRESULT rc = S_OK;
11004
11005 // use appropriate locked media map (online or offline)
11006 MediumLockListMap lockedMediaOffline;
11007 MediumLockListMap *lockedMediaMap;
11008 if (aOnline)
11009 lockedMediaMap = &mData->mSession.mLockedMedia;
11010 else
11011 lockedMediaMap = &lockedMediaOffline;
11012
11013 try
11014 {
11015 if (!aOnline)
11016 {
11017 /* lock all attached hard disks early to detect "in use"
11018 * situations before creating actual diffs */
11019 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11020 it != mMediaData->mAttachments.end();
11021 ++it)
11022 {
11023 MediumAttachment* pAtt = *it;
11024 if (pAtt->getType() == DeviceType_HardDisk)
11025 {
11026 Medium* pMedium = pAtt->getMedium();
11027 Assert(pMedium);
11028
11029 MediumLockList *pMediumLockList(new MediumLockList());
11030 alock.release();
11031 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11032 false /* fMediumLockWrite */,
11033 NULL,
11034 *pMediumLockList);
11035 alock.acquire();
11036 if (FAILED(rc))
11037 {
11038 delete pMediumLockList;
11039 throw rc;
11040 }
11041 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11042 if (FAILED(rc))
11043 {
11044 throw setError(rc,
11045 tr("Collecting locking information for all attached media failed"));
11046 }
11047 }
11048 }
11049
11050 /* Now lock all media. If this fails, nothing is locked. */
11051 alock.release();
11052 rc = lockedMediaMap->Lock();
11053 alock.acquire();
11054 if (FAILED(rc))
11055 {
11056 throw setError(rc,
11057 tr("Locking of attached media failed"));
11058 }
11059 }
11060
11061 /* remember the current list (note that we don't use backup() since
11062 * mMediaData may be already backed up) */
11063 MediaData::AttachmentList atts = mMediaData->mAttachments;
11064
11065 /* start from scratch */
11066 mMediaData->mAttachments.clear();
11067
11068 /* go through remembered attachments and create diffs for normal hard
11069 * disks and attach them */
11070 for (MediaData::AttachmentList::const_iterator it = atts.begin();
11071 it != atts.end();
11072 ++it)
11073 {
11074 MediumAttachment* pAtt = *it;
11075
11076 DeviceType_T devType = pAtt->getType();
11077 Medium* pMedium = pAtt->getMedium();
11078
11079 if ( devType != DeviceType_HardDisk
11080 || pMedium == NULL
11081 || pMedium->getType() != MediumType_Normal)
11082 {
11083 /* copy the attachment as is */
11084
11085 /** @todo the progress object created in Console::TakeSnaphot
11086 * only expects operations for hard disks. Later other
11087 * device types need to show up in the progress as well. */
11088 if (devType == DeviceType_HardDisk)
11089 {
11090 if (pMedium == NULL)
11091 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11092 aWeight); // weight
11093 else
11094 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11095 pMedium->getBase()->getName().c_str()).raw(),
11096 aWeight); // weight
11097 }
11098
11099 mMediaData->mAttachments.push_back(pAtt);
11100 continue;
11101 }
11102
11103 /* need a diff */
11104 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11105 pMedium->getBase()->getName().c_str()).raw(),
11106 aWeight); // weight
11107
11108 Utf8Str strFullSnapshotFolder;
11109 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11110
11111 ComObjPtr<Medium> diff;
11112 diff.createObject();
11113 // store the diff in the same registry as the parent
11114 // (this cannot fail here because we can't create implicit diffs for
11115 // unregistered images)
11116 Guid uuidRegistryParent;
11117 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
11118 Assert(fInRegistry); NOREF(fInRegistry);
11119 rc = diff->init(mParent,
11120 pMedium->getPreferredDiffFormat(),
11121 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11122 uuidRegistryParent);
11123 if (FAILED(rc)) throw rc;
11124
11125 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11126 * the push_back? Looks like we're going to release medium with the
11127 * wrong kind of lock (general issue with if we fail anywhere at all)
11128 * and an orphaned VDI in the snapshots folder. */
11129
11130 /* update the appropriate lock list */
11131 MediumLockList *pMediumLockList;
11132 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11133 AssertComRCThrowRC(rc);
11134 if (aOnline)
11135 {
11136 alock.release();
11137 /* The currently attached medium will be read-only, change
11138 * the lock type to read. */
11139 rc = pMediumLockList->Update(pMedium, false);
11140 alock.acquire();
11141 AssertComRCThrowRC(rc);
11142 }
11143
11144 /* release the locks before the potentially lengthy operation */
11145 alock.release();
11146 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
11147 pMediumLockList,
11148 NULL /* aProgress */,
11149 true /* aWait */);
11150 alock.acquire();
11151 if (FAILED(rc)) throw rc;
11152
11153 /* actual lock list update is done in Medium::commitMedia */
11154
11155 rc = diff->addBackReference(mData->mUuid);
11156 AssertComRCThrowRC(rc);
11157
11158 /* add a new attachment */
11159 ComObjPtr<MediumAttachment> attachment;
11160 attachment.createObject();
11161 rc = attachment->init(this,
11162 diff,
11163 pAtt->getControllerName(),
11164 pAtt->getPort(),
11165 pAtt->getDevice(),
11166 DeviceType_HardDisk,
11167 true /* aImplicit */,
11168 false /* aPassthrough */,
11169 false /* aTempEject */,
11170 pAtt->getNonRotational(),
11171 pAtt->getDiscard(),
11172 pAtt->getBandwidthGroup());
11173 if (FAILED(rc)) throw rc;
11174
11175 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11176 AssertComRCThrowRC(rc);
11177 mMediaData->mAttachments.push_back(attachment);
11178 }
11179 }
11180 catch (HRESULT aRC) { rc = aRC; }
11181
11182 /* unlock all hard disks we locked when there is no VM */
11183 if (!aOnline)
11184 {
11185 ErrorInfoKeeper eik;
11186
11187 HRESULT rc1 = lockedMediaMap->Clear();
11188 AssertComRC(rc1);
11189 }
11190
11191 return rc;
11192}
11193
11194/**
11195 * Deletes implicit differencing hard disks created either by
11196 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
11197 *
11198 * Note that to delete hard disks created by #AttachDevice() this method is
11199 * called from #fixupMedia() when the changes are rolled back.
11200 *
11201 * @note Locks this object and the media tree for writing.
11202 */
11203HRESULT Machine::deleteImplicitDiffs(bool aOnline)
11204{
11205 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11206
11207 AutoCaller autoCaller(this);
11208 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11209
11210 AutoMultiWriteLock2 alock(this->lockHandle(),
11211 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11212
11213 /* We absolutely must have backed up state. */
11214 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
11215
11216 /* Check if there are any implicitly created diff images. */
11217 bool fImplicitDiffs = false;
11218 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11219 it != mMediaData->mAttachments.end();
11220 ++it)
11221 {
11222 const ComObjPtr<MediumAttachment> &pAtt = *it;
11223 if (pAtt->isImplicit())
11224 {
11225 fImplicitDiffs = true;
11226 break;
11227 }
11228 }
11229 /* If there is nothing to do, leave early. This saves lots of image locking
11230 * effort. It also avoids a MachineStateChanged event without real reason.
11231 * This is important e.g. when loading a VM config, because there should be
11232 * no events. Otherwise API clients can become thoroughly confused for
11233 * inaccessible VMs (the code for loading VM configs uses this method for
11234 * cleanup if the config makes no sense), as they take such events as an
11235 * indication that the VM is alive, and they would force the VM config to
11236 * be reread, leading to an endless loop. */
11237 if (!fImplicitDiffs)
11238 return S_OK;
11239
11240 HRESULT rc = S_OK;
11241 MachineState_T oldState = mData->mMachineState;
11242
11243 /* will release the lock before the potentially lengthy operation,
11244 * so protect with the special state (unless already protected) */
11245 if ( oldState != MachineState_Saving
11246 && oldState != MachineState_LiveSnapshotting
11247 && oldState != MachineState_RestoringSnapshot
11248 && oldState != MachineState_DeletingSnapshot
11249 && oldState != MachineState_DeletingSnapshotOnline
11250 && oldState != MachineState_DeletingSnapshotPaused
11251 )
11252 setMachineState(MachineState_SettingUp);
11253
11254 // use appropriate locked media map (online or offline)
11255 MediumLockListMap lockedMediaOffline;
11256 MediumLockListMap *lockedMediaMap;
11257 if (aOnline)
11258 lockedMediaMap = &mData->mSession.mLockedMedia;
11259 else
11260 lockedMediaMap = &lockedMediaOffline;
11261
11262 try
11263 {
11264 if (!aOnline)
11265 {
11266 /* lock all attached hard disks early to detect "in use"
11267 * situations before deleting actual diffs */
11268 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11269 it != mMediaData->mAttachments.end();
11270 ++it)
11271 {
11272 MediumAttachment* pAtt = *it;
11273 if (pAtt->getType() == DeviceType_HardDisk)
11274 {
11275 Medium* pMedium = pAtt->getMedium();
11276 Assert(pMedium);
11277
11278 MediumLockList *pMediumLockList(new MediumLockList());
11279 alock.release();
11280 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11281 false /* fMediumLockWrite */,
11282 NULL,
11283 *pMediumLockList);
11284 alock.acquire();
11285
11286 if (FAILED(rc))
11287 {
11288 delete pMediumLockList;
11289 throw rc;
11290 }
11291
11292 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11293 if (FAILED(rc))
11294 throw rc;
11295 }
11296 }
11297
11298 if (FAILED(rc))
11299 throw rc;
11300 } // end of offline
11301
11302 /* Lock lists are now up to date and include implicitly created media */
11303
11304 /* Go through remembered attachments and delete all implicitly created
11305 * diffs and fix up the attachment information */
11306 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11307 MediaData::AttachmentList implicitAtts;
11308 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11309 it != mMediaData->mAttachments.end();
11310 ++it)
11311 {
11312 ComObjPtr<MediumAttachment> pAtt = *it;
11313 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11314 if (pMedium.isNull())
11315 continue;
11316
11317 // Implicit attachments go on the list for deletion and back references are removed.
11318 if (pAtt->isImplicit())
11319 {
11320 /* Deassociate and mark for deletion */
11321 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
11322 rc = pMedium->removeBackReference(mData->mUuid);
11323 if (FAILED(rc))
11324 throw rc;
11325 implicitAtts.push_back(pAtt);
11326 continue;
11327 }
11328
11329 /* Was this medium attached before? */
11330 if (!findAttachment(oldAtts, pMedium))
11331 {
11332 /* no: de-associate */
11333 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
11334 rc = pMedium->removeBackReference(mData->mUuid);
11335 if (FAILED(rc))
11336 throw rc;
11337 continue;
11338 }
11339 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
11340 }
11341
11342 /* If there are implicit attachments to delete, throw away the lock
11343 * map contents (which will unlock all media) since the medium
11344 * attachments will be rolled back. Below we need to completely
11345 * recreate the lock map anyway since it is infinitely complex to
11346 * do this incrementally (would need reconstructing each attachment
11347 * change, which would be extremely hairy). */
11348 if (implicitAtts.size() != 0)
11349 {
11350 ErrorInfoKeeper eik;
11351
11352 HRESULT rc1 = lockedMediaMap->Clear();
11353 AssertComRC(rc1);
11354 }
11355
11356 /* rollback hard disk changes */
11357 mMediaData.rollback();
11358
11359 MultiResult mrc(S_OK);
11360
11361 // Delete unused implicit diffs.
11362 if (implicitAtts.size() != 0)
11363 {
11364 alock.release();
11365
11366 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11367 it != implicitAtts.end();
11368 ++it)
11369 {
11370 // Remove medium associated with this attachment.
11371 ComObjPtr<MediumAttachment> pAtt = *it;
11372 Assert(pAtt);
11373 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11374 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11375 Assert(pMedium);
11376
11377 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11378 // continue on delete failure, just collect error messages
11379 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11380 mrc = rc;
11381 }
11382
11383 alock.acquire();
11384
11385 /* if there is a VM recreate media lock map as mentioned above,
11386 * otherwise it is a waste of time and we leave things unlocked */
11387 if (aOnline)
11388 {
11389 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11390 /* must never be NULL, but better safe than sorry */
11391 if (!pMachine.isNull())
11392 {
11393 alock.release();
11394 rc = mData->mSession.mMachine->lockMedia();
11395 alock.acquire();
11396 if (FAILED(rc))
11397 throw rc;
11398 }
11399 }
11400 }
11401 }
11402 catch (HRESULT aRC) {rc = aRC;}
11403
11404 if (mData->mMachineState == MachineState_SettingUp)
11405 setMachineState(oldState);
11406
11407 /* unlock all hard disks we locked when there is no VM */
11408 if (!aOnline)
11409 {
11410 ErrorInfoKeeper eik;
11411
11412 HRESULT rc1 = lockedMediaMap->Clear();
11413 AssertComRC(rc1);
11414 }
11415
11416 return rc;
11417}
11418
11419
11420/**
11421 * Looks through the given list of media attachments for one with the given parameters
11422 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11423 * can be searched as well if needed.
11424 *
11425 * @param list
11426 * @param aControllerName
11427 * @param aControllerPort
11428 * @param aDevice
11429 * @return
11430 */
11431MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11432 IN_BSTR aControllerName,
11433 LONG aControllerPort,
11434 LONG aDevice)
11435{
11436 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11437 it != ll.end();
11438 ++it)
11439 {
11440 MediumAttachment *pAttach = *it;
11441 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11442 return pAttach;
11443 }
11444
11445 return NULL;
11446}
11447
11448/**
11449 * Looks through the given list of media attachments for one with the given parameters
11450 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11451 * can be searched as well if needed.
11452 *
11453 * @param list
11454 * @param aControllerName
11455 * @param aControllerPort
11456 * @param aDevice
11457 * @return
11458 */
11459MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11460 ComObjPtr<Medium> pMedium)
11461{
11462 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11463 it != ll.end();
11464 ++it)
11465 {
11466 MediumAttachment *pAttach = *it;
11467 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11468 if (pMediumThis == pMedium)
11469 return pAttach;
11470 }
11471
11472 return NULL;
11473}
11474
11475/**
11476 * Looks through the given list of media attachments for one with the given parameters
11477 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11478 * can be searched as well if needed.
11479 *
11480 * @param list
11481 * @param aControllerName
11482 * @param aControllerPort
11483 * @param aDevice
11484 * @return
11485 */
11486MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11487 Guid &id)
11488{
11489 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11490 it != ll.end();
11491 ++it)
11492 {
11493 MediumAttachment *pAttach = *it;
11494 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11495 if (pMediumThis->getId() == id)
11496 return pAttach;
11497 }
11498
11499 return NULL;
11500}
11501
11502/**
11503 * Main implementation for Machine::DetachDevice. This also gets called
11504 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11505 *
11506 * @param pAttach Medium attachment to detach.
11507 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11508 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11509 * @return
11510 */
11511HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11512 AutoWriteLock &writeLock,
11513 Snapshot *pSnapshot)
11514{
11515 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11516 DeviceType_T mediumType = pAttach->getType();
11517
11518 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11519
11520 if (pAttach->isImplicit())
11521 {
11522 /* attempt to implicitly delete the implicitly created diff */
11523
11524 /// @todo move the implicit flag from MediumAttachment to Medium
11525 /// and forbid any hard disk operation when it is implicit. Or maybe
11526 /// a special media state for it to make it even more simple.
11527
11528 Assert(mMediaData.isBackedUp());
11529
11530 /* will release the lock before the potentially lengthy operation, so
11531 * protect with the special state */
11532 MachineState_T oldState = mData->mMachineState;
11533 setMachineState(MachineState_SettingUp);
11534
11535 writeLock.release();
11536
11537 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11538 true /*aWait*/);
11539
11540 writeLock.acquire();
11541
11542 setMachineState(oldState);
11543
11544 if (FAILED(rc)) return rc;
11545 }
11546
11547 setModified(IsModified_Storage);
11548 mMediaData.backup();
11549 mMediaData->mAttachments.remove(pAttach);
11550
11551 if (!oldmedium.isNull())
11552 {
11553 // if this is from a snapshot, do not defer detachment to commitMedia()
11554 if (pSnapshot)
11555 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11556 // else if non-hard disk media, do not defer detachment to commitMedia() either
11557 else if (mediumType != DeviceType_HardDisk)
11558 oldmedium->removeBackReference(mData->mUuid);
11559 }
11560
11561 return S_OK;
11562}
11563
11564/**
11565 * Goes thru all media of the given list and
11566 *
11567 * 1) calls detachDevice() on each of them for this machine and
11568 * 2) adds all Medium objects found in the process to the given list,
11569 * depending on cleanupMode.
11570 *
11571 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11572 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11573 * media to the list.
11574 *
11575 * This gets called from Machine::Unregister, both for the actual Machine and
11576 * the SnapshotMachine objects that might be found in the snapshots.
11577 *
11578 * Requires caller and locking. The machine lock must be passed in because it
11579 * will be passed on to detachDevice which needs it for temporary unlocking.
11580 *
11581 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11582 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11583 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11584 * otherwise no media get added.
11585 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11586 * @return
11587 */
11588HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11589 Snapshot *pSnapshot,
11590 CleanupMode_T cleanupMode,
11591 MediaList &llMedia)
11592{
11593 Assert(isWriteLockOnCurrentThread());
11594
11595 HRESULT rc;
11596
11597 // make a temporary list because detachDevice invalidates iterators into
11598 // mMediaData->mAttachments
11599 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11600
11601 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11602 it != llAttachments2.end();
11603 ++it)
11604 {
11605 ComObjPtr<MediumAttachment> &pAttach = *it;
11606 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11607
11608 if (!pMedium.isNull())
11609 {
11610 AutoCaller mac(pMedium);
11611 if (FAILED(mac.rc())) return mac.rc();
11612 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11613 DeviceType_T devType = pMedium->getDeviceType();
11614 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11615 && devType == DeviceType_HardDisk)
11616 || (cleanupMode == CleanupMode_Full)
11617 )
11618 {
11619 llMedia.push_back(pMedium);
11620 ComObjPtr<Medium> pParent = pMedium->getParent();
11621 /*
11622 * Search for medias which are not attached to any machine, but
11623 * in the chain to an attached disk. Mediums are only consided
11624 * if they are:
11625 * - have only one child
11626 * - no references to any machines
11627 * - are of normal medium type
11628 */
11629 while (!pParent.isNull())
11630 {
11631 AutoCaller mac1(pParent);
11632 if (FAILED(mac1.rc())) return mac1.rc();
11633 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11634 if (pParent->getChildren().size() == 1)
11635 {
11636 if ( pParent->getMachineBackRefCount() == 0
11637 && pParent->getType() == MediumType_Normal
11638 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11639 llMedia.push_back(pParent);
11640 }
11641 else
11642 break;
11643 pParent = pParent->getParent();
11644 }
11645 }
11646 }
11647
11648 // real machine: then we need to use the proper method
11649 rc = detachDevice(pAttach, writeLock, pSnapshot);
11650
11651 if (FAILED(rc))
11652 return rc;
11653 }
11654
11655 return S_OK;
11656}
11657
11658/**
11659 * Perform deferred hard disk detachments.
11660 *
11661 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11662 * backed up).
11663 *
11664 * If @a aOnline is @c true then this method will also unlock the old hard disks
11665 * for which the new implicit diffs were created and will lock these new diffs for
11666 * writing.
11667 *
11668 * @param aOnline Whether the VM was online prior to this operation.
11669 *
11670 * @note Locks this object for writing!
11671 */
11672void Machine::commitMedia(bool aOnline /*= false*/)
11673{
11674 AutoCaller autoCaller(this);
11675 AssertComRCReturnVoid(autoCaller.rc());
11676
11677 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11678
11679 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11680
11681 HRESULT rc = S_OK;
11682
11683 /* no attach/detach operations -- nothing to do */
11684 if (!mMediaData.isBackedUp())
11685 return;
11686
11687 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11688 bool fMediaNeedsLocking = false;
11689
11690 /* enumerate new attachments */
11691 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11692 it != mMediaData->mAttachments.end();
11693 ++it)
11694 {
11695 MediumAttachment *pAttach = *it;
11696
11697 pAttach->commit();
11698
11699 Medium* pMedium = pAttach->getMedium();
11700 bool fImplicit = pAttach->isImplicit();
11701
11702 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11703 (pMedium) ? pMedium->getName().c_str() : "NULL",
11704 fImplicit));
11705
11706 /** @todo convert all this Machine-based voodoo to MediumAttachment
11707 * based commit logic. */
11708 if (fImplicit)
11709 {
11710 /* convert implicit attachment to normal */
11711 pAttach->setImplicit(false);
11712
11713 if ( aOnline
11714 && pMedium
11715 && pAttach->getType() == DeviceType_HardDisk
11716 )
11717 {
11718 ComObjPtr<Medium> parent = pMedium->getParent();
11719 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11720
11721 /* update the appropriate lock list */
11722 MediumLockList *pMediumLockList;
11723 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11724 AssertComRC(rc);
11725 if (pMediumLockList)
11726 {
11727 /* unlock if there's a need to change the locking */
11728 if (!fMediaNeedsLocking)
11729 {
11730 rc = mData->mSession.mLockedMedia.Unlock();
11731 AssertComRC(rc);
11732 fMediaNeedsLocking = true;
11733 }
11734 rc = pMediumLockList->Update(parent, false);
11735 AssertComRC(rc);
11736 rc = pMediumLockList->Append(pMedium, true);
11737 AssertComRC(rc);
11738 }
11739 }
11740
11741 continue;
11742 }
11743
11744 if (pMedium)
11745 {
11746 /* was this medium attached before? */
11747 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11748 oldIt != oldAtts.end();
11749 ++oldIt)
11750 {
11751 MediumAttachment *pOldAttach = *oldIt;
11752 if (pOldAttach->getMedium() == pMedium)
11753 {
11754 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11755
11756 /* yes: remove from old to avoid de-association */
11757 oldAtts.erase(oldIt);
11758 break;
11759 }
11760 }
11761 }
11762 }
11763
11764 /* enumerate remaining old attachments and de-associate from the
11765 * current machine state */
11766 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11767 it != oldAtts.end();
11768 ++it)
11769 {
11770 MediumAttachment *pAttach = *it;
11771 Medium* pMedium = pAttach->getMedium();
11772
11773 /* Detach only hard disks, since DVD/floppy media is detached
11774 * instantly in MountMedium. */
11775 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11776 {
11777 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11778
11779 /* now de-associate from the current machine state */
11780 rc = pMedium->removeBackReference(mData->mUuid);
11781 AssertComRC(rc);
11782
11783 if (aOnline)
11784 {
11785 /* unlock since medium is not used anymore */
11786 MediumLockList *pMediumLockList;
11787 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11788 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11789 {
11790 /* this happens for online snapshots, there the attachment
11791 * is changing, but only to a diff image created under
11792 * the old one, so there is no separate lock list */
11793 Assert(!pMediumLockList);
11794 }
11795 else
11796 {
11797 AssertComRC(rc);
11798 if (pMediumLockList)
11799 {
11800 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11801 AssertComRC(rc);
11802 }
11803 }
11804 }
11805 }
11806 }
11807
11808 /* take media locks again so that the locking state is consistent */
11809 if (fMediaNeedsLocking)
11810 {
11811 Assert(aOnline);
11812 rc = mData->mSession.mLockedMedia.Lock();
11813 AssertComRC(rc);
11814 }
11815
11816 /* commit the hard disk changes */
11817 mMediaData.commit();
11818
11819 if (isSessionMachine())
11820 {
11821 /*
11822 * Update the parent machine to point to the new owner.
11823 * This is necessary because the stored parent will point to the
11824 * session machine otherwise and cause crashes or errors later
11825 * when the session machine gets invalid.
11826 */
11827 /** @todo Change the MediumAttachment class to behave like any other
11828 * class in this regard by creating peer MediumAttachment
11829 * objects for session machines and share the data with the peer
11830 * machine.
11831 */
11832 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11833 it != mMediaData->mAttachments.end();
11834 ++it)
11835 {
11836 (*it)->updateParentMachine(mPeer);
11837 }
11838
11839 /* attach new data to the primary machine and reshare it */
11840 mPeer->mMediaData.attach(mMediaData);
11841 }
11842
11843 return;
11844}
11845
11846/**
11847 * Perform deferred deletion of implicitly created diffs.
11848 *
11849 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11850 * backed up).
11851 *
11852 * @note Locks this object for writing!
11853 */
11854void Machine::rollbackMedia()
11855{
11856 AutoCaller autoCaller(this);
11857 AssertComRCReturnVoid(autoCaller.rc());
11858
11859 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11860 LogFlowThisFunc(("Entering rollbackMedia\n"));
11861
11862 HRESULT rc = S_OK;
11863
11864 /* no attach/detach operations -- nothing to do */
11865 if (!mMediaData.isBackedUp())
11866 return;
11867
11868 /* enumerate new attachments */
11869 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11870 it != mMediaData->mAttachments.end();
11871 ++it)
11872 {
11873 MediumAttachment *pAttach = *it;
11874 /* Fix up the backrefs for DVD/floppy media. */
11875 if (pAttach->getType() != DeviceType_HardDisk)
11876 {
11877 Medium* pMedium = pAttach->getMedium();
11878 if (pMedium)
11879 {
11880 rc = pMedium->removeBackReference(mData->mUuid);
11881 AssertComRC(rc);
11882 }
11883 }
11884
11885 (*it)->rollback();
11886
11887 pAttach = *it;
11888 /* Fix up the backrefs for DVD/floppy media. */
11889 if (pAttach->getType() != DeviceType_HardDisk)
11890 {
11891 Medium* pMedium = pAttach->getMedium();
11892 if (pMedium)
11893 {
11894 rc = pMedium->addBackReference(mData->mUuid);
11895 AssertComRC(rc);
11896 }
11897 }
11898 }
11899
11900 /** @todo convert all this Machine-based voodoo to MediumAttachment
11901 * based rollback logic. */
11902 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11903
11904 return;
11905}
11906
11907/**
11908 * Returns true if the settings file is located in the directory named exactly
11909 * as the machine; this means, among other things, that the machine directory
11910 * should be auto-renamed.
11911 *
11912 * @param aSettingsDir if not NULL, the full machine settings file directory
11913 * name will be assigned there.
11914 *
11915 * @note Doesn't lock anything.
11916 * @note Not thread safe (must be called from this object's lock).
11917 */
11918bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11919{
11920 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11921 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11922 if (aSettingsDir)
11923 *aSettingsDir = strMachineDirName;
11924 strMachineDirName.stripPath(); // vmname
11925 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11926 strConfigFileOnly.stripPath() // vmname.vbox
11927 .stripExt(); // vmname
11928 /** @todo hack, make somehow use of ComposeMachineFilename */
11929 if (mUserData->s.fDirectoryIncludesUUID)
11930 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11931
11932 AssertReturn(!strMachineDirName.isEmpty(), false);
11933 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11934
11935 return strMachineDirName == strConfigFileOnly;
11936}
11937
11938/**
11939 * Discards all changes to machine settings.
11940 *
11941 * @param aNotify Whether to notify the direct session about changes or not.
11942 *
11943 * @note Locks objects for writing!
11944 */
11945void Machine::rollback(bool aNotify)
11946{
11947 AutoCaller autoCaller(this);
11948 AssertComRCReturn(autoCaller.rc(), (void)0);
11949
11950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11951
11952 if (!mStorageControllers.isNull())
11953 {
11954 if (mStorageControllers.isBackedUp())
11955 {
11956 /* unitialize all new devices (absent in the backed up list). */
11957 StorageControllerList::const_iterator it = mStorageControllers->begin();
11958 StorageControllerList *backedList = mStorageControllers.backedUpData();
11959 while (it != mStorageControllers->end())
11960 {
11961 if ( std::find(backedList->begin(), backedList->end(), *it)
11962 == backedList->end()
11963 )
11964 {
11965 (*it)->uninit();
11966 }
11967 ++it;
11968 }
11969
11970 /* restore the list */
11971 mStorageControllers.rollback();
11972 }
11973
11974 /* rollback any changes to devices after restoring the list */
11975 if (mData->flModifications & IsModified_Storage)
11976 {
11977 StorageControllerList::const_iterator it = mStorageControllers->begin();
11978 while (it != mStorageControllers->end())
11979 {
11980 (*it)->rollback();
11981 ++it;
11982 }
11983 }
11984 }
11985
11986 if (!mUSBControllers.isNull())
11987 {
11988 if (mUSBControllers.isBackedUp())
11989 {
11990 /* unitialize all new devices (absent in the backed up list). */
11991 USBControllerList::const_iterator it = mUSBControllers->begin();
11992 USBControllerList *backedList = mUSBControllers.backedUpData();
11993 while (it != mUSBControllers->end())
11994 {
11995 if ( std::find(backedList->begin(), backedList->end(), *it)
11996 == backedList->end()
11997 )
11998 {
11999 (*it)->uninit();
12000 }
12001 ++it;
12002 }
12003
12004 /* restore the list */
12005 mUSBControllers.rollback();
12006 }
12007
12008 /* rollback any changes to devices after restoring the list */
12009 if (mData->flModifications & IsModified_USB)
12010 {
12011 USBControllerList::const_iterator it = mUSBControllers->begin();
12012 while (it != mUSBControllers->end())
12013 {
12014 (*it)->rollback();
12015 ++it;
12016 }
12017 }
12018 }
12019
12020 mUserData.rollback();
12021
12022 mHWData.rollback();
12023
12024 if (mData->flModifications & IsModified_Storage)
12025 rollbackMedia();
12026
12027 if (mBIOSSettings)
12028 mBIOSSettings->rollback();
12029
12030 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12031 mVRDEServer->rollback();
12032
12033 if (mAudioAdapter)
12034 mAudioAdapter->rollback();
12035
12036 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12037 mUSBDeviceFilters->rollback();
12038
12039 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12040 mBandwidthControl->rollback();
12041
12042 if (!mHWData.isNull())
12043 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12044 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12045 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12046 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12047
12048 if (mData->flModifications & IsModified_NetworkAdapters)
12049 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12050 if ( mNetworkAdapters[slot]
12051 && mNetworkAdapters[slot]->isModified())
12052 {
12053 mNetworkAdapters[slot]->rollback();
12054 networkAdapters[slot] = mNetworkAdapters[slot];
12055 }
12056
12057 if (mData->flModifications & IsModified_SerialPorts)
12058 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12059 if ( mSerialPorts[slot]
12060 && mSerialPorts[slot]->isModified())
12061 {
12062 mSerialPorts[slot]->rollback();
12063 serialPorts[slot] = mSerialPorts[slot];
12064 }
12065
12066 if (mData->flModifications & IsModified_ParallelPorts)
12067 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12068 if ( mParallelPorts[slot]
12069 && mParallelPorts[slot]->isModified())
12070 {
12071 mParallelPorts[slot]->rollback();
12072 parallelPorts[slot] = mParallelPorts[slot];
12073 }
12074
12075 if (aNotify)
12076 {
12077 /* inform the direct session about changes */
12078
12079 ComObjPtr<Machine> that = this;
12080 uint32_t flModifications = mData->flModifications;
12081 alock.release();
12082
12083 if (flModifications & IsModified_SharedFolders)
12084 that->onSharedFolderChange();
12085
12086 if (flModifications & IsModified_VRDEServer)
12087 that->onVRDEServerChange(/* aRestart */ TRUE);
12088 if (flModifications & IsModified_USB)
12089 that->onUSBControllerChange();
12090
12091 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
12092 if (networkAdapters[slot])
12093 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
12094 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
12095 if (serialPorts[slot])
12096 that->onSerialPortChange(serialPorts[slot]);
12097 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
12098 if (parallelPorts[slot])
12099 that->onParallelPortChange(parallelPorts[slot]);
12100
12101 if (flModifications & IsModified_Storage)
12102 that->onStorageControllerChange();
12103
12104#if 0
12105 if (flModifications & IsModified_BandwidthControl)
12106 that->onBandwidthControlChange();
12107#endif
12108 }
12109}
12110
12111/**
12112 * Commits all the changes to machine settings.
12113 *
12114 * Note that this operation is supposed to never fail.
12115 *
12116 * @note Locks this object and children for writing.
12117 */
12118void Machine::commit()
12119{
12120 AutoCaller autoCaller(this);
12121 AssertComRCReturnVoid(autoCaller.rc());
12122
12123 AutoCaller peerCaller(mPeer);
12124 AssertComRCReturnVoid(peerCaller.rc());
12125
12126 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12127
12128 /*
12129 * use safe commit to ensure Snapshot machines (that share mUserData)
12130 * will still refer to a valid memory location
12131 */
12132 mUserData.commitCopy();
12133
12134 mHWData.commit();
12135
12136 if (mMediaData.isBackedUp())
12137 commitMedia(Global::IsOnline(mData->mMachineState));
12138
12139 mBIOSSettings->commit();
12140 mVRDEServer->commit();
12141 mAudioAdapter->commit();
12142 mUSBDeviceFilters->commit();
12143 mBandwidthControl->commit();
12144
12145 /* Since mNetworkAdapters is a list which might have been changed (resized)
12146 * without using the Backupable<> template we need to handle the copying
12147 * of the list entries manually, including the creation of peers for the
12148 * new objects. */
12149 bool commitNetworkAdapters = false;
12150 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12151 if (mPeer)
12152 {
12153 /* commit everything, even the ones which will go away */
12154 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12155 mNetworkAdapters[slot]->commit();
12156 /* copy over the new entries, creating a peer and uninit the original */
12157 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12158 for (size_t slot = 0; slot < newSize; slot++)
12159 {
12160 /* look if this adapter has a peer device */
12161 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
12162 if (!peer)
12163 {
12164 /* no peer means the adapter is a newly created one;
12165 * create a peer owning data this data share it with */
12166 peer.createObject();
12167 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12168 }
12169 mPeer->mNetworkAdapters[slot] = peer;
12170 }
12171 /* uninit any no longer needed network adapters */
12172 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
12173 mNetworkAdapters[slot]->uninit();
12174 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
12175 {
12176 if (mPeer->mNetworkAdapters[slot])
12177 mPeer->mNetworkAdapters[slot]->uninit();
12178 }
12179 /* Keep the original network adapter count until this point, so that
12180 * discarding a chipset type change will not lose settings. */
12181 mNetworkAdapters.resize(newSize);
12182 mPeer->mNetworkAdapters.resize(newSize);
12183 }
12184 else
12185 {
12186 /* we have no peer (our parent is the newly created machine);
12187 * just commit changes to the network adapters */
12188 commitNetworkAdapters = true;
12189 }
12190 if (commitNetworkAdapters)
12191 {
12192 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12193 mNetworkAdapters[slot]->commit();
12194 }
12195
12196 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12197 mSerialPorts[slot]->commit();
12198 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12199 mParallelPorts[slot]->commit();
12200
12201 bool commitStorageControllers = false;
12202
12203 if (mStorageControllers.isBackedUp())
12204 {
12205 mStorageControllers.commit();
12206
12207 if (mPeer)
12208 {
12209 /* Commit all changes to new controllers (this will reshare data with
12210 * peers for those who have peers) */
12211 StorageControllerList *newList = new StorageControllerList();
12212 StorageControllerList::const_iterator it = mStorageControllers->begin();
12213 while (it != mStorageControllers->end())
12214 {
12215 (*it)->commit();
12216
12217 /* look if this controller has a peer device */
12218 ComObjPtr<StorageController> peer = (*it)->getPeer();
12219 if (!peer)
12220 {
12221 /* no peer means the device is a newly created one;
12222 * create a peer owning data this device share it with */
12223 peer.createObject();
12224 peer->init(mPeer, *it, true /* aReshare */);
12225 }
12226 else
12227 {
12228 /* remove peer from the old list */
12229 mPeer->mStorageControllers->remove(peer);
12230 }
12231 /* and add it to the new list */
12232 newList->push_back(peer);
12233
12234 ++it;
12235 }
12236
12237 /* uninit old peer's controllers that are left */
12238 it = mPeer->mStorageControllers->begin();
12239 while (it != mPeer->mStorageControllers->end())
12240 {
12241 (*it)->uninit();
12242 ++it;
12243 }
12244
12245 /* attach new list of controllers to our peer */
12246 mPeer->mStorageControllers.attach(newList);
12247 }
12248 else
12249 {
12250 /* we have no peer (our parent is the newly created machine);
12251 * just commit changes to devices */
12252 commitStorageControllers = true;
12253 }
12254 }
12255 else
12256 {
12257 /* the list of controllers itself is not changed,
12258 * just commit changes to controllers themselves */
12259 commitStorageControllers = true;
12260 }
12261
12262 if (commitStorageControllers)
12263 {
12264 StorageControllerList::const_iterator it = mStorageControllers->begin();
12265 while (it != mStorageControllers->end())
12266 {
12267 (*it)->commit();
12268 ++it;
12269 }
12270 }
12271
12272 bool commitUSBControllers = false;
12273
12274 if (mUSBControllers.isBackedUp())
12275 {
12276 mUSBControllers.commit();
12277
12278 if (mPeer)
12279 {
12280 /* Commit all changes to new controllers (this will reshare data with
12281 * peers for those who have peers) */
12282 USBControllerList *newList = new USBControllerList();
12283 USBControllerList::const_iterator it = mUSBControllers->begin();
12284 while (it != mUSBControllers->end())
12285 {
12286 (*it)->commit();
12287
12288 /* look if this controller has a peer device */
12289 ComObjPtr<USBController> peer = (*it)->getPeer();
12290 if (!peer)
12291 {
12292 /* no peer means the device is a newly created one;
12293 * create a peer owning data this device share it with */
12294 peer.createObject();
12295 peer->init(mPeer, *it, true /* aReshare */);
12296 }
12297 else
12298 {
12299 /* remove peer from the old list */
12300 mPeer->mUSBControllers->remove(peer);
12301 }
12302 /* and add it to the new list */
12303 newList->push_back(peer);
12304
12305 ++it;
12306 }
12307
12308 /* uninit old peer's controllers that are left */
12309 it = mPeer->mUSBControllers->begin();
12310 while (it != mPeer->mUSBControllers->end())
12311 {
12312 (*it)->uninit();
12313 ++it;
12314 }
12315
12316 /* attach new list of controllers to our peer */
12317 mPeer->mUSBControllers.attach(newList);
12318 }
12319 else
12320 {
12321 /* we have no peer (our parent is the newly created machine);
12322 * just commit changes to devices */
12323 commitUSBControllers = true;
12324 }
12325 }
12326 else
12327 {
12328 /* the list of controllers itself is not changed,
12329 * just commit changes to controllers themselves */
12330 commitUSBControllers = true;
12331 }
12332
12333 if (commitUSBControllers)
12334 {
12335 USBControllerList::const_iterator it = mUSBControllers->begin();
12336 while (it != mUSBControllers->end())
12337 {
12338 (*it)->commit();
12339 ++it;
12340 }
12341 }
12342
12343 if (isSessionMachine())
12344 {
12345 /* attach new data to the primary machine and reshare it */
12346 mPeer->mUserData.attach(mUserData);
12347 mPeer->mHWData.attach(mHWData);
12348 /* mMediaData is reshared by fixupMedia */
12349 // mPeer->mMediaData.attach(mMediaData);
12350 Assert(mPeer->mMediaData.data() == mMediaData.data());
12351 }
12352}
12353
12354/**
12355 * Copies all the hardware data from the given machine.
12356 *
12357 * Currently, only called when the VM is being restored from a snapshot. In
12358 * particular, this implies that the VM is not running during this method's
12359 * call.
12360 *
12361 * @note This method must be called from under this object's lock.
12362 *
12363 * @note This method doesn't call #commit(), so all data remains backed up and
12364 * unsaved.
12365 */
12366void Machine::copyFrom(Machine *aThat)
12367{
12368 AssertReturnVoid(!isSnapshotMachine());
12369 AssertReturnVoid(aThat->isSnapshotMachine());
12370
12371 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12372
12373 mHWData.assignCopy(aThat->mHWData);
12374
12375 // create copies of all shared folders (mHWData after attaching a copy
12376 // contains just references to original objects)
12377 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12378 it != mHWData->mSharedFolders.end();
12379 ++it)
12380 {
12381 ComObjPtr<SharedFolder> folder;
12382 folder.createObject();
12383 HRESULT rc = folder->initCopy(getMachine(), *it);
12384 AssertComRC(rc);
12385 *it = folder;
12386 }
12387
12388 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
12389 mVRDEServer->copyFrom(aThat->mVRDEServer);
12390 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
12391 mUSBDeviceFilters->copyFrom(aThat->mUSBDeviceFilters);
12392 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
12393
12394 /* create private copies of all controllers */
12395 mStorageControllers.backup();
12396 mStorageControllers->clear();
12397 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12398 it != aThat->mStorageControllers->end();
12399 ++it)
12400 {
12401 ComObjPtr<StorageController> ctrl;
12402 ctrl.createObject();
12403 ctrl->initCopy(this, *it);
12404 mStorageControllers->push_back(ctrl);
12405 }
12406
12407 /* create private copies of all USB controllers */
12408 mUSBControllers.backup();
12409 mUSBControllers->clear();
12410 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12411 it != aThat->mUSBControllers->end();
12412 ++it)
12413 {
12414 ComObjPtr<USBController> ctrl;
12415 ctrl.createObject();
12416 ctrl->initCopy(this, *it);
12417 mUSBControllers->push_back(ctrl);
12418 }
12419
12420 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12421 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12422 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
12423 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12424 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
12425 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12426 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
12427}
12428
12429/**
12430 * Returns whether the given storage controller is hotplug capable.
12431 *
12432 * @returns true if the controller supports hotplugging
12433 * false otherwise.
12434 * @param enmCtrlType The controller type to check for.
12435 */
12436bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12437{
12438 switch (enmCtrlType)
12439 {
12440 case StorageControllerType_IntelAhci:
12441 return true;
12442 case StorageControllerType_LsiLogic:
12443 case StorageControllerType_LsiLogicSas:
12444 case StorageControllerType_BusLogic:
12445 case StorageControllerType_PIIX3:
12446 case StorageControllerType_PIIX4:
12447 case StorageControllerType_ICH6:
12448 case StorageControllerType_I82078:
12449 default:
12450 return false;
12451 }
12452}
12453
12454#ifdef VBOX_WITH_RESOURCE_USAGE_API
12455
12456void Machine::getDiskList(MediaList &list)
12457{
12458 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12459 it != mMediaData->mAttachments.end();
12460 ++it)
12461 {
12462 MediumAttachment* pAttach = *it;
12463 /* just in case */
12464 AssertStmt(pAttach, continue);
12465
12466 AutoCaller localAutoCallerA(pAttach);
12467 if (FAILED(localAutoCallerA.rc())) continue;
12468
12469 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12470
12471 if (pAttach->getType() == DeviceType_HardDisk)
12472 list.push_back(pAttach->getMedium());
12473 }
12474}
12475
12476void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12477{
12478 AssertReturnVoid(isWriteLockOnCurrentThread());
12479 AssertPtrReturnVoid(aCollector);
12480
12481 pm::CollectorHAL *hal = aCollector->getHAL();
12482 /* Create sub metrics */
12483 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12484 "Percentage of processor time spent in user mode by the VM process.");
12485 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12486 "Percentage of processor time spent in kernel mode by the VM process.");
12487 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12488 "Size of resident portion of VM process in memory.");
12489 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12490 "Actual size of all VM disks combined.");
12491 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12492 "Network receive rate.");
12493 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12494 "Network transmit rate.");
12495 /* Create and register base metrics */
12496 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12497 cpuLoadUser, cpuLoadKernel);
12498 aCollector->registerBaseMetric(cpuLoad);
12499 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12500 ramUsageUsed);
12501 aCollector->registerBaseMetric(ramUsage);
12502 MediaList disks;
12503 getDiskList(disks);
12504 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12505 diskUsageUsed);
12506 aCollector->registerBaseMetric(diskUsage);
12507
12508 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12509 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12510 new pm::AggregateAvg()));
12511 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12512 new pm::AggregateMin()));
12513 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12514 new pm::AggregateMax()));
12515 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12516 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12517 new pm::AggregateAvg()));
12518 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12519 new pm::AggregateMin()));
12520 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12521 new pm::AggregateMax()));
12522
12523 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12524 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12525 new pm::AggregateAvg()));
12526 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12527 new pm::AggregateMin()));
12528 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12529 new pm::AggregateMax()));
12530
12531 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12532 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12533 new pm::AggregateAvg()));
12534 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12535 new pm::AggregateMin()));
12536 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12537 new pm::AggregateMax()));
12538
12539
12540 /* Guest metrics collector */
12541 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12542 aCollector->registerGuest(mCollectorGuest);
12543 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12544 this, __PRETTY_FUNCTION__, mCollectorGuest));
12545
12546 /* Create sub metrics */
12547 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12548 "Percentage of processor time spent in user mode as seen by the guest.");
12549 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12550 "Percentage of processor time spent in kernel mode as seen by the guest.");
12551 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12552 "Percentage of processor time spent idling as seen by the guest.");
12553
12554 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12555 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12556 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12557 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12558 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12559 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12560
12561 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12562
12563 /* Create and register base metrics */
12564 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12565 machineNetRx, machineNetTx);
12566 aCollector->registerBaseMetric(machineNetRate);
12567
12568 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12569 guestLoadUser, guestLoadKernel, guestLoadIdle);
12570 aCollector->registerBaseMetric(guestCpuLoad);
12571
12572 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12573 guestMemTotal, guestMemFree,
12574 guestMemBalloon, guestMemShared,
12575 guestMemCache, guestPagedTotal);
12576 aCollector->registerBaseMetric(guestCpuMem);
12577
12578 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12579 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12580 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12581 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12582
12583 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12584 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12585 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12586 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12587
12588 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12589 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12590 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12591 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12592
12593 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12594 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12595 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12596 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12597
12598 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12599 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12600 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12601 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12602
12603 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12604 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12605 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12606 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12607
12608 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12609 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12610 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12611 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12612
12613 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12614 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12615 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12616 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12617
12618 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12619 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12620 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12621 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12622
12623 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12624 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12625 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12626 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12627
12628 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12629 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12630 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12631 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12632}
12633
12634void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12635{
12636 AssertReturnVoid(isWriteLockOnCurrentThread());
12637
12638 if (aCollector)
12639 {
12640 aCollector->unregisterMetricsFor(aMachine);
12641 aCollector->unregisterBaseMetricsFor(aMachine);
12642 }
12643}
12644
12645#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12646
12647
12648////////////////////////////////////////////////////////////////////////////////
12649
12650DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12651
12652HRESULT SessionMachine::FinalConstruct()
12653{
12654 LogFlowThisFunc(("\n"));
12655
12656 mClientToken = NULL;
12657
12658 return BaseFinalConstruct();
12659}
12660
12661void SessionMachine::FinalRelease()
12662{
12663 LogFlowThisFunc(("\n"));
12664
12665 Assert(!mClientToken);
12666 /* paranoia, should not hang around any more */
12667 if (mClientToken)
12668 {
12669 delete mClientToken;
12670 mClientToken = NULL;
12671 }
12672
12673 uninit(Uninit::Unexpected);
12674
12675 BaseFinalRelease();
12676}
12677
12678/**
12679 * @note Must be called only by Machine::LockMachine() from its own write lock.
12680 */
12681HRESULT SessionMachine::init(Machine *aMachine)
12682{
12683 LogFlowThisFuncEnter();
12684 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12685
12686 AssertReturn(aMachine, E_INVALIDARG);
12687
12688 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12689
12690 /* Enclose the state transition NotReady->InInit->Ready */
12691 AutoInitSpan autoInitSpan(this);
12692 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12693
12694 HRESULT rc = S_OK;
12695
12696 /* create the machine client token */
12697 try
12698 {
12699 mClientToken = new ClientToken(aMachine);
12700 if (!mClientToken->isReady())
12701 {
12702 delete mClientToken;
12703 mClientToken = NULL;
12704 rc = E_FAIL;
12705 }
12706 }
12707 catch (std::bad_alloc &)
12708 {
12709 rc = E_OUTOFMEMORY;
12710 }
12711 if (FAILED(rc))
12712 return rc;
12713
12714 /* memorize the peer Machine */
12715 unconst(mPeer) = aMachine;
12716 /* share the parent pointer */
12717 unconst(mParent) = aMachine->mParent;
12718
12719 /* take the pointers to data to share */
12720 mData.share(aMachine->mData);
12721 mSSData.share(aMachine->mSSData);
12722
12723 mUserData.share(aMachine->mUserData);
12724 mHWData.share(aMachine->mHWData);
12725 mMediaData.share(aMachine->mMediaData);
12726
12727 mStorageControllers.allocate();
12728 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12729 it != aMachine->mStorageControllers->end();
12730 ++it)
12731 {
12732 ComObjPtr<StorageController> ctl;
12733 ctl.createObject();
12734 ctl->init(this, *it);
12735 mStorageControllers->push_back(ctl);
12736 }
12737
12738 mUSBControllers.allocate();
12739 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12740 it != aMachine->mUSBControllers->end();
12741 ++it)
12742 {
12743 ComObjPtr<USBController> ctl;
12744 ctl.createObject();
12745 ctl->init(this, *it);
12746 mUSBControllers->push_back(ctl);
12747 }
12748
12749 unconst(mBIOSSettings).createObject();
12750 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12751 /* create another VRDEServer object that will be mutable */
12752 unconst(mVRDEServer).createObject();
12753 mVRDEServer->init(this, aMachine->mVRDEServer);
12754 /* create another audio adapter object that will be mutable */
12755 unconst(mAudioAdapter).createObject();
12756 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12757 /* create a list of serial ports that will be mutable */
12758 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12759 {
12760 unconst(mSerialPorts[slot]).createObject();
12761 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12762 }
12763 /* create a list of parallel ports that will be mutable */
12764 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12765 {
12766 unconst(mParallelPorts[slot]).createObject();
12767 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12768 }
12769
12770 /* create another USB device filters object that will be mutable */
12771 unconst(mUSBDeviceFilters).createObject();
12772 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12773
12774 /* create a list of network adapters that will be mutable */
12775 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12776 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12777 {
12778 unconst(mNetworkAdapters[slot]).createObject();
12779 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12780 }
12781
12782 /* create another bandwidth control object that will be mutable */
12783 unconst(mBandwidthControl).createObject();
12784 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12785
12786 /* default is to delete saved state on Saved -> PoweredOff transition */
12787 mRemoveSavedState = true;
12788
12789 /* Confirm a successful initialization when it's the case */
12790 autoInitSpan.setSucceeded();
12791
12792 LogFlowThisFuncLeave();
12793 return rc;
12794}
12795
12796/**
12797 * Uninitializes this session object. If the reason is other than
12798 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12799 *
12800 * @param aReason uninitialization reason
12801 *
12802 * @note Locks mParent + this object for writing.
12803 */
12804void SessionMachine::uninit(Uninit::Reason aReason)
12805{
12806 LogFlowThisFuncEnter();
12807 LogFlowThisFunc(("reason=%d\n", aReason));
12808
12809 /*
12810 * Strongly reference ourselves to prevent this object deletion after
12811 * mData->mSession.mMachine.setNull() below (which can release the last
12812 * reference and call the destructor). Important: this must be done before
12813 * accessing any members (and before AutoUninitSpan that does it as well).
12814 * This self reference will be released as the very last step on return.
12815 */
12816 ComObjPtr<SessionMachine> selfRef = this;
12817
12818 /* Enclose the state transition Ready->InUninit->NotReady */
12819 AutoUninitSpan autoUninitSpan(this);
12820 if (autoUninitSpan.uninitDone())
12821 {
12822 LogFlowThisFunc(("Already uninitialized\n"));
12823 LogFlowThisFuncLeave();
12824 return;
12825 }
12826
12827 if (autoUninitSpan.initFailed())
12828 {
12829 /* We've been called by init() because it's failed. It's not really
12830 * necessary (nor it's safe) to perform the regular uninit sequence
12831 * below, the following is enough.
12832 */
12833 LogFlowThisFunc(("Initialization failed.\n"));
12834 /* destroy the machine client token */
12835 if (mClientToken)
12836 {
12837 delete mClientToken;
12838 mClientToken = NULL;
12839 }
12840 uninitDataAndChildObjects();
12841 mData.free();
12842 unconst(mParent) = NULL;
12843 unconst(mPeer) = NULL;
12844 LogFlowThisFuncLeave();
12845 return;
12846 }
12847
12848 MachineState_T lastState;
12849 {
12850 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12851 lastState = mData->mMachineState;
12852 }
12853 NOREF(lastState);
12854
12855#ifdef VBOX_WITH_USB
12856 // release all captured USB devices, but do this before requesting the locks below
12857 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12858 {
12859 /* Console::captureUSBDevices() is called in the VM process only after
12860 * setting the machine state to Starting or Restoring.
12861 * Console::detachAllUSBDevices() will be called upon successful
12862 * termination. So, we need to release USB devices only if there was
12863 * an abnormal termination of a running VM.
12864 *
12865 * This is identical to SessionMachine::DetachAllUSBDevices except
12866 * for the aAbnormal argument. */
12867 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
12868 AssertComRC(rc);
12869 NOREF(rc);
12870
12871 USBProxyService *service = mParent->host()->usbProxyService();
12872 if (service)
12873 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12874 }
12875#endif /* VBOX_WITH_USB */
12876
12877 // we need to lock this object in uninit() because the lock is shared
12878 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12879 // and others need mParent lock, and USB needs host lock.
12880 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12881
12882#ifdef VBOX_WITH_RESOURCE_USAGE_API
12883 /*
12884 * It is safe to call Machine::unregisterMetrics() here because
12885 * PerformanceCollector::samplerCallback no longer accesses guest methods
12886 * holding the lock.
12887 */
12888 unregisterMetrics(mParent->performanceCollector(), mPeer);
12889 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12890 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12891 this, __PRETTY_FUNCTION__, mCollectorGuest));
12892 if (mCollectorGuest)
12893 {
12894 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12895 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12896 mCollectorGuest = NULL;
12897 }
12898#endif
12899
12900 if (aReason == Uninit::Abnormal)
12901 {
12902 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12903 Global::IsOnlineOrTransient(lastState)));
12904
12905 /* reset the state to Aborted */
12906 if (mData->mMachineState != MachineState_Aborted)
12907 setMachineState(MachineState_Aborted);
12908 }
12909
12910 // any machine settings modified?
12911 if (mData->flModifications)
12912 {
12913 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12914 rollback(false /* aNotify */);
12915 }
12916
12917 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12918 || !mConsoleTaskData.mSnapshot);
12919 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12920 {
12921 LogWarningThisFunc(("canceling failed save state request!\n"));
12922 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12923 }
12924 else if (!mConsoleTaskData.mSnapshot.isNull())
12925 {
12926 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12927
12928 /* delete all differencing hard disks created (this will also attach
12929 * their parents back by rolling back mMediaData) */
12930 rollbackMedia();
12931
12932 // delete the saved state file (it might have been already created)
12933 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12934 // think it's still in use
12935 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12936 mConsoleTaskData.mSnapshot->uninit();
12937 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12938 }
12939
12940 if (!mData->mSession.mType.isEmpty())
12941 {
12942 /* mType is not null when this machine's process has been started by
12943 * Machine::LaunchVMProcess(), therefore it is our child. We
12944 * need to queue the PID to reap the process (and avoid zombies on
12945 * Linux). */
12946 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12947 mParent->addProcessToReap(mData->mSession.mPID);
12948 }
12949
12950 mData->mSession.mPID = NIL_RTPROCESS;
12951
12952 if (aReason == Uninit::Unexpected)
12953 {
12954 /* Uninitialization didn't come from #checkForDeath(), so tell the
12955 * client watcher thread to update the set of machines that have open
12956 * sessions. */
12957 mParent->updateClientWatcher();
12958 }
12959
12960 /* uninitialize all remote controls */
12961 if (mData->mSession.mRemoteControls.size())
12962 {
12963 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12964 mData->mSession.mRemoteControls.size()));
12965
12966 Data::Session::RemoteControlList::iterator it =
12967 mData->mSession.mRemoteControls.begin();
12968 while (it != mData->mSession.mRemoteControls.end())
12969 {
12970 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12971 HRESULT rc = (*it)->Uninitialize();
12972 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12973 if (FAILED(rc))
12974 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12975 ++it;
12976 }
12977 mData->mSession.mRemoteControls.clear();
12978 }
12979
12980 /*
12981 * An expected uninitialization can come only from #checkForDeath().
12982 * Otherwise it means that something's gone really wrong (for example,
12983 * the Session implementation has released the VirtualBox reference
12984 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12985 * etc). However, it's also possible, that the client releases the IPC
12986 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12987 * but the VirtualBox release event comes first to the server process.
12988 * This case is practically possible, so we should not assert on an
12989 * unexpected uninit, just log a warning.
12990 */
12991
12992 if ((aReason == Uninit::Unexpected))
12993 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12994
12995 if (aReason != Uninit::Normal)
12996 {
12997 mData->mSession.mDirectControl.setNull();
12998 }
12999 else
13000 {
13001 /* this must be null here (see #OnSessionEnd()) */
13002 Assert(mData->mSession.mDirectControl.isNull());
13003 Assert(mData->mSession.mState == SessionState_Unlocking);
13004 Assert(!mData->mSession.mProgress.isNull());
13005 }
13006 if (mData->mSession.mProgress)
13007 {
13008 if (aReason == Uninit::Normal)
13009 mData->mSession.mProgress->notifyComplete(S_OK);
13010 else
13011 mData->mSession.mProgress->notifyComplete(E_FAIL,
13012 COM_IIDOF(ISession),
13013 getComponentName(),
13014 tr("The VM session was aborted"));
13015 mData->mSession.mProgress.setNull();
13016 }
13017
13018 /* remove the association between the peer machine and this session machine */
13019 Assert( (SessionMachine*)mData->mSession.mMachine == this
13020 || aReason == Uninit::Unexpected);
13021
13022 /* reset the rest of session data */
13023 mData->mSession.mMachine.setNull();
13024 mData->mSession.mState = SessionState_Unlocked;
13025 mData->mSession.mType.setNull();
13026
13027 /* destroy the machine client token before leaving the exclusive lock */
13028 if (mClientToken)
13029 {
13030 delete mClientToken;
13031 mClientToken = NULL;
13032 }
13033
13034 /* fire an event */
13035 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13036
13037 uninitDataAndChildObjects();
13038
13039 /* free the essential data structure last */
13040 mData.free();
13041
13042 /* release the exclusive lock before setting the below two to NULL */
13043 multilock.release();
13044
13045 RTThreadSleep(500);
13046 mParent->AddRef();
13047 LONG c = mParent->Release();
13048 LogFlowThisFunc(("vbox ref=%d\n", c)); NOREF(c);
13049 unconst(mParent) = NULL;
13050 unconst(mPeer) = NULL;
13051
13052 LogFlowThisFuncLeave();
13053}
13054
13055// util::Lockable interface
13056////////////////////////////////////////////////////////////////////////////////
13057
13058/**
13059 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13060 * with the primary Machine instance (mPeer).
13061 */
13062RWLockHandle *SessionMachine::lockHandle() const
13063{
13064 AssertReturn(mPeer != NULL, NULL);
13065 return mPeer->lockHandle();
13066}
13067
13068// IInternalMachineControl methods
13069////////////////////////////////////////////////////////////////////////////////
13070
13071/**
13072 * Passes collected guest statistics to performance collector object
13073 */
13074STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13075 ULONG aCpuKernel, ULONG aCpuIdle,
13076 ULONG aMemTotal, ULONG aMemFree,
13077 ULONG aMemBalloon, ULONG aMemShared,
13078 ULONG aMemCache, ULONG aPageTotal,
13079 ULONG aAllocVMM, ULONG aFreeVMM,
13080 ULONG aBalloonedVMM, ULONG aSharedVMM,
13081 ULONG aVmNetRx, ULONG aVmNetTx)
13082{
13083#ifdef VBOX_WITH_RESOURCE_USAGE_API
13084 if (mCollectorGuest)
13085 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13086 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13087 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13088 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13089
13090 return S_OK;
13091#else
13092 NOREF(aValidStats);
13093 NOREF(aCpuUser);
13094 NOREF(aCpuKernel);
13095 NOREF(aCpuIdle);
13096 NOREF(aMemTotal);
13097 NOREF(aMemFree);
13098 NOREF(aMemBalloon);
13099 NOREF(aMemShared);
13100 NOREF(aMemCache);
13101 NOREF(aPageTotal);
13102 NOREF(aAllocVMM);
13103 NOREF(aFreeVMM);
13104 NOREF(aBalloonedVMM);
13105 NOREF(aSharedVMM);
13106 NOREF(aVmNetRx);
13107 NOREF(aVmNetTx);
13108 return E_NOTIMPL;
13109#endif
13110}
13111
13112/**
13113 * @note Locks this object for writing.
13114 */
13115STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
13116{
13117 AutoCaller autoCaller(this);
13118 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13119
13120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13121
13122 mRemoveSavedState = aRemove;
13123
13124 return S_OK;
13125}
13126
13127/**
13128 * @note Locks the same as #setMachineState() does.
13129 */
13130STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
13131{
13132 return setMachineState(aMachineState);
13133}
13134
13135/**
13136 * @note Locks this object for writing.
13137 */
13138STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
13139{
13140 LogFlowThisFunc(("aProgress=%p\n", aProgress));
13141 AutoCaller autoCaller(this);
13142 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13143
13144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13145
13146 if (mData->mSession.mState != SessionState_Locked)
13147 return VBOX_E_INVALID_OBJECT_STATE;
13148
13149 if (!mData->mSession.mProgress.isNull())
13150 mData->mSession.mProgress->setOtherProgressObject(aProgress);
13151
13152 LogFlowThisFunc(("returns S_OK.\n"));
13153 return S_OK;
13154}
13155
13156/**
13157 * @note Locks this object for writing.
13158 */
13159STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
13160{
13161 AutoCaller autoCaller(this);
13162 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13163
13164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13165
13166 if (mData->mSession.mState != SessionState_Locked)
13167 return VBOX_E_INVALID_OBJECT_STATE;
13168
13169 /* Finalize the LaunchVMProcess progress object. */
13170 if (mData->mSession.mProgress)
13171 {
13172 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
13173 mData->mSession.mProgress.setNull();
13174 }
13175
13176 if (SUCCEEDED((HRESULT)iResult))
13177 {
13178#ifdef VBOX_WITH_RESOURCE_USAGE_API
13179 /* The VM has been powered up successfully, so it makes sense
13180 * now to offer the performance metrics for a running machine
13181 * object. Doing it earlier wouldn't be safe. */
13182 registerMetrics(mParent->performanceCollector(), mPeer,
13183 mData->mSession.mPID);
13184#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13185 }
13186
13187 return S_OK;
13188}
13189
13190/**
13191 * @note Locks this object for writing.
13192 */
13193STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
13194{
13195 LogFlowThisFuncEnter();
13196
13197 CheckComArgOutPointerValid(aProgress);
13198
13199 AutoCaller autoCaller(this);
13200 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13201
13202 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13203
13204 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13205 E_FAIL);
13206
13207 /* create a progress object to track operation completion */
13208 ComObjPtr<Progress> pProgress;
13209 pProgress.createObject();
13210 pProgress->init(getVirtualBox(),
13211 static_cast<IMachine *>(this) /* aInitiator */,
13212 Bstr(tr("Stopping the virtual machine")).raw(),
13213 FALSE /* aCancelable */);
13214
13215 /* fill in the console task data */
13216 mConsoleTaskData.mLastState = mData->mMachineState;
13217 mConsoleTaskData.mProgress = pProgress;
13218
13219 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13220 setMachineState(MachineState_Stopping);
13221
13222 pProgress.queryInterfaceTo(aProgress);
13223
13224 return S_OK;
13225}
13226
13227/**
13228 * @note Locks this object for writing.
13229 */
13230STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
13231{
13232 LogFlowThisFuncEnter();
13233
13234 AutoCaller autoCaller(this);
13235 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13236
13237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13238
13239 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
13240 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
13241 && mConsoleTaskData.mLastState != MachineState_Null,
13242 E_FAIL);
13243
13244 /*
13245 * On failure, set the state to the state we had when BeginPoweringDown()
13246 * was called (this is expected by Console::PowerDown() and the associated
13247 * task). On success the VM process already changed the state to
13248 * MachineState_PoweredOff, so no need to do anything.
13249 */
13250 if (FAILED(iResult))
13251 setMachineState(mConsoleTaskData.mLastState);
13252
13253 /* notify the progress object about operation completion */
13254 Assert(mConsoleTaskData.mProgress);
13255 if (SUCCEEDED(iResult))
13256 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13257 else
13258 {
13259 Utf8Str strErrMsg(aErrMsg);
13260 if (strErrMsg.length())
13261 mConsoleTaskData.mProgress->notifyComplete(iResult,
13262 COM_IIDOF(ISession),
13263 getComponentName(),
13264 strErrMsg.c_str());
13265 else
13266 mConsoleTaskData.mProgress->notifyComplete(iResult);
13267 }
13268
13269 /* clear out the temporary saved state data */
13270 mConsoleTaskData.mLastState = MachineState_Null;
13271 mConsoleTaskData.mProgress.setNull();
13272
13273 LogFlowThisFuncLeave();
13274 return S_OK;
13275}
13276
13277
13278/**
13279 * Goes through the USB filters of the given machine to see if the given
13280 * device matches any filter or not.
13281 *
13282 * @note Locks the same as USBController::hasMatchingFilter() does.
13283 */
13284STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13285 BOOL *aMatched,
13286 ULONG *aMaskedIfs)
13287{
13288 LogFlowThisFunc(("\n"));
13289
13290 CheckComArgNotNull(aUSBDevice);
13291 CheckComArgOutPointerValid(aMatched);
13292
13293 AutoCaller autoCaller(this);
13294 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13295
13296#ifdef VBOX_WITH_USB
13297 *aMatched = mUSBDeviceFilters->hasMatchingFilter(aUSBDevice, aMaskedIfs);
13298#else
13299 NOREF(aUSBDevice);
13300 NOREF(aMaskedIfs);
13301 *aMatched = FALSE;
13302#endif
13303
13304 return S_OK;
13305}
13306
13307/**
13308 * @note Locks the same as Host::captureUSBDevice() does.
13309 */
13310STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13311{
13312 LogFlowThisFunc(("\n"));
13313
13314 AutoCaller autoCaller(this);
13315 AssertComRCReturnRC(autoCaller.rc());
13316
13317#ifdef VBOX_WITH_USB
13318 /* if captureDeviceForVM() fails, it must have set extended error info */
13319 clearError();
13320 MultiResult rc = mParent->host()->checkUSBProxyService();
13321 if (FAILED(rc)) return rc;
13322
13323 USBProxyService *service = mParent->host()->usbProxyService();
13324 AssertReturn(service, E_FAIL);
13325 return service->captureDeviceForVM(this, Guid(aId).ref());
13326#else
13327 NOREF(aId);
13328 return E_NOTIMPL;
13329#endif
13330}
13331
13332/**
13333 * @note Locks the same as Host::detachUSBDevice() does.
13334 */
13335STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13336{
13337 LogFlowThisFunc(("\n"));
13338
13339 AutoCaller autoCaller(this);
13340 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13341
13342#ifdef VBOX_WITH_USB
13343 USBProxyService *service = mParent->host()->usbProxyService();
13344 AssertReturn(service, E_FAIL);
13345 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13346#else
13347 NOREF(aId);
13348 NOREF(aDone);
13349 return E_NOTIMPL;
13350#endif
13351}
13352
13353/**
13354 * Inserts all machine filters to the USB proxy service and then calls
13355 * Host::autoCaptureUSBDevices().
13356 *
13357 * Called by Console from the VM process upon VM startup.
13358 *
13359 * @note Locks what called methods lock.
13360 */
13361STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13362{
13363 LogFlowThisFunc(("\n"));
13364
13365 AutoCaller autoCaller(this);
13366 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13367
13368#ifdef VBOX_WITH_USB
13369 HRESULT rc = mUSBDeviceFilters->notifyProxy(true /* aInsertFilters */);
13370 AssertComRC(rc);
13371 NOREF(rc);
13372
13373 USBProxyService *service = mParent->host()->usbProxyService();
13374 AssertReturn(service, E_FAIL);
13375 return service->autoCaptureDevicesForVM(this);
13376#else
13377 return S_OK;
13378#endif
13379}
13380
13381/**
13382 * Removes all machine filters from the USB proxy service and then calls
13383 * Host::detachAllUSBDevices().
13384 *
13385 * Called by Console from the VM process upon normal VM termination or by
13386 * SessionMachine::uninit() upon abnormal VM termination (from under the
13387 * Machine/SessionMachine lock).
13388 *
13389 * @note Locks what called methods lock.
13390 */
13391STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13392{
13393 LogFlowThisFunc(("\n"));
13394
13395 AutoCaller autoCaller(this);
13396 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13397
13398#ifdef VBOX_WITH_USB
13399 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
13400 AssertComRC(rc);
13401 NOREF(rc);
13402
13403 USBProxyService *service = mParent->host()->usbProxyService();
13404 AssertReturn(service, E_FAIL);
13405 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13406#else
13407 NOREF(aDone);
13408 return S_OK;
13409#endif
13410}
13411
13412/**
13413 * @note Locks this object for writing.
13414 */
13415STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13416 IProgress **aProgress)
13417{
13418 LogFlowThisFuncEnter();
13419
13420 AssertReturn(aSession, E_INVALIDARG);
13421 AssertReturn(aProgress, E_INVALIDARG);
13422
13423 AutoCaller autoCaller(this);
13424
13425 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13426 /*
13427 * We don't assert below because it might happen that a non-direct session
13428 * informs us it is closed right after we've been uninitialized -- it's ok.
13429 */
13430 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13431
13432 /* get IInternalSessionControl interface */
13433 ComPtr<IInternalSessionControl> control(aSession);
13434
13435 ComAssertRet(!control.isNull(), E_INVALIDARG);
13436
13437 /* Creating a Progress object requires the VirtualBox lock, and
13438 * thus locking it here is required by the lock order rules. */
13439 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13440
13441 if (control == mData->mSession.mDirectControl)
13442 {
13443 ComAssertRet(aProgress, E_POINTER);
13444
13445 /* The direct session is being normally closed by the client process
13446 * ----------------------------------------------------------------- */
13447
13448 /* go to the closing state (essential for all open*Session() calls and
13449 * for #checkForDeath()) */
13450 Assert(mData->mSession.mState == SessionState_Locked);
13451 mData->mSession.mState = SessionState_Unlocking;
13452
13453 /* set direct control to NULL to release the remote instance */
13454 mData->mSession.mDirectControl.setNull();
13455 LogFlowThisFunc(("Direct control is set to NULL\n"));
13456
13457 if (mData->mSession.mProgress)
13458 {
13459 /* finalize the progress, someone might wait if a frontend
13460 * closes the session before powering on the VM. */
13461 mData->mSession.mProgress->notifyComplete(E_FAIL,
13462 COM_IIDOF(ISession),
13463 getComponentName(),
13464 tr("The VM session was closed before any attempt to power it on"));
13465 mData->mSession.mProgress.setNull();
13466 }
13467
13468 /* Create the progress object the client will use to wait until
13469 * #checkForDeath() is called to uninitialize this session object after
13470 * it releases the IPC semaphore.
13471 * Note! Because we're "reusing" mProgress here, this must be a proxy
13472 * object just like for LaunchVMProcess. */
13473 Assert(mData->mSession.mProgress.isNull());
13474 ComObjPtr<ProgressProxy> progress;
13475 progress.createObject();
13476 ComPtr<IUnknown> pPeer(mPeer);
13477 progress->init(mParent, pPeer,
13478 Bstr(tr("Closing session")).raw(),
13479 FALSE /* aCancelable */);
13480 progress.queryInterfaceTo(aProgress);
13481 mData->mSession.mProgress = progress;
13482 }
13483 else
13484 {
13485 /* the remote session is being normally closed */
13486 Data::Session::RemoteControlList::iterator it =
13487 mData->mSession.mRemoteControls.begin();
13488 while (it != mData->mSession.mRemoteControls.end())
13489 {
13490 if (control == *it)
13491 break;
13492 ++it;
13493 }
13494 BOOL found = it != mData->mSession.mRemoteControls.end();
13495 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13496 E_INVALIDARG);
13497 // This MUST be erase(it), not remove(*it) as the latter triggers a
13498 // very nasty use after free due to the place where the value "lives".
13499 mData->mSession.mRemoteControls.erase(it);
13500 }
13501
13502 /* signal the client watcher thread, because the client is going away */
13503 mParent->updateClientWatcher();
13504
13505 LogFlowThisFuncLeave();
13506 return S_OK;
13507}
13508
13509/**
13510 * @note Locks this object for writing.
13511 */
13512STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13513{
13514 LogFlowThisFuncEnter();
13515
13516 CheckComArgOutPointerValid(aProgress);
13517 CheckComArgOutPointerValid(aStateFilePath);
13518
13519 AutoCaller autoCaller(this);
13520 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13521
13522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13523
13524 AssertReturn( mData->mMachineState == MachineState_Paused
13525 && mConsoleTaskData.mLastState == MachineState_Null
13526 && mConsoleTaskData.strStateFilePath.isEmpty(),
13527 E_FAIL);
13528
13529 /* create a progress object to track operation completion */
13530 ComObjPtr<Progress> pProgress;
13531 pProgress.createObject();
13532 pProgress->init(getVirtualBox(),
13533 static_cast<IMachine *>(this) /* aInitiator */,
13534 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13535 FALSE /* aCancelable */);
13536
13537 Utf8Str strStateFilePath;
13538 /* stateFilePath is null when the machine is not running */
13539 if (mData->mMachineState == MachineState_Paused)
13540 composeSavedStateFilename(strStateFilePath);
13541
13542 /* fill in the console task data */
13543 mConsoleTaskData.mLastState = mData->mMachineState;
13544 mConsoleTaskData.strStateFilePath = strStateFilePath;
13545 mConsoleTaskData.mProgress = pProgress;
13546
13547 /* set the state to Saving (this is expected by Console::SaveState()) */
13548 setMachineState(MachineState_Saving);
13549
13550 strStateFilePath.cloneTo(aStateFilePath);
13551 pProgress.queryInterfaceTo(aProgress);
13552
13553 return S_OK;
13554}
13555
13556/**
13557 * @note Locks mParent + this object for writing.
13558 */
13559STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13560{
13561 LogFlowThisFunc(("\n"));
13562
13563 AutoCaller autoCaller(this);
13564 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13565
13566 /* endSavingState() need mParent lock */
13567 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13568
13569 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13570 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13571 && mConsoleTaskData.mLastState != MachineState_Null
13572 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13573 E_FAIL);
13574
13575 /*
13576 * On failure, set the state to the state we had when BeginSavingState()
13577 * was called (this is expected by Console::SaveState() and the associated
13578 * task). On success the VM process already changed the state to
13579 * MachineState_Saved, so no need to do anything.
13580 */
13581 if (FAILED(iResult))
13582 setMachineState(mConsoleTaskData.mLastState);
13583
13584 return endSavingState(iResult, aErrMsg);
13585}
13586
13587/**
13588 * @note Locks this object for writing.
13589 */
13590STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13591{
13592 LogFlowThisFunc(("\n"));
13593
13594 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13595
13596 AutoCaller autoCaller(this);
13597 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13598
13599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13600
13601 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13602 || mData->mMachineState == MachineState_Teleported
13603 || mData->mMachineState == MachineState_Aborted
13604 , E_FAIL); /** @todo setError. */
13605
13606 Utf8Str stateFilePathFull = aSavedStateFile;
13607 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13608 if (RT_FAILURE(vrc))
13609 return setError(VBOX_E_FILE_ERROR,
13610 tr("Invalid saved state file path '%ls' (%Rrc)"),
13611 aSavedStateFile,
13612 vrc);
13613
13614 mSSData->strStateFilePath = stateFilePathFull;
13615
13616 /* The below setMachineState() will detect the state transition and will
13617 * update the settings file */
13618
13619 return setMachineState(MachineState_Saved);
13620}
13621
13622STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13623 ComSafeArrayOut(BSTR, aValues),
13624 ComSafeArrayOut(LONG64, aTimestamps),
13625 ComSafeArrayOut(BSTR, aFlags))
13626{
13627 LogFlowThisFunc(("\n"));
13628
13629#ifdef VBOX_WITH_GUEST_PROPS
13630 using namespace guestProp;
13631
13632 AutoCaller autoCaller(this);
13633 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13634
13635 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13636
13637 CheckComArgOutSafeArrayPointerValid(aNames);
13638 CheckComArgOutSafeArrayPointerValid(aValues);
13639 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13640 CheckComArgOutSafeArrayPointerValid(aFlags);
13641
13642 size_t cEntries = mHWData->mGuestProperties.size();
13643 com::SafeArray<BSTR> names(cEntries);
13644 com::SafeArray<BSTR> values(cEntries);
13645 com::SafeArray<LONG64> timestamps(cEntries);
13646 com::SafeArray<BSTR> flags(cEntries);
13647 unsigned i = 0;
13648 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13649 it != mHWData->mGuestProperties.end();
13650 ++it)
13651 {
13652 char szFlags[MAX_FLAGS_LEN + 1];
13653 it->first.cloneTo(&names[i]);
13654 it->second.strValue.cloneTo(&values[i]);
13655 timestamps[i] = it->second.mTimestamp;
13656 /* If it is NULL, keep it NULL. */
13657 if (it->second.mFlags)
13658 {
13659 writeFlags(it->second.mFlags, szFlags);
13660 Bstr(szFlags).cloneTo(&flags[i]);
13661 }
13662 else
13663 flags[i] = NULL;
13664 ++i;
13665 }
13666 names.detachTo(ComSafeArrayOutArg(aNames));
13667 values.detachTo(ComSafeArrayOutArg(aValues));
13668 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13669 flags.detachTo(ComSafeArrayOutArg(aFlags));
13670 return S_OK;
13671#else
13672 ReturnComNotImplemented();
13673#endif
13674}
13675
13676STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13677 IN_BSTR aValue,
13678 LONG64 aTimestamp,
13679 IN_BSTR aFlags)
13680{
13681 LogFlowThisFunc(("\n"));
13682
13683#ifdef VBOX_WITH_GUEST_PROPS
13684 using namespace guestProp;
13685
13686 CheckComArgStrNotEmptyOrNull(aName);
13687 CheckComArgNotNull(aValue);
13688 CheckComArgNotNull(aFlags);
13689
13690 try
13691 {
13692 /*
13693 * Convert input up front.
13694 */
13695 Utf8Str utf8Name(aName);
13696 uint32_t fFlags = NILFLAG;
13697 if (aFlags)
13698 {
13699 Utf8Str utf8Flags(aFlags);
13700 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13701 AssertRCReturn(vrc, E_INVALIDARG);
13702 }
13703
13704 /*
13705 * Now grab the object lock, validate the state and do the update.
13706 */
13707 AutoCaller autoCaller(this);
13708 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13709
13710 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13711
13712 switch (mData->mMachineState)
13713 {
13714 case MachineState_Paused:
13715 case MachineState_Running:
13716 case MachineState_Teleporting:
13717 case MachineState_TeleportingPausedVM:
13718 case MachineState_LiveSnapshotting:
13719 case MachineState_DeletingSnapshotOnline:
13720 case MachineState_DeletingSnapshotPaused:
13721 case MachineState_Saving:
13722 case MachineState_Stopping:
13723 break;
13724
13725 default:
13726 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13727 VBOX_E_INVALID_VM_STATE);
13728 }
13729
13730 setModified(IsModified_MachineData);
13731 mHWData.backup();
13732
13733 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13734 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13735 if (it != mHWData->mGuestProperties.end())
13736 {
13737 if (!fDelete)
13738 {
13739 it->second.strValue = aValue;
13740 it->second.mTimestamp = aTimestamp;
13741 it->second.mFlags = fFlags;
13742 }
13743 else
13744 mHWData->mGuestProperties.erase(it);
13745
13746 mData->mGuestPropertiesModified = TRUE;
13747 }
13748 else if (!fDelete)
13749 {
13750 HWData::GuestProperty prop;
13751 prop.strValue = aValue;
13752 prop.mTimestamp = aTimestamp;
13753 prop.mFlags = fFlags;
13754
13755 mHWData->mGuestProperties[utf8Name] = prop;
13756 mData->mGuestPropertiesModified = TRUE;
13757 }
13758
13759 /*
13760 * Send a callback notification if appropriate
13761 */
13762 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13763 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13764 RTSTR_MAX,
13765 utf8Name.c_str(),
13766 RTSTR_MAX, NULL)
13767 )
13768 {
13769 alock.release();
13770
13771 mParent->onGuestPropertyChange(mData->mUuid,
13772 aName,
13773 aValue,
13774 aFlags);
13775 }
13776 }
13777 catch (...)
13778 {
13779 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13780 }
13781 return S_OK;
13782#else
13783 ReturnComNotImplemented();
13784#endif
13785}
13786
13787STDMETHODIMP SessionMachine::LockMedia()
13788{
13789 AutoCaller autoCaller(this);
13790 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13791
13792 AutoMultiWriteLock2 alock(this->lockHandle(),
13793 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13794
13795 AssertReturn( mData->mMachineState == MachineState_Starting
13796 || mData->mMachineState == MachineState_Restoring
13797 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13798
13799 clearError();
13800 alock.release();
13801 return lockMedia();
13802}
13803
13804STDMETHODIMP SessionMachine::UnlockMedia()
13805{
13806 unlockMedia();
13807 return S_OK;
13808}
13809
13810STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13811 IMediumAttachment **aNewAttachment)
13812{
13813 CheckComArgNotNull(aAttachment);
13814 CheckComArgOutPointerValid(aNewAttachment);
13815
13816 AutoCaller autoCaller(this);
13817 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13818
13819 // request the host lock first, since might be calling Host methods for getting host drives;
13820 // next, protect the media tree all the while we're in here, as well as our member variables
13821 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13822 this->lockHandle(),
13823 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13824
13825 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13826
13827 Bstr ctrlName;
13828 LONG lPort;
13829 LONG lDevice;
13830 bool fTempEject;
13831 {
13832 AutoCaller autoAttachCaller(this);
13833 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13834
13835 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13836
13837 /* Need to query the details first, as the IMediumAttachment reference
13838 * might be to the original settings, which we are going to change. */
13839 ctrlName = pAttach->getControllerName();
13840 lPort = pAttach->getPort();
13841 lDevice = pAttach->getDevice();
13842 fTempEject = pAttach->getTempEject();
13843 }
13844
13845 if (!fTempEject)
13846 {
13847 /* Remember previously mounted medium. The medium before taking the
13848 * backup is not necessarily the same thing. */
13849 ComObjPtr<Medium> oldmedium;
13850 oldmedium = pAttach->getMedium();
13851
13852 setModified(IsModified_Storage);
13853 mMediaData.backup();
13854
13855 // The backup operation makes the pAttach reference point to the
13856 // old settings. Re-get the correct reference.
13857 pAttach = findAttachment(mMediaData->mAttachments,
13858 ctrlName.raw(),
13859 lPort,
13860 lDevice);
13861
13862 {
13863 AutoCaller autoAttachCaller(this);
13864 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13865
13866 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13867 if (!oldmedium.isNull())
13868 oldmedium->removeBackReference(mData->mUuid);
13869
13870 pAttach->updateMedium(NULL);
13871 pAttach->updateEjected();
13872 }
13873
13874 setModified(IsModified_Storage);
13875 }
13876 else
13877 {
13878 {
13879 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13880 pAttach->updateEjected();
13881 }
13882 }
13883
13884 pAttach.queryInterfaceTo(aNewAttachment);
13885
13886 return S_OK;
13887}
13888
13889// public methods only for internal purposes
13890/////////////////////////////////////////////////////////////////////////////
13891
13892/**
13893 * Called from the client watcher thread to check for expected or unexpected
13894 * death of the client process that has a direct session to this machine.
13895 *
13896 * On Win32 and on OS/2, this method is called only when we've got the
13897 * mutex (i.e. the client has either died or terminated normally) so it always
13898 * returns @c true (the client is terminated, the session machine is
13899 * uninitialized).
13900 *
13901 * On other platforms, the method returns @c true if the client process has
13902 * terminated normally or abnormally and the session machine was uninitialized,
13903 * and @c false if the client process is still alive.
13904 *
13905 * @note Locks this object for writing.
13906 */
13907bool SessionMachine::checkForDeath()
13908{
13909 Uninit::Reason reason;
13910 bool terminated = false;
13911
13912 /* Enclose autoCaller with a block because calling uninit() from under it
13913 * will deadlock. */
13914 {
13915 AutoCaller autoCaller(this);
13916 if (!autoCaller.isOk())
13917 {
13918 /* return true if not ready, to cause the client watcher to exclude
13919 * the corresponding session from watching */
13920 LogFlowThisFunc(("Already uninitialized!\n"));
13921 return true;
13922 }
13923
13924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13925
13926 /* Determine the reason of death: if the session state is Closing here,
13927 * everything is fine. Otherwise it means that the client did not call
13928 * OnSessionEnd() before it released the IPC semaphore. This may happen
13929 * either because the client process has abnormally terminated, or
13930 * because it simply forgot to call ISession::Close() before exiting. We
13931 * threat the latter also as an abnormal termination (see
13932 * Session::uninit() for details). */
13933 reason = mData->mSession.mState == SessionState_Unlocking ?
13934 Uninit::Normal :
13935 Uninit::Abnormal;
13936
13937 if (mClientToken)
13938 terminated = mClientToken->release();
13939 } /* AutoCaller block */
13940
13941 if (terminated)
13942 uninit(reason);
13943
13944 return terminated;
13945}
13946
13947void SessionMachine::getTokenId(Utf8Str &strTokenId)
13948{
13949 LogFlowThisFunc(("\n"));
13950
13951 strTokenId.setNull();
13952
13953 AutoCaller autoCaller(this);
13954 AssertComRCReturnVoid(autoCaller.rc());
13955
13956 Assert(mClientToken);
13957 if (mClientToken)
13958 mClientToken->getId(strTokenId);
13959}
13960
13961Machine::ClientToken *SessionMachine::getClientToken()
13962{
13963 LogFlowThisFunc(("\n"));
13964
13965 AutoCaller autoCaller(this);
13966 AssertComRCReturn(autoCaller.rc(), NULL);
13967
13968 return mClientToken;
13969}
13970
13971
13972/**
13973 * @note Locks this object for reading.
13974 */
13975HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13976{
13977 LogFlowThisFunc(("\n"));
13978
13979 AutoCaller autoCaller(this);
13980 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13981
13982 ComPtr<IInternalSessionControl> directControl;
13983 {
13984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13985 directControl = mData->mSession.mDirectControl;
13986 }
13987
13988 /* ignore notifications sent after #OnSessionEnd() is called */
13989 if (!directControl)
13990 return S_OK;
13991
13992 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13993}
13994
13995/**
13996 * @note Locks this object for reading.
13997 */
13998HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13999 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
14000{
14001 LogFlowThisFunc(("\n"));
14002
14003 AutoCaller autoCaller(this);
14004 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14005
14006 ComPtr<IInternalSessionControl> directControl;
14007 {
14008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14009 directControl = mData->mSession.mDirectControl;
14010 }
14011
14012 /* ignore notifications sent after #OnSessionEnd() is called */
14013 if (!directControl)
14014 return S_OK;
14015 /*
14016 * instead acting like callback we ask IVirtualBox deliver corresponding event
14017 */
14018
14019 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14020 return S_OK;
14021}
14022
14023/**
14024 * @note Locks this object for reading.
14025 */
14026HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
14027{
14028 LogFlowThisFunc(("\n"));
14029
14030 AutoCaller autoCaller(this);
14031 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14032
14033 ComPtr<IInternalSessionControl> directControl;
14034 {
14035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14036 directControl = mData->mSession.mDirectControl;
14037 }
14038
14039 /* ignore notifications sent after #OnSessionEnd() is called */
14040 if (!directControl)
14041 return S_OK;
14042
14043 return directControl->OnSerialPortChange(serialPort);
14044}
14045
14046/**
14047 * @note Locks this object for reading.
14048 */
14049HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
14050{
14051 LogFlowThisFunc(("\n"));
14052
14053 AutoCaller autoCaller(this);
14054 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14055
14056 ComPtr<IInternalSessionControl> directControl;
14057 {
14058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14059 directControl = mData->mSession.mDirectControl;
14060 }
14061
14062 /* ignore notifications sent after #OnSessionEnd() is called */
14063 if (!directControl)
14064 return S_OK;
14065
14066 return directControl->OnParallelPortChange(parallelPort);
14067}
14068
14069/**
14070 * @note Locks this object for reading.
14071 */
14072HRESULT SessionMachine::onStorageControllerChange()
14073{
14074 LogFlowThisFunc(("\n"));
14075
14076 AutoCaller autoCaller(this);
14077 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14078
14079 ComPtr<IInternalSessionControl> directControl;
14080 {
14081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14082 directControl = mData->mSession.mDirectControl;
14083 }
14084
14085 /* ignore notifications sent after #OnSessionEnd() is called */
14086 if (!directControl)
14087 return S_OK;
14088
14089 return directControl->OnStorageControllerChange();
14090}
14091
14092/**
14093 * @note Locks this object for reading.
14094 */
14095HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14096{
14097 LogFlowThisFunc(("\n"));
14098
14099 AutoCaller autoCaller(this);
14100 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14101
14102 ComPtr<IInternalSessionControl> directControl;
14103 {
14104 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14105 directControl = mData->mSession.mDirectControl;
14106 }
14107
14108 /* ignore notifications sent after #OnSessionEnd() is called */
14109 if (!directControl)
14110 return S_OK;
14111
14112 return directControl->OnMediumChange(aAttachment, aForce);
14113}
14114
14115/**
14116 * @note Locks this object for reading.
14117 */
14118HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
14119{
14120 LogFlowThisFunc(("\n"));
14121
14122 AutoCaller autoCaller(this);
14123 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14124
14125 ComPtr<IInternalSessionControl> directControl;
14126 {
14127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14128 directControl = mData->mSession.mDirectControl;
14129 }
14130
14131 /* ignore notifications sent after #OnSessionEnd() is called */
14132 if (!directControl)
14133 return S_OK;
14134
14135 return directControl->OnCPUChange(aCPU, aRemove);
14136}
14137
14138HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
14139{
14140 LogFlowThisFunc(("\n"));
14141
14142 AutoCaller autoCaller(this);
14143 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14144
14145 ComPtr<IInternalSessionControl> directControl;
14146 {
14147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14148 directControl = mData->mSession.mDirectControl;
14149 }
14150
14151 /* ignore notifications sent after #OnSessionEnd() is called */
14152 if (!directControl)
14153 return S_OK;
14154
14155 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14156}
14157
14158/**
14159 * @note Locks this object for reading.
14160 */
14161HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
14162{
14163 LogFlowThisFunc(("\n"));
14164
14165 AutoCaller autoCaller(this);
14166 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14167
14168 ComPtr<IInternalSessionControl> directControl;
14169 {
14170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14171 directControl = mData->mSession.mDirectControl;
14172 }
14173
14174 /* ignore notifications sent after #OnSessionEnd() is called */
14175 if (!directControl)
14176 return S_OK;
14177
14178 return directControl->OnVRDEServerChange(aRestart);
14179}
14180
14181/**
14182 * @note Locks this object for reading.
14183 */
14184HRESULT SessionMachine::onVideoCaptureChange()
14185{
14186 LogFlowThisFunc(("\n"));
14187
14188 AutoCaller autoCaller(this);
14189 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14190
14191 ComPtr<IInternalSessionControl> directControl;
14192 {
14193 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14194 directControl = mData->mSession.mDirectControl;
14195 }
14196
14197 /* ignore notifications sent after #OnSessionEnd() is called */
14198 if (!directControl)
14199 return S_OK;
14200
14201 return directControl->OnVideoCaptureChange();
14202}
14203
14204/**
14205 * @note Locks this object for reading.
14206 */
14207HRESULT SessionMachine::onUSBControllerChange()
14208{
14209 LogFlowThisFunc(("\n"));
14210
14211 AutoCaller autoCaller(this);
14212 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14213
14214 ComPtr<IInternalSessionControl> directControl;
14215 {
14216 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14217 directControl = mData->mSession.mDirectControl;
14218 }
14219
14220 /* ignore notifications sent after #OnSessionEnd() is called */
14221 if (!directControl)
14222 return S_OK;
14223
14224 return directControl->OnUSBControllerChange();
14225}
14226
14227/**
14228 * @note Locks this object for reading.
14229 */
14230HRESULT SessionMachine::onSharedFolderChange()
14231{
14232 LogFlowThisFunc(("\n"));
14233
14234 AutoCaller autoCaller(this);
14235 AssertComRCReturnRC(autoCaller.rc());
14236
14237 ComPtr<IInternalSessionControl> directControl;
14238 {
14239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14240 directControl = mData->mSession.mDirectControl;
14241 }
14242
14243 /* ignore notifications sent after #OnSessionEnd() is called */
14244 if (!directControl)
14245 return S_OK;
14246
14247 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14248}
14249
14250/**
14251 * @note Locks this object for reading.
14252 */
14253HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14254{
14255 LogFlowThisFunc(("\n"));
14256
14257 AutoCaller autoCaller(this);
14258 AssertComRCReturnRC(autoCaller.rc());
14259
14260 ComPtr<IInternalSessionControl> directControl;
14261 {
14262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14263 directControl = mData->mSession.mDirectControl;
14264 }
14265
14266 /* ignore notifications sent after #OnSessionEnd() is called */
14267 if (!directControl)
14268 return S_OK;
14269
14270 return directControl->OnClipboardModeChange(aClipboardMode);
14271}
14272
14273/**
14274 * @note Locks this object for reading.
14275 */
14276HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14277{
14278 LogFlowThisFunc(("\n"));
14279
14280 AutoCaller autoCaller(this);
14281 AssertComRCReturnRC(autoCaller.rc());
14282
14283 ComPtr<IInternalSessionControl> directControl;
14284 {
14285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14286 directControl = mData->mSession.mDirectControl;
14287 }
14288
14289 /* ignore notifications sent after #OnSessionEnd() is called */
14290 if (!directControl)
14291 return S_OK;
14292
14293 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14294}
14295
14296/**
14297 * @note Locks this object for reading.
14298 */
14299HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14300{
14301 LogFlowThisFunc(("\n"));
14302
14303 AutoCaller autoCaller(this);
14304 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14305
14306 ComPtr<IInternalSessionControl> directControl;
14307 {
14308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14309 directControl = mData->mSession.mDirectControl;
14310 }
14311
14312 /* ignore notifications sent after #OnSessionEnd() is called */
14313 if (!directControl)
14314 return S_OK;
14315
14316 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14317}
14318
14319/**
14320 * @note Locks this object for reading.
14321 */
14322HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14323{
14324 LogFlowThisFunc(("\n"));
14325
14326 AutoCaller autoCaller(this);
14327 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14328
14329 ComPtr<IInternalSessionControl> directControl;
14330 {
14331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14332 directControl = mData->mSession.mDirectControl;
14333 }
14334
14335 /* ignore notifications sent after #OnSessionEnd() is called */
14336 if (!directControl)
14337 return S_OK;
14338
14339 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14340}
14341
14342/**
14343 * Returns @c true if this machine's USB controller reports it has a matching
14344 * filter for the given USB device and @c false otherwise.
14345 *
14346 * @note locks this object for reading.
14347 */
14348bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14349{
14350 AutoCaller autoCaller(this);
14351 /* silently return if not ready -- this method may be called after the
14352 * direct machine session has been called */
14353 if (!autoCaller.isOk())
14354 return false;
14355
14356#ifdef VBOX_WITH_USB
14357 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14358
14359 switch (mData->mMachineState)
14360 {
14361 case MachineState_Starting:
14362 case MachineState_Restoring:
14363 case MachineState_TeleportingIn:
14364 case MachineState_Paused:
14365 case MachineState_Running:
14366 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14367 * elsewhere... */
14368 alock.release();
14369 return mUSBDeviceFilters->hasMatchingFilter(aDevice, aMaskedIfs);
14370 default: break;
14371 }
14372#else
14373 NOREF(aDevice);
14374 NOREF(aMaskedIfs);
14375#endif
14376 return false;
14377}
14378
14379/**
14380 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14381 */
14382HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14383 IVirtualBoxErrorInfo *aError,
14384 ULONG aMaskedIfs)
14385{
14386 LogFlowThisFunc(("\n"));
14387
14388 AutoCaller autoCaller(this);
14389
14390 /* This notification may happen after the machine object has been
14391 * uninitialized (the session was closed), so don't assert. */
14392 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14393
14394 ComPtr<IInternalSessionControl> directControl;
14395 {
14396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14397 directControl = mData->mSession.mDirectControl;
14398 }
14399
14400 /* fail on notifications sent after #OnSessionEnd() is called, it is
14401 * expected by the caller */
14402 if (!directControl)
14403 return E_FAIL;
14404
14405 /* No locks should be held at this point. */
14406 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14407 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14408
14409 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14410}
14411
14412/**
14413 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14414 */
14415HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14416 IVirtualBoxErrorInfo *aError)
14417{
14418 LogFlowThisFunc(("\n"));
14419
14420 AutoCaller autoCaller(this);
14421
14422 /* This notification may happen after the machine object has been
14423 * uninitialized (the session was closed), so don't assert. */
14424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14425
14426 ComPtr<IInternalSessionControl> directControl;
14427 {
14428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14429 directControl = mData->mSession.mDirectControl;
14430 }
14431
14432 /* fail on notifications sent after #OnSessionEnd() is called, it is
14433 * expected by the caller */
14434 if (!directControl)
14435 return E_FAIL;
14436
14437 /* No locks should be held at this point. */
14438 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14439 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14440
14441 return directControl->OnUSBDeviceDetach(aId, aError);
14442}
14443
14444// protected methods
14445/////////////////////////////////////////////////////////////////////////////
14446
14447/**
14448 * Helper method to finalize saving the state.
14449 *
14450 * @note Must be called from under this object's lock.
14451 *
14452 * @param aRc S_OK if the snapshot has been taken successfully
14453 * @param aErrMsg human readable error message for failure
14454 *
14455 * @note Locks mParent + this objects for writing.
14456 */
14457HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14458{
14459 LogFlowThisFuncEnter();
14460
14461 AutoCaller autoCaller(this);
14462 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14463
14464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14465
14466 HRESULT rc = S_OK;
14467
14468 if (SUCCEEDED(aRc))
14469 {
14470 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14471
14472 /* save all VM settings */
14473 rc = saveSettings(NULL);
14474 // no need to check whether VirtualBox.xml needs saving also since
14475 // we can't have a name change pending at this point
14476 }
14477 else
14478 {
14479 // delete the saved state file (it might have been already created);
14480 // we need not check whether this is shared with a snapshot here because
14481 // we certainly created this saved state file here anew
14482 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14483 }
14484
14485 /* notify the progress object about operation completion */
14486 Assert(mConsoleTaskData.mProgress);
14487 if (SUCCEEDED(aRc))
14488 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14489 else
14490 {
14491 if (aErrMsg.length())
14492 mConsoleTaskData.mProgress->notifyComplete(aRc,
14493 COM_IIDOF(ISession),
14494 getComponentName(),
14495 aErrMsg.c_str());
14496 else
14497 mConsoleTaskData.mProgress->notifyComplete(aRc);
14498 }
14499
14500 /* clear out the temporary saved state data */
14501 mConsoleTaskData.mLastState = MachineState_Null;
14502 mConsoleTaskData.strStateFilePath.setNull();
14503 mConsoleTaskData.mProgress.setNull();
14504
14505 LogFlowThisFuncLeave();
14506 return rc;
14507}
14508
14509/**
14510 * Deletes the given file if it is no longer in use by either the current machine state
14511 * (if the machine is "saved") or any of the machine's snapshots.
14512 *
14513 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14514 * but is different for each SnapshotMachine. When calling this, the order of calling this
14515 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14516 * is therefore critical. I know, it's all rather messy.
14517 *
14518 * @param strStateFile
14519 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14520 */
14521void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14522 Snapshot *pSnapshotToIgnore)
14523{
14524 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14525 if ( (strStateFile.isNotEmpty())
14526 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14527 )
14528 // ... and it must also not be shared with other snapshots
14529 if ( !mData->mFirstSnapshot
14530 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14531 // this checks the SnapshotMachine's state file paths
14532 )
14533 RTFileDelete(strStateFile.c_str());
14534}
14535
14536/**
14537 * Locks the attached media.
14538 *
14539 * All attached hard disks are locked for writing and DVD/floppy are locked for
14540 * reading. Parents of attached hard disks (if any) are locked for reading.
14541 *
14542 * This method also performs accessibility check of all media it locks: if some
14543 * media is inaccessible, the method will return a failure and a bunch of
14544 * extended error info objects per each inaccessible medium.
14545 *
14546 * Note that this method is atomic: if it returns a success, all media are
14547 * locked as described above; on failure no media is locked at all (all
14548 * succeeded individual locks will be undone).
14549 *
14550 * The caller is responsible for doing the necessary state sanity checks.
14551 *
14552 * The locks made by this method must be undone by calling #unlockMedia() when
14553 * no more needed.
14554 */
14555HRESULT SessionMachine::lockMedia()
14556{
14557 AutoCaller autoCaller(this);
14558 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14559
14560 AutoMultiWriteLock2 alock(this->lockHandle(),
14561 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14562
14563 /* bail out if trying to lock things with already set up locking */
14564 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14565
14566 MultiResult mrc(S_OK);
14567
14568 /* Collect locking information for all medium objects attached to the VM. */
14569 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14570 it != mMediaData->mAttachments.end();
14571 ++it)
14572 {
14573 MediumAttachment* pAtt = *it;
14574 DeviceType_T devType = pAtt->getType();
14575 Medium *pMedium = pAtt->getMedium();
14576
14577 MediumLockList *pMediumLockList(new MediumLockList());
14578 // There can be attachments without a medium (floppy/dvd), and thus
14579 // it's impossible to create a medium lock list. It still makes sense
14580 // to have the empty medium lock list in the map in case a medium is
14581 // attached later.
14582 if (pMedium != NULL)
14583 {
14584 MediumType_T mediumType = pMedium->getType();
14585 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14586 || mediumType == MediumType_Shareable;
14587 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14588
14589 alock.release();
14590 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14591 !fIsReadOnlyLock /* fMediumLockWrite */,
14592 NULL,
14593 *pMediumLockList);
14594 alock.acquire();
14595 if (FAILED(mrc))
14596 {
14597 delete pMediumLockList;
14598 mData->mSession.mLockedMedia.Clear();
14599 break;
14600 }
14601 }
14602
14603 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14604 if (FAILED(rc))
14605 {
14606 mData->mSession.mLockedMedia.Clear();
14607 mrc = setError(rc,
14608 tr("Collecting locking information for all attached media failed"));
14609 break;
14610 }
14611 }
14612
14613 if (SUCCEEDED(mrc))
14614 {
14615 /* Now lock all media. If this fails, nothing is locked. */
14616 alock.release();
14617 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14618 alock.acquire();
14619 if (FAILED(rc))
14620 {
14621 mrc = setError(rc,
14622 tr("Locking of attached media failed"));
14623 }
14624 }
14625
14626 return mrc;
14627}
14628
14629/**
14630 * Undoes the locks made by by #lockMedia().
14631 */
14632void SessionMachine::unlockMedia()
14633{
14634 AutoCaller autoCaller(this);
14635 AssertComRCReturnVoid(autoCaller.rc());
14636
14637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14638
14639 /* we may be holding important error info on the current thread;
14640 * preserve it */
14641 ErrorInfoKeeper eik;
14642
14643 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14644 AssertComRC(rc);
14645}
14646
14647/**
14648 * Helper to change the machine state (reimplementation).
14649 *
14650 * @note Locks this object for writing.
14651 * @note This method must not call saveSettings or SaveSettings, otherwise
14652 * it can cause crashes in random places due to unexpectedly committing
14653 * the current settings. The caller is responsible for that. The call
14654 * to saveStateSettings is fine, because this method does not commit.
14655 */
14656HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14657{
14658 LogFlowThisFuncEnter();
14659 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14660
14661 AutoCaller autoCaller(this);
14662 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14663
14664 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14665
14666 MachineState_T oldMachineState = mData->mMachineState;
14667
14668 AssertMsgReturn(oldMachineState != aMachineState,
14669 ("oldMachineState=%s, aMachineState=%s\n",
14670 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14671 E_FAIL);
14672
14673 HRESULT rc = S_OK;
14674
14675 int stsFlags = 0;
14676 bool deleteSavedState = false;
14677
14678 /* detect some state transitions */
14679
14680 if ( ( oldMachineState == MachineState_Saved
14681 && aMachineState == MachineState_Restoring)
14682 || ( ( oldMachineState == MachineState_PoweredOff
14683 || oldMachineState == MachineState_Teleported
14684 || oldMachineState == MachineState_Aborted
14685 )
14686 && ( aMachineState == MachineState_TeleportingIn
14687 || aMachineState == MachineState_Starting
14688 )
14689 )
14690 )
14691 {
14692 /* The EMT thread is about to start */
14693
14694 /* Nothing to do here for now... */
14695
14696 /// @todo NEWMEDIA don't let mDVDDrive and other children
14697 /// change anything when in the Starting/Restoring state
14698 }
14699 else if ( ( oldMachineState == MachineState_Running
14700 || oldMachineState == MachineState_Paused
14701 || oldMachineState == MachineState_Teleporting
14702 || oldMachineState == MachineState_LiveSnapshotting
14703 || oldMachineState == MachineState_Stuck
14704 || oldMachineState == MachineState_Starting
14705 || oldMachineState == MachineState_Stopping
14706 || oldMachineState == MachineState_Saving
14707 || oldMachineState == MachineState_Restoring
14708 || oldMachineState == MachineState_TeleportingPausedVM
14709 || oldMachineState == MachineState_TeleportingIn
14710 )
14711 && ( aMachineState == MachineState_PoweredOff
14712 || aMachineState == MachineState_Saved
14713 || aMachineState == MachineState_Teleported
14714 || aMachineState == MachineState_Aborted
14715 )
14716 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14717 * snapshot */
14718 && ( mConsoleTaskData.mSnapshot.isNull()
14719 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14720 )
14721 )
14722 {
14723 /* The EMT thread has just stopped, unlock attached media. Note that as
14724 * opposed to locking that is done from Console, we do unlocking here
14725 * because the VM process may have aborted before having a chance to
14726 * properly unlock all media it locked. */
14727
14728 unlockMedia();
14729 }
14730
14731 if (oldMachineState == MachineState_Restoring)
14732 {
14733 if (aMachineState != MachineState_Saved)
14734 {
14735 /*
14736 * delete the saved state file once the machine has finished
14737 * restoring from it (note that Console sets the state from
14738 * Restoring to Saved if the VM couldn't restore successfully,
14739 * to give the user an ability to fix an error and retry --
14740 * we keep the saved state file in this case)
14741 */
14742 deleteSavedState = true;
14743 }
14744 }
14745 else if ( oldMachineState == MachineState_Saved
14746 && ( aMachineState == MachineState_PoweredOff
14747 || aMachineState == MachineState_Aborted
14748 || aMachineState == MachineState_Teleported
14749 )
14750 )
14751 {
14752 /*
14753 * delete the saved state after Console::ForgetSavedState() is called
14754 * or if the VM process (owning a direct VM session) crashed while the
14755 * VM was Saved
14756 */
14757
14758 /// @todo (dmik)
14759 // Not sure that deleting the saved state file just because of the
14760 // client death before it attempted to restore the VM is a good
14761 // thing. But when it crashes we need to go to the Aborted state
14762 // which cannot have the saved state file associated... The only
14763 // way to fix this is to make the Aborted condition not a VM state
14764 // but a bool flag: i.e., when a crash occurs, set it to true and
14765 // change the state to PoweredOff or Saved depending on the
14766 // saved state presence.
14767
14768 deleteSavedState = true;
14769 mData->mCurrentStateModified = TRUE;
14770 stsFlags |= SaveSTS_CurStateModified;
14771 }
14772
14773 if ( aMachineState == MachineState_Starting
14774 || aMachineState == MachineState_Restoring
14775 || aMachineState == MachineState_TeleportingIn
14776 )
14777 {
14778 /* set the current state modified flag to indicate that the current
14779 * state is no more identical to the state in the
14780 * current snapshot */
14781 if (!mData->mCurrentSnapshot.isNull())
14782 {
14783 mData->mCurrentStateModified = TRUE;
14784 stsFlags |= SaveSTS_CurStateModified;
14785 }
14786 }
14787
14788 if (deleteSavedState)
14789 {
14790 if (mRemoveSavedState)
14791 {
14792 Assert(!mSSData->strStateFilePath.isEmpty());
14793
14794 // it is safe to delete the saved state file if ...
14795 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14796 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14797 // ... none of the snapshots share the saved state file
14798 )
14799 RTFileDelete(mSSData->strStateFilePath.c_str());
14800 }
14801
14802 mSSData->strStateFilePath.setNull();
14803 stsFlags |= SaveSTS_StateFilePath;
14804 }
14805
14806 /* redirect to the underlying peer machine */
14807 mPeer->setMachineState(aMachineState);
14808
14809 if ( aMachineState == MachineState_PoweredOff
14810 || aMachineState == MachineState_Teleported
14811 || aMachineState == MachineState_Aborted
14812 || aMachineState == MachineState_Saved)
14813 {
14814 /* the machine has stopped execution
14815 * (or the saved state file was adopted) */
14816 stsFlags |= SaveSTS_StateTimeStamp;
14817 }
14818
14819 if ( ( oldMachineState == MachineState_PoweredOff
14820 || oldMachineState == MachineState_Aborted
14821 || oldMachineState == MachineState_Teleported
14822 )
14823 && aMachineState == MachineState_Saved)
14824 {
14825 /* the saved state file was adopted */
14826 Assert(!mSSData->strStateFilePath.isEmpty());
14827 stsFlags |= SaveSTS_StateFilePath;
14828 }
14829
14830#ifdef VBOX_WITH_GUEST_PROPS
14831 if ( aMachineState == MachineState_PoweredOff
14832 || aMachineState == MachineState_Aborted
14833 || aMachineState == MachineState_Teleported)
14834 {
14835 /* Make sure any transient guest properties get removed from the
14836 * property store on shutdown. */
14837
14838 HWData::GuestPropertyMap::const_iterator it;
14839 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14840 if (!fNeedsSaving)
14841 for (it = mHWData->mGuestProperties.begin();
14842 it != mHWData->mGuestProperties.end(); ++it)
14843 if ( (it->second.mFlags & guestProp::TRANSIENT)
14844 || (it->second.mFlags & guestProp::TRANSRESET))
14845 {
14846 fNeedsSaving = true;
14847 break;
14848 }
14849 if (fNeedsSaving)
14850 {
14851 mData->mCurrentStateModified = TRUE;
14852 stsFlags |= SaveSTS_CurStateModified;
14853 }
14854 }
14855#endif
14856
14857 rc = saveStateSettings(stsFlags);
14858
14859 if ( ( oldMachineState != MachineState_PoweredOff
14860 && oldMachineState != MachineState_Aborted
14861 && oldMachineState != MachineState_Teleported
14862 )
14863 && ( aMachineState == MachineState_PoweredOff
14864 || aMachineState == MachineState_Aborted
14865 || aMachineState == MachineState_Teleported
14866 )
14867 )
14868 {
14869 /* we've been shut down for any reason */
14870 /* no special action so far */
14871 }
14872
14873 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14874 LogFlowThisFuncLeave();
14875 return rc;
14876}
14877
14878/**
14879 * Sends the current machine state value to the VM process.
14880 *
14881 * @note Locks this object for reading, then calls a client process.
14882 */
14883HRESULT SessionMachine::updateMachineStateOnClient()
14884{
14885 AutoCaller autoCaller(this);
14886 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14887
14888 ComPtr<IInternalSessionControl> directControl;
14889 {
14890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14891 AssertReturn(!!mData, E_FAIL);
14892 directControl = mData->mSession.mDirectControl;
14893
14894 /* directControl may be already set to NULL here in #OnSessionEnd()
14895 * called too early by the direct session process while there is still
14896 * some operation (like deleting the snapshot) in progress. The client
14897 * process in this case is waiting inside Session::close() for the
14898 * "end session" process object to complete, while #uninit() called by
14899 * #checkForDeath() on the Watcher thread is waiting for the pending
14900 * operation to complete. For now, we accept this inconsistent behavior
14901 * and simply do nothing here. */
14902
14903 if (mData->mSession.mState == SessionState_Unlocking)
14904 return S_OK;
14905
14906 AssertReturn(!directControl.isNull(), E_FAIL);
14907 }
14908
14909 return directControl->UpdateMachineState(mData->mMachineState);
14910}
Note: See TracBrowser for help on using the repository browser.

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