VirtualBox

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

Last change on this file since 47743 was 47561, checked in by vboxsync, 11 years ago

Main/VirtualBox+Machine+Session: separate out the client death detection functionality into separate objects

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 499.0 KB
Line 
1/* $Id: MachineImpl.cpp 47561 2013-08-06 15:18:17Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
90# define HOSTSUFF_EXE ".exe"
91#else /* !RT_OS_WINDOWS */
92# define HOSTSUFF_EXE ""
93#endif /* !RT_OS_WINDOWS */
94
95// defines / prototypes
96/////////////////////////////////////////////////////////////////////////////
97
98/////////////////////////////////////////////////////////////////////////////
99// Machine::Data structure
100/////////////////////////////////////////////////////////////////////////////
101
102Machine::Data::Data()
103{
104 mRegistered = FALSE;
105 pMachineConfigFile = NULL;
106 /* Contains hints on what has changed when the user is using the VM (config
107 * changes, running the VM, ...). This is used to decide if a config needs
108 * to be written to disk. */
109 flModifications = 0;
110 /* VM modification usually also trigger setting the current state to
111 * "Modified". Although this is not always the case. An e.g. is the VM
112 * initialization phase or when snapshot related data is changed. The
113 * actually behavior is controlled by the following flag. */
114 m_fAllowStateModification = false;
115 mAccessible = FALSE;
116 /* mUuid is initialized in Machine::init() */
117
118 mMachineState = MachineState_PoweredOff;
119 RTTimeNow(&mLastStateChange);
120
121 mMachineStateDeps = 0;
122 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
123 mMachineStateChangePending = 0;
124
125 mCurrentStateModified = TRUE;
126 mGuestPropertiesModified = FALSE;
127
128 mSession.mPID = NIL_RTPROCESS;
129 mSession.mState = SessionState_Unlocked;
130}
131
132Machine::Data::~Data()
133{
134 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
135 {
136 RTSemEventMultiDestroy(mMachineStateDepsSem);
137 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
138 }
139 if (pMachineConfigFile)
140 {
141 delete pMachineConfigFile;
142 pMachineConfigFile = NULL;
143 }
144}
145
146/////////////////////////////////////////////////////////////////////////////
147// Machine::HWData structure
148/////////////////////////////////////////////////////////////////////////////
149
150Machine::HWData::HWData()
151{
152 /* default values for a newly created machine */
153 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
154 mMemorySize = 128;
155 mCPUCount = 1;
156 mCPUHotPlugEnabled = false;
157 mMemoryBalloonSize = 0;
158 mPageFusionEnabled = false;
159 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
160 mVRAMSize = 8;
161 mAccelerate3DEnabled = false;
162 mAccelerate2DVideoEnabled = false;
163 mMonitorCount = 1;
164 mVideoCaptureWidth = 1024;
165 mVideoCaptureHeight = 768;
166 mVideoCaptureRate = 512;
167 mVideoCaptureFPS = 25;
168 mVideoCaptureEnabled = false;
169 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++)
170 maVideoCaptureScreens[i] = true;
171
172 mHWVirtExEnabled = true;
173 mHWVirtExNestedPagingEnabled = true;
174#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
175 mHWVirtExLargePagesEnabled = true;
176#else
177 /* Not supported on 32 bits hosts. */
178 mHWVirtExLargePagesEnabled = false;
179#endif
180 mHWVirtExVPIDEnabled = true;
181 mHWVirtExUXEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
184 mHWVirtExExclusive = false;
185#else
186 mHWVirtExExclusive = true;
187#endif
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
194 mSyntheticCpu = false;
195 mHPETEnabled = false;
196
197 /* default boot order: floppy - DVD - HDD */
198 mBootOrder[0] = DeviceType_Floppy;
199 mBootOrder[1] = DeviceType_DVD;
200 mBootOrder[2] = DeviceType_HardDisk;
201 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
202 mBootOrder[i] = DeviceType_Null;
203
204 mClipboardMode = ClipboardMode_Disabled;
205 mDragAndDropMode = DragAndDropMode_Disabled;
206 mGuestPropertyNotificationPatterns = "";
207
208 mFirmwareType = FirmwareType_BIOS;
209 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
210 mPointingHIDType = PointingHIDType_PS2Mouse;
211 mChipsetType = ChipsetType_PIIX3;
212 mEmulatedUSBWebcamEnabled = FALSE;
213 mEmulatedUSBCardReaderEnabled = FALSE;
214
215 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
216 mCPUAttached[i] = false;
217
218 mIOCacheEnabled = true;
219 mIOCacheSize = 5; /* 5MB */
220
221 /* Maximum CPU execution cap by default. */
222 mCpuExecutionCap = 100;
223}
224
225Machine::HWData::~HWData()
226{
227}
228
229/////////////////////////////////////////////////////////////////////////////
230// Machine::HDData structure
231/////////////////////////////////////////////////////////////////////////////
232
233Machine::MediaData::MediaData()
234{
235}
236
237Machine::MediaData::~MediaData()
238{
239}
240
241/////////////////////////////////////////////////////////////////////////////
242// Machine class
243/////////////////////////////////////////////////////////////////////////////
244
245// constructor / destructor
246/////////////////////////////////////////////////////////////////////////////
247
248Machine::Machine() :
249#ifdef VBOX_WITH_RESOURCE_USAGE_API
250 mCollectorGuest(NULL),
251#endif
252 mPeer(NULL),
253 mParent(NULL),
254 mSerialPorts(),
255 mParallelPorts(),
256 uRegistryNeedsSaving(0)
257{}
258
259Machine::~Machine()
260{}
261
262HRESULT Machine::FinalConstruct()
263{
264 LogFlowThisFunc(("\n"));
265 return BaseFinalConstruct();
266}
267
268void Machine::FinalRelease()
269{
270 LogFlowThisFunc(("\n"));
271 uninit();
272 BaseFinalRelease();
273}
274
275/**
276 * Initializes a new machine instance; this init() variant creates a new, empty machine.
277 * This gets called from VirtualBox::CreateMachine().
278 *
279 * @param aParent Associated parent object
280 * @param strConfigFile Local file system path to the VM settings file (can
281 * be relative to the VirtualBox config directory).
282 * @param strName name for the machine
283 * @param llGroups list of groups for the machine
284 * @param aOsType OS Type of this machine or NULL.
285 * @param aId UUID for the new machine.
286 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
287 *
288 * @return Success indicator. if not S_OK, the machine object is invalid
289 */
290HRESULT Machine::init(VirtualBox *aParent,
291 const Utf8Str &strConfigFile,
292 const Utf8Str &strName,
293 const StringsList &llGroups,
294 GuestOSType *aOsType,
295 const Guid &aId,
296 bool fForceOverwrite,
297 bool fDirectoryIncludesUUID)
298{
299 LogFlowThisFuncEnter();
300 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
301
302 /* Enclose the state transition NotReady->InInit->Ready */
303 AutoInitSpan autoInitSpan(this);
304 AssertReturn(autoInitSpan.isOk(), E_FAIL);
305
306 HRESULT rc = initImpl(aParent, strConfigFile);
307 if (FAILED(rc)) return rc;
308
309 rc = tryCreateMachineConfigFile(fForceOverwrite);
310 if (FAILED(rc)) return rc;
311
312 if (SUCCEEDED(rc))
313 {
314 // create an empty machine config
315 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
316
317 rc = initDataAndChildObjects();
318 }
319
320 if (SUCCEEDED(rc))
321 {
322 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
323 mData->mAccessible = TRUE;
324
325 unconst(mData->mUuid) = aId;
326
327 mUserData->s.strName = strName;
328
329 mUserData->s.llGroups = llGroups;
330
331 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
332 // the "name sync" flag determines whether the machine directory gets renamed along
333 // with the machine file; say so if the settings file name is the same as the
334 // settings file parent directory (machine directory)
335 mUserData->s.fNameSync = isInOwnDir();
336
337 // initialize the default snapshots folder
338 rc = COMSETTER(SnapshotFolder)(NULL);
339 AssertComRC(rc);
340
341 if (aOsType)
342 {
343 /* Store OS type */
344 mUserData->s.strOsType = aOsType->id();
345
346 /* Apply BIOS defaults */
347 mBIOSSettings->applyDefaults(aOsType);
348
349 /* Apply network adapters defaults */
350 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
351 mNetworkAdapters[slot]->applyDefaults(aOsType);
352
353 /* Apply serial port defaults */
354 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
355 mSerialPorts[slot]->applyDefaults(aOsType);
356
357 /* Let the OS type select 64-bit ness. */
358 mHWData->mLongMode = aOsType->is64Bit()
359 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
360 }
361
362 /* At this point the changing of the current state modification
363 * flag is allowed. */
364 allowStateModification();
365
366 /* commit all changes made during the initialization */
367 commit();
368 }
369
370 /* Confirm a successful initialization when it's the case */
371 if (SUCCEEDED(rc))
372 {
373 if (mData->mAccessible)
374 autoInitSpan.setSucceeded();
375 else
376 autoInitSpan.setLimited();
377 }
378
379 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
380 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
381 mData->mRegistered,
382 mData->mAccessible,
383 rc));
384
385 LogFlowThisFuncLeave();
386
387 return rc;
388}
389
390/**
391 * Initializes a new instance with data from machine XML (formerly Init_Registered).
392 * Gets called in two modes:
393 *
394 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
395 * UUID is specified and we mark the machine as "registered";
396 *
397 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
398 * and the machine remains unregistered until RegisterMachine() is called.
399 *
400 * @param aParent Associated parent object
401 * @param aConfigFile Local file system path to the VM settings file (can
402 * be relative to the VirtualBox config directory).
403 * @param aId UUID of the machine or NULL (see above).
404 *
405 * @return Success indicator. if not S_OK, the machine object is invalid
406 */
407HRESULT Machine::initFromSettings(VirtualBox *aParent,
408 const Utf8Str &strConfigFile,
409 const Guid *aId)
410{
411 LogFlowThisFuncEnter();
412 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
413
414 /* Enclose the state transition NotReady->InInit->Ready */
415 AutoInitSpan autoInitSpan(this);
416 AssertReturn(autoInitSpan.isOk(), E_FAIL);
417
418 HRESULT rc = initImpl(aParent, strConfigFile);
419 if (FAILED(rc)) return rc;
420
421 if (aId)
422 {
423 // loading a registered VM:
424 unconst(mData->mUuid) = *aId;
425 mData->mRegistered = TRUE;
426 // now load the settings from XML:
427 rc = registeredInit();
428 // this calls initDataAndChildObjects() and loadSettings()
429 }
430 else
431 {
432 // opening an unregistered VM (VirtualBox::OpenMachine()):
433 rc = initDataAndChildObjects();
434
435 if (SUCCEEDED(rc))
436 {
437 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
438 mData->mAccessible = TRUE;
439
440 try
441 {
442 // load and parse machine XML; this will throw on XML or logic errors
443 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
444
445 // reject VM UUID duplicates, they can happen if someone
446 // tries to register an already known VM config again
447 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
448 true /* fPermitInaccessible */,
449 false /* aDoSetError */,
450 NULL) != VBOX_E_OBJECT_NOT_FOUND)
451 {
452 throw setError(E_FAIL,
453 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
454 mData->m_strConfigFile.c_str());
455 }
456
457 // use UUID from machine config
458 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
459
460 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
461 NULL /* puuidRegistry */);
462 if (FAILED(rc)) throw rc;
463
464 /* At this point the changing of the current state modification
465 * flag is allowed. */
466 allowStateModification();
467
468 commit();
469 }
470 catch (HRESULT err)
471 {
472 /* we assume that error info is set by the thrower */
473 rc = err;
474 }
475 catch (...)
476 {
477 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
478 }
479 }
480 }
481
482 /* Confirm a successful initialization when it's the case */
483 if (SUCCEEDED(rc))
484 {
485 if (mData->mAccessible)
486 autoInitSpan.setSucceeded();
487 else
488 {
489 autoInitSpan.setLimited();
490
491 // uninit media from this machine's media registry, or else
492 // reloading the settings will fail
493 mParent->unregisterMachineMedia(getId());
494 }
495 }
496
497 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
498 "rc=%08X\n",
499 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
500 mData->mRegistered, mData->mAccessible, rc));
501
502 LogFlowThisFuncLeave();
503
504 return rc;
505}
506
507/**
508 * Initializes a new instance from a machine config that is already in memory
509 * (import OVF case). Since we are importing, the UUID in the machine
510 * config is ignored and we always generate a fresh one.
511 *
512 * @param strName Name for the new machine; this overrides what is specified in config and is used
513 * for the settings file as well.
514 * @param config Machine configuration loaded and parsed from XML.
515 *
516 * @return Success indicator. if not S_OK, the machine object is invalid
517 */
518HRESULT Machine::init(VirtualBox *aParent,
519 const Utf8Str &strName,
520 const settings::MachineConfigFile &config)
521{
522 LogFlowThisFuncEnter();
523
524 /* Enclose the state transition NotReady->InInit->Ready */
525 AutoInitSpan autoInitSpan(this);
526 AssertReturn(autoInitSpan.isOk(), E_FAIL);
527
528 Utf8Str strConfigFile;
529 aParent->getDefaultMachineFolder(strConfigFile);
530 strConfigFile.append(RTPATH_DELIMITER);
531 strConfigFile.append(strName);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(".vbox");
535
536 HRESULT rc = initImpl(aParent, strConfigFile);
537 if (FAILED(rc)) return rc;
538
539 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
540 if (FAILED(rc)) return rc;
541
542 rc = initDataAndChildObjects();
543
544 if (SUCCEEDED(rc))
545 {
546 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
547 mData->mAccessible = TRUE;
548
549 // create empty machine config for instance data
550 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
551
552 // generate fresh UUID, ignore machine config
553 unconst(mData->mUuid).create();
554
555 rc = loadMachineDataFromSettings(config,
556 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
557
558 // override VM name as well, it may be different
559 mUserData->s.strName = strName;
560
561 if (SUCCEEDED(rc))
562 {
563 /* At this point the changing of the current state modification
564 * flag is allowed. */
565 allowStateModification();
566
567 /* commit all changes made during the initialization */
568 commit();
569 }
570 }
571
572 /* Confirm a successful initialization when it's the case */
573 if (SUCCEEDED(rc))
574 {
575 if (mData->mAccessible)
576 autoInitSpan.setSucceeded();
577 else
578 {
579 autoInitSpan.setLimited();
580
581 // uninit media from this machine's media registry, or else
582 // reloading the settings will fail
583 mParent->unregisterMachineMedia(getId());
584 }
585 }
586
587 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
588 "rc=%08X\n",
589 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
590 mData->mRegistered, mData->mAccessible, rc));
591
592 LogFlowThisFuncLeave();
593
594 return rc;
595}
596
597/**
598 * Shared code between the various init() implementations.
599 * @param aParent
600 * @return
601 */
602HRESULT Machine::initImpl(VirtualBox *aParent,
603 const Utf8Str &strConfigFile)
604{
605 LogFlowThisFuncEnter();
606
607 AssertReturn(aParent, E_INVALIDARG);
608 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
609
610 HRESULT rc = S_OK;
611
612 /* share the parent weakly */
613 unconst(mParent) = aParent;
614
615 /* allocate the essential machine data structure (the rest will be
616 * allocated later by initDataAndChildObjects() */
617 mData.allocate();
618
619 /* memorize the config file name (as provided) */
620 mData->m_strConfigFile = strConfigFile;
621
622 /* get the full file name */
623 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
624 if (RT_FAILURE(vrc1))
625 return setError(VBOX_E_FILE_ERROR,
626 tr("Invalid machine settings file name '%s' (%Rrc)"),
627 strConfigFile.c_str(),
628 vrc1);
629
630 LogFlowThisFuncLeave();
631
632 return rc;
633}
634
635/**
636 * Tries to create a machine settings file in the path stored in the machine
637 * instance data. Used when a new machine is created to fail gracefully if
638 * the settings file could not be written (e.g. because machine dir is read-only).
639 * @return
640 */
641HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
642{
643 HRESULT rc = S_OK;
644
645 // when we create a new machine, we must be able to create the settings file
646 RTFILE f = NIL_RTFILE;
647 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
648 if ( RT_SUCCESS(vrc)
649 || vrc == VERR_SHARING_VIOLATION
650 )
651 {
652 if (RT_SUCCESS(vrc))
653 RTFileClose(f);
654 if (!fForceOverwrite)
655 rc = setError(VBOX_E_FILE_ERROR,
656 tr("Machine settings file '%s' already exists"),
657 mData->m_strConfigFileFull.c_str());
658 else
659 {
660 /* try to delete the config file, as otherwise the creation
661 * of a new settings file will fail. */
662 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
663 if (RT_FAILURE(vrc2))
664 rc = setError(VBOX_E_FILE_ERROR,
665 tr("Could not delete the existing settings file '%s' (%Rrc)"),
666 mData->m_strConfigFileFull.c_str(), vrc2);
667 }
668 }
669 else if ( vrc != VERR_FILE_NOT_FOUND
670 && vrc != VERR_PATH_NOT_FOUND
671 )
672 rc = setError(VBOX_E_FILE_ERROR,
673 tr("Invalid machine settings file name '%s' (%Rrc)"),
674 mData->m_strConfigFileFull.c_str(),
675 vrc);
676 return rc;
677}
678
679/**
680 * Initializes the registered machine by loading the settings file.
681 * This method is separated from #init() in order to make it possible to
682 * retry the operation after VirtualBox startup instead of refusing to
683 * startup the whole VirtualBox server in case if the settings file of some
684 * registered VM is invalid or inaccessible.
685 *
686 * @note Must be always called from this object's write lock
687 * (unless called from #init() that doesn't need any locking).
688 * @note Locks the mUSBController method for writing.
689 * @note Subclasses must not call this method.
690 */
691HRESULT Machine::registeredInit()
692{
693 AssertReturn(!isSessionMachine(), E_FAIL);
694 AssertReturn(!isSnapshotMachine(), E_FAIL);
695 AssertReturn(mData->mUuid.isValid(), E_FAIL);
696 AssertReturn(!mData->mAccessible, E_FAIL);
697
698 HRESULT rc = initDataAndChildObjects();
699
700 if (SUCCEEDED(rc))
701 {
702 /* Temporarily reset the registered flag in order to let setters
703 * potentially called from loadSettings() succeed (isMutable() used in
704 * all setters will return FALSE for a Machine instance if mRegistered
705 * is TRUE). */
706 mData->mRegistered = FALSE;
707
708 try
709 {
710 // load and parse machine XML; this will throw on XML or logic errors
711 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
712
713 if (mData->mUuid != mData->pMachineConfigFile->uuid)
714 throw setError(E_FAIL,
715 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
716 mData->pMachineConfigFile->uuid.raw(),
717 mData->m_strConfigFileFull.c_str(),
718 mData->mUuid.toString().c_str(),
719 mParent->settingsFilePath().c_str());
720
721 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
722 NULL /* const Guid *puuidRegistry */);
723 if (FAILED(rc)) throw rc;
724 }
725 catch (HRESULT err)
726 {
727 /* we assume that error info is set by the thrower */
728 rc = err;
729 }
730 catch (...)
731 {
732 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
733 }
734
735 /* Restore the registered flag (even on failure) */
736 mData->mRegistered = TRUE;
737 }
738
739 if (SUCCEEDED(rc))
740 {
741 /* Set mAccessible to TRUE only if we successfully locked and loaded
742 * the settings file */
743 mData->mAccessible = TRUE;
744
745 /* commit all changes made during loading the settings file */
746 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
747 /// @todo r=klaus for some reason the settings loading logic backs up
748 // the settings, and therefore a commit is needed. Should probably be changed.
749 }
750 else
751 {
752 /* If the machine is registered, then, instead of returning a
753 * failure, we mark it as inaccessible and set the result to
754 * success to give it a try later */
755
756 /* fetch the current error info */
757 mData->mAccessError = com::ErrorInfo();
758 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
759 mData->mUuid.raw(),
760 mData->mAccessError.getText().raw()));
761
762 /* rollback all changes */
763 rollback(false /* aNotify */);
764
765 // uninit media from this machine's media registry, or else
766 // reloading the settings will fail
767 mParent->unregisterMachineMedia(getId());
768
769 /* uninitialize the common part to make sure all data is reset to
770 * default (null) values */
771 uninitDataAndChildObjects();
772
773 rc = S_OK;
774 }
775
776 return rc;
777}
778
779/**
780 * Uninitializes the instance.
781 * Called either from FinalRelease() or by the parent when it gets destroyed.
782 *
783 * @note The caller of this method must make sure that this object
784 * a) doesn't have active callers on the current thread and b) is not locked
785 * by the current thread; otherwise uninit() will hang either a) due to
786 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
787 * a dead-lock caused by this thread waiting for all callers on the other
788 * threads are done but preventing them from doing so by holding a lock.
789 */
790void Machine::uninit()
791{
792 LogFlowThisFuncEnter();
793
794 Assert(!isWriteLockOnCurrentThread());
795
796 Assert(!uRegistryNeedsSaving);
797 if (uRegistryNeedsSaving)
798 {
799 AutoCaller autoCaller(this);
800 if (SUCCEEDED(autoCaller.rc()))
801 {
802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
803 saveSettings(NULL, Machine::SaveS_Force);
804 }
805 }
806
807 /* Enclose the state transition Ready->InUninit->NotReady */
808 AutoUninitSpan autoUninitSpan(this);
809 if (autoUninitSpan.uninitDone())
810 return;
811
812 Assert(!isSnapshotMachine());
813 Assert(!isSessionMachine());
814 Assert(!!mData);
815
816 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
817 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
818
819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
820
821 if (!mData->mSession.mMachine.isNull())
822 {
823 /* Theoretically, this can only happen if the VirtualBox server has been
824 * terminated while there were clients running that owned open direct
825 * sessions. Since in this case we are definitely called by
826 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
827 * won't happen on the client watcher thread (because it does
828 * VirtualBox::addCaller() for the duration of the
829 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
830 * cannot happen until the VirtualBox caller is released). This is
831 * important, because SessionMachine::uninit() cannot correctly operate
832 * after we return from this method (it expects the Machine instance is
833 * still valid). We'll call it ourselves below.
834 */
835 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
836 (SessionMachine*)mData->mSession.mMachine));
837
838 if (Global::IsOnlineOrTransient(mData->mMachineState))
839 {
840 LogWarningThisFunc(("Setting state to Aborted!\n"));
841 /* set machine state using SessionMachine reimplementation */
842 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
843 }
844
845 /*
846 * Uninitialize SessionMachine using public uninit() to indicate
847 * an unexpected uninitialization.
848 */
849 mData->mSession.mMachine->uninit();
850 /* SessionMachine::uninit() must set mSession.mMachine to null */
851 Assert(mData->mSession.mMachine.isNull());
852 }
853
854 // uninit media from this machine's media registry, if they're still there
855 Guid uuidMachine(getId());
856
857 /* the lock is no more necessary (SessionMachine is uninitialized) */
858 alock.release();
859
860 /* XXX This will fail with
861 * "cannot be closed because it is still attached to 1 virtual machines"
862 * because at this point we did not call uninitDataAndChildObjects() yet
863 * and therefore also removeBackReference() for all these mediums was not called! */
864
865 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
866 mParent->unregisterMachineMedia(uuidMachine);
867
868 // has machine been modified?
869 if (mData->flModifications)
870 {
871 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
872 rollback(false /* aNotify */);
873 }
874
875 if (mData->mAccessible)
876 uninitDataAndChildObjects();
877
878 /* free the essential data structure last */
879 mData.free();
880
881 LogFlowThisFuncLeave();
882}
883
884// IMachine properties
885/////////////////////////////////////////////////////////////////////////////
886
887STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
888{
889 CheckComArgOutPointerValid(aParent);
890
891 AutoLimitedCaller autoCaller(this);
892 if (FAILED(autoCaller.rc())) return autoCaller.rc();
893
894 /* mParent is constant during life time, no need to lock */
895 ComObjPtr<VirtualBox> pVirtualBox(mParent);
896 pVirtualBox.queryInterfaceTo(aParent);
897
898 return S_OK;
899}
900
901STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
902{
903 CheckComArgOutPointerValid(aAccessible);
904
905 AutoLimitedCaller autoCaller(this);
906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
907
908 LogFlowThisFunc(("ENTER\n"));
909
910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
911
912 HRESULT rc = S_OK;
913
914 if (!mData->mAccessible)
915 {
916 /* try to initialize the VM once more if not accessible */
917
918 AutoReinitSpan autoReinitSpan(this);
919 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
920
921#ifdef DEBUG
922 LogFlowThisFunc(("Dumping media backreferences\n"));
923 mParent->dumpAllBackRefs();
924#endif
925
926 if (mData->pMachineConfigFile)
927 {
928 // reset the XML file to force loadSettings() (called from registeredInit())
929 // to parse it again; the file might have changed
930 delete mData->pMachineConfigFile;
931 mData->pMachineConfigFile = NULL;
932 }
933
934 rc = registeredInit();
935
936 if (SUCCEEDED(rc) && mData->mAccessible)
937 {
938 autoReinitSpan.setSucceeded();
939
940 /* make sure interesting parties will notice the accessibility
941 * state change */
942 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
943 mParent->onMachineDataChange(mData->mUuid);
944 }
945 }
946
947 if (SUCCEEDED(rc))
948 *aAccessible = mData->mAccessible;
949
950 LogFlowThisFuncLeave();
951
952 return rc;
953}
954
955STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
956{
957 CheckComArgOutPointerValid(aAccessError);
958
959 AutoLimitedCaller autoCaller(this);
960 if (FAILED(autoCaller.rc())) return autoCaller.rc();
961
962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
963
964 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
965 {
966 /* return shortly */
967 aAccessError = NULL;
968 return S_OK;
969 }
970
971 HRESULT rc = S_OK;
972
973 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
974 rc = errorInfo.createObject();
975 if (SUCCEEDED(rc))
976 {
977 errorInfo->init(mData->mAccessError.getResultCode(),
978 mData->mAccessError.getInterfaceID().ref(),
979 Utf8Str(mData->mAccessError.getComponent()).c_str(),
980 Utf8Str(mData->mAccessError.getText()));
981 rc = errorInfo.queryInterfaceTo(aAccessError);
982 }
983
984 return rc;
985}
986
987STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
988{
989 CheckComArgOutPointerValid(aName);
990
991 AutoCaller autoCaller(this);
992 if (FAILED(autoCaller.rc())) return autoCaller.rc();
993
994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
995
996 mUserData->s.strName.cloneTo(aName);
997
998 return S_OK;
999}
1000
1001STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1002{
1003 CheckComArgStrNotEmptyOrNull(aName);
1004
1005 AutoCaller autoCaller(this);
1006 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1007
1008 // prohibit setting a UUID only as the machine name, or else it can
1009 // never be found by findMachine()
1010 Guid test(aName);
1011
1012 if (test.isValid())
1013 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1014
1015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1016
1017 HRESULT rc = checkStateDependency(MutableStateDep);
1018 if (FAILED(rc)) return rc;
1019
1020 setModified(IsModified_MachineData);
1021 mUserData.backup();
1022 mUserData->s.strName = aName;
1023
1024 return S_OK;
1025}
1026
1027STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1028{
1029 CheckComArgOutPointerValid(aDescription);
1030
1031 AutoCaller autoCaller(this);
1032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1033
1034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1035
1036 mUserData->s.strDescription.cloneTo(aDescription);
1037
1038 return S_OK;
1039}
1040
1041STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1042{
1043 AutoCaller autoCaller(this);
1044 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1045
1046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1047
1048 // this can be done in principle in any state as it doesn't affect the VM
1049 // significantly, but play safe by not messing around while complex
1050 // activities are going on
1051 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1052 if (FAILED(rc)) return rc;
1053
1054 setModified(IsModified_MachineData);
1055 mUserData.backup();
1056 mUserData->s.strDescription = aDescription;
1057
1058 return S_OK;
1059}
1060
1061STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1062{
1063 CheckComArgOutPointerValid(aId);
1064
1065 AutoLimitedCaller autoCaller(this);
1066 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1067
1068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1069
1070 mData->mUuid.toUtf16().cloneTo(aId);
1071
1072 return S_OK;
1073}
1074
1075STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1076{
1077 CheckComArgOutSafeArrayPointerValid(aGroups);
1078
1079 AutoCaller autoCaller(this);
1080 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1081
1082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1083 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1084 size_t i = 0;
1085 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1086 it != mUserData->s.llGroups.end();
1087 ++it, i++)
1088 {
1089 Bstr tmp = *it;
1090 tmp.cloneTo(&groups[i]);
1091 }
1092 groups.detachTo(ComSafeArrayOutArg(aGroups));
1093
1094 return S_OK;
1095}
1096
1097STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1098{
1099 AutoCaller autoCaller(this);
1100 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1101
1102 StringsList llGroups;
1103 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1104 if (FAILED(rc))
1105 return rc;
1106
1107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1108
1109 // changing machine groups is possible while the VM is offline
1110 rc = checkStateDependency(OfflineStateDep);
1111 if (FAILED(rc)) return rc;
1112
1113 setModified(IsModified_MachineData);
1114 mUserData.backup();
1115 mUserData->s.llGroups = llGroups;
1116
1117 return S_OK;
1118}
1119
1120STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1121{
1122 CheckComArgOutPointerValid(aOSTypeId);
1123
1124 AutoCaller autoCaller(this);
1125 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1126
1127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1128
1129 mUserData->s.strOsType.cloneTo(aOSTypeId);
1130
1131 return S_OK;
1132}
1133
1134STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1135{
1136 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1137
1138 AutoCaller autoCaller(this);
1139 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1140
1141 /* look up the object by Id to check it is valid */
1142 ComPtr<IGuestOSType> guestOSType;
1143 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1144 if (FAILED(rc)) return rc;
1145
1146 /* when setting, always use the "etalon" value for consistency -- lookup
1147 * by ID is case-insensitive and the input value may have different case */
1148 Bstr osTypeId;
1149 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1150 if (FAILED(rc)) return rc;
1151
1152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1153
1154 rc = checkStateDependency(MutableStateDep);
1155 if (FAILED(rc)) return rc;
1156
1157 setModified(IsModified_MachineData);
1158 mUserData.backup();
1159 mUserData->s.strOsType = osTypeId;
1160
1161 return S_OK;
1162}
1163
1164
1165STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1166{
1167 CheckComArgOutPointerValid(aFirmwareType);
1168
1169 AutoCaller autoCaller(this);
1170 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1171
1172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1173
1174 *aFirmwareType = mHWData->mFirmwareType;
1175
1176 return S_OK;
1177}
1178
1179STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1180{
1181 AutoCaller autoCaller(this);
1182 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1184
1185 HRESULT rc = checkStateDependency(MutableStateDep);
1186 if (FAILED(rc)) return rc;
1187
1188 setModified(IsModified_MachineData);
1189 mHWData.backup();
1190 mHWData->mFirmwareType = aFirmwareType;
1191
1192 return S_OK;
1193}
1194
1195STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1196{
1197 CheckComArgOutPointerValid(aKeyboardHIDType);
1198
1199 AutoCaller autoCaller(this);
1200 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1201
1202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1203
1204 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1205
1206 return S_OK;
1207}
1208
1209STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1210{
1211 AutoCaller autoCaller(this);
1212 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1214
1215 HRESULT rc = checkStateDependency(MutableStateDep);
1216 if (FAILED(rc)) return rc;
1217
1218 setModified(IsModified_MachineData);
1219 mHWData.backup();
1220 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1221
1222 return S_OK;
1223}
1224
1225STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1226{
1227 CheckComArgOutPointerValid(aPointingHIDType);
1228
1229 AutoCaller autoCaller(this);
1230 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1231
1232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1233
1234 *aPointingHIDType = mHWData->mPointingHIDType;
1235
1236 return S_OK;
1237}
1238
1239STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1240{
1241 AutoCaller autoCaller(this);
1242 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1244
1245 HRESULT rc = checkStateDependency(MutableStateDep);
1246 if (FAILED(rc)) return rc;
1247
1248 setModified(IsModified_MachineData);
1249 mHWData.backup();
1250 mHWData->mPointingHIDType = aPointingHIDType;
1251
1252 return S_OK;
1253}
1254
1255STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1256{
1257 CheckComArgOutPointerValid(aChipsetType);
1258
1259 AutoCaller autoCaller(this);
1260 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1261
1262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1263
1264 *aChipsetType = mHWData->mChipsetType;
1265
1266 return S_OK;
1267}
1268
1269STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1270{
1271 AutoCaller autoCaller(this);
1272 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1273 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1274
1275 HRESULT rc = checkStateDependency(MutableStateDep);
1276 if (FAILED(rc)) return rc;
1277
1278 if (aChipsetType != mHWData->mChipsetType)
1279 {
1280 setModified(IsModified_MachineData);
1281 mHWData.backup();
1282 mHWData->mChipsetType = aChipsetType;
1283
1284 // Resize network adapter array, to be finalized on commit/rollback.
1285 // We must not throw away entries yet, otherwise settings are lost
1286 // without a way to roll back.
1287 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1288 size_t oldCount = mNetworkAdapters.size();
1289 if (newCount > oldCount)
1290 {
1291 mNetworkAdapters.resize(newCount);
1292 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1293 {
1294 unconst(mNetworkAdapters[slot]).createObject();
1295 mNetworkAdapters[slot]->init(this, slot);
1296 }
1297 }
1298 }
1299
1300 return S_OK;
1301}
1302
1303STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1304{
1305 CheckComArgOutPointerValid(aHWVersion);
1306
1307 AutoCaller autoCaller(this);
1308 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1309
1310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1311
1312 mHWData->mHWVersion.cloneTo(aHWVersion);
1313
1314 return S_OK;
1315}
1316
1317STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1318{
1319 /* check known version */
1320 Utf8Str hwVersion = aHWVersion;
1321 if ( hwVersion.compare("1") != 0
1322 && hwVersion.compare("2") != 0)
1323 return setError(E_INVALIDARG,
1324 tr("Invalid hardware version: %ls\n"), aHWVersion);
1325
1326 AutoCaller autoCaller(this);
1327 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1328
1329 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1330
1331 HRESULT rc = checkStateDependency(MutableStateDep);
1332 if (FAILED(rc)) return rc;
1333
1334 setModified(IsModified_MachineData);
1335 mHWData.backup();
1336 mHWData->mHWVersion = hwVersion;
1337
1338 return S_OK;
1339}
1340
1341STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1342{
1343 CheckComArgOutPointerValid(aUUID);
1344
1345 AutoCaller autoCaller(this);
1346 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1347
1348 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1349
1350 if (mHWData->mHardwareUUID.isValid())
1351 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1352 else
1353 mData->mUuid.toUtf16().cloneTo(aUUID);
1354
1355 return S_OK;
1356}
1357
1358STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1359{
1360 Guid hardwareUUID(aUUID);
1361 if (!hardwareUUID.isValid())
1362 return E_INVALIDARG;
1363
1364 AutoCaller autoCaller(this);
1365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1366
1367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1368
1369 HRESULT rc = checkStateDependency(MutableStateDep);
1370 if (FAILED(rc)) return rc;
1371
1372 setModified(IsModified_MachineData);
1373 mHWData.backup();
1374 if (hardwareUUID == mData->mUuid)
1375 mHWData->mHardwareUUID.clear();
1376 else
1377 mHWData->mHardwareUUID = hardwareUUID;
1378
1379 return S_OK;
1380}
1381
1382STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1383{
1384 CheckComArgOutPointerValid(memorySize);
1385
1386 AutoCaller autoCaller(this);
1387 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1388
1389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1390
1391 *memorySize = mHWData->mMemorySize;
1392
1393 return S_OK;
1394}
1395
1396STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1397{
1398 /* check RAM limits */
1399 if ( memorySize < MM_RAM_MIN_IN_MB
1400 || memorySize > MM_RAM_MAX_IN_MB
1401 )
1402 return setError(E_INVALIDARG,
1403 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1404 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1405
1406 AutoCaller autoCaller(this);
1407 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1408
1409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1410
1411 HRESULT rc = checkStateDependency(MutableStateDep);
1412 if (FAILED(rc)) return rc;
1413
1414 setModified(IsModified_MachineData);
1415 mHWData.backup();
1416 mHWData->mMemorySize = memorySize;
1417
1418 return S_OK;
1419}
1420
1421STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1422{
1423 CheckComArgOutPointerValid(CPUCount);
1424
1425 AutoCaller autoCaller(this);
1426 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1427
1428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1429
1430 *CPUCount = mHWData->mCPUCount;
1431
1432 return S_OK;
1433}
1434
1435STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1436{
1437 /* check CPU limits */
1438 if ( CPUCount < SchemaDefs::MinCPUCount
1439 || CPUCount > SchemaDefs::MaxCPUCount
1440 )
1441 return setError(E_INVALIDARG,
1442 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1443 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1444
1445 AutoCaller autoCaller(this);
1446 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1447
1448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1449
1450 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1451 if (mHWData->mCPUHotPlugEnabled)
1452 {
1453 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1454 {
1455 if (mHWData->mCPUAttached[idx])
1456 return setError(E_INVALIDARG,
1457 tr("There is still a CPU attached to socket %lu."
1458 "Detach the CPU before removing the socket"),
1459 CPUCount, idx+1);
1460 }
1461 }
1462
1463 HRESULT rc = checkStateDependency(MutableStateDep);
1464 if (FAILED(rc)) return rc;
1465
1466 setModified(IsModified_MachineData);
1467 mHWData.backup();
1468 mHWData->mCPUCount = CPUCount;
1469
1470 return S_OK;
1471}
1472
1473STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1474{
1475 CheckComArgOutPointerValid(aExecutionCap);
1476
1477 AutoCaller autoCaller(this);
1478 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1479
1480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1481
1482 *aExecutionCap = mHWData->mCpuExecutionCap;
1483
1484 return S_OK;
1485}
1486
1487STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1488{
1489 HRESULT rc = S_OK;
1490
1491 /* check throttle limits */
1492 if ( aExecutionCap < 1
1493 || aExecutionCap > 100
1494 )
1495 return setError(E_INVALIDARG,
1496 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1497 aExecutionCap, 1, 100);
1498
1499 AutoCaller autoCaller(this);
1500 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1501
1502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1503
1504 alock.release();
1505 rc = onCPUExecutionCapChange(aExecutionCap);
1506 alock.acquire();
1507 if (FAILED(rc)) return rc;
1508
1509 setModified(IsModified_MachineData);
1510 mHWData.backup();
1511 mHWData->mCpuExecutionCap = aExecutionCap;
1512
1513 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1514 if (Global::IsOnline(mData->mMachineState))
1515 saveSettings(NULL);
1516
1517 return S_OK;
1518}
1519
1520
1521STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1522{
1523 CheckComArgOutPointerValid(aEnabled);
1524
1525 AutoCaller autoCaller(this);
1526 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1527
1528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1529
1530 *aEnabled = mHWData->mCPUHotPlugEnabled;
1531
1532 return S_OK;
1533}
1534
1535STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1536{
1537 HRESULT rc = S_OK;
1538
1539 AutoCaller autoCaller(this);
1540 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1541
1542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1543
1544 rc = checkStateDependency(MutableStateDep);
1545 if (FAILED(rc)) return rc;
1546
1547 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1548 {
1549 if (aEnabled)
1550 {
1551 setModified(IsModified_MachineData);
1552 mHWData.backup();
1553
1554 /* Add the amount of CPUs currently attached */
1555 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1556 {
1557 mHWData->mCPUAttached[i] = true;
1558 }
1559 }
1560 else
1561 {
1562 /*
1563 * We can disable hotplug only if the amount of maximum CPUs is equal
1564 * to the amount of attached CPUs
1565 */
1566 unsigned cCpusAttached = 0;
1567 unsigned iHighestId = 0;
1568
1569 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1570 {
1571 if (mHWData->mCPUAttached[i])
1572 {
1573 cCpusAttached++;
1574 iHighestId = i;
1575 }
1576 }
1577
1578 if ( (cCpusAttached != mHWData->mCPUCount)
1579 || (iHighestId >= mHWData->mCPUCount))
1580 return setError(E_INVALIDARG,
1581 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1582
1583 setModified(IsModified_MachineData);
1584 mHWData.backup();
1585 }
1586 }
1587
1588 mHWData->mCPUHotPlugEnabled = aEnabled;
1589
1590 return rc;
1591}
1592
1593STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1594{
1595#ifdef VBOX_WITH_USB_CARDREADER
1596 CheckComArgOutPointerValid(aEnabled);
1597
1598 AutoCaller autoCaller(this);
1599 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1600
1601 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1602
1603 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1604
1605 return S_OK;
1606#else
1607 NOREF(aEnabled);
1608 return E_NOTIMPL;
1609#endif
1610}
1611
1612STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1613{
1614#ifdef VBOX_WITH_USB_CARDREADER
1615 AutoCaller autoCaller(this);
1616 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1618
1619 HRESULT rc = checkStateDependency(MutableStateDep);
1620 if (FAILED(rc)) return rc;
1621
1622 setModified(IsModified_MachineData);
1623 mHWData.backup();
1624 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1625
1626 return S_OK;
1627#else
1628 NOREF(aEnabled);
1629 return E_NOTIMPL;
1630#endif
1631}
1632
1633STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled)
1634{
1635#ifdef VBOX_WITH_USB_VIDEO
1636 CheckComArgOutPointerValid(aEnabled);
1637
1638 AutoCaller autoCaller(this);
1639 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1640
1641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1642
1643 *aEnabled = mHWData->mEmulatedUSBWebcamEnabled;
1644
1645 return S_OK;
1646#else
1647 NOREF(aEnabled);
1648 return E_NOTIMPL;
1649#endif
1650}
1651
1652STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled)
1653{
1654#ifdef VBOX_WITH_USB_VIDEO
1655 AutoCaller autoCaller(this);
1656 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1657 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1658
1659 HRESULT rc = checkStateDependency(MutableStateDep);
1660 if (FAILED(rc)) return rc;
1661
1662 setModified(IsModified_MachineData);
1663 mHWData.backup();
1664 mHWData->mEmulatedUSBWebcamEnabled = aEnabled;
1665
1666 return S_OK;
1667#else
1668 NOREF(aEnabled);
1669 return E_NOTIMPL;
1670#endif
1671}
1672
1673STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1674{
1675 CheckComArgOutPointerValid(aEnabled);
1676
1677 AutoCaller autoCaller(this);
1678 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1680
1681 *aEnabled = mHWData->mHPETEnabled;
1682
1683 return S_OK;
1684}
1685
1686STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1687{
1688 HRESULT rc = S_OK;
1689
1690 AutoCaller autoCaller(this);
1691 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1692 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1693
1694 rc = checkStateDependency(MutableStateDep);
1695 if (FAILED(rc)) return rc;
1696
1697 setModified(IsModified_MachineData);
1698 mHWData.backup();
1699
1700 mHWData->mHPETEnabled = aEnabled;
1701
1702 return rc;
1703}
1704
1705STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1706{
1707 AutoCaller autoCaller(this);
1708 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1709
1710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1711
1712 *fEnabled = mHWData->mVideoCaptureEnabled;
1713 return S_OK;
1714}
1715
1716STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1717{
1718 HRESULT rc = S_OK;
1719
1720 AutoCaller autoCaller(this);
1721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1723
1724 setModified(IsModified_MachineData);
1725 mHWData.backup();
1726 mHWData->mVideoCaptureEnabled = fEnabled;
1727
1728 alock.release();
1729 rc = onVideoCaptureChange();
1730 alock.acquire();
1731 if (FAILED(rc))
1732 {
1733 /*
1734 * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded.
1735 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1736 * determine if it should start or stop capturing. Therefore we need to manually
1737 * undo change.
1738 */
1739 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1740 return rc;
1741 }
1742
1743 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1744 if (Global::IsOnline(mData->mMachineState))
1745 saveSettings(NULL);
1746
1747 return rc;
1748}
1749
1750STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1751{
1752 CheckComArgOutSafeArrayPointerValid(aScreens);
1753
1754 AutoCaller autoCaller(this);
1755 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1756
1757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1758
1759 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1760 for (unsigned i = 0; i < screens.size(); i++)
1761 screens[i] = mHWData->maVideoCaptureScreens[i];
1762 screens.detachTo(ComSafeArrayOutArg(aScreens));
1763 return S_OK;
1764}
1765
1766STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1767{
1768 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1769 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1770 bool fChanged = false;
1771
1772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1773
1774 for (unsigned i = 0; i < screens.size(); i++)
1775 {
1776 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1777 {
1778 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1779 fChanged = true;
1780 }
1781 }
1782 if (fChanged)
1783 {
1784 alock.release();
1785 HRESULT rc = onVideoCaptureChange();
1786 alock.acquire();
1787 if (FAILED(rc)) return rc;
1788 setModified(IsModified_MachineData);
1789
1790 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1791 if (Global::IsOnline(mData->mMachineState))
1792 saveSettings(NULL);
1793 }
1794
1795 return S_OK;
1796}
1797
1798STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1799{
1800 AutoCaller autoCaller(this);
1801 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1802
1803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1804 if (mHWData->mVideoCaptureFile.isEmpty())
1805 {
1806 Utf8Str defaultFile;
1807 getDefaultVideoCaptureFile(defaultFile);
1808 defaultFile.cloneTo(apFile);
1809 }
1810 else
1811 mHWData->mVideoCaptureFile.cloneTo(apFile);
1812 return S_OK;
1813}
1814
1815STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1816{
1817 Utf8Str strFile(aFile);
1818 AutoCaller autoCaller(this);
1819 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1820
1821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1822
1823 if ( Global::IsOnline(mData->mMachineState)
1824 && mHWData->mVideoCaptureEnabled)
1825 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1826
1827 if (!RTPathStartsWithRoot(strFile.c_str()))
1828 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1829
1830 if (!strFile.isEmpty())
1831 {
1832 Utf8Str defaultFile;
1833 getDefaultVideoCaptureFile(defaultFile);
1834 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1835 strFile.setNull();
1836 }
1837
1838 setModified(IsModified_MachineData);
1839 mHWData.backup();
1840 mHWData->mVideoCaptureFile = strFile;
1841
1842 return S_OK;
1843}
1844
1845STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1846{
1847 AutoCaller autoCaller(this);
1848 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1849
1850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1851 *aHorzRes = mHWData->mVideoCaptureWidth;
1852 return S_OK;
1853}
1854
1855STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1856{
1857 AutoCaller autoCaller(this);
1858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1859
1860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 if ( Global::IsOnline(mData->mMachineState)
1863 && mHWData->mVideoCaptureEnabled)
1864 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1865
1866 setModified(IsModified_MachineData);
1867 mHWData.backup();
1868 mHWData->mVideoCaptureWidth = aHorzRes;
1869
1870 return S_OK;
1871}
1872
1873STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1874{
1875 AutoCaller autoCaller(this);
1876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1877
1878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1879 *aVertRes = mHWData->mVideoCaptureHeight;
1880 return S_OK;
1881}
1882
1883STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1884{
1885 AutoCaller autoCaller(this);
1886 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1887
1888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1889
1890 if ( Global::IsOnline(mData->mMachineState)
1891 && mHWData->mVideoCaptureEnabled)
1892 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1893
1894 setModified(IsModified_MachineData);
1895 mHWData.backup();
1896 mHWData->mVideoCaptureHeight = aVertRes;
1897
1898 return S_OK;
1899}
1900
1901STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1902{
1903 AutoCaller autoCaller(this);
1904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1905
1906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1907 *aRate = mHWData->mVideoCaptureRate;
1908 return S_OK;
1909}
1910
1911STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1912{
1913 AutoCaller autoCaller(this);
1914 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1915
1916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1917
1918 if ( Global::IsOnline(mData->mMachineState)
1919 && mHWData->mVideoCaptureEnabled)
1920 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1921
1922 setModified(IsModified_MachineData);
1923 mHWData.backup();
1924 mHWData->mVideoCaptureRate = aRate;
1925
1926 return S_OK;
1927}
1928
1929STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1930{
1931 AutoCaller autoCaller(this);
1932 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1933
1934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1935 *aFPS = mHWData->mVideoCaptureFPS;
1936 return S_OK;
1937}
1938
1939STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1940{
1941 AutoCaller autoCaller(this);
1942 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1943
1944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1945
1946 if ( Global::IsOnline(mData->mMachineState)
1947 && mHWData->mVideoCaptureEnabled)
1948 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1949
1950 setModified(IsModified_MachineData);
1951 mHWData.backup();
1952 mHWData->mVideoCaptureFPS = aFPS;
1953
1954 return S_OK;
1955}
1956
1957STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1958{
1959 CheckComArgOutPointerValid(aGraphicsControllerType);
1960
1961 AutoCaller autoCaller(this);
1962 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1963
1964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1965
1966 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1967
1968 return S_OK;
1969}
1970
1971STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1972{
1973 switch (aGraphicsControllerType)
1974 {
1975 case GraphicsControllerType_Null:
1976 case GraphicsControllerType_VBoxVGA:
1977 break;
1978 default:
1979 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1980 }
1981
1982 AutoCaller autoCaller(this);
1983 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1984
1985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1986
1987 HRESULT rc = checkStateDependency(MutableStateDep);
1988 if (FAILED(rc)) return rc;
1989
1990 setModified(IsModified_MachineData);
1991 mHWData.backup();
1992 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1993
1994 return S_OK;
1995}
1996
1997STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1998{
1999 CheckComArgOutPointerValid(memorySize);
2000
2001 AutoCaller autoCaller(this);
2002 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2003
2004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2005
2006 *memorySize = mHWData->mVRAMSize;
2007
2008 return S_OK;
2009}
2010
2011STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
2012{
2013 /* check VRAM limits */
2014 if (memorySize < SchemaDefs::MinGuestVRAM ||
2015 memorySize > SchemaDefs::MaxGuestVRAM)
2016 return setError(E_INVALIDARG,
2017 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2018 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2019
2020 AutoCaller autoCaller(this);
2021 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2022
2023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2024
2025 HRESULT rc = checkStateDependency(MutableStateDep);
2026 if (FAILED(rc)) return rc;
2027
2028 setModified(IsModified_MachineData);
2029 mHWData.backup();
2030 mHWData->mVRAMSize = memorySize;
2031
2032 return S_OK;
2033}
2034
2035/** @todo this method should not be public */
2036STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
2037{
2038 CheckComArgOutPointerValid(memoryBalloonSize);
2039
2040 AutoCaller autoCaller(this);
2041 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2042
2043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2044
2045 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2046
2047 return S_OK;
2048}
2049
2050/**
2051 * Set the memory balloon size.
2052 *
2053 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2054 * we have to make sure that we never call IGuest from here.
2055 */
2056STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2057{
2058 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2059#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2060 /* check limits */
2061 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2062 return setError(E_INVALIDARG,
2063 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2064 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2065
2066 AutoCaller autoCaller(this);
2067 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2068
2069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2070
2071 setModified(IsModified_MachineData);
2072 mHWData.backup();
2073 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2074
2075 return S_OK;
2076#else
2077 NOREF(memoryBalloonSize);
2078 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2079#endif
2080}
2081
2082STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2083{
2084 CheckComArgOutPointerValid(aEnabled);
2085
2086 AutoCaller autoCaller(this);
2087 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2088
2089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2090
2091 *aEnabled = mHWData->mPageFusionEnabled;
2092 return S_OK;
2093}
2094
2095STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2096{
2097#ifdef VBOX_WITH_PAGE_SHARING
2098 AutoCaller autoCaller(this);
2099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2100
2101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2102
2103 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2104 setModified(IsModified_MachineData);
2105 mHWData.backup();
2106 mHWData->mPageFusionEnabled = aEnabled;
2107 return S_OK;
2108#else
2109 NOREF(aEnabled);
2110 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2111#endif
2112}
2113
2114STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2115{
2116 CheckComArgOutPointerValid(aEnabled);
2117
2118 AutoCaller autoCaller(this);
2119 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2120
2121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2122
2123 *aEnabled = mHWData->mAccelerate3DEnabled;
2124
2125 return S_OK;
2126}
2127
2128STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2129{
2130 AutoCaller autoCaller(this);
2131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2132
2133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2134
2135 HRESULT rc = checkStateDependency(MutableStateDep);
2136 if (FAILED(rc)) return rc;
2137
2138 /** @todo check validity! */
2139
2140 setModified(IsModified_MachineData);
2141 mHWData.backup();
2142 mHWData->mAccelerate3DEnabled = enable;
2143
2144 return S_OK;
2145}
2146
2147
2148STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2149{
2150 CheckComArgOutPointerValid(aEnabled);
2151
2152 AutoCaller autoCaller(this);
2153 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2154
2155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2156
2157 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2158
2159 return S_OK;
2160}
2161
2162STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2163{
2164 AutoCaller autoCaller(this);
2165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2166
2167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2168
2169 HRESULT rc = checkStateDependency(MutableStateDep);
2170 if (FAILED(rc)) return rc;
2171
2172 /** @todo check validity! */
2173
2174 setModified(IsModified_MachineData);
2175 mHWData.backup();
2176 mHWData->mAccelerate2DVideoEnabled = enable;
2177
2178 return S_OK;
2179}
2180
2181STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2182{
2183 CheckComArgOutPointerValid(monitorCount);
2184
2185 AutoCaller autoCaller(this);
2186 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2187
2188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2189
2190 *monitorCount = mHWData->mMonitorCount;
2191
2192 return S_OK;
2193}
2194
2195STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2196{
2197 /* make sure monitor count is a sensible number */
2198 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2199 return setError(E_INVALIDARG,
2200 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2201 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2202
2203 AutoCaller autoCaller(this);
2204 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2205
2206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2207
2208 HRESULT rc = checkStateDependency(MutableStateDep);
2209 if (FAILED(rc)) return rc;
2210
2211 setModified(IsModified_MachineData);
2212 mHWData.backup();
2213 mHWData->mMonitorCount = monitorCount;
2214
2215 return S_OK;
2216}
2217
2218STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2219{
2220 CheckComArgOutPointerValid(biosSettings);
2221
2222 AutoCaller autoCaller(this);
2223 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2224
2225 /* mBIOSSettings is constant during life time, no need to lock */
2226 mBIOSSettings.queryInterfaceTo(biosSettings);
2227
2228 return S_OK;
2229}
2230
2231STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2232{
2233 CheckComArgOutPointerValid(aVal);
2234
2235 AutoCaller autoCaller(this);
2236 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2237
2238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2239
2240 switch (property)
2241 {
2242 case CPUPropertyType_PAE:
2243 *aVal = mHWData->mPAEEnabled;
2244 break;
2245
2246 case CPUPropertyType_Synthetic:
2247 *aVal = mHWData->mSyntheticCpu;
2248 break;
2249
2250 case CPUPropertyType_LongMode:
2251 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2252 *aVal = TRUE;
2253 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2254 *aVal = FALSE;
2255#if HC_ARCH_BITS == 64
2256 else
2257 *aVal = TRUE;
2258#else
2259 else
2260 {
2261 *aVal = FALSE;
2262
2263 ComPtr<IGuestOSType> ptrGuestOSType;
2264 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2265 if (SUCCEEDED(hrc2))
2266 {
2267 BOOL fIs64Bit = FALSE;
2268 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2269 if (SUCCEEDED(hrc2) && fIs64Bit)
2270 {
2271 ComObjPtr<Host> ptrHost = mParent->host();
2272 alock.release();
2273
2274 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2275 if (FAILED(hrc2))
2276 *aVal = FALSE;
2277 }
2278 }
2279 }
2280#endif
2281 break;
2282
2283 default:
2284 return E_INVALIDARG;
2285 }
2286 return S_OK;
2287}
2288
2289STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2290{
2291 AutoCaller autoCaller(this);
2292 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2293
2294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2295
2296 HRESULT rc = checkStateDependency(MutableStateDep);
2297 if (FAILED(rc)) return rc;
2298
2299 switch (property)
2300 {
2301 case CPUPropertyType_PAE:
2302 setModified(IsModified_MachineData);
2303 mHWData.backup();
2304 mHWData->mPAEEnabled = !!aVal;
2305 break;
2306
2307 case CPUPropertyType_Synthetic:
2308 setModified(IsModified_MachineData);
2309 mHWData.backup();
2310 mHWData->mSyntheticCpu = !!aVal;
2311 break;
2312
2313 case CPUPropertyType_LongMode:
2314 setModified(IsModified_MachineData);
2315 mHWData.backup();
2316 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2317 break;
2318
2319 default:
2320 return E_INVALIDARG;
2321 }
2322 return S_OK;
2323}
2324
2325STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2326{
2327 CheckComArgOutPointerValid(aValEax);
2328 CheckComArgOutPointerValid(aValEbx);
2329 CheckComArgOutPointerValid(aValEcx);
2330 CheckComArgOutPointerValid(aValEdx);
2331
2332 AutoCaller autoCaller(this);
2333 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2334
2335 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2336
2337 switch(aId)
2338 {
2339 case 0x0:
2340 case 0x1:
2341 case 0x2:
2342 case 0x3:
2343 case 0x4:
2344 case 0x5:
2345 case 0x6:
2346 case 0x7:
2347 case 0x8:
2348 case 0x9:
2349 case 0xA:
2350 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2351 return E_INVALIDARG;
2352
2353 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2354 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2355 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2356 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2357 break;
2358
2359 case 0x80000000:
2360 case 0x80000001:
2361 case 0x80000002:
2362 case 0x80000003:
2363 case 0x80000004:
2364 case 0x80000005:
2365 case 0x80000006:
2366 case 0x80000007:
2367 case 0x80000008:
2368 case 0x80000009:
2369 case 0x8000000A:
2370 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2371 return E_INVALIDARG;
2372
2373 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2374 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2375 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2376 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2377 break;
2378
2379 default:
2380 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2381 }
2382 return S_OK;
2383}
2384
2385STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2386{
2387 AutoCaller autoCaller(this);
2388 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2389
2390 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2391
2392 HRESULT rc = checkStateDependency(MutableStateDep);
2393 if (FAILED(rc)) return rc;
2394
2395 switch(aId)
2396 {
2397 case 0x0:
2398 case 0x1:
2399 case 0x2:
2400 case 0x3:
2401 case 0x4:
2402 case 0x5:
2403 case 0x6:
2404 case 0x7:
2405 case 0x8:
2406 case 0x9:
2407 case 0xA:
2408 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2409 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2410 setModified(IsModified_MachineData);
2411 mHWData.backup();
2412 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2413 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2414 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2415 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2416 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2417 break;
2418
2419 case 0x80000000:
2420 case 0x80000001:
2421 case 0x80000002:
2422 case 0x80000003:
2423 case 0x80000004:
2424 case 0x80000005:
2425 case 0x80000006:
2426 case 0x80000007:
2427 case 0x80000008:
2428 case 0x80000009:
2429 case 0x8000000A:
2430 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2431 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2432 setModified(IsModified_MachineData);
2433 mHWData.backup();
2434 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2435 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2436 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2437 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2438 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2439 break;
2440
2441 default:
2442 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2443 }
2444 return S_OK;
2445}
2446
2447STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2448{
2449 AutoCaller autoCaller(this);
2450 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2451
2452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2453
2454 HRESULT rc = checkStateDependency(MutableStateDep);
2455 if (FAILED(rc)) return rc;
2456
2457 switch(aId)
2458 {
2459 case 0x0:
2460 case 0x1:
2461 case 0x2:
2462 case 0x3:
2463 case 0x4:
2464 case 0x5:
2465 case 0x6:
2466 case 0x7:
2467 case 0x8:
2468 case 0x9:
2469 case 0xA:
2470 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2471 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2472 setModified(IsModified_MachineData);
2473 mHWData.backup();
2474 /* Invalidate leaf. */
2475 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2476 break;
2477
2478 case 0x80000000:
2479 case 0x80000001:
2480 case 0x80000002:
2481 case 0x80000003:
2482 case 0x80000004:
2483 case 0x80000005:
2484 case 0x80000006:
2485 case 0x80000007:
2486 case 0x80000008:
2487 case 0x80000009:
2488 case 0x8000000A:
2489 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2490 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2491 setModified(IsModified_MachineData);
2492 mHWData.backup();
2493 /* Invalidate leaf. */
2494 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2495 break;
2496
2497 default:
2498 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2499 }
2500 return S_OK;
2501}
2502
2503STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2504{
2505 AutoCaller autoCaller(this);
2506 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2507
2508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2509
2510 HRESULT rc = checkStateDependency(MutableStateDep);
2511 if (FAILED(rc)) return rc;
2512
2513 setModified(IsModified_MachineData);
2514 mHWData.backup();
2515
2516 /* Invalidate all standard leafs. */
2517 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2518 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2519
2520 /* Invalidate all extended leafs. */
2521 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2522 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2523
2524 return S_OK;
2525}
2526
2527STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2528{
2529 CheckComArgOutPointerValid(aVal);
2530
2531 AutoCaller autoCaller(this);
2532 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2533
2534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2535
2536 switch(property)
2537 {
2538 case HWVirtExPropertyType_Enabled:
2539 *aVal = mHWData->mHWVirtExEnabled;
2540 break;
2541
2542 case HWVirtExPropertyType_Exclusive:
2543 *aVal = mHWData->mHWVirtExExclusive;
2544 break;
2545
2546 case HWVirtExPropertyType_VPID:
2547 *aVal = mHWData->mHWVirtExVPIDEnabled;
2548 break;
2549
2550 case HWVirtExPropertyType_NestedPaging:
2551 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2552 break;
2553
2554 case HWVirtExPropertyType_UnrestrictedExecution:
2555 *aVal = mHWData->mHWVirtExUXEnabled;
2556 break;
2557
2558 case HWVirtExPropertyType_LargePages:
2559 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2560#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2561 *aVal = FALSE;
2562#endif
2563 break;
2564
2565 case HWVirtExPropertyType_Force:
2566 *aVal = mHWData->mHWVirtExForceEnabled;
2567 break;
2568
2569 default:
2570 return E_INVALIDARG;
2571 }
2572 return S_OK;
2573}
2574
2575STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2576{
2577 AutoCaller autoCaller(this);
2578 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2579
2580 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2581
2582 HRESULT rc = checkStateDependency(MutableStateDep);
2583 if (FAILED(rc)) return rc;
2584
2585 switch(property)
2586 {
2587 case HWVirtExPropertyType_Enabled:
2588 setModified(IsModified_MachineData);
2589 mHWData.backup();
2590 mHWData->mHWVirtExEnabled = !!aVal;
2591 break;
2592
2593 case HWVirtExPropertyType_Exclusive:
2594 setModified(IsModified_MachineData);
2595 mHWData.backup();
2596 mHWData->mHWVirtExExclusive = !!aVal;
2597 break;
2598
2599 case HWVirtExPropertyType_VPID:
2600 setModified(IsModified_MachineData);
2601 mHWData.backup();
2602 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2603 break;
2604
2605 case HWVirtExPropertyType_NestedPaging:
2606 setModified(IsModified_MachineData);
2607 mHWData.backup();
2608 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2609 break;
2610
2611 case HWVirtExPropertyType_UnrestrictedExecution:
2612 setModified(IsModified_MachineData);
2613 mHWData.backup();
2614 mHWData->mHWVirtExUXEnabled = !!aVal;
2615 break;
2616
2617 case HWVirtExPropertyType_LargePages:
2618 setModified(IsModified_MachineData);
2619 mHWData.backup();
2620 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2621 break;
2622
2623 case HWVirtExPropertyType_Force:
2624 setModified(IsModified_MachineData);
2625 mHWData.backup();
2626 mHWData->mHWVirtExForceEnabled = !!aVal;
2627 break;
2628
2629 default:
2630 return E_INVALIDARG;
2631 }
2632
2633 return S_OK;
2634}
2635
2636STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2637{
2638 CheckComArgOutPointerValid(aSnapshotFolder);
2639
2640 AutoCaller autoCaller(this);
2641 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2642
2643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2644
2645 Utf8Str strFullSnapshotFolder;
2646 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2647 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2648
2649 return S_OK;
2650}
2651
2652STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2653{
2654 /* @todo (r=dmik):
2655 * 1. Allow to change the name of the snapshot folder containing snapshots
2656 * 2. Rename the folder on disk instead of just changing the property
2657 * value (to be smart and not to leave garbage). Note that it cannot be
2658 * done here because the change may be rolled back. Thus, the right
2659 * place is #saveSettings().
2660 */
2661
2662 AutoCaller autoCaller(this);
2663 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2664
2665 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2666
2667 HRESULT rc = checkStateDependency(MutableStateDep);
2668 if (FAILED(rc)) return rc;
2669
2670 if (!mData->mCurrentSnapshot.isNull())
2671 return setError(E_FAIL,
2672 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2673
2674 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2675
2676 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2677 if (strSnapshotFolder.isEmpty())
2678 strSnapshotFolder = "Snapshots";
2679 int vrc = calculateFullPath(strSnapshotFolder,
2680 strSnapshotFolder);
2681 if (RT_FAILURE(vrc))
2682 return setError(E_FAIL,
2683 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2684 aSnapshotFolder, vrc);
2685
2686 setModified(IsModified_MachineData);
2687 mUserData.backup();
2688
2689 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2690
2691 return S_OK;
2692}
2693
2694STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2695{
2696 CheckComArgOutSafeArrayPointerValid(aAttachments);
2697
2698 AutoCaller autoCaller(this);
2699 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2700
2701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2702
2703 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2704 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2705
2706 return S_OK;
2707}
2708
2709STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2710{
2711 CheckComArgOutPointerValid(vrdeServer);
2712
2713 AutoCaller autoCaller(this);
2714 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2715
2716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2717
2718 Assert(!!mVRDEServer);
2719 mVRDEServer.queryInterfaceTo(vrdeServer);
2720
2721 return S_OK;
2722}
2723
2724STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2725{
2726 CheckComArgOutPointerValid(audioAdapter);
2727
2728 AutoCaller autoCaller(this);
2729 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2730
2731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2732
2733 mAudioAdapter.queryInterfaceTo(audioAdapter);
2734 return S_OK;
2735}
2736
2737STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers))
2738{
2739#ifdef VBOX_WITH_VUSB
2740 CheckComArgOutPointerValid(aUSBControllers);
2741
2742 AutoCaller autoCaller(this);
2743 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2744
2745 clearError();
2746 MultiResult rc(S_OK);
2747
2748# ifdef VBOX_WITH_USB
2749 rc = mParent->host()->checkUSBProxyService();
2750 if (FAILED(rc)) return rc;
2751# endif
2752
2753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2754
2755 SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data());
2756 ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers));
2757 return S_OK;
2758#else
2759 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2760 * extended error info to indicate that USB is simply not available
2761 * (w/o treating it as a failure), for example, as in OSE */
2762 NOREF(aUSBControllers);
2763 ReturnComNotImplemented();
2764#endif /* VBOX_WITH_VUSB */
2765}
2766
2767STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters)
2768{
2769#ifdef VBOX_WITH_VUSB
2770 CheckComArgOutPointerValid(aUSBDeviceFilters);
2771
2772 AutoCaller autoCaller(this);
2773 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2774
2775 clearError();
2776 MultiResult rc(S_OK);
2777
2778# ifdef VBOX_WITH_USB
2779 rc = mParent->host()->checkUSBProxyService();
2780 if (FAILED(rc)) return rc;
2781# endif
2782
2783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2784
2785 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters);
2786#else
2787 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2788 * extended error info to indicate that USB is simply not available
2789 * (w/o treating it as a failure), for example, as in OSE */
2790 NOREF(aUSBDeviceFilters);
2791 ReturnComNotImplemented();
2792#endif /* VBOX_WITH_VUSB */
2793}
2794
2795STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2796{
2797 CheckComArgOutPointerValid(aFilePath);
2798
2799 AutoLimitedCaller autoCaller(this);
2800 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2801
2802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2803
2804 mData->m_strConfigFileFull.cloneTo(aFilePath);
2805 return S_OK;
2806}
2807
2808STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2809{
2810 CheckComArgOutPointerValid(aModified);
2811
2812 AutoCaller autoCaller(this);
2813 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2814
2815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2816
2817 HRESULT rc = checkStateDependency(MutableStateDep);
2818 if (FAILED(rc)) return rc;
2819
2820 if (!mData->pMachineConfigFile->fileExists())
2821 // this is a new machine, and no config file exists yet:
2822 *aModified = TRUE;
2823 else
2824 *aModified = (mData->flModifications != 0);
2825
2826 return S_OK;
2827}
2828
2829STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2830{
2831 CheckComArgOutPointerValid(aSessionState);
2832
2833 AutoCaller autoCaller(this);
2834 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2835
2836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2837
2838 *aSessionState = mData->mSession.mState;
2839
2840 return S_OK;
2841}
2842
2843STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2844{
2845 CheckComArgOutPointerValid(aSessionType);
2846
2847 AutoCaller autoCaller(this);
2848 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2849
2850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2851
2852 mData->mSession.mType.cloneTo(aSessionType);
2853
2854 return S_OK;
2855}
2856
2857STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2858{
2859 CheckComArgOutPointerValid(aSessionPID);
2860
2861 AutoCaller autoCaller(this);
2862 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2863
2864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2865
2866 *aSessionPID = mData->mSession.mPID;
2867
2868 return S_OK;
2869}
2870
2871STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2872{
2873 CheckComArgOutPointerValid(machineState);
2874
2875 AutoCaller autoCaller(this);
2876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2877
2878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2879
2880 *machineState = mData->mMachineState;
2881
2882 return S_OK;
2883}
2884
2885STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2886{
2887 CheckComArgOutPointerValid(aLastStateChange);
2888
2889 AutoCaller autoCaller(this);
2890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2891
2892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2893
2894 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2895
2896 return S_OK;
2897}
2898
2899STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2900{
2901 CheckComArgOutPointerValid(aStateFilePath);
2902
2903 AutoCaller autoCaller(this);
2904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2905
2906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2909
2910 return S_OK;
2911}
2912
2913STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2914{
2915 CheckComArgOutPointerValid(aLogFolder);
2916
2917 AutoCaller autoCaller(this);
2918 AssertComRCReturnRC(autoCaller.rc());
2919
2920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2921
2922 Utf8Str logFolder;
2923 getLogFolder(logFolder);
2924 logFolder.cloneTo(aLogFolder);
2925
2926 return S_OK;
2927}
2928
2929STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2930{
2931 CheckComArgOutPointerValid(aCurrentSnapshot);
2932
2933 AutoCaller autoCaller(this);
2934 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2935
2936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2937
2938 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2939
2940 return S_OK;
2941}
2942
2943STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2944{
2945 CheckComArgOutPointerValid(aSnapshotCount);
2946
2947 AutoCaller autoCaller(this);
2948 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2949
2950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2953 ? 0
2954 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2955
2956 return S_OK;
2957}
2958
2959STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2960{
2961 CheckComArgOutPointerValid(aCurrentStateModified);
2962
2963 AutoCaller autoCaller(this);
2964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2965
2966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2967
2968 /* Note: for machines with no snapshots, we always return FALSE
2969 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2970 * reasons :) */
2971
2972 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2973 ? FALSE
2974 : mData->mCurrentStateModified;
2975
2976 return S_OK;
2977}
2978
2979STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2980{
2981 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2982
2983 AutoCaller autoCaller(this);
2984 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2985
2986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2987
2988 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2989 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2990
2991 return S_OK;
2992}
2993
2994STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2995{
2996 CheckComArgOutPointerValid(aClipboardMode);
2997
2998 AutoCaller autoCaller(this);
2999 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3000
3001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3002
3003 *aClipboardMode = mHWData->mClipboardMode;
3004
3005 return S_OK;
3006}
3007
3008STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
3009{
3010 HRESULT rc = S_OK;
3011
3012 AutoCaller autoCaller(this);
3013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3014
3015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3016
3017 alock.release();
3018 rc = onClipboardModeChange(aClipboardMode);
3019 alock.acquire();
3020 if (FAILED(rc)) return rc;
3021
3022 setModified(IsModified_MachineData);
3023 mHWData.backup();
3024 mHWData->mClipboardMode = aClipboardMode;
3025
3026 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3027 if (Global::IsOnline(mData->mMachineState))
3028 saveSettings(NULL);
3029
3030 return S_OK;
3031}
3032
3033STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
3034{
3035 CheckComArgOutPointerValid(aDragAndDropMode);
3036
3037 AutoCaller autoCaller(this);
3038 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3039
3040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3041
3042 *aDragAndDropMode = mHWData->mDragAndDropMode;
3043
3044 return S_OK;
3045}
3046
3047STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3048{
3049 HRESULT rc = S_OK;
3050
3051 AutoCaller autoCaller(this);
3052 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3053
3054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3055
3056 alock.release();
3057 rc = onDragAndDropModeChange(aDragAndDropMode);
3058 alock.acquire();
3059 if (FAILED(rc)) return rc;
3060
3061 setModified(IsModified_MachineData);
3062 mHWData.backup();
3063 mHWData->mDragAndDropMode = aDragAndDropMode;
3064
3065 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3066 if (Global::IsOnline(mData->mMachineState))
3067 saveSettings(NULL);
3068
3069 return S_OK;
3070}
3071
3072STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3073{
3074 CheckComArgOutPointerValid(aPatterns);
3075
3076 AutoCaller autoCaller(this);
3077 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3078
3079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3080
3081 try
3082 {
3083 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3084 }
3085 catch (...)
3086 {
3087 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3088 }
3089
3090 return S_OK;
3091}
3092
3093STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3094{
3095 AutoCaller autoCaller(this);
3096 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3097
3098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3099
3100 HRESULT rc = checkStateDependency(MutableStateDep);
3101 if (FAILED(rc)) return rc;
3102
3103 setModified(IsModified_MachineData);
3104 mHWData.backup();
3105 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3106 return rc;
3107}
3108
3109STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3110{
3111 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3112
3113 AutoCaller autoCaller(this);
3114 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3115
3116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3117
3118 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3119 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3120
3121 return S_OK;
3122}
3123
3124STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3125{
3126 CheckComArgOutPointerValid(aEnabled);
3127
3128 AutoCaller autoCaller(this);
3129 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3130
3131 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3132
3133 *aEnabled = mUserData->s.fTeleporterEnabled;
3134
3135 return S_OK;
3136}
3137
3138STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3139{
3140 AutoCaller autoCaller(this);
3141 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3142
3143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3144
3145 /* Only allow it to be set to true when PoweredOff or Aborted.
3146 (Clearing it is always permitted.) */
3147 if ( aEnabled
3148 && mData->mRegistered
3149 && ( !isSessionMachine()
3150 || ( mData->mMachineState != MachineState_PoweredOff
3151 && mData->mMachineState != MachineState_Teleported
3152 && mData->mMachineState != MachineState_Aborted
3153 )
3154 )
3155 )
3156 return setError(VBOX_E_INVALID_VM_STATE,
3157 tr("The machine is not powered off (state is %s)"),
3158 Global::stringifyMachineState(mData->mMachineState));
3159
3160 setModified(IsModified_MachineData);
3161 mUserData.backup();
3162 mUserData->s.fTeleporterEnabled = !!aEnabled;
3163
3164 return S_OK;
3165}
3166
3167STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3168{
3169 CheckComArgOutPointerValid(aPort);
3170
3171 AutoCaller autoCaller(this);
3172 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3173
3174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3175
3176 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3177
3178 return S_OK;
3179}
3180
3181STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3182{
3183 if (aPort >= _64K)
3184 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3185
3186 AutoCaller autoCaller(this);
3187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3188
3189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3190
3191 HRESULT rc = checkStateDependency(MutableStateDep);
3192 if (FAILED(rc)) return rc;
3193
3194 setModified(IsModified_MachineData);
3195 mUserData.backup();
3196 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3197
3198 return S_OK;
3199}
3200
3201STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3202{
3203 CheckComArgOutPointerValid(aAddress);
3204
3205 AutoCaller autoCaller(this);
3206 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3207
3208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3209
3210 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3211
3212 return S_OK;
3213}
3214
3215STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3216{
3217 AutoCaller autoCaller(this);
3218 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3219
3220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3221
3222 HRESULT rc = checkStateDependency(MutableStateDep);
3223 if (FAILED(rc)) return rc;
3224
3225 setModified(IsModified_MachineData);
3226 mUserData.backup();
3227 mUserData->s.strTeleporterAddress = aAddress;
3228
3229 return S_OK;
3230}
3231
3232STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3233{
3234 CheckComArgOutPointerValid(aPassword);
3235
3236 AutoCaller autoCaller(this);
3237 HRESULT hrc = autoCaller.rc();
3238 if (SUCCEEDED(hrc))
3239 {
3240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3241 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3242 }
3243
3244 return hrc;
3245}
3246
3247STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3248{
3249 /*
3250 * Hash the password first.
3251 */
3252 Utf8Str strPassword(aPassword);
3253 if (!strPassword.isEmpty())
3254 {
3255 if (VBoxIsPasswordHashed(&strPassword))
3256 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3257 VBoxHashPassword(&strPassword);
3258 }
3259
3260 /*
3261 * Do the update.
3262 */
3263 AutoCaller autoCaller(this);
3264 HRESULT hrc = autoCaller.rc();
3265 if (SUCCEEDED(hrc))
3266 {
3267 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3268 hrc = checkStateDependency(MutableStateDep);
3269 if (SUCCEEDED(hrc))
3270 {
3271 setModified(IsModified_MachineData);
3272 mUserData.backup();
3273 mUserData->s.strTeleporterPassword = strPassword;
3274 }
3275 }
3276
3277 return hrc;
3278}
3279
3280STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3281{
3282 CheckComArgOutPointerValid(aState);
3283
3284 AutoCaller autoCaller(this);
3285 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3286
3287 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3288
3289 *aState = mUserData->s.enmFaultToleranceState;
3290 return S_OK;
3291}
3292
3293STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3294{
3295 AutoCaller autoCaller(this);
3296 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3297
3298 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3299
3300 /* @todo deal with running state change. */
3301 HRESULT rc = checkStateDependency(MutableStateDep);
3302 if (FAILED(rc)) return rc;
3303
3304 setModified(IsModified_MachineData);
3305 mUserData.backup();
3306 mUserData->s.enmFaultToleranceState = aState;
3307 return S_OK;
3308}
3309
3310STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3311{
3312 CheckComArgOutPointerValid(aAddress);
3313
3314 AutoCaller autoCaller(this);
3315 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3316
3317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3318
3319 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3320 return S_OK;
3321}
3322
3323STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3324{
3325 AutoCaller autoCaller(this);
3326 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3327
3328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3329
3330 /* @todo deal with running state change. */
3331 HRESULT rc = checkStateDependency(MutableStateDep);
3332 if (FAILED(rc)) return rc;
3333
3334 setModified(IsModified_MachineData);
3335 mUserData.backup();
3336 mUserData->s.strFaultToleranceAddress = aAddress;
3337 return S_OK;
3338}
3339
3340STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3341{
3342 CheckComArgOutPointerValid(aPort);
3343
3344 AutoCaller autoCaller(this);
3345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3346
3347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3348
3349 *aPort = mUserData->s.uFaultTolerancePort;
3350 return S_OK;
3351}
3352
3353STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3354{
3355 AutoCaller autoCaller(this);
3356 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3357
3358 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3359
3360 /* @todo deal with running state change. */
3361 HRESULT rc = checkStateDependency(MutableStateDep);
3362 if (FAILED(rc)) return rc;
3363
3364 setModified(IsModified_MachineData);
3365 mUserData.backup();
3366 mUserData->s.uFaultTolerancePort = aPort;
3367 return S_OK;
3368}
3369
3370STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3371{
3372 CheckComArgOutPointerValid(aPassword);
3373
3374 AutoCaller autoCaller(this);
3375 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3376
3377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3378
3379 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3380
3381 return S_OK;
3382}
3383
3384STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3385{
3386 AutoCaller autoCaller(this);
3387 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3388
3389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3390
3391 /* @todo deal with running state change. */
3392 HRESULT rc = checkStateDependency(MutableStateDep);
3393 if (FAILED(rc)) return rc;
3394
3395 setModified(IsModified_MachineData);
3396 mUserData.backup();
3397 mUserData->s.strFaultTolerancePassword = aPassword;
3398
3399 return S_OK;
3400}
3401
3402STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3403{
3404 CheckComArgOutPointerValid(aInterval);
3405
3406 AutoCaller autoCaller(this);
3407 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3408
3409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3410
3411 *aInterval = mUserData->s.uFaultToleranceInterval;
3412 return S_OK;
3413}
3414
3415STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3416{
3417 AutoCaller autoCaller(this);
3418 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3419
3420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3421
3422 /* @todo deal with running state change. */
3423 HRESULT rc = checkStateDependency(MutableStateDep);
3424 if (FAILED(rc)) return rc;
3425
3426 setModified(IsModified_MachineData);
3427 mUserData.backup();
3428 mUserData->s.uFaultToleranceInterval = aInterval;
3429 return S_OK;
3430}
3431
3432STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3433{
3434 CheckComArgOutPointerValid(aEnabled);
3435
3436 AutoCaller autoCaller(this);
3437 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3438
3439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3440
3441 *aEnabled = mUserData->s.fRTCUseUTC;
3442
3443 return S_OK;
3444}
3445
3446STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3447{
3448 AutoCaller autoCaller(this);
3449 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3450
3451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3452
3453 /* Only allow it to be set to true when PoweredOff or Aborted.
3454 (Clearing it is always permitted.) */
3455 if ( aEnabled
3456 && mData->mRegistered
3457 && ( !isSessionMachine()
3458 || ( mData->mMachineState != MachineState_PoweredOff
3459 && mData->mMachineState != MachineState_Teleported
3460 && mData->mMachineState != MachineState_Aborted
3461 )
3462 )
3463 )
3464 return setError(VBOX_E_INVALID_VM_STATE,
3465 tr("The machine is not powered off (state is %s)"),
3466 Global::stringifyMachineState(mData->mMachineState));
3467
3468 setModified(IsModified_MachineData);
3469 mUserData.backup();
3470 mUserData->s.fRTCUseUTC = !!aEnabled;
3471
3472 return S_OK;
3473}
3474
3475STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3476{
3477 CheckComArgOutPointerValid(aEnabled);
3478
3479 AutoCaller autoCaller(this);
3480 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3481
3482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3483
3484 *aEnabled = mHWData->mIOCacheEnabled;
3485
3486 return S_OK;
3487}
3488
3489STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3490{
3491 AutoCaller autoCaller(this);
3492 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3493
3494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3495
3496 HRESULT rc = checkStateDependency(MutableStateDep);
3497 if (FAILED(rc)) return rc;
3498
3499 setModified(IsModified_MachineData);
3500 mHWData.backup();
3501 mHWData->mIOCacheEnabled = aEnabled;
3502
3503 return S_OK;
3504}
3505
3506STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3507{
3508 CheckComArgOutPointerValid(aIOCacheSize);
3509
3510 AutoCaller autoCaller(this);
3511 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3512
3513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3514
3515 *aIOCacheSize = mHWData->mIOCacheSize;
3516
3517 return S_OK;
3518}
3519
3520STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3521{
3522 AutoCaller autoCaller(this);
3523 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3524
3525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3526
3527 HRESULT rc = checkStateDependency(MutableStateDep);
3528 if (FAILED(rc)) return rc;
3529
3530 setModified(IsModified_MachineData);
3531 mHWData.backup();
3532 mHWData->mIOCacheSize = aIOCacheSize;
3533
3534 return S_OK;
3535}
3536
3537
3538/**
3539 * @note Locks objects!
3540 */
3541STDMETHODIMP Machine::LockMachine(ISession *aSession,
3542 LockType_T lockType)
3543{
3544 CheckComArgNotNull(aSession);
3545
3546 AutoCaller autoCaller(this);
3547 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3548
3549 /* check the session state */
3550 SessionState_T state;
3551 HRESULT rc = aSession->COMGETTER(State)(&state);
3552 if (FAILED(rc)) return rc;
3553
3554 if (state != SessionState_Unlocked)
3555 return setError(VBOX_E_INVALID_OBJECT_STATE,
3556 tr("The given session is busy"));
3557
3558 // get the client's IInternalSessionControl interface
3559 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3560 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3561 E_INVALIDARG);
3562
3563 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3564
3565 if (!mData->mRegistered)
3566 return setError(E_UNEXPECTED,
3567 tr("The machine '%s' is not registered"),
3568 mUserData->s.strName.c_str());
3569
3570 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3571
3572 SessionState_T oldState = mData->mSession.mState;
3573 /* Hack: in case the session is closing and there is a progress object
3574 * which allows waiting for the session to be closed, take the opportunity
3575 * and do a limited wait (max. 1 second). This helps a lot when the system
3576 * is busy and thus session closing can take a little while. */
3577 if ( mData->mSession.mState == SessionState_Unlocking
3578 && mData->mSession.mProgress)
3579 {
3580 alock.release();
3581 mData->mSession.mProgress->WaitForCompletion(1000);
3582 alock.acquire();
3583 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3584 }
3585
3586 // try again now
3587 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3588 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3589 )
3590 {
3591 // OK, share the session... we are now dealing with three processes:
3592 // 1) VBoxSVC (where this code runs);
3593 // 2) process C: the caller's client process (who wants a shared session);
3594 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3595
3596 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3597 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3598 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3599 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3600 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3601
3602 /*
3603 * Release the lock before calling the client process. It's safe here
3604 * since the only thing to do after we get the lock again is to add
3605 * the remote control to the list (which doesn't directly influence
3606 * anything).
3607 */
3608 alock.release();
3609
3610 // get the console of the session holding the write lock (this is a remote call)
3611 ComPtr<IConsole> pConsoleW;
3612 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3613 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3614 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3615 if (FAILED(rc))
3616 // the failure may occur w/o any error info (from RPC), so provide one
3617 return setError(VBOX_E_VM_ERROR,
3618 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3619
3620 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3621
3622 // share the session machine and W's console with the caller's session
3623 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3624 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3625 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3626
3627 if (FAILED(rc))
3628 // the failure may occur w/o any error info (from RPC), so provide one
3629 return setError(VBOX_E_VM_ERROR,
3630 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3631 alock.acquire();
3632
3633 // need to revalidate the state after acquiring the lock again
3634 if (mData->mSession.mState != SessionState_Locked)
3635 {
3636 pSessionControl->Uninitialize();
3637 return setError(VBOX_E_INVALID_SESSION_STATE,
3638 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3639 mUserData->s.strName.c_str());
3640 }
3641
3642 // add the caller's session to the list
3643 mData->mSession.mRemoteControls.push_back(pSessionControl);
3644 }
3645 else if ( mData->mSession.mState == SessionState_Locked
3646 || mData->mSession.mState == SessionState_Unlocking
3647 )
3648 {
3649 // sharing not permitted, or machine still unlocking:
3650 return setError(VBOX_E_INVALID_OBJECT_STATE,
3651 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3652 mUserData->s.strName.c_str());
3653 }
3654 else
3655 {
3656 // machine is not locked: then write-lock the machine (create the session machine)
3657
3658 // must not be busy
3659 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3660
3661 // get the caller's session PID
3662 RTPROCESS pid = NIL_RTPROCESS;
3663 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3664 pSessionControl->GetPID((ULONG*)&pid);
3665 Assert(pid != NIL_RTPROCESS);
3666
3667 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3668
3669 if (fLaunchingVMProcess)
3670 {
3671 // this machine is awaiting for a spawning session to be opened:
3672 // then the calling process must be the one that got started by
3673 // LaunchVMProcess()
3674
3675 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3676 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3677
3678 if (mData->mSession.mPID != pid)
3679 return setError(E_ACCESSDENIED,
3680 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3681 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3682 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3683 }
3684
3685 // create the mutable SessionMachine from the current machine
3686 ComObjPtr<SessionMachine> sessionMachine;
3687 sessionMachine.createObject();
3688 rc = sessionMachine->init(this);
3689 AssertComRC(rc);
3690
3691 /* NOTE: doing return from this function after this point but
3692 * before the end is forbidden since it may call SessionMachine::uninit()
3693 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3694 * lock while still holding the Machine lock in alock so that a deadlock
3695 * is possible due to the wrong lock order. */
3696
3697 if (SUCCEEDED(rc))
3698 {
3699 /*
3700 * Set the session state to Spawning to protect against subsequent
3701 * attempts to open a session and to unregister the machine after
3702 * we release the lock.
3703 */
3704 SessionState_T origState = mData->mSession.mState;
3705 mData->mSession.mState = SessionState_Spawning;
3706
3707 /* Get the client token ID to be passed to the client process */
3708 Utf8Str strTokenId;
3709 sessionMachine->getTokenId(strTokenId);
3710 Assert(!strTokenId.isEmpty());
3711
3712 /*
3713 * Release the lock before calling the client process -- it will call
3714 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3715 * because the state is Spawning, so that LaunchVMProcess() and
3716 * LockMachine() calls will fail. This method, called before we
3717 * acquire the lock again, will fail because of the wrong PID.
3718 *
3719 * Note that mData->mSession.mRemoteControls accessed outside
3720 * the lock may not be modified when state is Spawning, so it's safe.
3721 */
3722 alock.release();
3723
3724 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3725 rc = pSessionControl->AssignMachine(sessionMachine, lockType, Bstr(strTokenId).raw());
3726 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3727
3728 /* The failure may occur w/o any error info (from RPC), so provide one */
3729 if (FAILED(rc))
3730 setError(VBOX_E_VM_ERROR,
3731 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3732
3733 if ( SUCCEEDED(rc)
3734 && fLaunchingVMProcess
3735 )
3736 {
3737 /* complete the remote session initialization */
3738
3739 /* get the console from the direct session */
3740 ComPtr<IConsole> console;
3741 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3742 ComAssertComRC(rc);
3743
3744 if (SUCCEEDED(rc) && !console)
3745 {
3746 ComAssert(!!console);
3747 rc = E_FAIL;
3748 }
3749
3750 /* assign machine & console to the remote session */
3751 if (SUCCEEDED(rc))
3752 {
3753 /*
3754 * after LaunchVMProcess(), the first and the only
3755 * entry in remoteControls is that remote session
3756 */
3757 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3758 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3759 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3760
3761 /* The failure may occur w/o any error info (from RPC), so provide one */
3762 if (FAILED(rc))
3763 setError(VBOX_E_VM_ERROR,
3764 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3765 }
3766
3767 if (FAILED(rc))
3768 pSessionControl->Uninitialize();
3769 }
3770
3771 /* acquire the lock again */
3772 alock.acquire();
3773
3774 /* Restore the session state */
3775 mData->mSession.mState = origState;
3776 }
3777
3778 // finalize spawning anyway (this is why we don't return on errors above)
3779 if (fLaunchingVMProcess)
3780 {
3781 /* Note that the progress object is finalized later */
3782 /** @todo Consider checking mData->mSession.mProgress for cancellation
3783 * around here. */
3784
3785 /* We don't reset mSession.mPID here because it is necessary for
3786 * SessionMachine::uninit() to reap the child process later. */
3787
3788 if (FAILED(rc))
3789 {
3790 /* Close the remote session, remove the remote control from the list
3791 * and reset session state to Closed (@note keep the code in sync
3792 * with the relevant part in checkForSpawnFailure()). */
3793
3794 Assert(mData->mSession.mRemoteControls.size() == 1);
3795 if (mData->mSession.mRemoteControls.size() == 1)
3796 {
3797 ErrorInfoKeeper eik;
3798 mData->mSession.mRemoteControls.front()->Uninitialize();
3799 }
3800
3801 mData->mSession.mRemoteControls.clear();
3802 mData->mSession.mState = SessionState_Unlocked;
3803 }
3804 }
3805 else
3806 {
3807 /* memorize PID of the directly opened session */
3808 if (SUCCEEDED(rc))
3809 mData->mSession.mPID = pid;
3810 }
3811
3812 if (SUCCEEDED(rc))
3813 {
3814 /* memorize the direct session control and cache IUnknown for it */
3815 mData->mSession.mDirectControl = pSessionControl;
3816 mData->mSession.mState = SessionState_Locked;
3817 /* associate the SessionMachine with this Machine */
3818 mData->mSession.mMachine = sessionMachine;
3819
3820 /* request an IUnknown pointer early from the remote party for later
3821 * identity checks (it will be internally cached within mDirectControl
3822 * at least on XPCOM) */
3823 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3824 NOREF(unk);
3825 }
3826
3827 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3828 * would break the lock order */
3829 alock.release();
3830
3831 /* uninitialize the created session machine on failure */
3832 if (FAILED(rc))
3833 sessionMachine->uninit();
3834
3835 }
3836
3837 if (SUCCEEDED(rc))
3838 {
3839 /*
3840 * tell the client watcher thread to update the set of
3841 * machines that have open sessions
3842 */
3843 mParent->updateClientWatcher();
3844
3845 if (oldState != SessionState_Locked)
3846 /* fire an event */
3847 mParent->onSessionStateChange(getId(), SessionState_Locked);
3848 }
3849
3850 return rc;
3851}
3852
3853/**
3854 * @note Locks objects!
3855 */
3856STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3857 IN_BSTR aFrontend,
3858 IN_BSTR aEnvironment,
3859 IProgress **aProgress)
3860{
3861 CheckComArgStr(aFrontend);
3862 Utf8Str strFrontend(aFrontend);
3863 Utf8Str strEnvironment(aEnvironment);
3864 /* "emergencystop" doesn't need the session, so skip the checks/interface
3865 * retrieval. This code doesn't quite fit in here, but introducing a
3866 * special API method would be even more effort, and would require explicit
3867 * support by every API client. It's better to hide the feature a bit. */
3868 if (strFrontend != "emergencystop")
3869 CheckComArgNotNull(aSession);
3870 CheckComArgOutPointerValid(aProgress);
3871
3872 AutoCaller autoCaller(this);
3873 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3874
3875 HRESULT rc = S_OK;
3876 if (strFrontend.isEmpty())
3877 {
3878 Bstr bstrFrontend;
3879 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3880 if (FAILED(rc))
3881 return rc;
3882 strFrontend = bstrFrontend;
3883 if (strFrontend.isEmpty())
3884 {
3885 ComPtr<ISystemProperties> systemProperties;
3886 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3887 if (FAILED(rc))
3888 return rc;
3889 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3890 if (FAILED(rc))
3891 return rc;
3892 strFrontend = bstrFrontend;
3893 }
3894 /* paranoia - emergencystop is not a valid default */
3895 if (strFrontend == "emergencystop")
3896 strFrontend = Utf8Str::Empty;
3897 }
3898 /* default frontend: Qt GUI */
3899 if (strFrontend.isEmpty())
3900 strFrontend = "GUI/Qt";
3901
3902 if (strFrontend != "emergencystop")
3903 {
3904 /* check the session state */
3905 SessionState_T state;
3906 rc = aSession->COMGETTER(State)(&state);
3907 if (FAILED(rc))
3908 return rc;
3909
3910 if (state != SessionState_Unlocked)
3911 return setError(VBOX_E_INVALID_OBJECT_STATE,
3912 tr("The given session is busy"));
3913
3914 /* get the IInternalSessionControl interface */
3915 ComPtr<IInternalSessionControl> control(aSession);
3916 ComAssertMsgRet(!control.isNull(),
3917 ("No IInternalSessionControl interface"),
3918 E_INVALIDARG);
3919
3920 /* get the teleporter enable state for the progress object init. */
3921 BOOL fTeleporterEnabled;
3922 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3923 if (FAILED(rc))
3924 return rc;
3925
3926 /* create a progress object */
3927 ComObjPtr<ProgressProxy> progress;
3928 progress.createObject();
3929 rc = progress->init(mParent,
3930 static_cast<IMachine*>(this),
3931 Bstr(tr("Starting VM")).raw(),
3932 TRUE /* aCancelable */,
3933 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3934 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3935 2 /* uFirstOperationWeight */,
3936 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3937
3938 if (SUCCEEDED(rc))
3939 {
3940 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3941 if (SUCCEEDED(rc))
3942 {
3943 progress.queryInterfaceTo(aProgress);
3944
3945 /* signal the client watcher thread */
3946 mParent->updateClientWatcher();
3947
3948 /* fire an event */
3949 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3950 }
3951 }
3952 }
3953 else
3954 {
3955 /* no progress object - either instant success or failure */
3956 *aProgress = NULL;
3957
3958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3959
3960 if (mData->mSession.mState != SessionState_Locked)
3961 return setError(VBOX_E_INVALID_OBJECT_STATE,
3962 tr("The machine '%s' is not locked by a session"),
3963 mUserData->s.strName.c_str());
3964
3965 /* must have a VM process associated - do not kill normal API clients
3966 * with an open session */
3967 if (!Global::IsOnline(mData->mMachineState))
3968 return setError(VBOX_E_INVALID_OBJECT_STATE,
3969 tr("The machine '%s' does not have a VM process"),
3970 mUserData->s.strName.c_str());
3971
3972 /* forcibly terminate the VM process */
3973 if (mData->mSession.mPID != NIL_RTPROCESS)
3974 RTProcTerminate(mData->mSession.mPID);
3975
3976 /* signal the client watcher thread, as most likely the client has
3977 * been terminated */
3978 mParent->updateClientWatcher();
3979 }
3980
3981 return rc;
3982}
3983
3984STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3985{
3986 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3987 return setError(E_INVALIDARG,
3988 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3989 aPosition, SchemaDefs::MaxBootPosition);
3990
3991 if (aDevice == DeviceType_USB)
3992 return setError(E_NOTIMPL,
3993 tr("Booting from USB device is currently not supported"));
3994
3995 AutoCaller autoCaller(this);
3996 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3997
3998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3999
4000 HRESULT rc = checkStateDependency(MutableStateDep);
4001 if (FAILED(rc)) return rc;
4002
4003 setModified(IsModified_MachineData);
4004 mHWData.backup();
4005 mHWData->mBootOrder[aPosition - 1] = aDevice;
4006
4007 return S_OK;
4008}
4009
4010STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
4011{
4012 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
4013 return setError(E_INVALIDARG,
4014 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
4015 aPosition, SchemaDefs::MaxBootPosition);
4016
4017 AutoCaller autoCaller(this);
4018 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4019
4020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4021
4022 *aDevice = mHWData->mBootOrder[aPosition - 1];
4023
4024 return S_OK;
4025}
4026
4027STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
4028 LONG aControllerPort,
4029 LONG aDevice,
4030 DeviceType_T aType,
4031 IMedium *aMedium)
4032{
4033 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4034 aControllerName, aControllerPort, aDevice, aType, aMedium));
4035
4036 CheckComArgStrNotEmptyOrNull(aControllerName);
4037
4038 AutoCaller autoCaller(this);
4039 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4040
4041 // request the host lock first, since might be calling Host methods for getting host drives;
4042 // next, protect the media tree all the while we're in here, as well as our member variables
4043 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
4044 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4045
4046 HRESULT rc = checkStateDependency(MutableStateDep);
4047 if (FAILED(rc)) return rc;
4048
4049 /// @todo NEWMEDIA implicit machine registration
4050 if (!mData->mRegistered)
4051 return setError(VBOX_E_INVALID_OBJECT_STATE,
4052 tr("Cannot attach storage devices to an unregistered machine"));
4053
4054 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4055
4056 /* Check for an existing controller. */
4057 ComObjPtr<StorageController> ctl;
4058 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4059 if (FAILED(rc)) return rc;
4060
4061 StorageControllerType_T ctrlType;
4062 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4063 if (FAILED(rc))
4064 return setError(E_FAIL,
4065 tr("Could not get type of controller '%ls'"),
4066 aControllerName);
4067
4068 bool fSilent = false;
4069 Utf8Str strReconfig;
4070
4071 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4072 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4073 if (FAILED(rc))
4074 return rc;
4075 if ( mData->mMachineState == MachineState_Paused
4076 && strReconfig == "1")
4077 fSilent = true;
4078
4079 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4080 bool fHotplug = false;
4081 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4082 fHotplug = true;
4083
4084 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4085 return setError(VBOX_E_INVALID_VM_STATE,
4086 tr("Controller '%ls' does not support hotplugging"),
4087 aControllerName);
4088
4089 // check that the port and device are not out of range
4090 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
4091 if (FAILED(rc)) return rc;
4092
4093 /* check if the device slot is already busy */
4094 MediumAttachment *pAttachTemp;
4095 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4096 aControllerName,
4097 aControllerPort,
4098 aDevice)))
4099 {
4100 Medium *pMedium = pAttachTemp->getMedium();
4101 if (pMedium)
4102 {
4103 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4104 return setError(VBOX_E_OBJECT_IN_USE,
4105 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4106 pMedium->getLocationFull().c_str(),
4107 aControllerPort,
4108 aDevice,
4109 aControllerName);
4110 }
4111 else
4112 return setError(VBOX_E_OBJECT_IN_USE,
4113 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4114 aControllerPort, aDevice, aControllerName);
4115 }
4116
4117 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4118 if (aMedium && medium.isNull())
4119 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4120
4121 AutoCaller mediumCaller(medium);
4122 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4123
4124 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4125
4126 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4127 && !medium.isNull()
4128 )
4129 return setError(VBOX_E_OBJECT_IN_USE,
4130 tr("Medium '%s' is already attached to this virtual machine"),
4131 medium->getLocationFull().c_str());
4132
4133 if (!medium.isNull())
4134 {
4135 MediumType_T mtype = medium->getType();
4136 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4137 // For DVDs it's not written to the config file, so needs no global config
4138 // version bump. For floppies it's a new attribute "type", which is ignored
4139 // by older VirtualBox version, so needs no global config version bump either.
4140 // For hard disks this type is not accepted.
4141 if (mtype == MediumType_MultiAttach)
4142 {
4143 // This type is new with VirtualBox 4.0 and therefore requires settings
4144 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4145 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4146 // two reasons: The medium type is a property of the media registry tree, which
4147 // can reside in the global config file (for pre-4.0 media); we would therefore
4148 // possibly need to bump the global config version. We don't want to do that though
4149 // because that might make downgrading to pre-4.0 impossible.
4150 // As a result, we can only use these two new types if the medium is NOT in the
4151 // global registry:
4152 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4153 if ( medium->isInRegistry(uuidGlobalRegistry)
4154 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4155 )
4156 return setError(VBOX_E_INVALID_OBJECT_STATE,
4157 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4158 "to machines that were created with VirtualBox 4.0 or later"),
4159 medium->getLocationFull().c_str());
4160 }
4161 }
4162
4163 bool fIndirect = false;
4164 if (!medium.isNull())
4165 fIndirect = medium->isReadOnly();
4166 bool associate = true;
4167
4168 do
4169 {
4170 if ( aType == DeviceType_HardDisk
4171 && mMediaData.isBackedUp())
4172 {
4173 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4174
4175 /* check if the medium was attached to the VM before we started
4176 * changing attachments in which case the attachment just needs to
4177 * be restored */
4178 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4179 {
4180 AssertReturn(!fIndirect, E_FAIL);
4181
4182 /* see if it's the same bus/channel/device */
4183 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4184 {
4185 /* the simplest case: restore the whole attachment
4186 * and return, nothing else to do */
4187 mMediaData->mAttachments.push_back(pAttachTemp);
4188
4189 /* Reattach the medium to the VM. */
4190 if (fHotplug || fSilent)
4191 {
4192 mediumLock.release();
4193 treeLock.release();
4194 alock.release();
4195
4196 MediumLockList *pMediumLockList(new MediumLockList());
4197
4198 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4199 true /* fMediumLockWrite */,
4200 NULL,
4201 *pMediumLockList);
4202 alock.acquire();
4203 if (FAILED(rc))
4204 delete pMediumLockList;
4205 else
4206 {
4207 mData->mSession.mLockedMedia.Unlock();
4208 alock.release();
4209 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4210 mData->mSession.mLockedMedia.Lock();
4211 alock.acquire();
4212 }
4213 alock.release();
4214
4215 if (SUCCEEDED(rc))
4216 {
4217 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4218 /* Remove lock list in case of error. */
4219 if (FAILED(rc))
4220 {
4221 mData->mSession.mLockedMedia.Unlock();
4222 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4223 mData->mSession.mLockedMedia.Lock();
4224 }
4225 }
4226 }
4227
4228 return S_OK;
4229 }
4230
4231 /* bus/channel/device differ; we need a new attachment object,
4232 * but don't try to associate it again */
4233 associate = false;
4234 break;
4235 }
4236 }
4237
4238 /* go further only if the attachment is to be indirect */
4239 if (!fIndirect)
4240 break;
4241
4242 /* perform the so called smart attachment logic for indirect
4243 * attachments. Note that smart attachment is only applicable to base
4244 * hard disks. */
4245
4246 if (medium->getParent().isNull())
4247 {
4248 /* first, investigate the backup copy of the current hard disk
4249 * attachments to make it possible to re-attach existing diffs to
4250 * another device slot w/o losing their contents */
4251 if (mMediaData.isBackedUp())
4252 {
4253 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4254
4255 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4256 uint32_t foundLevel = 0;
4257
4258 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4259 it != oldAtts.end();
4260 ++it)
4261 {
4262 uint32_t level = 0;
4263 MediumAttachment *pAttach = *it;
4264 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4265 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4266 if (pMedium.isNull())
4267 continue;
4268
4269 if (pMedium->getBase(&level) == medium)
4270 {
4271 /* skip the hard disk if its currently attached (we
4272 * cannot attach the same hard disk twice) */
4273 if (findAttachment(mMediaData->mAttachments,
4274 pMedium))
4275 continue;
4276
4277 /* matched device, channel and bus (i.e. attached to the
4278 * same place) will win and immediately stop the search;
4279 * otherwise the attachment that has the youngest
4280 * descendant of medium will be used
4281 */
4282 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4283 {
4284 /* the simplest case: restore the whole attachment
4285 * and return, nothing else to do */
4286 mMediaData->mAttachments.push_back(*it);
4287
4288 /* Reattach the medium to the VM. */
4289 if (fHotplug || fSilent)
4290 {
4291 mediumLock.release();
4292 treeLock.release();
4293 alock.release();
4294
4295 MediumLockList *pMediumLockList(new MediumLockList());
4296
4297 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4298 true /* fMediumLockWrite */,
4299 NULL,
4300 *pMediumLockList);
4301 alock.acquire();
4302 if (FAILED(rc))
4303 delete pMediumLockList;
4304 else
4305 {
4306 mData->mSession.mLockedMedia.Unlock();
4307 alock.release();
4308 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4309 mData->mSession.mLockedMedia.Lock();
4310 alock.acquire();
4311 }
4312 alock.release();
4313
4314 if (SUCCEEDED(rc))
4315 {
4316 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4317 /* Remove lock list in case of error. */
4318 if (FAILED(rc))
4319 {
4320 mData->mSession.mLockedMedia.Unlock();
4321 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4322 mData->mSession.mLockedMedia.Lock();
4323 }
4324 }
4325 }
4326
4327 return S_OK;
4328 }
4329 else if ( foundIt == oldAtts.end()
4330 || level > foundLevel /* prefer younger */
4331 )
4332 {
4333 foundIt = it;
4334 foundLevel = level;
4335 }
4336 }
4337 }
4338
4339 if (foundIt != oldAtts.end())
4340 {
4341 /* use the previously attached hard disk */
4342 medium = (*foundIt)->getMedium();
4343 mediumCaller.attach(medium);
4344 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4345 mediumLock.attach(medium);
4346 /* not implicit, doesn't require association with this VM */
4347 fIndirect = false;
4348 associate = false;
4349 /* go right to the MediumAttachment creation */
4350 break;
4351 }
4352 }
4353
4354 /* must give up the medium lock and medium tree lock as below we
4355 * go over snapshots, which needs a lock with higher lock order. */
4356 mediumLock.release();
4357 treeLock.release();
4358
4359 /* then, search through snapshots for the best diff in the given
4360 * hard disk's chain to base the new diff on */
4361
4362 ComObjPtr<Medium> base;
4363 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4364 while (snap)
4365 {
4366 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4367
4368 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4369
4370 MediumAttachment *pAttachFound = NULL;
4371 uint32_t foundLevel = 0;
4372
4373 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4374 it != snapAtts.end();
4375 ++it)
4376 {
4377 MediumAttachment *pAttach = *it;
4378 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4379 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4380 if (pMedium.isNull())
4381 continue;
4382
4383 uint32_t level = 0;
4384 if (pMedium->getBase(&level) == medium)
4385 {
4386 /* matched device, channel and bus (i.e. attached to the
4387 * same place) will win and immediately stop the search;
4388 * otherwise the attachment that has the youngest
4389 * descendant of medium will be used
4390 */
4391 if ( pAttach->getDevice() == aDevice
4392 && pAttach->getPort() == aControllerPort
4393 && pAttach->getControllerName() == aControllerName
4394 )
4395 {
4396 pAttachFound = pAttach;
4397 break;
4398 }
4399 else if ( !pAttachFound
4400 || level > foundLevel /* prefer younger */
4401 )
4402 {
4403 pAttachFound = pAttach;
4404 foundLevel = level;
4405 }
4406 }
4407 }
4408
4409 if (pAttachFound)
4410 {
4411 base = pAttachFound->getMedium();
4412 break;
4413 }
4414
4415 snap = snap->getParent();
4416 }
4417
4418 /* re-lock medium tree and the medium, as we need it below */
4419 treeLock.acquire();
4420 mediumLock.acquire();
4421
4422 /* found a suitable diff, use it as a base */
4423 if (!base.isNull())
4424 {
4425 medium = base;
4426 mediumCaller.attach(medium);
4427 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4428 mediumLock.attach(medium);
4429 }
4430 }
4431
4432 Utf8Str strFullSnapshotFolder;
4433 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4434
4435 ComObjPtr<Medium> diff;
4436 diff.createObject();
4437 // store this diff in the same registry as the parent
4438 Guid uuidRegistryParent;
4439 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4440 {
4441 // parent image has no registry: this can happen if we're attaching a new immutable
4442 // image that has not yet been attached (medium then points to the base and we're
4443 // creating the diff image for the immutable, and the parent is not yet registered);
4444 // put the parent in the machine registry then
4445 mediumLock.release();
4446 treeLock.release();
4447 alock.release();
4448 addMediumToRegistry(medium);
4449 alock.acquire();
4450 treeLock.acquire();
4451 mediumLock.acquire();
4452 medium->getFirstRegistryMachineId(uuidRegistryParent);
4453 }
4454 rc = diff->init(mParent,
4455 medium->getPreferredDiffFormat(),
4456 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4457 uuidRegistryParent);
4458 if (FAILED(rc)) return rc;
4459
4460 /* Apply the normal locking logic to the entire chain. */
4461 MediumLockList *pMediumLockList(new MediumLockList());
4462 mediumLock.release();
4463 treeLock.release();
4464 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4465 true /* fMediumLockWrite */,
4466 medium,
4467 *pMediumLockList);
4468 treeLock.acquire();
4469 mediumLock.acquire();
4470 if (SUCCEEDED(rc))
4471 {
4472 mediumLock.release();
4473 treeLock.release();
4474 rc = pMediumLockList->Lock();
4475 treeLock.acquire();
4476 mediumLock.acquire();
4477 if (FAILED(rc))
4478 setError(rc,
4479 tr("Could not lock medium when creating diff '%s'"),
4480 diff->getLocationFull().c_str());
4481 else
4482 {
4483 /* will release the lock before the potentially lengthy
4484 * operation, so protect with the special state */
4485 MachineState_T oldState = mData->mMachineState;
4486 setMachineState(MachineState_SettingUp);
4487
4488 mediumLock.release();
4489 treeLock.release();
4490 alock.release();
4491
4492 rc = medium->createDiffStorage(diff,
4493 MediumVariant_Standard,
4494 pMediumLockList,
4495 NULL /* aProgress */,
4496 true /* aWait */);
4497
4498 alock.acquire();
4499 treeLock.acquire();
4500 mediumLock.acquire();
4501
4502 setMachineState(oldState);
4503 }
4504 }
4505
4506 /* Unlock the media and free the associated memory. */
4507 delete pMediumLockList;
4508
4509 if (FAILED(rc)) return rc;
4510
4511 /* use the created diff for the actual attachment */
4512 medium = diff;
4513 mediumCaller.attach(medium);
4514 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4515 mediumLock.attach(medium);
4516 }
4517 while (0);
4518
4519 ComObjPtr<MediumAttachment> attachment;
4520 attachment.createObject();
4521 rc = attachment->init(this,
4522 medium,
4523 aControllerName,
4524 aControllerPort,
4525 aDevice,
4526 aType,
4527 fIndirect,
4528 false /* fPassthrough */,
4529 false /* fTempEject */,
4530 false /* fNonRotational */,
4531 false /* fDiscard */,
4532 Utf8Str::Empty);
4533 if (FAILED(rc)) return rc;
4534
4535 if (associate && !medium.isNull())
4536 {
4537 // as the last step, associate the medium to the VM
4538 rc = medium->addBackReference(mData->mUuid);
4539 // here we can fail because of Deleting, or being in process of creating a Diff
4540 if (FAILED(rc)) return rc;
4541
4542 mediumLock.release();
4543 treeLock.release();
4544 alock.release();
4545 addMediumToRegistry(medium);
4546 alock.acquire();
4547 treeLock.acquire();
4548 mediumLock.acquire();
4549 }
4550
4551 /* success: finally remember the attachment */
4552 setModified(IsModified_Storage);
4553 mMediaData.backup();
4554 mMediaData->mAttachments.push_back(attachment);
4555
4556 mediumLock.release();
4557 treeLock.release();
4558 alock.release();
4559
4560 if (fHotplug || fSilent)
4561 {
4562 if (!medium.isNull())
4563 {
4564 MediumLockList *pMediumLockList(new MediumLockList());
4565
4566 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4567 true /* fMediumLockWrite */,
4568 NULL,
4569 *pMediumLockList);
4570 alock.acquire();
4571 if (FAILED(rc))
4572 delete pMediumLockList;
4573 else
4574 {
4575 mData->mSession.mLockedMedia.Unlock();
4576 alock.release();
4577 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4578 mData->mSession.mLockedMedia.Lock();
4579 alock.acquire();
4580 }
4581 alock.release();
4582 }
4583
4584 if (SUCCEEDED(rc))
4585 {
4586 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4587 /* Remove lock list in case of error. */
4588 if (FAILED(rc))
4589 {
4590 mData->mSession.mLockedMedia.Unlock();
4591 mData->mSession.mLockedMedia.Remove(attachment);
4592 mData->mSession.mLockedMedia.Lock();
4593 }
4594 }
4595 }
4596
4597 mParent->saveModifiedRegistries();
4598
4599 return rc;
4600}
4601
4602STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4603 LONG aDevice)
4604{
4605 CheckComArgStrNotEmptyOrNull(aControllerName);
4606
4607 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4608 aControllerName, aControllerPort, aDevice));
4609
4610 AutoCaller autoCaller(this);
4611 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4612
4613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4614
4615 HRESULT rc = checkStateDependency(MutableStateDep);
4616 if (FAILED(rc)) return rc;
4617
4618 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4619
4620 /* Check for an existing controller. */
4621 ComObjPtr<StorageController> ctl;
4622 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4623 if (FAILED(rc)) return rc;
4624
4625 StorageControllerType_T ctrlType;
4626 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4627 if (FAILED(rc))
4628 return setError(E_FAIL,
4629 tr("Could not get type of controller '%ls'"),
4630 aControllerName);
4631
4632 bool fSilent = false;
4633 Utf8Str strReconfig;
4634
4635 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4636 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4637 if (FAILED(rc))
4638 return rc;
4639 if ( mData->mMachineState == MachineState_Paused
4640 && strReconfig == "1")
4641 fSilent = true;
4642
4643 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4644 bool fHotplug = false;
4645 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4646 fHotplug = true;
4647
4648 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4649 return setError(VBOX_E_INVALID_VM_STATE,
4650 tr("Controller '%ls' does not support hotplugging"),
4651 aControllerName);
4652
4653 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4654 aControllerName,
4655 aControllerPort,
4656 aDevice);
4657 if (!pAttach)
4658 return setError(VBOX_E_OBJECT_NOT_FOUND,
4659 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4660 aDevice, aControllerPort, aControllerName);
4661
4662 /*
4663 * The VM has to detach the device before we delete any implicit diffs.
4664 * If this fails we can roll back without loosing data.
4665 */
4666 if (fHotplug || fSilent)
4667 {
4668 alock.release();
4669 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4670 alock.acquire();
4671 }
4672 if (FAILED(rc)) return rc;
4673
4674 /* If we are here everything went well and we can delete the implicit now. */
4675 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4676
4677 alock.release();
4678
4679 mParent->saveModifiedRegistries();
4680
4681 return rc;
4682}
4683
4684STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4685 LONG aDevice, BOOL aPassthrough)
4686{
4687 CheckComArgStrNotEmptyOrNull(aControllerName);
4688
4689 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4690 aControllerName, aControllerPort, aDevice, aPassthrough));
4691
4692 AutoCaller autoCaller(this);
4693 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4694
4695 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4696
4697 HRESULT rc = checkStateDependency(MutableStateDep);
4698 if (FAILED(rc)) return rc;
4699
4700 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4701
4702 if (Global::IsOnlineOrTransient(mData->mMachineState))
4703 return setError(VBOX_E_INVALID_VM_STATE,
4704 tr("Invalid machine state: %s"),
4705 Global::stringifyMachineState(mData->mMachineState));
4706
4707 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4708 aControllerName,
4709 aControllerPort,
4710 aDevice);
4711 if (!pAttach)
4712 return setError(VBOX_E_OBJECT_NOT_FOUND,
4713 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4714 aDevice, aControllerPort, aControllerName);
4715
4716
4717 setModified(IsModified_Storage);
4718 mMediaData.backup();
4719
4720 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4721
4722 if (pAttach->getType() != DeviceType_DVD)
4723 return setError(E_INVALIDARG,
4724 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4725 aDevice, aControllerPort, aControllerName);
4726 pAttach->updatePassthrough(!!aPassthrough);
4727
4728 return S_OK;
4729}
4730
4731STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4732 LONG aDevice, BOOL aTemporaryEject)
4733{
4734 CheckComArgStrNotEmptyOrNull(aControllerName);
4735
4736 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4737 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4738
4739 AutoCaller autoCaller(this);
4740 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4741
4742 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4743
4744 HRESULT rc = checkStateDependency(MutableStateDep);
4745 if (FAILED(rc)) return rc;
4746
4747 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4748 aControllerName,
4749 aControllerPort,
4750 aDevice);
4751 if (!pAttach)
4752 return setError(VBOX_E_OBJECT_NOT_FOUND,
4753 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4754 aDevice, aControllerPort, aControllerName);
4755
4756
4757 setModified(IsModified_Storage);
4758 mMediaData.backup();
4759
4760 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4761
4762 if (pAttach->getType() != DeviceType_DVD)
4763 return setError(E_INVALIDARG,
4764 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4765 aDevice, aControllerPort, aControllerName);
4766 pAttach->updateTempEject(!!aTemporaryEject);
4767
4768 return S_OK;
4769}
4770
4771STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4772 LONG aDevice, BOOL aNonRotational)
4773{
4774 CheckComArgStrNotEmptyOrNull(aControllerName);
4775
4776 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4777 aControllerName, aControllerPort, aDevice, aNonRotational));
4778
4779 AutoCaller autoCaller(this);
4780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4781
4782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4783
4784 HRESULT rc = checkStateDependency(MutableStateDep);
4785 if (FAILED(rc)) return rc;
4786
4787 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4788
4789 if (Global::IsOnlineOrTransient(mData->mMachineState))
4790 return setError(VBOX_E_INVALID_VM_STATE,
4791 tr("Invalid machine state: %s"),
4792 Global::stringifyMachineState(mData->mMachineState));
4793
4794 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4795 aControllerName,
4796 aControllerPort,
4797 aDevice);
4798 if (!pAttach)
4799 return setError(VBOX_E_OBJECT_NOT_FOUND,
4800 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4801 aDevice, aControllerPort, aControllerName);
4802
4803
4804 setModified(IsModified_Storage);
4805 mMediaData.backup();
4806
4807 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4808
4809 if (pAttach->getType() != DeviceType_HardDisk)
4810 return setError(E_INVALIDARG,
4811 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"),
4812 aDevice, aControllerPort, aControllerName);
4813 pAttach->updateNonRotational(!!aNonRotational);
4814
4815 return S_OK;
4816}
4817
4818STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4819 LONG aDevice, BOOL aDiscard)
4820{
4821 CheckComArgStrNotEmptyOrNull(aControllerName);
4822
4823 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4824 aControllerName, aControllerPort, aDevice, aDiscard));
4825
4826 AutoCaller autoCaller(this);
4827 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4828
4829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4830
4831 HRESULT rc = checkStateDependency(MutableStateDep);
4832 if (FAILED(rc)) return rc;
4833
4834 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4835
4836 if (Global::IsOnlineOrTransient(mData->mMachineState))
4837 return setError(VBOX_E_INVALID_VM_STATE,
4838 tr("Invalid machine state: %s"),
4839 Global::stringifyMachineState(mData->mMachineState));
4840
4841 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4842 aControllerName,
4843 aControllerPort,
4844 aDevice);
4845 if (!pAttach)
4846 return setError(VBOX_E_OBJECT_NOT_FOUND,
4847 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4848 aDevice, aControllerPort, aControllerName);
4849
4850
4851 setModified(IsModified_Storage);
4852 mMediaData.backup();
4853
4854 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4855
4856 if (pAttach->getType() != DeviceType_HardDisk)
4857 return setError(E_INVALIDARG,
4858 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"),
4859 aDevice, aControllerPort, aControllerName);
4860 pAttach->updateDiscard(!!aDiscard);
4861
4862 return S_OK;
4863}
4864
4865STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4866 LONG aDevice)
4867{
4868 int rc = S_OK;
4869 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4870 aControllerName, aControllerPort, aDevice));
4871
4872 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4873
4874 return rc;
4875}
4876
4877STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4878 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4879{
4880 CheckComArgStrNotEmptyOrNull(aControllerName);
4881
4882 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4883 aControllerName, aControllerPort, aDevice));
4884
4885 AutoCaller autoCaller(this);
4886 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4887
4888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4889
4890 HRESULT rc = checkStateDependency(MutableStateDep);
4891 if (FAILED(rc)) return rc;
4892
4893 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4894
4895 if (Global::IsOnlineOrTransient(mData->mMachineState))
4896 return setError(VBOX_E_INVALID_VM_STATE,
4897 tr("Invalid machine state: %s"),
4898 Global::stringifyMachineState(mData->mMachineState));
4899
4900 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4901 aControllerName,
4902 aControllerPort,
4903 aDevice);
4904 if (!pAttach)
4905 return setError(VBOX_E_OBJECT_NOT_FOUND,
4906 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4907 aDevice, aControllerPort, aControllerName);
4908
4909
4910 setModified(IsModified_Storage);
4911 mMediaData.backup();
4912
4913 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4914 if (aBandwidthGroup && group.isNull())
4915 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4916
4917 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4918
4919 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4920 if (strBandwidthGroupOld.isNotEmpty())
4921 {
4922 /* Get the bandwidth group object and release it - this must not fail. */
4923 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4924 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4925 Assert(SUCCEEDED(rc));
4926
4927 pBandwidthGroupOld->release();
4928 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4929 }
4930
4931 if (!group.isNull())
4932 {
4933 group->reference();
4934 pAttach->updateBandwidthGroup(group->getName());
4935 }
4936
4937 return S_OK;
4938}
4939
4940STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4941 LONG aControllerPort,
4942 LONG aDevice,
4943 DeviceType_T aType)
4944{
4945 HRESULT rc = S_OK;
4946
4947 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4948 aControllerName, aControllerPort, aDevice, aType));
4949
4950 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4951
4952 return rc;
4953}
4954
4955
4956
4957STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4958 LONG aControllerPort,
4959 LONG aDevice,
4960 BOOL aForce)
4961{
4962 int rc = S_OK;
4963 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4964 aControllerName, aControllerPort, aForce));
4965
4966 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4967
4968 return rc;
4969}
4970
4971STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4972 LONG aControllerPort,
4973 LONG aDevice,
4974 IMedium *aMedium,
4975 BOOL aForce)
4976{
4977 int rc = S_OK;
4978 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4979 aControllerName, aControllerPort, aDevice, aForce));
4980
4981 CheckComArgStrNotEmptyOrNull(aControllerName);
4982
4983 AutoCaller autoCaller(this);
4984 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4985
4986 // request the host lock first, since might be calling Host methods for getting host drives;
4987 // next, protect the media tree all the while we're in here, as well as our member variables
4988 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4989 this->lockHandle(),
4990 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4991
4992 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4993 aControllerName,
4994 aControllerPort,
4995 aDevice);
4996 if (pAttach.isNull())
4997 return setError(VBOX_E_OBJECT_NOT_FOUND,
4998 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4999 aDevice, aControllerPort, aControllerName);
5000
5001 /* Remember previously mounted medium. The medium before taking the
5002 * backup is not necessarily the same thing. */
5003 ComObjPtr<Medium> oldmedium;
5004 oldmedium = pAttach->getMedium();
5005
5006 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
5007 if (aMedium && pMedium.isNull())
5008 return setError(E_INVALIDARG, "The given medium pointer is invalid");
5009
5010 AutoCaller mediumCaller(pMedium);
5011 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
5012
5013 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5014 if (pMedium)
5015 {
5016 DeviceType_T mediumType = pAttach->getType();
5017 switch (mediumType)
5018 {
5019 case DeviceType_DVD:
5020 case DeviceType_Floppy:
5021 break;
5022
5023 default:
5024 return setError(VBOX_E_INVALID_OBJECT_STATE,
5025 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
5026 aControllerPort,
5027 aDevice,
5028 aControllerName);
5029 }
5030 }
5031
5032 setModified(IsModified_Storage);
5033 mMediaData.backup();
5034
5035 {
5036 // The backup operation makes the pAttach reference point to the
5037 // old settings. Re-get the correct reference.
5038 pAttach = findAttachment(mMediaData->mAttachments,
5039 aControllerName,
5040 aControllerPort,
5041 aDevice);
5042 if (!oldmedium.isNull())
5043 oldmedium->removeBackReference(mData->mUuid);
5044 if (!pMedium.isNull())
5045 {
5046 pMedium->addBackReference(mData->mUuid);
5047
5048 mediumLock.release();
5049 multiLock.release();
5050 addMediumToRegistry(pMedium);
5051 multiLock.acquire();
5052 mediumLock.acquire();
5053 }
5054
5055 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5056 pAttach->updateMedium(pMedium);
5057 }
5058
5059 setModified(IsModified_Storage);
5060
5061 mediumLock.release();
5062 multiLock.release();
5063 rc = onMediumChange(pAttach, aForce);
5064 multiLock.acquire();
5065 mediumLock.acquire();
5066
5067 /* On error roll back this change only. */
5068 if (FAILED(rc))
5069 {
5070 if (!pMedium.isNull())
5071 pMedium->removeBackReference(mData->mUuid);
5072 pAttach = findAttachment(mMediaData->mAttachments,
5073 aControllerName,
5074 aControllerPort,
5075 aDevice);
5076 /* If the attachment is gone in the meantime, bail out. */
5077 if (pAttach.isNull())
5078 return rc;
5079 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5080 if (!oldmedium.isNull())
5081 oldmedium->addBackReference(mData->mUuid);
5082 pAttach->updateMedium(oldmedium);
5083 }
5084
5085 mediumLock.release();
5086 multiLock.release();
5087
5088 mParent->saveModifiedRegistries();
5089
5090 return rc;
5091}
5092
5093STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5094 LONG aControllerPort,
5095 LONG aDevice,
5096 IMedium **aMedium)
5097{
5098 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5099 aControllerName, aControllerPort, aDevice));
5100
5101 CheckComArgStrNotEmptyOrNull(aControllerName);
5102 CheckComArgOutPointerValid(aMedium);
5103
5104 AutoCaller autoCaller(this);
5105 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5106
5107 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5108
5109 *aMedium = NULL;
5110
5111 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5112 aControllerName,
5113 aControllerPort,
5114 aDevice);
5115 if (pAttach.isNull())
5116 return setError(VBOX_E_OBJECT_NOT_FOUND,
5117 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5118 aDevice, aControllerPort, aControllerName);
5119
5120 pAttach->getMedium().queryInterfaceTo(aMedium);
5121
5122 return S_OK;
5123}
5124
5125STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5126{
5127 CheckComArgOutPointerValid(port);
5128 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5129
5130 AutoCaller autoCaller(this);
5131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5132
5133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5134
5135 mSerialPorts[slot].queryInterfaceTo(port);
5136
5137 return S_OK;
5138}
5139
5140STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5141{
5142 CheckComArgOutPointerValid(port);
5143 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5144
5145 AutoCaller autoCaller(this);
5146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5147
5148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5149
5150 mParallelPorts[slot].queryInterfaceTo(port);
5151
5152 return S_OK;
5153}
5154
5155STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5156{
5157 CheckComArgOutPointerValid(adapter);
5158 /* Do not assert if slot is out of range, just return the advertised
5159 status. testdriver/vbox.py triggers this in logVmInfo. */
5160 if (slot >= mNetworkAdapters.size())
5161 return setError(E_INVALIDARG,
5162 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5163 slot, mNetworkAdapters.size());
5164
5165 AutoCaller autoCaller(this);
5166 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5167
5168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5169
5170 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5171
5172 return S_OK;
5173}
5174
5175STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5176{
5177 CheckComArgOutSafeArrayPointerValid(aKeys);
5178
5179 AutoCaller autoCaller(this);
5180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5181
5182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5183
5184 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5185 int i = 0;
5186 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5187 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5188 ++it, ++i)
5189 {
5190 const Utf8Str &strKey = it->first;
5191 strKey.cloneTo(&saKeys[i]);
5192 }
5193 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5194
5195 return S_OK;
5196 }
5197
5198 /**
5199 * @note Locks this object for reading.
5200 */
5201STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5202 BSTR *aValue)
5203{
5204 CheckComArgStrNotEmptyOrNull(aKey);
5205 CheckComArgOutPointerValid(aValue);
5206
5207 AutoCaller autoCaller(this);
5208 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5209
5210 /* start with nothing found */
5211 Bstr bstrResult("");
5212
5213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5214
5215 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5216 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5217 // found:
5218 bstrResult = it->second; // source is a Utf8Str
5219
5220 /* return the result to caller (may be empty) */
5221 bstrResult.cloneTo(aValue);
5222
5223 return S_OK;
5224}
5225
5226 /**
5227 * @note Locks mParent for writing + this object for writing.
5228 */
5229STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5230{
5231 CheckComArgStrNotEmptyOrNull(aKey);
5232
5233 AutoCaller autoCaller(this);
5234 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5235
5236 Utf8Str strKey(aKey);
5237 Utf8Str strValue(aValue);
5238 Utf8Str strOldValue; // empty
5239
5240 // locking note: we only hold the read lock briefly to look up the old value,
5241 // then release it and call the onExtraCanChange callbacks. There is a small
5242 // chance of a race insofar as the callback might be called twice if two callers
5243 // change the same key at the same time, but that's a much better solution
5244 // than the deadlock we had here before. The actual changing of the extradata
5245 // is then performed under the write lock and race-free.
5246
5247 // look up the old value first; if nothing has changed then we need not do anything
5248 {
5249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5250 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5251 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5252 strOldValue = it->second;
5253 }
5254
5255 bool fChanged;
5256 if ((fChanged = (strOldValue != strValue)))
5257 {
5258 // ask for permission from all listeners outside the locks;
5259 // onExtraDataCanChange() only briefly requests the VirtualBox
5260 // lock to copy the list of callbacks to invoke
5261 Bstr error;
5262 Bstr bstrValue(aValue);
5263
5264 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5265 {
5266 const char *sep = error.isEmpty() ? "" : ": ";
5267 CBSTR err = error.raw();
5268 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5269 sep, err));
5270 return setError(E_ACCESSDENIED,
5271 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5272 aKey,
5273 bstrValue.raw(),
5274 sep,
5275 err);
5276 }
5277
5278 // data is changing and change not vetoed: then write it out under the lock
5279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5280
5281 if (isSnapshotMachine())
5282 {
5283 HRESULT rc = checkStateDependency(MutableStateDep);
5284 if (FAILED(rc)) return rc;
5285 }
5286
5287 if (strValue.isEmpty())
5288 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5289 else
5290 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5291 // creates a new key if needed
5292
5293 bool fNeedsGlobalSaveSettings = false;
5294 saveSettings(&fNeedsGlobalSaveSettings);
5295
5296 if (fNeedsGlobalSaveSettings)
5297 {
5298 // save the global settings; for that we should hold only the VirtualBox lock
5299 alock.release();
5300 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5301 mParent->saveSettings();
5302 }
5303 }
5304
5305 // fire notification outside the lock
5306 if (fChanged)
5307 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5308
5309 return S_OK;
5310}
5311
5312STDMETHODIMP Machine::SaveSettings()
5313{
5314 AutoCaller autoCaller(this);
5315 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5316
5317 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5318
5319 /* when there was auto-conversion, we want to save the file even if
5320 * the VM is saved */
5321 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5322 if (FAILED(rc)) return rc;
5323
5324 /* the settings file path may never be null */
5325 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5326
5327 /* save all VM data excluding snapshots */
5328 bool fNeedsGlobalSaveSettings = false;
5329 rc = saveSettings(&fNeedsGlobalSaveSettings);
5330 mlock.release();
5331
5332 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5333 {
5334 // save the global settings; for that we should hold only the VirtualBox lock
5335 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5336 rc = mParent->saveSettings();
5337 }
5338
5339 return rc;
5340}
5341
5342STDMETHODIMP Machine::DiscardSettings()
5343{
5344 AutoCaller autoCaller(this);
5345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5346
5347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5348
5349 HRESULT rc = checkStateDependency(MutableStateDep);
5350 if (FAILED(rc)) return rc;
5351
5352 /*
5353 * during this rollback, the session will be notified if data has
5354 * been actually changed
5355 */
5356 rollback(true /* aNotify */);
5357
5358 return S_OK;
5359}
5360
5361/** @note Locks objects! */
5362STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5363 ComSafeArrayOut(IMedium*, aMedia))
5364{
5365 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5366 AutoLimitedCaller autoCaller(this);
5367 AssertComRCReturnRC(autoCaller.rc());
5368
5369 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5370
5371 Guid id(getId());
5372
5373 if (mData->mSession.mState != SessionState_Unlocked)
5374 return setError(VBOX_E_INVALID_OBJECT_STATE,
5375 tr("Cannot unregister the machine '%s' while it is locked"),
5376 mUserData->s.strName.c_str());
5377
5378 // wait for state dependents to drop to zero
5379 ensureNoStateDependencies();
5380
5381 if (!mData->mAccessible)
5382 {
5383 // inaccessible maschines can only be unregistered; uninitialize ourselves
5384 // here because currently there may be no unregistered that are inaccessible
5385 // (this state combination is not supported). Note releasing the caller and
5386 // leaving the lock before calling uninit()
5387 alock.release();
5388 autoCaller.release();
5389
5390 uninit();
5391
5392 mParent->unregisterMachine(this, id);
5393 // calls VirtualBox::saveSettings()
5394
5395 return S_OK;
5396 }
5397
5398 HRESULT rc = S_OK;
5399
5400 // discard saved state
5401 if (mData->mMachineState == MachineState_Saved)
5402 {
5403 // add the saved state file to the list of files the caller should delete
5404 Assert(!mSSData->strStateFilePath.isEmpty());
5405 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5406
5407 mSSData->strStateFilePath.setNull();
5408
5409 // unconditionally set the machine state to powered off, we now
5410 // know no session has locked the machine
5411 mData->mMachineState = MachineState_PoweredOff;
5412 }
5413
5414 size_t cSnapshots = 0;
5415 if (mData->mFirstSnapshot)
5416 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5417 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5418 // fail now before we start detaching media
5419 return setError(VBOX_E_INVALID_OBJECT_STATE,
5420 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5421 mUserData->s.strName.c_str(), cSnapshots);
5422
5423 // This list collects the medium objects from all medium attachments
5424 // which we will detach from the machine and its snapshots, in a specific
5425 // order which allows for closing all media without getting "media in use"
5426 // errors, simply by going through the list from the front to the back:
5427 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5428 // and must be closed before the parent media from the snapshots, or closing the parents
5429 // will fail because they still have children);
5430 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5431 // the root ("first") snapshot of the machine.
5432 MediaList llMedia;
5433
5434 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5435 && mMediaData->mAttachments.size()
5436 )
5437 {
5438 // we have media attachments: detach them all and add the Medium objects to our list
5439 if (cleanupMode != CleanupMode_UnregisterOnly)
5440 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5441 else
5442 return setError(VBOX_E_INVALID_OBJECT_STATE,
5443 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5444 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5445 }
5446
5447 if (cSnapshots)
5448 {
5449 // autoCleanup must be true here, or we would have failed above
5450
5451 // add the media from the medium attachments of the snapshots to llMedia
5452 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5453 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5454 // into the children first
5455
5456 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5457 MachineState_T oldState = mData->mMachineState;
5458 mData->mMachineState = MachineState_DeletingSnapshot;
5459
5460 // make a copy of the first snapshot so the refcount does not drop to 0
5461 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5462 // because of the AutoCaller voodoo)
5463 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5464
5465 // GO!
5466 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5467
5468 mData->mMachineState = oldState;
5469 }
5470
5471 if (FAILED(rc))
5472 {
5473 rollbackMedia();
5474 return rc;
5475 }
5476
5477 // commit all the media changes made above
5478 commitMedia();
5479
5480 mData->mRegistered = false;
5481
5482 // machine lock no longer needed
5483 alock.release();
5484
5485 // return media to caller
5486 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5487 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5488
5489 mParent->unregisterMachine(this, id);
5490 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5491
5492 return S_OK;
5493}
5494
5495struct Machine::DeleteTask
5496{
5497 ComObjPtr<Machine> pMachine;
5498 RTCList<ComPtr<IMedium> > llMediums;
5499 StringsList llFilesToDelete;
5500 ComObjPtr<Progress> pProgress;
5501};
5502
5503STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5504{
5505 LogFlowFuncEnter();
5506
5507 AutoCaller autoCaller(this);
5508 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5509
5510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5511
5512 HRESULT rc = checkStateDependency(MutableStateDep);
5513 if (FAILED(rc)) return rc;
5514
5515 if (mData->mRegistered)
5516 return setError(VBOX_E_INVALID_VM_STATE,
5517 tr("Cannot delete settings of a registered machine"));
5518
5519 DeleteTask *pTask = new DeleteTask;
5520 pTask->pMachine = this;
5521 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5522
5523 // collect files to delete
5524 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5525
5526 for (size_t i = 0; i < sfaMedia.size(); ++i)
5527 {
5528 IMedium *pIMedium(sfaMedia[i]);
5529 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5530 if (pMedium.isNull())
5531 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5532 SafeArray<BSTR> ids;
5533 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5534 if (FAILED(rc)) return rc;
5535 /* At this point the medium should not have any back references
5536 * anymore. If it has it is attached to another VM and *must* not
5537 * deleted. */
5538 if (ids.size() < 1)
5539 pTask->llMediums.append(pMedium);
5540 }
5541 if (mData->pMachineConfigFile->fileExists())
5542 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5543
5544 pTask->pProgress.createObject();
5545 pTask->pProgress->init(getVirtualBox(),
5546 static_cast<IMachine*>(this) /* aInitiator */,
5547 Bstr(tr("Deleting files")).raw(),
5548 true /* fCancellable */,
5549 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5550 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5551
5552 int vrc = RTThreadCreate(NULL,
5553 Machine::deleteThread,
5554 (void*)pTask,
5555 0,
5556 RTTHREADTYPE_MAIN_WORKER,
5557 0,
5558 "MachineDelete");
5559
5560 pTask->pProgress.queryInterfaceTo(aProgress);
5561
5562 if (RT_FAILURE(vrc))
5563 {
5564 delete pTask;
5565 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5566 }
5567
5568 LogFlowFuncLeave();
5569
5570 return S_OK;
5571}
5572
5573/**
5574 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5575 * calls Machine::deleteTaskWorker() on the actual machine object.
5576 * @param Thread
5577 * @param pvUser
5578 * @return
5579 */
5580/*static*/
5581DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5582{
5583 LogFlowFuncEnter();
5584
5585 DeleteTask *pTask = (DeleteTask*)pvUser;
5586 Assert(pTask);
5587 Assert(pTask->pMachine);
5588 Assert(pTask->pProgress);
5589
5590 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5591 pTask->pProgress->notifyComplete(rc);
5592
5593 delete pTask;
5594
5595 LogFlowFuncLeave();
5596
5597 NOREF(Thread);
5598
5599 return VINF_SUCCESS;
5600}
5601
5602/**
5603 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5604 * @param task
5605 * @return
5606 */
5607HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5608{
5609 AutoCaller autoCaller(this);
5610 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5611
5612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5613
5614 HRESULT rc = S_OK;
5615
5616 try
5617 {
5618 ULONG uLogHistoryCount = 3;
5619 ComPtr<ISystemProperties> systemProperties;
5620 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5621 if (FAILED(rc)) throw rc;
5622
5623 if (!systemProperties.isNull())
5624 {
5625 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5626 if (FAILED(rc)) throw rc;
5627 }
5628
5629 MachineState_T oldState = mData->mMachineState;
5630 setMachineState(MachineState_SettingUp);
5631 alock.release();
5632 for (size_t i = 0; i < task.llMediums.size(); ++i)
5633 {
5634 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5635 {
5636 AutoCaller mac(pMedium);
5637 if (FAILED(mac.rc())) throw mac.rc();
5638 Utf8Str strLocation = pMedium->getLocationFull();
5639 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5640 if (FAILED(rc)) throw rc;
5641 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5642 }
5643 ComPtr<IProgress> pProgress2;
5644 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5645 if (FAILED(rc)) throw rc;
5646 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5647 if (FAILED(rc)) throw rc;
5648 /* Check the result of the asynchrony process. */
5649 LONG iRc;
5650 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5651 if (FAILED(rc)) throw rc;
5652 /* If the thread of the progress object has an error, then
5653 * retrieve the error info from there, or it'll be lost. */
5654 if (FAILED(iRc))
5655 throw setError(ProgressErrorInfo(pProgress2));
5656 }
5657 setMachineState(oldState);
5658 alock.acquire();
5659
5660 // delete the files pushed on the task list by Machine::Delete()
5661 // (this includes saved states of the machine and snapshots and
5662 // medium storage files from the IMedium list passed in, and the
5663 // machine XML file)
5664 StringsList::const_iterator it = task.llFilesToDelete.begin();
5665 while (it != task.llFilesToDelete.end())
5666 {
5667 const Utf8Str &strFile = *it;
5668 LogFunc(("Deleting file %s\n", strFile.c_str()));
5669 int vrc = RTFileDelete(strFile.c_str());
5670 if (RT_FAILURE(vrc))
5671 throw setError(VBOX_E_IPRT_ERROR,
5672 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5673
5674 ++it;
5675 if (it == task.llFilesToDelete.end())
5676 {
5677 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5678 if (FAILED(rc)) throw rc;
5679 break;
5680 }
5681
5682 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5683 if (FAILED(rc)) throw rc;
5684 }
5685
5686 /* delete the settings only when the file actually exists */
5687 if (mData->pMachineConfigFile->fileExists())
5688 {
5689 /* Delete any backup or uncommitted XML files. Ignore failures.
5690 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5691 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5692 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5693 RTFileDelete(otherXml.c_str());
5694 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5695 RTFileDelete(otherXml.c_str());
5696
5697 /* delete the Logs folder, nothing important should be left
5698 * there (we don't check for errors because the user might have
5699 * some private files there that we don't want to delete) */
5700 Utf8Str logFolder;
5701 getLogFolder(logFolder);
5702 Assert(logFolder.length());
5703 if (RTDirExists(logFolder.c_str()))
5704 {
5705 /* Delete all VBox.log[.N] files from the Logs folder
5706 * (this must be in sync with the rotation logic in
5707 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5708 * files that may have been created by the GUI. */
5709 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5710 logFolder.c_str(), RTPATH_DELIMITER);
5711 RTFileDelete(log.c_str());
5712 log = Utf8StrFmt("%s%cVBox.png",
5713 logFolder.c_str(), RTPATH_DELIMITER);
5714 RTFileDelete(log.c_str());
5715 for (int i = uLogHistoryCount; i > 0; i--)
5716 {
5717 log = Utf8StrFmt("%s%cVBox.log.%d",
5718 logFolder.c_str(), RTPATH_DELIMITER, i);
5719 RTFileDelete(log.c_str());
5720 log = Utf8StrFmt("%s%cVBox.png.%d",
5721 logFolder.c_str(), RTPATH_DELIMITER, i);
5722 RTFileDelete(log.c_str());
5723 }
5724
5725 RTDirRemove(logFolder.c_str());
5726 }
5727
5728 /* delete the Snapshots folder, nothing important should be left
5729 * there (we don't check for errors because the user might have
5730 * some private files there that we don't want to delete) */
5731 Utf8Str strFullSnapshotFolder;
5732 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5733 Assert(!strFullSnapshotFolder.isEmpty());
5734 if (RTDirExists(strFullSnapshotFolder.c_str()))
5735 RTDirRemove(strFullSnapshotFolder.c_str());
5736
5737 // delete the directory that contains the settings file, but only
5738 // if it matches the VM name
5739 Utf8Str settingsDir;
5740 if (isInOwnDir(&settingsDir))
5741 RTDirRemove(settingsDir.c_str());
5742 }
5743
5744 alock.release();
5745
5746 mParent->saveModifiedRegistries();
5747 }
5748 catch (HRESULT aRC) { rc = aRC; }
5749
5750 return rc;
5751}
5752
5753STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5754{
5755 CheckComArgOutPointerValid(aSnapshot);
5756
5757 AutoCaller autoCaller(this);
5758 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5759
5760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5761
5762 ComObjPtr<Snapshot> pSnapshot;
5763 HRESULT rc;
5764
5765 if (!aNameOrId || !*aNameOrId)
5766 // null case (caller wants root snapshot): findSnapshotById() handles this
5767 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5768 else
5769 {
5770 Guid uuid(aNameOrId);
5771 if (uuid.isValid())
5772 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5773 else
5774 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5775 }
5776 pSnapshot.queryInterfaceTo(aSnapshot);
5777
5778 return rc;
5779}
5780
5781STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5782{
5783 CheckComArgStrNotEmptyOrNull(aName);
5784 CheckComArgStrNotEmptyOrNull(aHostPath);
5785
5786 AutoCaller autoCaller(this);
5787 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5788
5789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5790
5791 HRESULT rc = checkStateDependency(MutableStateDep);
5792 if (FAILED(rc)) return rc;
5793
5794 Utf8Str strName(aName);
5795
5796 ComObjPtr<SharedFolder> sharedFolder;
5797 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5798 if (SUCCEEDED(rc))
5799 return setError(VBOX_E_OBJECT_IN_USE,
5800 tr("Shared folder named '%s' already exists"),
5801 strName.c_str());
5802
5803 sharedFolder.createObject();
5804 rc = sharedFolder->init(getMachine(),
5805 strName,
5806 aHostPath,
5807 !!aWritable,
5808 !!aAutoMount,
5809 true /* fFailOnError */);
5810 if (FAILED(rc)) return rc;
5811
5812 setModified(IsModified_SharedFolders);
5813 mHWData.backup();
5814 mHWData->mSharedFolders.push_back(sharedFolder);
5815
5816 /* inform the direct session if any */
5817 alock.release();
5818 onSharedFolderChange();
5819
5820 return S_OK;
5821}
5822
5823STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5824{
5825 CheckComArgStrNotEmptyOrNull(aName);
5826
5827 AutoCaller autoCaller(this);
5828 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5829
5830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5831
5832 HRESULT rc = checkStateDependency(MutableStateDep);
5833 if (FAILED(rc)) return rc;
5834
5835 ComObjPtr<SharedFolder> sharedFolder;
5836 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5837 if (FAILED(rc)) return rc;
5838
5839 setModified(IsModified_SharedFolders);
5840 mHWData.backup();
5841 mHWData->mSharedFolders.remove(sharedFolder);
5842
5843 /* inform the direct session if any */
5844 alock.release();
5845 onSharedFolderChange();
5846
5847 return S_OK;
5848}
5849
5850STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5851{
5852 CheckComArgOutPointerValid(aCanShow);
5853
5854 /* start with No */
5855 *aCanShow = FALSE;
5856
5857 AutoCaller autoCaller(this);
5858 AssertComRCReturnRC(autoCaller.rc());
5859
5860 ComPtr<IInternalSessionControl> directControl;
5861 {
5862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5863
5864 if (mData->mSession.mState != SessionState_Locked)
5865 return setError(VBOX_E_INVALID_VM_STATE,
5866 tr("Machine is not locked for session (session state: %s)"),
5867 Global::stringifySessionState(mData->mSession.mState));
5868
5869 directControl = mData->mSession.mDirectControl;
5870 }
5871
5872 /* ignore calls made after #OnSessionEnd() is called */
5873 if (!directControl)
5874 return S_OK;
5875
5876 LONG64 dummy;
5877 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5878}
5879
5880STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5881{
5882 CheckComArgOutPointerValid(aWinId);
5883
5884 AutoCaller autoCaller(this);
5885 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5886
5887 ComPtr<IInternalSessionControl> directControl;
5888 {
5889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5890
5891 if (mData->mSession.mState != SessionState_Locked)
5892 return setError(E_FAIL,
5893 tr("Machine is not locked for session (session state: %s)"),
5894 Global::stringifySessionState(mData->mSession.mState));
5895
5896 directControl = mData->mSession.mDirectControl;
5897 }
5898
5899 /* ignore calls made after #OnSessionEnd() is called */
5900 if (!directControl)
5901 return S_OK;
5902
5903 BOOL dummy;
5904 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5905}
5906
5907#ifdef VBOX_WITH_GUEST_PROPS
5908/**
5909 * Look up a guest property in VBoxSVC's internal structures.
5910 */
5911HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5912 BSTR *aValue,
5913 LONG64 *aTimestamp,
5914 BSTR *aFlags) const
5915{
5916 using namespace guestProp;
5917
5918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5919 Utf8Str strName(aName);
5920 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5921
5922 if (it != mHWData->mGuestProperties.end())
5923 {
5924 char szFlags[MAX_FLAGS_LEN + 1];
5925 it->second.strValue.cloneTo(aValue);
5926 *aTimestamp = it->second.mTimestamp;
5927 writeFlags(it->second.mFlags, szFlags);
5928 Bstr(szFlags).cloneTo(aFlags);
5929 }
5930
5931 return S_OK;
5932}
5933
5934/**
5935 * Query the VM that a guest property belongs to for the property.
5936 * @returns E_ACCESSDENIED if the VM process is not available or not
5937 * currently handling queries and the lookup should then be done in
5938 * VBoxSVC.
5939 */
5940HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5941 BSTR *aValue,
5942 LONG64 *aTimestamp,
5943 BSTR *aFlags) const
5944{
5945 HRESULT rc;
5946 ComPtr<IInternalSessionControl> directControl;
5947 directControl = mData->mSession.mDirectControl;
5948
5949 /* fail if we were called after #OnSessionEnd() is called. This is a
5950 * silly race condition. */
5951
5952 if (!directControl)
5953 rc = E_ACCESSDENIED;
5954 else
5955 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5956 false /* isSetter */,
5957 aValue, aTimestamp, aFlags);
5958 return rc;
5959}
5960#endif // VBOX_WITH_GUEST_PROPS
5961
5962STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5963 BSTR *aValue,
5964 LONG64 *aTimestamp,
5965 BSTR *aFlags)
5966{
5967#ifndef VBOX_WITH_GUEST_PROPS
5968 ReturnComNotImplemented();
5969#else // VBOX_WITH_GUEST_PROPS
5970 CheckComArgStrNotEmptyOrNull(aName);
5971 CheckComArgOutPointerValid(aValue);
5972 CheckComArgOutPointerValid(aTimestamp);
5973 CheckComArgOutPointerValid(aFlags);
5974
5975 AutoCaller autoCaller(this);
5976 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5977
5978 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5979 if (rc == E_ACCESSDENIED)
5980 /* The VM is not running or the service is not (yet) accessible */
5981 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5982 return rc;
5983#endif // VBOX_WITH_GUEST_PROPS
5984}
5985
5986STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5987{
5988 LONG64 dummyTimestamp;
5989 Bstr dummyFlags;
5990 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5991}
5992
5993STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5994{
5995 Bstr dummyValue;
5996 Bstr dummyFlags;
5997 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5998}
5999
6000#ifdef VBOX_WITH_GUEST_PROPS
6001/**
6002 * Set a guest property in VBoxSVC's internal structures.
6003 */
6004HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
6005 IN_BSTR aFlags)
6006{
6007 using namespace guestProp;
6008
6009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6010 HRESULT rc = S_OK;
6011
6012 rc = checkStateDependency(MutableStateDep);
6013 if (FAILED(rc)) return rc;
6014
6015 try
6016 {
6017 Utf8Str utf8Name(aName);
6018 Utf8Str utf8Flags(aFlags);
6019 uint32_t fFlags = NILFLAG;
6020 if ( aFlags != NULL
6021 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
6022 return setError(E_INVALIDARG,
6023 tr("Invalid guest property flag values: '%ls'"),
6024 aFlags);
6025
6026 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
6027 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
6028 if (it == mHWData->mGuestProperties.end())
6029 {
6030 if (!fDelete)
6031 {
6032 setModified(IsModified_MachineData);
6033 mHWData.backupEx();
6034
6035 RTTIMESPEC time;
6036 HWData::GuestProperty prop;
6037 prop.strValue = aValue;
6038 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6039 prop.mFlags = fFlags;
6040 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
6041 }
6042 }
6043 else
6044 {
6045 if (it->second.mFlags & (RDONLYHOST))
6046 {
6047 rc = setError(E_ACCESSDENIED,
6048 tr("The property '%ls' cannot be changed by the host"),
6049 aName);
6050 }
6051 else
6052 {
6053 setModified(IsModified_MachineData);
6054 mHWData.backupEx();
6055
6056 /* The backupEx() operation invalidates our iterator,
6057 * so get a new one. */
6058 it = mHWData->mGuestProperties.find(utf8Name);
6059 Assert(it != mHWData->mGuestProperties.end());
6060
6061 if (!fDelete)
6062 {
6063 RTTIMESPEC time;
6064 it->second.strValue = aValue;
6065 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6066 it->second.mFlags = fFlags;
6067 }
6068 else
6069 mHWData->mGuestProperties.erase(it);
6070 }
6071 }
6072
6073 if ( SUCCEEDED(rc)
6074 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6075 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6076 RTSTR_MAX,
6077 utf8Name.c_str(),
6078 RTSTR_MAX,
6079 NULL)
6080 )
6081 )
6082 {
6083 alock.release();
6084
6085 mParent->onGuestPropertyChange(mData->mUuid, aName,
6086 aValue ? aValue : Bstr("").raw(),
6087 aFlags ? aFlags : Bstr("").raw());
6088 }
6089 }
6090 catch (std::bad_alloc &)
6091 {
6092 rc = E_OUTOFMEMORY;
6093 }
6094
6095 return rc;
6096}
6097
6098/**
6099 * Set a property on the VM that that property belongs to.
6100 * @returns E_ACCESSDENIED if the VM process is not available or not
6101 * currently handling queries and the setting should then be done in
6102 * VBoxSVC.
6103 */
6104HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6105 IN_BSTR aFlags)
6106{
6107 HRESULT rc;
6108
6109 try
6110 {
6111 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6112
6113 BSTR dummy = NULL; /* will not be changed (setter) */
6114 LONG64 dummy64;
6115 if (!directControl)
6116 rc = E_ACCESSDENIED;
6117 else
6118 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6119 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6120 true /* isSetter */,
6121 &dummy, &dummy64, &dummy);
6122 }
6123 catch (std::bad_alloc &)
6124 {
6125 rc = E_OUTOFMEMORY;
6126 }
6127
6128 return rc;
6129}
6130#endif // VBOX_WITH_GUEST_PROPS
6131
6132STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6133 IN_BSTR aFlags)
6134{
6135#ifndef VBOX_WITH_GUEST_PROPS
6136 ReturnComNotImplemented();
6137#else // VBOX_WITH_GUEST_PROPS
6138 CheckComArgStrNotEmptyOrNull(aName);
6139 CheckComArgMaybeNull(aFlags);
6140 CheckComArgMaybeNull(aValue);
6141
6142 AutoCaller autoCaller(this);
6143 if (FAILED(autoCaller.rc()))
6144 return autoCaller.rc();
6145
6146 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6147 if (rc == E_ACCESSDENIED)
6148 /* The VM is not running or the service is not (yet) accessible */
6149 rc = setGuestPropertyToService(aName, aValue, aFlags);
6150 return rc;
6151#endif // VBOX_WITH_GUEST_PROPS
6152}
6153
6154STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6155{
6156 return SetGuestProperty(aName, aValue, NULL);
6157}
6158
6159STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6160{
6161 return SetGuestProperty(aName, NULL, NULL);
6162}
6163
6164#ifdef VBOX_WITH_GUEST_PROPS
6165/**
6166 * Enumerate the guest properties in VBoxSVC's internal structures.
6167 */
6168HRESULT Machine::enumerateGuestPropertiesInService
6169 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6170 ComSafeArrayOut(BSTR, aValues),
6171 ComSafeArrayOut(LONG64, aTimestamps),
6172 ComSafeArrayOut(BSTR, aFlags))
6173{
6174 using namespace guestProp;
6175
6176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6177 Utf8Str strPatterns(aPatterns);
6178
6179 HWData::GuestPropertyMap propMap;
6180
6181 /*
6182 * Look for matching patterns and build up a list.
6183 */
6184 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6185 while (it != mHWData->mGuestProperties.end())
6186 {
6187 if ( strPatterns.isEmpty()
6188 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6189 RTSTR_MAX,
6190 it->first.c_str(),
6191 RTSTR_MAX,
6192 NULL)
6193 )
6194 {
6195 propMap.insert(*it);
6196 }
6197
6198 it++;
6199 }
6200
6201 alock.release();
6202
6203 /*
6204 * And build up the arrays for returning the property information.
6205 */
6206 size_t cEntries = propMap.size();
6207 SafeArray<BSTR> names(cEntries);
6208 SafeArray<BSTR> values(cEntries);
6209 SafeArray<LONG64> timestamps(cEntries);
6210 SafeArray<BSTR> flags(cEntries);
6211 size_t iProp = 0;
6212
6213 it = propMap.begin();
6214 while (it != propMap.end())
6215 {
6216 char szFlags[MAX_FLAGS_LEN + 1];
6217 it->first.cloneTo(&names[iProp]);
6218 it->second.strValue.cloneTo(&values[iProp]);
6219 timestamps[iProp] = it->second.mTimestamp;
6220 writeFlags(it->second.mFlags, szFlags);
6221 Bstr(szFlags).cloneTo(&flags[iProp++]);
6222 it++;
6223 }
6224 names.detachTo(ComSafeArrayOutArg(aNames));
6225 values.detachTo(ComSafeArrayOutArg(aValues));
6226 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6227 flags.detachTo(ComSafeArrayOutArg(aFlags));
6228 return S_OK;
6229}
6230
6231/**
6232 * Enumerate the properties managed by a VM.
6233 * @returns E_ACCESSDENIED if the VM process is not available or not
6234 * currently handling queries and the setting should then be done in
6235 * VBoxSVC.
6236 */
6237HRESULT Machine::enumerateGuestPropertiesOnVM
6238 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6239 ComSafeArrayOut(BSTR, aValues),
6240 ComSafeArrayOut(LONG64, aTimestamps),
6241 ComSafeArrayOut(BSTR, aFlags))
6242{
6243 HRESULT rc;
6244 ComPtr<IInternalSessionControl> directControl;
6245 directControl = mData->mSession.mDirectControl;
6246
6247 if (!directControl)
6248 rc = E_ACCESSDENIED;
6249 else
6250 rc = directControl->EnumerateGuestProperties
6251 (aPatterns, ComSafeArrayOutArg(aNames),
6252 ComSafeArrayOutArg(aValues),
6253 ComSafeArrayOutArg(aTimestamps),
6254 ComSafeArrayOutArg(aFlags));
6255 return rc;
6256}
6257#endif // VBOX_WITH_GUEST_PROPS
6258
6259STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6260 ComSafeArrayOut(BSTR, aNames),
6261 ComSafeArrayOut(BSTR, aValues),
6262 ComSafeArrayOut(LONG64, aTimestamps),
6263 ComSafeArrayOut(BSTR, aFlags))
6264{
6265#ifndef VBOX_WITH_GUEST_PROPS
6266 ReturnComNotImplemented();
6267#else // VBOX_WITH_GUEST_PROPS
6268 CheckComArgMaybeNull(aPatterns);
6269 CheckComArgOutSafeArrayPointerValid(aNames);
6270 CheckComArgOutSafeArrayPointerValid(aValues);
6271 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6272 CheckComArgOutSafeArrayPointerValid(aFlags);
6273
6274 AutoCaller autoCaller(this);
6275 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6276
6277 HRESULT rc = enumerateGuestPropertiesOnVM
6278 (aPatterns, ComSafeArrayOutArg(aNames),
6279 ComSafeArrayOutArg(aValues),
6280 ComSafeArrayOutArg(aTimestamps),
6281 ComSafeArrayOutArg(aFlags));
6282 if (rc == E_ACCESSDENIED)
6283 /* The VM is not running or the service is not (yet) accessible */
6284 rc = enumerateGuestPropertiesInService
6285 (aPatterns, ComSafeArrayOutArg(aNames),
6286 ComSafeArrayOutArg(aValues),
6287 ComSafeArrayOutArg(aTimestamps),
6288 ComSafeArrayOutArg(aFlags));
6289 return rc;
6290#endif // VBOX_WITH_GUEST_PROPS
6291}
6292
6293STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6294 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6295{
6296 MediaData::AttachmentList atts;
6297
6298 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6299 if (FAILED(rc)) return rc;
6300
6301 SafeIfaceArray<IMediumAttachment> attachments(atts);
6302 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6303
6304 return S_OK;
6305}
6306
6307STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6308 LONG aControllerPort,
6309 LONG aDevice,
6310 IMediumAttachment **aAttachment)
6311{
6312 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6313 aControllerName, aControllerPort, aDevice));
6314
6315 CheckComArgStrNotEmptyOrNull(aControllerName);
6316 CheckComArgOutPointerValid(aAttachment);
6317
6318 AutoCaller autoCaller(this);
6319 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6320
6321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6322
6323 *aAttachment = NULL;
6324
6325 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6326 aControllerName,
6327 aControllerPort,
6328 aDevice);
6329 if (pAttach.isNull())
6330 return setError(VBOX_E_OBJECT_NOT_FOUND,
6331 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6332 aDevice, aControllerPort, aControllerName);
6333
6334 pAttach.queryInterfaceTo(aAttachment);
6335
6336 return S_OK;
6337}
6338
6339STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6340 StorageBus_T aConnectionType,
6341 IStorageController **controller)
6342{
6343 CheckComArgStrNotEmptyOrNull(aName);
6344
6345 if ( (aConnectionType <= StorageBus_Null)
6346 || (aConnectionType > StorageBus_SAS))
6347 return setError(E_INVALIDARG,
6348 tr("Invalid connection type: %d"),
6349 aConnectionType);
6350
6351 AutoCaller autoCaller(this);
6352 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6353
6354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6355
6356 HRESULT rc = checkStateDependency(MutableStateDep);
6357 if (FAILED(rc)) return rc;
6358
6359 /* try to find one with the name first. */
6360 ComObjPtr<StorageController> ctrl;
6361
6362 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6363 if (SUCCEEDED(rc))
6364 return setError(VBOX_E_OBJECT_IN_USE,
6365 tr("Storage controller named '%ls' already exists"),
6366 aName);
6367
6368 ctrl.createObject();
6369
6370 /* get a new instance number for the storage controller */
6371 ULONG ulInstance = 0;
6372 bool fBootable = true;
6373 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6374 it != mStorageControllers->end();
6375 ++it)
6376 {
6377 if ((*it)->getStorageBus() == aConnectionType)
6378 {
6379 ULONG ulCurInst = (*it)->getInstance();
6380
6381 if (ulCurInst >= ulInstance)
6382 ulInstance = ulCurInst + 1;
6383
6384 /* Only one controller of each type can be marked as bootable. */
6385 if ((*it)->getBootable())
6386 fBootable = false;
6387 }
6388 }
6389
6390 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6391 if (FAILED(rc)) return rc;
6392
6393 setModified(IsModified_Storage);
6394 mStorageControllers.backup();
6395 mStorageControllers->push_back(ctrl);
6396
6397 ctrl.queryInterfaceTo(controller);
6398
6399 /* inform the direct session if any */
6400 alock.release();
6401 onStorageControllerChange();
6402
6403 return S_OK;
6404}
6405
6406STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6407 IStorageController **aStorageController)
6408{
6409 CheckComArgStrNotEmptyOrNull(aName);
6410
6411 AutoCaller autoCaller(this);
6412 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6413
6414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6415
6416 ComObjPtr<StorageController> ctrl;
6417
6418 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6419 if (SUCCEEDED(rc))
6420 ctrl.queryInterfaceTo(aStorageController);
6421
6422 return rc;
6423}
6424
6425STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6426 IStorageController **aStorageController)
6427{
6428 AutoCaller autoCaller(this);
6429 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6430
6431 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6432
6433 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6434 it != mStorageControllers->end();
6435 ++it)
6436 {
6437 if ((*it)->getInstance() == aInstance)
6438 {
6439 (*it).queryInterfaceTo(aStorageController);
6440 return S_OK;
6441 }
6442 }
6443
6444 return setError(VBOX_E_OBJECT_NOT_FOUND,
6445 tr("Could not find a storage controller with instance number '%lu'"),
6446 aInstance);
6447}
6448
6449STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6450{
6451 AutoCaller autoCaller(this);
6452 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6453
6454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6455
6456 HRESULT rc = checkStateDependency(MutableStateDep);
6457 if (FAILED(rc)) return rc;
6458
6459 ComObjPtr<StorageController> ctrl;
6460
6461 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6462 if (SUCCEEDED(rc))
6463 {
6464 /* Ensure that only one controller of each type is marked as bootable. */
6465 if (fBootable == TRUE)
6466 {
6467 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6468 it != mStorageControllers->end();
6469 ++it)
6470 {
6471 ComObjPtr<StorageController> aCtrl = (*it);
6472
6473 if ( (aCtrl->getName() != Utf8Str(aName))
6474 && aCtrl->getBootable() == TRUE
6475 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6476 && aCtrl->getControllerType() == ctrl->getControllerType())
6477 {
6478 aCtrl->setBootable(FALSE);
6479 break;
6480 }
6481 }
6482 }
6483
6484 if (SUCCEEDED(rc))
6485 {
6486 ctrl->setBootable(fBootable);
6487 setModified(IsModified_Storage);
6488 }
6489 }
6490
6491 if (SUCCEEDED(rc))
6492 {
6493 /* inform the direct session if any */
6494 alock.release();
6495 onStorageControllerChange();
6496 }
6497
6498 return rc;
6499}
6500
6501STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6502{
6503 CheckComArgStrNotEmptyOrNull(aName);
6504
6505 AutoCaller autoCaller(this);
6506 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6507
6508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6509
6510 HRESULT rc = checkStateDependency(MutableStateDep);
6511 if (FAILED(rc)) return rc;
6512
6513 ComObjPtr<StorageController> ctrl;
6514 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6515 if (FAILED(rc)) return rc;
6516
6517 {
6518 /* find all attached devices to the appropriate storage controller and detach them all */
6519 // make a temporary list because detachDevice invalidates iterators into
6520 // mMediaData->mAttachments
6521 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6522
6523 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6524 it != llAttachments2.end();
6525 ++it)
6526 {
6527 MediumAttachment *pAttachTemp = *it;
6528
6529 AutoCaller localAutoCaller(pAttachTemp);
6530 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6531
6532 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6533
6534 if (pAttachTemp->getControllerName() == aName)
6535 {
6536 rc = detachDevice(pAttachTemp, alock, NULL);
6537 if (FAILED(rc)) return rc;
6538 }
6539 }
6540 }
6541
6542 /* We can remove it now. */
6543 setModified(IsModified_Storage);
6544 mStorageControllers.backup();
6545
6546 ctrl->unshare();
6547
6548 mStorageControllers->remove(ctrl);
6549
6550 /* inform the direct session if any */
6551 alock.release();
6552 onStorageControllerChange();
6553
6554 return S_OK;
6555}
6556
6557STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType,
6558 IUSBController **controller)
6559{
6560 if ( (aType <= USBControllerType_Null)
6561 || (aType >= USBControllerType_Last))
6562 return setError(E_INVALIDARG,
6563 tr("Invalid USB controller type: %d"),
6564 aType);
6565
6566 AutoCaller autoCaller(this);
6567 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6568
6569 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6570
6571 HRESULT rc = checkStateDependency(MutableStateDep);
6572 if (FAILED(rc)) return rc;
6573
6574 /* try to find one with the same type first. */
6575 ComObjPtr<USBController> ctrl;
6576
6577 rc = getUSBControllerByName(aName, ctrl, false /* aSetError */);
6578 if (SUCCEEDED(rc))
6579 return setError(VBOX_E_OBJECT_IN_USE,
6580 tr("USB controller named '%ls' already exists"),
6581 aName);
6582
6583 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6584 ULONG maxInstances;
6585 rc = mParent->getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6586 if (FAILED(rc))
6587 return rc;
6588
6589 ULONG cInstances = getUSBControllerCountByType(aType);
6590 if (cInstances >= maxInstances)
6591 return setError(E_INVALIDARG,
6592 tr("Too many USB controllers of this type"));
6593
6594 ctrl.createObject();
6595
6596 rc = ctrl->init(this, aName, aType);
6597 if (FAILED(rc)) return rc;
6598
6599 setModified(IsModified_USB);
6600 mUSBControllers.backup();
6601 mUSBControllers->push_back(ctrl);
6602
6603 ctrl.queryInterfaceTo(controller);
6604
6605 /* inform the direct session if any */
6606 alock.release();
6607 onUSBControllerChange();
6608
6609 return S_OK;
6610}
6611
6612STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController)
6613{
6614 CheckComArgStrNotEmptyOrNull(aName);
6615
6616 AutoCaller autoCaller(this);
6617 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6618
6619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6620
6621 ComObjPtr<USBController> ctrl;
6622
6623 HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6624 if (SUCCEEDED(rc))
6625 ctrl.queryInterfaceTo(aUSBController);
6626
6627 return rc;
6628}
6629
6630STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType,
6631 ULONG *aControllers)
6632{
6633 CheckComArgOutPointerValid(aControllers);
6634
6635 if ( (aType <= USBControllerType_Null)
6636 || (aType >= USBControllerType_Last))
6637 return setError(E_INVALIDARG,
6638 tr("Invalid USB controller type: %d"),
6639 aType);
6640
6641 AutoCaller autoCaller(this);
6642 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6643
6644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6645
6646 ComObjPtr<USBController> ctrl;
6647
6648 *aControllers = getUSBControllerCountByType(aType);
6649
6650 return S_OK;
6651}
6652
6653STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName)
6654{
6655 CheckComArgStrNotEmptyOrNull(aName);
6656
6657 AutoCaller autoCaller(this);
6658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6659
6660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6661
6662 HRESULT rc = checkStateDependency(MutableStateDep);
6663 if (FAILED(rc)) return rc;
6664
6665 ComObjPtr<USBController> ctrl;
6666 rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6667 if (FAILED(rc)) return rc;
6668
6669 setModified(IsModified_USB);
6670 mUSBControllers.backup();
6671
6672 ctrl->unshare();
6673
6674 mUSBControllers->remove(ctrl);
6675
6676 /* inform the direct session if any */
6677 alock.release();
6678 onUSBControllerChange();
6679
6680 return S_OK;
6681}
6682
6683STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6684 ULONG *puOriginX,
6685 ULONG *puOriginY,
6686 ULONG *puWidth,
6687 ULONG *puHeight,
6688 BOOL *pfEnabled)
6689{
6690 LogFlowThisFunc(("\n"));
6691
6692 CheckComArgNotNull(puOriginX);
6693 CheckComArgNotNull(puOriginY);
6694 CheckComArgNotNull(puWidth);
6695 CheckComArgNotNull(puHeight);
6696 CheckComArgNotNull(pfEnabled);
6697
6698 uint32_t u32OriginX= 0;
6699 uint32_t u32OriginY= 0;
6700 uint32_t u32Width = 0;
6701 uint32_t u32Height = 0;
6702 uint16_t u16Flags = 0;
6703
6704 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6705 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6706 if (RT_FAILURE(vrc))
6707 {
6708#ifdef RT_OS_WINDOWS
6709 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6710 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6711 * So just assign fEnable to TRUE again.
6712 * The right fix would be to change GUI API wrappers to make sure that parameters
6713 * are changed only if API succeeds.
6714 */
6715 *pfEnabled = TRUE;
6716#endif
6717 return setError(VBOX_E_IPRT_ERROR,
6718 tr("Saved guest size is not available (%Rrc)"),
6719 vrc);
6720 }
6721
6722 *puOriginX = u32OriginX;
6723 *puOriginY = u32OriginY;
6724 *puWidth = u32Width;
6725 *puHeight = u32Height;
6726 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6727
6728 return S_OK;
6729}
6730
6731STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6732{
6733 LogFlowThisFunc(("\n"));
6734
6735 CheckComArgNotNull(aSize);
6736 CheckComArgNotNull(aWidth);
6737 CheckComArgNotNull(aHeight);
6738
6739 if (aScreenId != 0)
6740 return E_NOTIMPL;
6741
6742 AutoCaller autoCaller(this);
6743 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6744
6745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6746
6747 uint8_t *pu8Data = NULL;
6748 uint32_t cbData = 0;
6749 uint32_t u32Width = 0;
6750 uint32_t u32Height = 0;
6751
6752 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6753
6754 if (RT_FAILURE(vrc))
6755 return setError(VBOX_E_IPRT_ERROR,
6756 tr("Saved screenshot data is not available (%Rrc)"),
6757 vrc);
6758
6759 *aSize = cbData;
6760 *aWidth = u32Width;
6761 *aHeight = u32Height;
6762
6763 freeSavedDisplayScreenshot(pu8Data);
6764
6765 return S_OK;
6766}
6767
6768STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6769{
6770 LogFlowThisFunc(("\n"));
6771
6772 CheckComArgNotNull(aWidth);
6773 CheckComArgNotNull(aHeight);
6774 CheckComArgOutSafeArrayPointerValid(aData);
6775
6776 if (aScreenId != 0)
6777 return E_NOTIMPL;
6778
6779 AutoCaller autoCaller(this);
6780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6781
6782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6783
6784 uint8_t *pu8Data = NULL;
6785 uint32_t cbData = 0;
6786 uint32_t u32Width = 0;
6787 uint32_t u32Height = 0;
6788
6789 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6790
6791 if (RT_FAILURE(vrc))
6792 return setError(VBOX_E_IPRT_ERROR,
6793 tr("Saved screenshot data is not available (%Rrc)"),
6794 vrc);
6795
6796 *aWidth = u32Width;
6797 *aHeight = u32Height;
6798
6799 com::SafeArray<BYTE> bitmap(cbData);
6800 /* Convert pixels to format expected by the API caller. */
6801 if (aBGR)
6802 {
6803 /* [0] B, [1] G, [2] R, [3] A. */
6804 for (unsigned i = 0; i < cbData; i += 4)
6805 {
6806 bitmap[i] = pu8Data[i];
6807 bitmap[i + 1] = pu8Data[i + 1];
6808 bitmap[i + 2] = pu8Data[i + 2];
6809 bitmap[i + 3] = 0xff;
6810 }
6811 }
6812 else
6813 {
6814 /* [0] R, [1] G, [2] B, [3] A. */
6815 for (unsigned i = 0; i < cbData; i += 4)
6816 {
6817 bitmap[i] = pu8Data[i + 2];
6818 bitmap[i + 1] = pu8Data[i + 1];
6819 bitmap[i + 2] = pu8Data[i];
6820 bitmap[i + 3] = 0xff;
6821 }
6822 }
6823 bitmap.detachTo(ComSafeArrayOutArg(aData));
6824
6825 freeSavedDisplayScreenshot(pu8Data);
6826
6827 return S_OK;
6828}
6829
6830
6831STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6832{
6833 LogFlowThisFunc(("\n"));
6834
6835 CheckComArgNotNull(aWidth);
6836 CheckComArgNotNull(aHeight);
6837 CheckComArgOutSafeArrayPointerValid(aData);
6838
6839 if (aScreenId != 0)
6840 return E_NOTIMPL;
6841
6842 AutoCaller autoCaller(this);
6843 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6844
6845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6846
6847 uint8_t *pu8Data = NULL;
6848 uint32_t cbData = 0;
6849 uint32_t u32Width = 0;
6850 uint32_t u32Height = 0;
6851
6852 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6853
6854 if (RT_FAILURE(vrc))
6855 return setError(VBOX_E_IPRT_ERROR,
6856 tr("Saved screenshot data is not available (%Rrc)"),
6857 vrc);
6858
6859 *aWidth = u32Width;
6860 *aHeight = u32Height;
6861
6862 HRESULT rc = S_OK;
6863 uint8_t *pu8PNG = NULL;
6864 uint32_t cbPNG = 0;
6865 uint32_t cxPNG = 0;
6866 uint32_t cyPNG = 0;
6867
6868 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6869
6870 if (RT_SUCCESS(vrc))
6871 {
6872 com::SafeArray<BYTE> screenData(cbPNG);
6873 screenData.initFrom(pu8PNG, cbPNG);
6874 if (pu8PNG)
6875 RTMemFree(pu8PNG);
6876 screenData.detachTo(ComSafeArrayOutArg(aData));
6877 }
6878 else
6879 {
6880 if (pu8PNG)
6881 RTMemFree(pu8PNG);
6882 return setError(VBOX_E_IPRT_ERROR,
6883 tr("Could not convert screenshot to PNG (%Rrc)"),
6884 vrc);
6885 }
6886
6887 freeSavedDisplayScreenshot(pu8Data);
6888
6889 return rc;
6890}
6891
6892STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6893{
6894 LogFlowThisFunc(("\n"));
6895
6896 CheckComArgNotNull(aSize);
6897 CheckComArgNotNull(aWidth);
6898 CheckComArgNotNull(aHeight);
6899
6900 if (aScreenId != 0)
6901 return E_NOTIMPL;
6902
6903 AutoCaller autoCaller(this);
6904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6905
6906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6907
6908 uint8_t *pu8Data = NULL;
6909 uint32_t cbData = 0;
6910 uint32_t u32Width = 0;
6911 uint32_t u32Height = 0;
6912
6913 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6914
6915 if (RT_FAILURE(vrc))
6916 return setError(VBOX_E_IPRT_ERROR,
6917 tr("Saved screenshot data is not available (%Rrc)"),
6918 vrc);
6919
6920 *aSize = cbData;
6921 *aWidth = u32Width;
6922 *aHeight = u32Height;
6923
6924 freeSavedDisplayScreenshot(pu8Data);
6925
6926 return S_OK;
6927}
6928
6929STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6930{
6931 LogFlowThisFunc(("\n"));
6932
6933 CheckComArgNotNull(aWidth);
6934 CheckComArgNotNull(aHeight);
6935 CheckComArgOutSafeArrayPointerValid(aData);
6936
6937 if (aScreenId != 0)
6938 return E_NOTIMPL;
6939
6940 AutoCaller autoCaller(this);
6941 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6942
6943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6944
6945 uint8_t *pu8Data = NULL;
6946 uint32_t cbData = 0;
6947 uint32_t u32Width = 0;
6948 uint32_t u32Height = 0;
6949
6950 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6951
6952 if (RT_FAILURE(vrc))
6953 return setError(VBOX_E_IPRT_ERROR,
6954 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6955 vrc);
6956
6957 *aWidth = u32Width;
6958 *aHeight = u32Height;
6959
6960 com::SafeArray<BYTE> png(cbData);
6961 png.initFrom(pu8Data, cbData);
6962 png.detachTo(ComSafeArrayOutArg(aData));
6963
6964 freeSavedDisplayScreenshot(pu8Data);
6965
6966 return S_OK;
6967}
6968
6969STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6970{
6971 HRESULT rc = S_OK;
6972 LogFlowThisFunc(("\n"));
6973
6974 AutoCaller autoCaller(this);
6975 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6976
6977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6978
6979 if (!mHWData->mCPUHotPlugEnabled)
6980 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6981
6982 if (aCpu >= mHWData->mCPUCount)
6983 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6984
6985 if (mHWData->mCPUAttached[aCpu])
6986 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6987
6988 alock.release();
6989 rc = onCPUChange(aCpu, false);
6990 alock.acquire();
6991 if (FAILED(rc)) return rc;
6992
6993 setModified(IsModified_MachineData);
6994 mHWData.backup();
6995 mHWData->mCPUAttached[aCpu] = true;
6996
6997 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6998 if (Global::IsOnline(mData->mMachineState))
6999 saveSettings(NULL);
7000
7001 return S_OK;
7002}
7003
7004STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
7005{
7006 HRESULT rc = S_OK;
7007 LogFlowThisFunc(("\n"));
7008
7009 AutoCaller autoCaller(this);
7010 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7011
7012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7013
7014 if (!mHWData->mCPUHotPlugEnabled)
7015 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7016
7017 if (aCpu >= SchemaDefs::MaxCPUCount)
7018 return setError(E_INVALIDARG,
7019 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
7020 SchemaDefs::MaxCPUCount);
7021
7022 if (!mHWData->mCPUAttached[aCpu])
7023 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
7024
7025 /* CPU 0 can't be detached */
7026 if (aCpu == 0)
7027 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
7028
7029 alock.release();
7030 rc = onCPUChange(aCpu, true);
7031 alock.acquire();
7032 if (FAILED(rc)) return rc;
7033
7034 setModified(IsModified_MachineData);
7035 mHWData.backup();
7036 mHWData->mCPUAttached[aCpu] = false;
7037
7038 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7039 if (Global::IsOnline(mData->mMachineState))
7040 saveSettings(NULL);
7041
7042 return S_OK;
7043}
7044
7045STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
7046{
7047 LogFlowThisFunc(("\n"));
7048
7049 CheckComArgNotNull(aCpuAttached);
7050
7051 *aCpuAttached = false;
7052
7053 AutoCaller autoCaller(this);
7054 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7055
7056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7057
7058 /* If hotplug is enabled the CPU is always enabled. */
7059 if (!mHWData->mCPUHotPlugEnabled)
7060 {
7061 if (aCpu < mHWData->mCPUCount)
7062 *aCpuAttached = true;
7063 }
7064 else
7065 {
7066 if (aCpu < SchemaDefs::MaxCPUCount)
7067 *aCpuAttached = mHWData->mCPUAttached[aCpu];
7068 }
7069
7070 return S_OK;
7071}
7072
7073STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
7074{
7075 CheckComArgOutPointerValid(aName);
7076
7077 AutoCaller autoCaller(this);
7078 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7079
7080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7081
7082 Utf8Str log = queryLogFilename(aIdx);
7083 if (!RTFileExists(log.c_str()))
7084 log.setNull();
7085 log.cloneTo(aName);
7086
7087 return S_OK;
7088}
7089
7090STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
7091{
7092 LogFlowThisFunc(("\n"));
7093 CheckComArgOutSafeArrayPointerValid(aData);
7094 if (aSize < 0)
7095 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
7096
7097 AutoCaller autoCaller(this);
7098 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7099
7100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7101
7102 HRESULT rc = S_OK;
7103 Utf8Str log = queryLogFilename(aIdx);
7104
7105 /* do not unnecessarily hold the lock while doing something which does
7106 * not need the lock and potentially takes a long time. */
7107 alock.release();
7108
7109 /* Limit the chunk size to 32K for now, as that gives better performance
7110 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
7111 * One byte expands to approx. 25 bytes of breathtaking XML. */
7112 size_t cbData = (size_t)RT_MIN(aSize, 32768);
7113 com::SafeArray<BYTE> logData(cbData);
7114
7115 RTFILE LogFile;
7116 int vrc = RTFileOpen(&LogFile, log.c_str(),
7117 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
7118 if (RT_SUCCESS(vrc))
7119 {
7120 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
7121 if (RT_SUCCESS(vrc))
7122 logData.resize(cbData);
7123 else
7124 rc = setError(VBOX_E_IPRT_ERROR,
7125 tr("Could not read log file '%s' (%Rrc)"),
7126 log.c_str(), vrc);
7127 RTFileClose(LogFile);
7128 }
7129 else
7130 rc = setError(VBOX_E_IPRT_ERROR,
7131 tr("Could not open log file '%s' (%Rrc)"),
7132 log.c_str(), vrc);
7133
7134 if (FAILED(rc))
7135 logData.resize(0);
7136 logData.detachTo(ComSafeArrayOutArg(aData));
7137
7138 return rc;
7139}
7140
7141
7142/**
7143 * Currently this method doesn't attach device to the running VM,
7144 * just makes sure it's plugged on next VM start.
7145 */
7146STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
7147{
7148 AutoCaller autoCaller(this);
7149 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7150
7151 // lock scope
7152 {
7153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7154
7155 HRESULT rc = checkStateDependency(MutableStateDep);
7156 if (FAILED(rc)) return rc;
7157
7158 ChipsetType_T aChipset = ChipsetType_PIIX3;
7159 COMGETTER(ChipsetType)(&aChipset);
7160
7161 if (aChipset != ChipsetType_ICH9)
7162 {
7163 return setError(E_INVALIDARG,
7164 tr("Host PCI attachment only supported with ICH9 chipset"));
7165 }
7166
7167 // check if device with this host PCI address already attached
7168 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7169 it != mHWData->mPCIDeviceAssignments.end();
7170 ++it)
7171 {
7172 LONG iHostAddress = -1;
7173 ComPtr<PCIDeviceAttachment> pAttach;
7174 pAttach = *it;
7175 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7176 if (iHostAddress == hostAddress)
7177 return setError(E_INVALIDARG,
7178 tr("Device with host PCI address already attached to this VM"));
7179 }
7180
7181 ComObjPtr<PCIDeviceAttachment> pda;
7182 char name[32];
7183
7184 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7185 Bstr bname(name);
7186 pda.createObject();
7187 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7188 setModified(IsModified_MachineData);
7189 mHWData.backup();
7190 mHWData->mPCIDeviceAssignments.push_back(pda);
7191 }
7192
7193 return S_OK;
7194}
7195
7196/**
7197 * Currently this method doesn't detach device from the running VM,
7198 * just makes sure it's not plugged on next VM start.
7199 */
7200STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7201{
7202 AutoCaller autoCaller(this);
7203 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7204
7205 ComObjPtr<PCIDeviceAttachment> pAttach;
7206 bool fRemoved = false;
7207 HRESULT rc;
7208
7209 // lock scope
7210 {
7211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7212
7213 rc = checkStateDependency(MutableStateDep);
7214 if (FAILED(rc)) return rc;
7215
7216 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7217 it != mHWData->mPCIDeviceAssignments.end();
7218 ++it)
7219 {
7220 LONG iHostAddress = -1;
7221 pAttach = *it;
7222 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7223 if (iHostAddress != -1 && iHostAddress == hostAddress)
7224 {
7225 setModified(IsModified_MachineData);
7226 mHWData.backup();
7227 mHWData->mPCIDeviceAssignments.remove(pAttach);
7228 fRemoved = true;
7229 break;
7230 }
7231 }
7232 }
7233
7234
7235 /* Fire event outside of the lock */
7236 if (fRemoved)
7237 {
7238 Assert(!pAttach.isNull());
7239 ComPtr<IEventSource> es;
7240 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7241 Assert(SUCCEEDED(rc));
7242 Bstr mid;
7243 rc = this->COMGETTER(Id)(mid.asOutParam());
7244 Assert(SUCCEEDED(rc));
7245 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7246 }
7247
7248 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7249 tr("No host PCI device %08x attached"),
7250 hostAddress
7251 );
7252}
7253
7254STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7255{
7256 CheckComArgOutSafeArrayPointerValid(aAssignments);
7257
7258 AutoCaller autoCaller(this);
7259 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7260
7261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7262
7263 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7264 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7265
7266 return S_OK;
7267}
7268
7269STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7270{
7271 CheckComArgOutPointerValid(aBandwidthControl);
7272
7273 AutoCaller autoCaller(this);
7274 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7275
7276 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7277
7278 return S_OK;
7279}
7280
7281STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7282{
7283 CheckComArgOutPointerValid(pfEnabled);
7284 AutoCaller autoCaller(this);
7285 HRESULT hrc = autoCaller.rc();
7286 if (SUCCEEDED(hrc))
7287 {
7288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7289 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7290 }
7291 return hrc;
7292}
7293
7294STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7295{
7296 AutoCaller autoCaller(this);
7297 HRESULT hrc = autoCaller.rc();
7298 if (SUCCEEDED(hrc))
7299 {
7300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7301 hrc = checkStateDependency(MutableStateDep);
7302 if (SUCCEEDED(hrc))
7303 {
7304 hrc = mHWData.backupEx();
7305 if (SUCCEEDED(hrc))
7306 {
7307 setModified(IsModified_MachineData);
7308 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7309 }
7310 }
7311 }
7312 return hrc;
7313}
7314
7315STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7316{
7317 CheckComArgOutPointerValid(pbstrConfig);
7318 AutoCaller autoCaller(this);
7319 HRESULT hrc = autoCaller.rc();
7320 if (SUCCEEDED(hrc))
7321 {
7322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7323 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7324 }
7325 return hrc;
7326}
7327
7328STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7329{
7330 CheckComArgStr(bstrConfig);
7331 AutoCaller autoCaller(this);
7332 HRESULT hrc = autoCaller.rc();
7333 if (SUCCEEDED(hrc))
7334 {
7335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7336 hrc = checkStateDependency(MutableStateDep);
7337 if (SUCCEEDED(hrc))
7338 {
7339 hrc = mHWData.backupEx();
7340 if (SUCCEEDED(hrc))
7341 {
7342 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7343 if (SUCCEEDED(hrc))
7344 setModified(IsModified_MachineData);
7345 }
7346 }
7347 }
7348 return hrc;
7349
7350}
7351
7352STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7353{
7354 CheckComArgOutPointerValid(pfAllow);
7355 AutoCaller autoCaller(this);
7356 HRESULT hrc = autoCaller.rc();
7357 if (SUCCEEDED(hrc))
7358 {
7359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7360 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7361 }
7362 return hrc;
7363}
7364
7365STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7366{
7367 AutoCaller autoCaller(this);
7368 HRESULT hrc = autoCaller.rc();
7369 if (SUCCEEDED(hrc))
7370 {
7371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7372 hrc = checkStateDependency(MutableStateDep);
7373 if (SUCCEEDED(hrc))
7374 {
7375 hrc = mHWData.backupEx();
7376 if (SUCCEEDED(hrc))
7377 {
7378 setModified(IsModified_MachineData);
7379 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7380 }
7381 }
7382 }
7383 return hrc;
7384}
7385
7386STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7387{
7388 CheckComArgOutPointerValid(pfEnabled);
7389 AutoCaller autoCaller(this);
7390 HRESULT hrc = autoCaller.rc();
7391 if (SUCCEEDED(hrc))
7392 {
7393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7394 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7395 }
7396 return hrc;
7397}
7398
7399STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7400{
7401 AutoCaller autoCaller(this);
7402 HRESULT hrc = autoCaller.rc();
7403 if (SUCCEEDED(hrc))
7404 {
7405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7406 hrc = checkStateDependency(MutableStateDep);
7407 if ( SUCCEEDED(hrc)
7408 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7409 {
7410 AutostartDb *autostartDb = mParent->getAutostartDb();
7411 int vrc;
7412
7413 if (fEnabled)
7414 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7415 else
7416 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7417
7418 if (RT_SUCCESS(vrc))
7419 {
7420 hrc = mHWData.backupEx();
7421 if (SUCCEEDED(hrc))
7422 {
7423 setModified(IsModified_MachineData);
7424 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7425 }
7426 }
7427 else if (vrc == VERR_NOT_SUPPORTED)
7428 hrc = setError(VBOX_E_NOT_SUPPORTED,
7429 tr("The VM autostart feature is not supported on this platform"));
7430 else if (vrc == VERR_PATH_NOT_FOUND)
7431 hrc = setError(E_FAIL,
7432 tr("The path to the autostart database is not set"));
7433 else
7434 hrc = setError(E_UNEXPECTED,
7435 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7436 fEnabled ? "Adding" : "Removing",
7437 mUserData->s.strName.c_str(), vrc);
7438 }
7439 }
7440 return hrc;
7441}
7442
7443STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7444{
7445 CheckComArgOutPointerValid(puDelay);
7446 AutoCaller autoCaller(this);
7447 HRESULT hrc = autoCaller.rc();
7448 if (SUCCEEDED(hrc))
7449 {
7450 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7451 *puDelay = mHWData->mAutostart.uAutostartDelay;
7452 }
7453 return hrc;
7454}
7455
7456STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7457{
7458 AutoCaller autoCaller(this);
7459 HRESULT hrc = autoCaller.rc();
7460 if (SUCCEEDED(hrc))
7461 {
7462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7463 hrc = checkStateDependency(MutableStateDep);
7464 if (SUCCEEDED(hrc))
7465 {
7466 hrc = mHWData.backupEx();
7467 if (SUCCEEDED(hrc))
7468 {
7469 setModified(IsModified_MachineData);
7470 mHWData->mAutostart.uAutostartDelay = uDelay;
7471 }
7472 }
7473 }
7474 return hrc;
7475}
7476
7477STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7478{
7479 CheckComArgOutPointerValid(penmAutostopType);
7480 AutoCaller autoCaller(this);
7481 HRESULT hrc = autoCaller.rc();
7482 if (SUCCEEDED(hrc))
7483 {
7484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7485 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7486 }
7487 return hrc;
7488}
7489
7490STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7491{
7492 AutoCaller autoCaller(this);
7493 HRESULT hrc = autoCaller.rc();
7494 if (SUCCEEDED(hrc))
7495 {
7496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7497 hrc = checkStateDependency(MutableStateDep);
7498 if ( SUCCEEDED(hrc)
7499 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7500 {
7501 AutostartDb *autostartDb = mParent->getAutostartDb();
7502 int vrc;
7503
7504 if (enmAutostopType != AutostopType_Disabled)
7505 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7506 else
7507 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7508
7509 if (RT_SUCCESS(vrc))
7510 {
7511 hrc = mHWData.backupEx();
7512 if (SUCCEEDED(hrc))
7513 {
7514 setModified(IsModified_MachineData);
7515 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7516 }
7517 }
7518 else if (vrc == VERR_NOT_SUPPORTED)
7519 hrc = setError(VBOX_E_NOT_SUPPORTED,
7520 tr("The VM autostop feature is not supported on this platform"));
7521 else if (vrc == VERR_PATH_NOT_FOUND)
7522 hrc = setError(E_FAIL,
7523 tr("The path to the autostart database is not set"));
7524 else
7525 hrc = setError(E_UNEXPECTED,
7526 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7527 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7528 mUserData->s.strName.c_str(), vrc);
7529 }
7530 }
7531 return hrc;
7532}
7533
7534STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7535{
7536 CheckComArgOutPointerValid(aDefaultFrontend);
7537 AutoCaller autoCaller(this);
7538 HRESULT hrc = autoCaller.rc();
7539 if (SUCCEEDED(hrc))
7540 {
7541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7542 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7543 }
7544 return hrc;
7545}
7546
7547STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7548{
7549 CheckComArgStr(aDefaultFrontend);
7550 AutoCaller autoCaller(this);
7551 HRESULT hrc = autoCaller.rc();
7552 if (SUCCEEDED(hrc))
7553 {
7554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7555 hrc = checkStateDependency(MutableOrSavedStateDep);
7556 if (SUCCEEDED(hrc))
7557 {
7558 hrc = mHWData.backupEx();
7559 if (SUCCEEDED(hrc))
7560 {
7561 setModified(IsModified_MachineData);
7562 mHWData->mDefaultFrontend = aDefaultFrontend;
7563 }
7564 }
7565 }
7566 return hrc;
7567}
7568
7569STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7570{
7571 CheckComArgSafeArrayNotNull(aIcon);
7572 CheckComArgOutSafeArrayPointerValid(aIcon);
7573 AutoCaller autoCaller(this);
7574 HRESULT hrc = autoCaller.rc();
7575 if (SUCCEEDED(hrc))
7576 {
7577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7578 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7579 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7580 icon.detachTo(ComSafeArrayOutArg(aIcon));
7581 }
7582 return hrc;
7583}
7584
7585STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7586{
7587 CheckComArgSafeArrayNotNull(aIcon);
7588 AutoCaller autoCaller(this);
7589 HRESULT hrc = autoCaller.rc();
7590 if (SUCCEEDED(hrc))
7591 {
7592 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7593 hrc = checkStateDependency(MutableOrSavedStateDep);
7594 if (SUCCEEDED(hrc))
7595 {
7596 setModified(IsModified_MachineData);
7597 mUserData.backup();
7598 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7599 mUserData->mIcon.clear();
7600 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7601 }
7602 }
7603 return hrc;
7604}
7605
7606STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable)
7607{
7608 CheckComArgOutPointerValid(aAvailable);
7609
7610 AutoCaller autoCaller(this);
7611 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7612
7613#ifdef VBOX_WITH_USB
7614 *aAvailable = true;
7615#else
7616 *aAvailable = false;
7617#endif
7618 return S_OK;
7619}
7620
7621STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7622{
7623 LogFlowFuncEnter();
7624
7625 CheckComArgNotNull(pTarget);
7626 CheckComArgOutPointerValid(pProgress);
7627
7628 /* Convert the options. */
7629 RTCList<CloneOptions_T> optList;
7630 if (options != NULL)
7631 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7632
7633 if (optList.contains(CloneOptions_Link))
7634 {
7635 if (!isSnapshotMachine())
7636 return setError(E_INVALIDARG,
7637 tr("Linked clone can only be created from a snapshot"));
7638 if (mode != CloneMode_MachineState)
7639 return setError(E_INVALIDARG,
7640 tr("Linked clone can only be created for a single machine state"));
7641 }
7642 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7643
7644 AutoCaller autoCaller(this);
7645 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7646
7647
7648 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7649
7650 HRESULT rc = pWorker->start(pProgress);
7651
7652 LogFlowFuncLeave();
7653
7654 return rc;
7655}
7656
7657// public methods for internal purposes
7658/////////////////////////////////////////////////////////////////////////////
7659
7660/**
7661 * Adds the given IsModified_* flag to the dirty flags of the machine.
7662 * This must be called either during loadSettings or under the machine write lock.
7663 * @param fl
7664 */
7665void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7666{
7667 mData->flModifications |= fl;
7668 if (fAllowStateModification && isStateModificationAllowed())
7669 mData->mCurrentStateModified = true;
7670}
7671
7672/**
7673 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7674 * care of the write locking.
7675 *
7676 * @param fModifications The flag to add.
7677 */
7678void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7679{
7680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7681 setModified(fModification, fAllowStateModification);
7682}
7683
7684/**
7685 * Saves the registry entry of this machine to the given configuration node.
7686 *
7687 * @param aEntryNode Node to save the registry entry to.
7688 *
7689 * @note locks this object for reading.
7690 */
7691HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7692{
7693 AutoLimitedCaller autoCaller(this);
7694 AssertComRCReturnRC(autoCaller.rc());
7695
7696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7697
7698 data.uuid = mData->mUuid;
7699 data.strSettingsFile = mData->m_strConfigFile;
7700
7701 return S_OK;
7702}
7703
7704/**
7705 * Calculates the absolute path of the given path taking the directory of the
7706 * machine settings file as the current directory.
7707 *
7708 * @param aPath Path to calculate the absolute path for.
7709 * @param aResult Where to put the result (used only on success, can be the
7710 * same Utf8Str instance as passed in @a aPath).
7711 * @return IPRT result.
7712 *
7713 * @note Locks this object for reading.
7714 */
7715int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7716{
7717 AutoCaller autoCaller(this);
7718 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7719
7720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7721
7722 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7723
7724 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7725
7726 strSettingsDir.stripFilename();
7727 char folder[RTPATH_MAX];
7728 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7729 if (RT_SUCCESS(vrc))
7730 aResult = folder;
7731
7732 return vrc;
7733}
7734
7735/**
7736 * Copies strSource to strTarget, making it relative to the machine folder
7737 * if it is a subdirectory thereof, or simply copying it otherwise.
7738 *
7739 * @param strSource Path to evaluate and copy.
7740 * @param strTarget Buffer to receive target path.
7741 *
7742 * @note Locks this object for reading.
7743 */
7744void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7745 Utf8Str &strTarget)
7746{
7747 AutoCaller autoCaller(this);
7748 AssertComRCReturn(autoCaller.rc(), (void)0);
7749
7750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7751
7752 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7753 // use strTarget as a temporary buffer to hold the machine settings dir
7754 strTarget = mData->m_strConfigFileFull;
7755 strTarget.stripFilename();
7756 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7757 {
7758 // is relative: then append what's left
7759 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7760 // for empty paths (only possible for subdirs) use "." to avoid
7761 // triggering default settings for not present config attributes.
7762 if (strTarget.isEmpty())
7763 strTarget = ".";
7764 }
7765 else
7766 // is not relative: then overwrite
7767 strTarget = strSource;
7768}
7769
7770/**
7771 * Returns the full path to the machine's log folder in the
7772 * \a aLogFolder argument.
7773 */
7774void Machine::getLogFolder(Utf8Str &aLogFolder)
7775{
7776 AutoCaller autoCaller(this);
7777 AssertComRCReturnVoid(autoCaller.rc());
7778
7779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7780
7781 char szTmp[RTPATH_MAX];
7782 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7783 if (RT_SUCCESS(vrc))
7784 {
7785 if (szTmp[0] && !mUserData.isNull())
7786 {
7787 char szTmp2[RTPATH_MAX];
7788 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7789 if (RT_SUCCESS(vrc))
7790 aLogFolder = BstrFmt("%s%c%s",
7791 szTmp2,
7792 RTPATH_DELIMITER,
7793 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7794 }
7795 else
7796 vrc = VERR_PATH_IS_RELATIVE;
7797 }
7798
7799 if (RT_FAILURE(vrc))
7800 {
7801 // fallback if VBOX_USER_LOGHOME is not set or invalid
7802 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7803 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7804 aLogFolder.append(RTPATH_DELIMITER);
7805 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7806 }
7807}
7808
7809/**
7810 * Returns the full path to the machine's log file for an given index.
7811 */
7812Utf8Str Machine::queryLogFilename(ULONG idx)
7813{
7814 Utf8Str logFolder;
7815 getLogFolder(logFolder);
7816 Assert(logFolder.length());
7817 Utf8Str log;
7818 if (idx == 0)
7819 log = Utf8StrFmt("%s%cVBox.log",
7820 logFolder.c_str(), RTPATH_DELIMITER);
7821 else
7822 log = Utf8StrFmt("%s%cVBox.log.%d",
7823 logFolder.c_str(), RTPATH_DELIMITER, idx);
7824 return log;
7825}
7826
7827/**
7828 * Composes a unique saved state filename based on the current system time. The filename is
7829 * granular to the second so this will work so long as no more than one snapshot is taken on
7830 * a machine per second.
7831 *
7832 * Before version 4.1, we used this formula for saved state files:
7833 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7834 * which no longer works because saved state files can now be shared between the saved state of the
7835 * "saved" machine and an online snapshot, and the following would cause problems:
7836 * 1) save machine
7837 * 2) create online snapshot from that machine state --> reusing saved state file
7838 * 3) save machine again --> filename would be reused, breaking the online snapshot
7839 *
7840 * So instead we now use a timestamp.
7841 *
7842 * @param str
7843 */
7844void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7845{
7846 AutoCaller autoCaller(this);
7847 AssertComRCReturnVoid(autoCaller.rc());
7848
7849 {
7850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7851 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7852 }
7853
7854 RTTIMESPEC ts;
7855 RTTimeNow(&ts);
7856 RTTIME time;
7857 RTTimeExplode(&time, &ts);
7858
7859 strStateFilePath += RTPATH_DELIMITER;
7860 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7861 time.i32Year, time.u8Month, time.u8MonthDay,
7862 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7863}
7864
7865/**
7866 * Returns the full path to the default video capture file.
7867 */
7868void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7869{
7870 AutoCaller autoCaller(this);
7871 AssertComRCReturnVoid(autoCaller.rc());
7872
7873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7874
7875 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7876 strFile.stripExt(); // path/to/machinesfolder/vmname/vmname
7877 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7878}
7879
7880/**
7881 * Returns whether at least one USB controller is present for the VM.
7882 */
7883bool Machine::isUSBControllerPresent()
7884{
7885 AutoCaller autoCaller(this);
7886 AssertComRCReturn(autoCaller.rc(), false);
7887
7888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7889
7890 return (mUSBControllers->size() > 0);
7891}
7892
7893/**
7894 * @note Locks this object for writing, calls the client process
7895 * (inside the lock).
7896 */
7897HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7898 const Utf8Str &strFrontend,
7899 const Utf8Str &strEnvironment,
7900 ProgressProxy *aProgress)
7901{
7902 LogFlowThisFuncEnter();
7903
7904 AssertReturn(aControl, E_FAIL);
7905 AssertReturn(aProgress, E_FAIL);
7906 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7907
7908 AutoCaller autoCaller(this);
7909 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7910
7911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7912
7913 if (!mData->mRegistered)
7914 return setError(E_UNEXPECTED,
7915 tr("The machine '%s' is not registered"),
7916 mUserData->s.strName.c_str());
7917
7918 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7919
7920 if ( mData->mSession.mState == SessionState_Locked
7921 || mData->mSession.mState == SessionState_Spawning
7922 || mData->mSession.mState == SessionState_Unlocking)
7923 return setError(VBOX_E_INVALID_OBJECT_STATE,
7924 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7925 mUserData->s.strName.c_str());
7926
7927 /* may not be busy */
7928 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7929
7930 /* get the path to the executable */
7931 char szPath[RTPATH_MAX];
7932 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7933 size_t sz = strlen(szPath);
7934 szPath[sz++] = RTPATH_DELIMITER;
7935 szPath[sz] = 0;
7936 char *cmd = szPath + sz;
7937 sz = RTPATH_MAX - sz;
7938
7939 int vrc = VINF_SUCCESS;
7940 RTPROCESS pid = NIL_RTPROCESS;
7941
7942 RTENV env = RTENV_DEFAULT;
7943
7944 if (!strEnvironment.isEmpty())
7945 {
7946 char *newEnvStr = NULL;
7947
7948 do
7949 {
7950 /* clone the current environment */
7951 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7952 AssertRCBreakStmt(vrc2, vrc = vrc2);
7953
7954 newEnvStr = RTStrDup(strEnvironment.c_str());
7955 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7956
7957 /* put new variables to the environment
7958 * (ignore empty variable names here since RTEnv API
7959 * intentionally doesn't do that) */
7960 char *var = newEnvStr;
7961 for (char *p = newEnvStr; *p; ++p)
7962 {
7963 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7964 {
7965 *p = '\0';
7966 if (*var)
7967 {
7968 char *val = strchr(var, '=');
7969 if (val)
7970 {
7971 *val++ = '\0';
7972 vrc2 = RTEnvSetEx(env, var, val);
7973 }
7974 else
7975 vrc2 = RTEnvUnsetEx(env, var);
7976 if (RT_FAILURE(vrc2))
7977 break;
7978 }
7979 var = p + 1;
7980 }
7981 }
7982 if (RT_SUCCESS(vrc2) && *var)
7983 vrc2 = RTEnvPutEx(env, var);
7984
7985 AssertRCBreakStmt(vrc2, vrc = vrc2);
7986 }
7987 while (0);
7988
7989 if (newEnvStr != NULL)
7990 RTStrFree(newEnvStr);
7991 }
7992
7993#ifdef VBOX_WITH_QTGUI
7994 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7995 {
7996# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7997 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7998# else
7999 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
8000# endif
8001 Assert(sz >= sizeof(VirtualBox_exe));
8002 strcpy(cmd, VirtualBox_exe);
8003
8004 Utf8Str idStr = mData->mUuid.toString();
8005 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
8006 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8007 }
8008#else /* !VBOX_WITH_QTGUI */
8009 if (0)
8010 ;
8011#endif /* VBOX_WITH_QTGUI */
8012
8013 else
8014
8015#ifdef VBOX_WITH_VBOXSDL
8016 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
8017 {
8018 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
8019 Assert(sz >= sizeof(VBoxSDL_exe));
8020 strcpy(cmd, VBoxSDL_exe);
8021
8022 Utf8Str idStr = mData->mUuid.toString();
8023 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
8024 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8025 }
8026#else /* !VBOX_WITH_VBOXSDL */
8027 if (0)
8028 ;
8029#endif /* !VBOX_WITH_VBOXSDL */
8030
8031 else
8032
8033#ifdef VBOX_WITH_HEADLESS
8034 if ( strFrontend == "headless"
8035 || strFrontend == "capture"
8036 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
8037 )
8038 {
8039 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
8040 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
8041 * and a VM works even if the server has not been installed.
8042 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
8043 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
8044 * differently in 4.0 and 3.x.
8045 */
8046 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
8047 Assert(sz >= sizeof(VBoxHeadless_exe));
8048 strcpy(cmd, VBoxHeadless_exe);
8049
8050 Utf8Str idStr = mData->mUuid.toString();
8051 /* Leave space for "--capture" arg. */
8052 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
8053 "--startvm", idStr.c_str(),
8054 "--vrde", "config",
8055 0, /* For "--capture". */
8056 0 };
8057 if (strFrontend == "capture")
8058 {
8059 unsigned pos = RT_ELEMENTS(args) - 2;
8060 args[pos] = "--capture";
8061 }
8062 vrc = RTProcCreate(szPath, args, env,
8063#ifdef RT_OS_WINDOWS
8064 RTPROC_FLAGS_NO_WINDOW
8065#else
8066 0
8067#endif
8068 , &pid);
8069 }
8070#else /* !VBOX_WITH_HEADLESS */
8071 if (0)
8072 ;
8073#endif /* !VBOX_WITH_HEADLESS */
8074 else
8075 {
8076 RTEnvDestroy(env);
8077 return setError(E_INVALIDARG,
8078 tr("Invalid frontend name: '%s'"),
8079 strFrontend.c_str());
8080 }
8081
8082 RTEnvDestroy(env);
8083
8084 if (RT_FAILURE(vrc))
8085 return setError(VBOX_E_IPRT_ERROR,
8086 tr("Could not launch a process for the machine '%s' (%Rrc)"),
8087 mUserData->s.strName.c_str(), vrc);
8088
8089 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
8090
8091 /*
8092 * Note that we don't release the lock here before calling the client,
8093 * because it doesn't need to call us back if called with a NULL argument.
8094 * Releasing the lock here is dangerous because we didn't prepare the
8095 * launch data yet, but the client we've just started may happen to be
8096 * too fast and call LockMachine() that will fail (because of PID, etc.),
8097 * so that the Machine will never get out of the Spawning session state.
8098 */
8099
8100 /* inform the session that it will be a remote one */
8101 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8102 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8103 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8104
8105 if (FAILED(rc))
8106 {
8107 /* restore the session state */
8108 mData->mSession.mState = SessionState_Unlocked;
8109 /* The failure may occur w/o any error info (from RPC), so provide one */
8110 return setError(VBOX_E_VM_ERROR,
8111 tr("Failed to assign the machine to the session (%Rrc)"), rc);
8112 }
8113
8114 /* attach launch data to the machine */
8115 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8116 mData->mSession.mRemoteControls.push_back(aControl);
8117 mData->mSession.mProgress = aProgress;
8118 mData->mSession.mPID = pid;
8119 mData->mSession.mState = SessionState_Spawning;
8120 mData->mSession.mType = strFrontend;
8121
8122 LogFlowThisFuncLeave();
8123 return S_OK;
8124}
8125
8126/**
8127 * Returns @c true if the given session machine instance has an open direct
8128 * session (and optionally also for direct sessions which are closing) and
8129 * returns the session control machine instance if so.
8130 *
8131 * Note that when the method returns @c false, the arguments remain unchanged.
8132 *
8133 * @param aMachine Session machine object.
8134 * @param aControl Direct session control object (optional).
8135 * @param aAllowClosing If true then additionally a session which is currently
8136 * being closed will also be allowed.
8137 *
8138 * @note locks this object for reading.
8139 */
8140bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8141 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8142 bool aAllowClosing /*= false*/)
8143{
8144 AutoLimitedCaller autoCaller(this);
8145 AssertComRCReturn(autoCaller.rc(), false);
8146
8147 /* just return false for inaccessible machines */
8148 if (autoCaller.state() != Ready)
8149 return false;
8150
8151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8152
8153 if ( mData->mSession.mState == SessionState_Locked
8154 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8155 )
8156 {
8157 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8158
8159 aMachine = mData->mSession.mMachine;
8160
8161 if (aControl != NULL)
8162 *aControl = mData->mSession.mDirectControl;
8163
8164 return true;
8165 }
8166
8167 return false;
8168}
8169
8170/**
8171 * Returns @c true if the given machine has an spawning direct session.
8172 *
8173 * @note locks this object for reading.
8174 */
8175bool Machine::isSessionSpawning()
8176{
8177 AutoLimitedCaller autoCaller(this);
8178 AssertComRCReturn(autoCaller.rc(), false);
8179
8180 /* just return false for inaccessible machines */
8181 if (autoCaller.state() != Ready)
8182 return false;
8183
8184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8185
8186 if (mData->mSession.mState == SessionState_Spawning)
8187 return true;
8188
8189 return false;
8190}
8191
8192/**
8193 * Called from the client watcher thread to check for unexpected client process
8194 * death during Session_Spawning state (e.g. before it successfully opened a
8195 * direct session).
8196 *
8197 * On Win32 and on OS/2, this method is called only when we've got the
8198 * direct client's process termination notification, so it always returns @c
8199 * true.
8200 *
8201 * On other platforms, this method returns @c true if the client process is
8202 * terminated and @c false if it's still alive.
8203 *
8204 * @note Locks this object for writing.
8205 */
8206bool Machine::checkForSpawnFailure()
8207{
8208 AutoCaller autoCaller(this);
8209 if (!autoCaller.isOk())
8210 {
8211 /* nothing to do */
8212 LogFlowThisFunc(("Already uninitialized!\n"));
8213 return true;
8214 }
8215
8216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8217
8218 if (mData->mSession.mState != SessionState_Spawning)
8219 {
8220 /* nothing to do */
8221 LogFlowThisFunc(("Not spawning any more!\n"));
8222 return true;
8223 }
8224
8225 HRESULT rc = S_OK;
8226
8227 /* PID not yet initialized, skip check. */
8228 if (mData->mSession.mPID == NIL_RTPROCESS)
8229 return false;
8230
8231 RTPROCSTATUS status;
8232 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8233
8234 if (vrc != VERR_PROCESS_RUNNING)
8235 {
8236 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8237 rc = setError(E_FAIL,
8238 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8239 getName().c_str(), status.iStatus);
8240 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8241 rc = setError(E_FAIL,
8242 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8243 getName().c_str(), status.iStatus);
8244 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8245 rc = setError(E_FAIL,
8246 tr("The virtual machine '%s' has terminated abnormally"),
8247 getName().c_str(), status.iStatus);
8248 else
8249 rc = setError(E_FAIL,
8250 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8251 getName().c_str(), rc);
8252 }
8253
8254 if (FAILED(rc))
8255 {
8256 /* Close the remote session, remove the remote control from the list
8257 * and reset session state to Closed (@note keep the code in sync with
8258 * the relevant part in LockMachine()). */
8259
8260 Assert(mData->mSession.mRemoteControls.size() == 1);
8261 if (mData->mSession.mRemoteControls.size() == 1)
8262 {
8263 ErrorInfoKeeper eik;
8264 mData->mSession.mRemoteControls.front()->Uninitialize();
8265 }
8266
8267 mData->mSession.mRemoteControls.clear();
8268 mData->mSession.mState = SessionState_Unlocked;
8269
8270 /* finalize the progress after setting the state */
8271 if (!mData->mSession.mProgress.isNull())
8272 {
8273 mData->mSession.mProgress->notifyComplete(rc);
8274 mData->mSession.mProgress.setNull();
8275 }
8276
8277 mData->mSession.mPID = NIL_RTPROCESS;
8278
8279 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8280 return true;
8281 }
8282
8283 return false;
8284}
8285
8286/**
8287 * Checks whether the machine can be registered. If so, commits and saves
8288 * all settings.
8289 *
8290 * @note Must be called from mParent's write lock. Locks this object and
8291 * children for writing.
8292 */
8293HRESULT Machine::prepareRegister()
8294{
8295 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8296
8297 AutoLimitedCaller autoCaller(this);
8298 AssertComRCReturnRC(autoCaller.rc());
8299
8300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8301
8302 /* wait for state dependents to drop to zero */
8303 ensureNoStateDependencies();
8304
8305 if (!mData->mAccessible)
8306 return setError(VBOX_E_INVALID_OBJECT_STATE,
8307 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8308 mUserData->s.strName.c_str(),
8309 mData->mUuid.toString().c_str());
8310
8311 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8312
8313 if (mData->mRegistered)
8314 return setError(VBOX_E_INVALID_OBJECT_STATE,
8315 tr("The machine '%s' with UUID {%s} is already registered"),
8316 mUserData->s.strName.c_str(),
8317 mData->mUuid.toString().c_str());
8318
8319 HRESULT rc = S_OK;
8320
8321 // Ensure the settings are saved. If we are going to be registered and
8322 // no config file exists yet, create it by calling saveSettings() too.
8323 if ( (mData->flModifications)
8324 || (!mData->pMachineConfigFile->fileExists())
8325 )
8326 {
8327 rc = saveSettings(NULL);
8328 // no need to check whether VirtualBox.xml needs saving too since
8329 // we can't have a machine XML file rename pending
8330 if (FAILED(rc)) return rc;
8331 }
8332
8333 /* more config checking goes here */
8334
8335 if (SUCCEEDED(rc))
8336 {
8337 /* we may have had implicit modifications we want to fix on success */
8338 commit();
8339
8340 mData->mRegistered = true;
8341 }
8342 else
8343 {
8344 /* we may have had implicit modifications we want to cancel on failure*/
8345 rollback(false /* aNotify */);
8346 }
8347
8348 return rc;
8349}
8350
8351/**
8352 * Increases the number of objects dependent on the machine state or on the
8353 * registered state. Guarantees that these two states will not change at least
8354 * until #releaseStateDependency() is called.
8355 *
8356 * Depending on the @a aDepType value, additional state checks may be made.
8357 * These checks will set extended error info on failure. See
8358 * #checkStateDependency() for more info.
8359 *
8360 * If this method returns a failure, the dependency is not added and the caller
8361 * is not allowed to rely on any particular machine state or registration state
8362 * value and may return the failed result code to the upper level.
8363 *
8364 * @param aDepType Dependency type to add.
8365 * @param aState Current machine state (NULL if not interested).
8366 * @param aRegistered Current registered state (NULL if not interested).
8367 *
8368 * @note Locks this object for writing.
8369 */
8370HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8371 MachineState_T *aState /* = NULL */,
8372 BOOL *aRegistered /* = NULL */)
8373{
8374 AutoCaller autoCaller(this);
8375 AssertComRCReturnRC(autoCaller.rc());
8376
8377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8378
8379 HRESULT rc = checkStateDependency(aDepType);
8380 if (FAILED(rc)) return rc;
8381
8382 {
8383 if (mData->mMachineStateChangePending != 0)
8384 {
8385 /* ensureNoStateDependencies() is waiting for state dependencies to
8386 * drop to zero so don't add more. It may make sense to wait a bit
8387 * and retry before reporting an error (since the pending state
8388 * transition should be really quick) but let's just assert for
8389 * now to see if it ever happens on practice. */
8390
8391 AssertFailed();
8392
8393 return setError(E_ACCESSDENIED,
8394 tr("Machine state change is in progress. Please retry the operation later."));
8395 }
8396
8397 ++mData->mMachineStateDeps;
8398 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8399 }
8400
8401 if (aState)
8402 *aState = mData->mMachineState;
8403 if (aRegistered)
8404 *aRegistered = mData->mRegistered;
8405
8406 return S_OK;
8407}
8408
8409/**
8410 * Decreases the number of objects dependent on the machine state.
8411 * Must always complete the #addStateDependency() call after the state
8412 * dependency is no more necessary.
8413 */
8414void Machine::releaseStateDependency()
8415{
8416 AutoCaller autoCaller(this);
8417 AssertComRCReturnVoid(autoCaller.rc());
8418
8419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8420
8421 /* releaseStateDependency() w/o addStateDependency()? */
8422 AssertReturnVoid(mData->mMachineStateDeps != 0);
8423 -- mData->mMachineStateDeps;
8424
8425 if (mData->mMachineStateDeps == 0)
8426 {
8427 /* inform ensureNoStateDependencies() that there are no more deps */
8428 if (mData->mMachineStateChangePending != 0)
8429 {
8430 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8431 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8432 }
8433 }
8434}
8435
8436Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8437{
8438 /* start with nothing found */
8439 Utf8Str strResult("");
8440
8441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8442
8443 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8444 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8445 // found:
8446 strResult = it->second; // source is a Utf8Str
8447
8448 return strResult;
8449}
8450
8451// protected methods
8452/////////////////////////////////////////////////////////////////////////////
8453
8454/**
8455 * Performs machine state checks based on the @a aDepType value. If a check
8456 * fails, this method will set extended error info, otherwise it will return
8457 * S_OK. It is supposed, that on failure, the caller will immediately return
8458 * the return value of this method to the upper level.
8459 *
8460 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8461 *
8462 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8463 * current state of this machine object allows to change settings of the
8464 * machine (i.e. the machine is not registered, or registered but not running
8465 * and not saved). It is useful to call this method from Machine setters
8466 * before performing any change.
8467 *
8468 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8469 * as for MutableStateDep except that if the machine is saved, S_OK is also
8470 * returned. This is useful in setters which allow changing machine
8471 * properties when it is in the saved state.
8472 *
8473 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8474 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8475 * Aborted).
8476 *
8477 * @param aDepType Dependency type to check.
8478 *
8479 * @note Non Machine based classes should use #addStateDependency() and
8480 * #releaseStateDependency() methods or the smart AutoStateDependency
8481 * template.
8482 *
8483 * @note This method must be called from under this object's read or write
8484 * lock.
8485 */
8486HRESULT Machine::checkStateDependency(StateDependency aDepType)
8487{
8488 switch (aDepType)
8489 {
8490 case AnyStateDep:
8491 {
8492 break;
8493 }
8494 case MutableStateDep:
8495 {
8496 if ( mData->mRegistered
8497 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8498 || ( mData->mMachineState != MachineState_Paused
8499 && mData->mMachineState != MachineState_Running
8500 && mData->mMachineState != MachineState_Aborted
8501 && mData->mMachineState != MachineState_Teleported
8502 && mData->mMachineState != MachineState_PoweredOff
8503 )
8504 )
8505 )
8506 return setError(VBOX_E_INVALID_VM_STATE,
8507 tr("The machine is not mutable (state is %s)"),
8508 Global::stringifyMachineState(mData->mMachineState));
8509 break;
8510 }
8511 case MutableOrSavedStateDep:
8512 {
8513 if ( mData->mRegistered
8514 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8515 || ( mData->mMachineState != MachineState_Paused
8516 && mData->mMachineState != MachineState_Running
8517 && mData->mMachineState != MachineState_Aborted
8518 && mData->mMachineState != MachineState_Teleported
8519 && mData->mMachineState != MachineState_Saved
8520 && mData->mMachineState != MachineState_PoweredOff
8521 )
8522 )
8523 )
8524 return setError(VBOX_E_INVALID_VM_STATE,
8525 tr("The machine is not mutable (state is %s)"),
8526 Global::stringifyMachineState(mData->mMachineState));
8527 break;
8528 }
8529 case OfflineStateDep:
8530 {
8531 if ( mData->mRegistered
8532 && ( !isSessionMachine()
8533 || ( mData->mMachineState != MachineState_PoweredOff
8534 && mData->mMachineState != MachineState_Saved
8535 && mData->mMachineState != MachineState_Aborted
8536 && mData->mMachineState != MachineState_Teleported
8537 )
8538 )
8539 )
8540 return setError(VBOX_E_INVALID_VM_STATE,
8541 tr("The machine is not offline (state is %s)"),
8542 Global::stringifyMachineState(mData->mMachineState));
8543 break;
8544 }
8545 }
8546
8547 return S_OK;
8548}
8549
8550/**
8551 * Helper to initialize all associated child objects and allocate data
8552 * structures.
8553 *
8554 * This method must be called as a part of the object's initialization procedure
8555 * (usually done in the #init() method).
8556 *
8557 * @note Must be called only from #init() or from #registeredInit().
8558 */
8559HRESULT Machine::initDataAndChildObjects()
8560{
8561 AutoCaller autoCaller(this);
8562 AssertComRCReturnRC(autoCaller.rc());
8563 AssertComRCReturn(autoCaller.state() == InInit ||
8564 autoCaller.state() == Limited, E_FAIL);
8565
8566 AssertReturn(!mData->mAccessible, E_FAIL);
8567
8568 /* allocate data structures */
8569 mSSData.allocate();
8570 mUserData.allocate();
8571 mHWData.allocate();
8572 mMediaData.allocate();
8573 mStorageControllers.allocate();
8574 mUSBControllers.allocate();
8575
8576 /* initialize mOSTypeId */
8577 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8578
8579 /* create associated BIOS settings object */
8580 unconst(mBIOSSettings).createObject();
8581 mBIOSSettings->init(this);
8582
8583 /* create an associated VRDE object (default is disabled) */
8584 unconst(mVRDEServer).createObject();
8585 mVRDEServer->init(this);
8586
8587 /* create associated serial port objects */
8588 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8589 {
8590 unconst(mSerialPorts[slot]).createObject();
8591 mSerialPorts[slot]->init(this, slot);
8592 }
8593
8594 /* create associated parallel port objects */
8595 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8596 {
8597 unconst(mParallelPorts[slot]).createObject();
8598 mParallelPorts[slot]->init(this, slot);
8599 }
8600
8601 /* create the audio adapter object (always present, default is disabled) */
8602 unconst(mAudioAdapter).createObject();
8603 mAudioAdapter->init(this);
8604
8605 /* create the USB device filters object (always present) */
8606 unconst(mUSBDeviceFilters).createObject();
8607 mUSBDeviceFilters->init(this);
8608
8609 /* create associated network adapter objects */
8610 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8611 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8612 {
8613 unconst(mNetworkAdapters[slot]).createObject();
8614 mNetworkAdapters[slot]->init(this, slot);
8615 }
8616
8617 /* create the bandwidth control */
8618 unconst(mBandwidthControl).createObject();
8619 mBandwidthControl->init(this);
8620
8621 return S_OK;
8622}
8623
8624/**
8625 * Helper to uninitialize all associated child objects and to free all data
8626 * structures.
8627 *
8628 * This method must be called as a part of the object's uninitialization
8629 * procedure (usually done in the #uninit() method).
8630 *
8631 * @note Must be called only from #uninit() or from #registeredInit().
8632 */
8633void Machine::uninitDataAndChildObjects()
8634{
8635 AutoCaller autoCaller(this);
8636 AssertComRCReturnVoid(autoCaller.rc());
8637 AssertComRCReturnVoid( autoCaller.state() == InUninit
8638 || autoCaller.state() == Limited);
8639
8640 /* tell all our other child objects we've been uninitialized */
8641 if (mBandwidthControl)
8642 {
8643 mBandwidthControl->uninit();
8644 unconst(mBandwidthControl).setNull();
8645 }
8646
8647 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8648 {
8649 if (mNetworkAdapters[slot])
8650 {
8651 mNetworkAdapters[slot]->uninit();
8652 unconst(mNetworkAdapters[slot]).setNull();
8653 }
8654 }
8655
8656 if (mUSBDeviceFilters)
8657 {
8658 mUSBDeviceFilters->uninit();
8659 unconst(mUSBDeviceFilters).setNull();
8660 }
8661
8662 if (mAudioAdapter)
8663 {
8664 mAudioAdapter->uninit();
8665 unconst(mAudioAdapter).setNull();
8666 }
8667
8668 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8669 {
8670 if (mParallelPorts[slot])
8671 {
8672 mParallelPorts[slot]->uninit();
8673 unconst(mParallelPorts[slot]).setNull();
8674 }
8675 }
8676
8677 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8678 {
8679 if (mSerialPorts[slot])
8680 {
8681 mSerialPorts[slot]->uninit();
8682 unconst(mSerialPorts[slot]).setNull();
8683 }
8684 }
8685
8686 if (mVRDEServer)
8687 {
8688 mVRDEServer->uninit();
8689 unconst(mVRDEServer).setNull();
8690 }
8691
8692 if (mBIOSSettings)
8693 {
8694 mBIOSSettings->uninit();
8695 unconst(mBIOSSettings).setNull();
8696 }
8697
8698 /* Deassociate media (only when a real Machine or a SnapshotMachine
8699 * instance is uninitialized; SessionMachine instances refer to real
8700 * Machine media). This is necessary for a clean re-initialization of
8701 * the VM after successfully re-checking the accessibility state. Note
8702 * that in case of normal Machine or SnapshotMachine uninitialization (as
8703 * a result of unregistering or deleting the snapshot), outdated media
8704 * attachments will already be uninitialized and deleted, so this
8705 * code will not affect them. */
8706 if ( !!mMediaData
8707 && (!isSessionMachine())
8708 )
8709 {
8710 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8711 it != mMediaData->mAttachments.end();
8712 ++it)
8713 {
8714 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8715 if (pMedium.isNull())
8716 continue;
8717 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8718 AssertComRC(rc);
8719 }
8720 }
8721
8722 if (!isSessionMachine() && !isSnapshotMachine())
8723 {
8724 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8725 if (mData->mFirstSnapshot)
8726 {
8727 // snapshots tree is protected by machine write lock; strictly
8728 // this isn't necessary here since we're deleting the entire
8729 // machine, but otherwise we assert in Snapshot::uninit()
8730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8731 mData->mFirstSnapshot->uninit();
8732 mData->mFirstSnapshot.setNull();
8733 }
8734
8735 mData->mCurrentSnapshot.setNull();
8736 }
8737
8738 /* free data structures (the essential mData structure is not freed here
8739 * since it may be still in use) */
8740 mMediaData.free();
8741 mStorageControllers.free();
8742 mUSBControllers.free();
8743 mHWData.free();
8744 mUserData.free();
8745 mSSData.free();
8746}
8747
8748/**
8749 * Returns a pointer to the Machine object for this machine that acts like a
8750 * parent for complex machine data objects such as shared folders, etc.
8751 *
8752 * For primary Machine objects and for SnapshotMachine objects, returns this
8753 * object's pointer itself. For SessionMachine objects, returns the peer
8754 * (primary) machine pointer.
8755 */
8756Machine* Machine::getMachine()
8757{
8758 if (isSessionMachine())
8759 return (Machine*)mPeer;
8760 return this;
8761}
8762
8763/**
8764 * Makes sure that there are no machine state dependents. If necessary, waits
8765 * for the number of dependents to drop to zero.
8766 *
8767 * Make sure this method is called from under this object's write lock to
8768 * guarantee that no new dependents may be added when this method returns
8769 * control to the caller.
8770 *
8771 * @note Locks this object for writing. The lock will be released while waiting
8772 * (if necessary).
8773 *
8774 * @warning To be used only in methods that change the machine state!
8775 */
8776void Machine::ensureNoStateDependencies()
8777{
8778 AssertReturnVoid(isWriteLockOnCurrentThread());
8779
8780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8781
8782 /* Wait for all state dependents if necessary */
8783 if (mData->mMachineStateDeps != 0)
8784 {
8785 /* lazy semaphore creation */
8786 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8787 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8788
8789 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8790 mData->mMachineStateDeps));
8791
8792 ++mData->mMachineStateChangePending;
8793
8794 /* reset the semaphore before waiting, the last dependent will signal
8795 * it */
8796 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8797
8798 alock.release();
8799
8800 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8801
8802 alock.acquire();
8803
8804 -- mData->mMachineStateChangePending;
8805 }
8806}
8807
8808/**
8809 * Changes the machine state and informs callbacks.
8810 *
8811 * This method is not intended to fail so it either returns S_OK or asserts (and
8812 * returns a failure).
8813 *
8814 * @note Locks this object for writing.
8815 */
8816HRESULT Machine::setMachineState(MachineState_T aMachineState)
8817{
8818 LogFlowThisFuncEnter();
8819 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8820
8821 AutoCaller autoCaller(this);
8822 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8823
8824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8825
8826 /* wait for state dependents to drop to zero */
8827 ensureNoStateDependencies();
8828
8829 if (mData->mMachineState != aMachineState)
8830 {
8831 mData->mMachineState = aMachineState;
8832
8833 RTTimeNow(&mData->mLastStateChange);
8834
8835 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8836 }
8837
8838 LogFlowThisFuncLeave();
8839 return S_OK;
8840}
8841
8842/**
8843 * Searches for a shared folder with the given logical name
8844 * in the collection of shared folders.
8845 *
8846 * @param aName logical name of the shared folder
8847 * @param aSharedFolder where to return the found object
8848 * @param aSetError whether to set the error info if the folder is
8849 * not found
8850 * @return
8851 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8852 *
8853 * @note
8854 * must be called from under the object's lock!
8855 */
8856HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8857 ComObjPtr<SharedFolder> &aSharedFolder,
8858 bool aSetError /* = false */)
8859{
8860 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8861 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8862 it != mHWData->mSharedFolders.end();
8863 ++it)
8864 {
8865 SharedFolder *pSF = *it;
8866 AutoCaller autoCaller(pSF);
8867 if (pSF->getName() == aName)
8868 {
8869 aSharedFolder = pSF;
8870 rc = S_OK;
8871 break;
8872 }
8873 }
8874
8875 if (aSetError && FAILED(rc))
8876 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8877
8878 return rc;
8879}
8880
8881/**
8882 * Initializes all machine instance data from the given settings structures
8883 * from XML. The exception is the machine UUID which needs special handling
8884 * depending on the caller's use case, so the caller needs to set that herself.
8885 *
8886 * This gets called in several contexts during machine initialization:
8887 *
8888 * -- When machine XML exists on disk already and needs to be loaded into memory,
8889 * for example, from registeredInit() to load all registered machines on
8890 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8891 * attached to the machine should be part of some media registry already.
8892 *
8893 * -- During OVF import, when a machine config has been constructed from an
8894 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8895 * ensure that the media listed as attachments in the config (which have
8896 * been imported from the OVF) receive the correct registry ID.
8897 *
8898 * -- During VM cloning.
8899 *
8900 * @param config Machine settings from XML.
8901 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8902 * @return
8903 */
8904HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8905 const Guid *puuidRegistry)
8906{
8907 // copy name, description, OS type, teleporter, UTC etc.
8908 #define DECODE_STR_MAX _1M
8909 mUserData->s = config.machineUserData;
8910
8911 // Decode the Icon overide data from config userdata and set onto Machine.
8912 const char* pszStr = config.machineUserData.ovIcon.c_str();
8913 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8914 if (cbOut > DECODE_STR_MAX)
8915 return setError(E_FAIL,
8916 tr("Icon Data too long.'%d' > '%d'"),
8917 cbOut,
8918 DECODE_STR_MAX);
8919 com::SafeArray<BYTE> iconByte(cbOut);
8920 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8921 if (FAILED(rc))
8922 return setError(E_FAIL,
8923 tr("Failure to Decode Icon Data. '%s' (%d)"),
8924 pszStr,
8925 rc);
8926 COMSETTER(Icon)(ComSafeArrayAsInParam(iconByte));
8927
8928 // look up the object by Id to check it is valid
8929 ComPtr<IGuestOSType> guestOSType;
8930 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8931 guestOSType.asOutParam());
8932 if (FAILED(rc)) return rc;
8933
8934 // stateFile (optional)
8935 if (config.strStateFile.isEmpty())
8936 mSSData->strStateFilePath.setNull();
8937 else
8938 {
8939 Utf8Str stateFilePathFull(config.strStateFile);
8940 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8941 if (RT_FAILURE(vrc))
8942 return setError(E_FAIL,
8943 tr("Invalid saved state file path '%s' (%Rrc)"),
8944 config.strStateFile.c_str(),
8945 vrc);
8946 mSSData->strStateFilePath = stateFilePathFull;
8947 }
8948
8949 // snapshot folder needs special processing so set it again
8950 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8951 if (FAILED(rc)) return rc;
8952
8953 /* Copy the extra data items (Not in any case config is already the same as
8954 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8955 * make sure the extra data map is copied). */
8956 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8957
8958 /* currentStateModified (optional, default is true) */
8959 mData->mCurrentStateModified = config.fCurrentStateModified;
8960
8961 mData->mLastStateChange = config.timeLastStateChange;
8962
8963 /*
8964 * note: all mUserData members must be assigned prior this point because
8965 * we need to commit changes in order to let mUserData be shared by all
8966 * snapshot machine instances.
8967 */
8968 mUserData.commitCopy();
8969
8970 // machine registry, if present (must be loaded before snapshots)
8971 if (config.canHaveOwnMediaRegistry())
8972 {
8973 // determine machine folder
8974 Utf8Str strMachineFolder = getSettingsFileFull();
8975 strMachineFolder.stripFilename();
8976 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8977 config.mediaRegistry,
8978 strMachineFolder);
8979 if (FAILED(rc)) return rc;
8980 }
8981
8982 /* Snapshot node (optional) */
8983 size_t cRootSnapshots;
8984 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8985 {
8986 // there must be only one root snapshot
8987 Assert(cRootSnapshots == 1);
8988
8989 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8990
8991 rc = loadSnapshot(snap,
8992 config.uuidCurrentSnapshot,
8993 NULL); // no parent == first snapshot
8994 if (FAILED(rc)) return rc;
8995 }
8996
8997 // hardware data
8998 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8999 if (FAILED(rc)) return rc;
9000
9001 // load storage controllers
9002 rc = loadStorageControllers(config.storageMachine,
9003 puuidRegistry,
9004 NULL /* puuidSnapshot */);
9005 if (FAILED(rc)) return rc;
9006
9007 /*
9008 * NOTE: the assignment below must be the last thing to do,
9009 * otherwise it will be not possible to change the settings
9010 * somewhere in the code above because all setters will be
9011 * blocked by checkStateDependency(MutableStateDep).
9012 */
9013
9014 /* set the machine state to Aborted or Saved when appropriate */
9015 if (config.fAborted)
9016 {
9017 mSSData->strStateFilePath.setNull();
9018
9019 /* no need to use setMachineState() during init() */
9020 mData->mMachineState = MachineState_Aborted;
9021 }
9022 else if (!mSSData->strStateFilePath.isEmpty())
9023 {
9024 /* no need to use setMachineState() during init() */
9025 mData->mMachineState = MachineState_Saved;
9026 }
9027
9028 // after loading settings, we are no longer different from the XML on disk
9029 mData->flModifications = 0;
9030
9031 return S_OK;
9032}
9033
9034/**
9035 * Recursively loads all snapshots starting from the given.
9036 *
9037 * @param aNode <Snapshot> node.
9038 * @param aCurSnapshotId Current snapshot ID from the settings file.
9039 * @param aParentSnapshot Parent snapshot.
9040 */
9041HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
9042 const Guid &aCurSnapshotId,
9043 Snapshot *aParentSnapshot)
9044{
9045 AssertReturn(!isSnapshotMachine(), E_FAIL);
9046 AssertReturn(!isSessionMachine(), E_FAIL);
9047
9048 HRESULT rc = S_OK;
9049
9050 Utf8Str strStateFile;
9051 if (!data.strStateFile.isEmpty())
9052 {
9053 /* optional */
9054 strStateFile = data.strStateFile;
9055 int vrc = calculateFullPath(strStateFile, strStateFile);
9056 if (RT_FAILURE(vrc))
9057 return setError(E_FAIL,
9058 tr("Invalid saved state file path '%s' (%Rrc)"),
9059 strStateFile.c_str(),
9060 vrc);
9061 }
9062
9063 /* create a snapshot machine object */
9064 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9065 pSnapshotMachine.createObject();
9066 rc = pSnapshotMachine->initFromSettings(this,
9067 data.hardware,
9068 &data.debugging,
9069 &data.autostart,
9070 data.storage,
9071 data.uuid.ref(),
9072 strStateFile);
9073 if (FAILED(rc)) return rc;
9074
9075 /* create a snapshot object */
9076 ComObjPtr<Snapshot> pSnapshot;
9077 pSnapshot.createObject();
9078 /* initialize the snapshot */
9079 rc = pSnapshot->init(mParent, // VirtualBox object
9080 data.uuid,
9081 data.strName,
9082 data.strDescription,
9083 data.timestamp,
9084 pSnapshotMachine,
9085 aParentSnapshot);
9086 if (FAILED(rc)) return rc;
9087
9088 /* memorize the first snapshot if necessary */
9089 if (!mData->mFirstSnapshot)
9090 mData->mFirstSnapshot = pSnapshot;
9091
9092 /* memorize the current snapshot when appropriate */
9093 if ( !mData->mCurrentSnapshot
9094 && pSnapshot->getId() == aCurSnapshotId
9095 )
9096 mData->mCurrentSnapshot = pSnapshot;
9097
9098 // now create the children
9099 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
9100 it != data.llChildSnapshots.end();
9101 ++it)
9102 {
9103 const settings::Snapshot &childData = *it;
9104 // recurse
9105 rc = loadSnapshot(childData,
9106 aCurSnapshotId,
9107 pSnapshot); // parent = the one we created above
9108 if (FAILED(rc)) return rc;
9109 }
9110
9111 return rc;
9112}
9113
9114/**
9115 * Loads settings into mHWData.
9116 *
9117 * @param data Reference to the hardware settings.
9118 * @param pDbg Pointer to the debugging settings.
9119 * @param pAutostart Pointer to the autostart settings.
9120 */
9121HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
9122 const settings::Autostart *pAutostart)
9123{
9124 AssertReturn(!isSessionMachine(), E_FAIL);
9125
9126 HRESULT rc = S_OK;
9127
9128 try
9129 {
9130 /* The hardware version attribute (optional). */
9131 mHWData->mHWVersion = data.strVersion;
9132 mHWData->mHardwareUUID = data.uuid;
9133
9134 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9135 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
9136 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9137 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9138 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9139 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9140 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9141 mHWData->mPAEEnabled = data.fPAE;
9142 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9143 mHWData->mLongMode = data.enmLongMode;
9144 mHWData->mCPUCount = data.cCPUs;
9145 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9146 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9147
9148 // cpu
9149 if (mHWData->mCPUHotPlugEnabled)
9150 {
9151 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9152 it != data.llCpus.end();
9153 ++it)
9154 {
9155 const settings::Cpu &cpu = *it;
9156
9157 mHWData->mCPUAttached[cpu.ulId] = true;
9158 }
9159 }
9160
9161 // cpuid leafs
9162 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9163 it != data.llCpuIdLeafs.end();
9164 ++it)
9165 {
9166 const settings::CpuIdLeaf &leaf = *it;
9167
9168 switch (leaf.ulId)
9169 {
9170 case 0x0:
9171 case 0x1:
9172 case 0x2:
9173 case 0x3:
9174 case 0x4:
9175 case 0x5:
9176 case 0x6:
9177 case 0x7:
9178 case 0x8:
9179 case 0x9:
9180 case 0xA:
9181 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9182 break;
9183
9184 case 0x80000000:
9185 case 0x80000001:
9186 case 0x80000002:
9187 case 0x80000003:
9188 case 0x80000004:
9189 case 0x80000005:
9190 case 0x80000006:
9191 case 0x80000007:
9192 case 0x80000008:
9193 case 0x80000009:
9194 case 0x8000000A:
9195 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9196 break;
9197
9198 default:
9199 /* just ignore */
9200 break;
9201 }
9202 }
9203
9204 mHWData->mMemorySize = data.ulMemorySizeMB;
9205 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9206
9207 // boot order
9208 for (size_t i = 0;
9209 i < RT_ELEMENTS(mHWData->mBootOrder);
9210 i++)
9211 {
9212 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9213 if (it == data.mapBootOrder.end())
9214 mHWData->mBootOrder[i] = DeviceType_Null;
9215 else
9216 mHWData->mBootOrder[i] = it->second;
9217 }
9218
9219 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9220 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9221 mHWData->mMonitorCount = data.cMonitors;
9222 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9223 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9224 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9225 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9226 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9227 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9228 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9229 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9230 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9231 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9232 if (!data.strVideoCaptureFile.isEmpty())
9233 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9234 else
9235 mHWData->mVideoCaptureFile.setNull();
9236 mHWData->mFirmwareType = data.firmwareType;
9237 mHWData->mPointingHIDType = data.pointingHIDType;
9238 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9239 mHWData->mChipsetType = data.chipsetType;
9240 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
9241 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9242 mHWData->mHPETEnabled = data.fHPETEnabled;
9243
9244 /* VRDEServer */
9245 rc = mVRDEServer->loadSettings(data.vrdeSettings);
9246 if (FAILED(rc)) return rc;
9247
9248 /* BIOS */
9249 rc = mBIOSSettings->loadSettings(data.biosSettings);
9250 if (FAILED(rc)) return rc;
9251
9252 // Bandwidth control (must come before network adapters)
9253 rc = mBandwidthControl->loadSettings(data.ioSettings);
9254 if (FAILED(rc)) return rc;
9255
9256 /* Shared folders */
9257 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9258 it != data.usbSettings.llUSBControllers.end();
9259 ++it)
9260 {
9261 const settings::USBController &settingsCtrl = *it;
9262 ComObjPtr<USBController> newCtrl;
9263
9264 newCtrl.createObject();
9265 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9266 mUSBControllers->push_back(newCtrl);
9267 }
9268
9269 /* USB device filters */
9270 rc = mUSBDeviceFilters->loadSettings(data.usbSettings);
9271 if (FAILED(rc)) return rc;
9272
9273 // network adapters
9274 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9275 uint32_t oldCount = mNetworkAdapters.size();
9276 if (newCount > oldCount)
9277 {
9278 mNetworkAdapters.resize(newCount);
9279 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9280 {
9281 unconst(mNetworkAdapters[slot]).createObject();
9282 mNetworkAdapters[slot]->init(this, slot);
9283 }
9284 }
9285 else if (newCount < oldCount)
9286 mNetworkAdapters.resize(newCount);
9287 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9288 it != data.llNetworkAdapters.end();
9289 ++it)
9290 {
9291 const settings::NetworkAdapter &nic = *it;
9292
9293 /* slot unicity is guaranteed by XML Schema */
9294 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9295 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9296 if (FAILED(rc)) return rc;
9297 }
9298
9299 // serial ports
9300 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9301 it != data.llSerialPorts.end();
9302 ++it)
9303 {
9304 const settings::SerialPort &s = *it;
9305
9306 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9307 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
9308 if (FAILED(rc)) return rc;
9309 }
9310
9311 // parallel ports (optional)
9312 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9313 it != data.llParallelPorts.end();
9314 ++it)
9315 {
9316 const settings::ParallelPort &p = *it;
9317
9318 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9319 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9320 if (FAILED(rc)) return rc;
9321 }
9322
9323 /* AudioAdapter */
9324 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9325 if (FAILED(rc)) return rc;
9326
9327 /* Shared folders */
9328 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9329 it != data.llSharedFolders.end();
9330 ++it)
9331 {
9332 const settings::SharedFolder &sf = *it;
9333
9334 ComObjPtr<SharedFolder> sharedFolder;
9335 /* Check for double entries. Not allowed! */
9336 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9337 if (SUCCEEDED(rc))
9338 return setError(VBOX_E_OBJECT_IN_USE,
9339 tr("Shared folder named '%s' already exists"),
9340 sf.strName.c_str());
9341
9342 /* Create the new shared folder. Don't break on error. This will be
9343 * reported when the machine starts. */
9344 sharedFolder.createObject();
9345 rc = sharedFolder->init(getMachine(),
9346 sf.strName,
9347 sf.strHostPath,
9348 RT_BOOL(sf.fWritable),
9349 RT_BOOL(sf.fAutoMount),
9350 false /* fFailOnError */);
9351 if (FAILED(rc)) return rc;
9352 mHWData->mSharedFolders.push_back(sharedFolder);
9353 }
9354
9355 // Clipboard
9356 mHWData->mClipboardMode = data.clipboardMode;
9357
9358 // drag'n'drop
9359 mHWData->mDragAndDropMode = data.dragAndDropMode;
9360
9361 // guest settings
9362 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9363
9364 // IO settings
9365 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9366 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9367
9368 // Host PCI devices
9369 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9370 it != data.pciAttachments.end();
9371 ++it)
9372 {
9373 const settings::HostPCIDeviceAttachment &hpda = *it;
9374 ComObjPtr<PCIDeviceAttachment> pda;
9375
9376 pda.createObject();
9377 pda->loadSettings(this, hpda);
9378 mHWData->mPCIDeviceAssignments.push_back(pda);
9379 }
9380
9381 /*
9382 * (The following isn't really real hardware, but it lives in HWData
9383 * for reasons of convenience.)
9384 */
9385
9386#ifdef VBOX_WITH_GUEST_PROPS
9387 /* Guest properties (optional) */
9388 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9389 it != data.llGuestProperties.end();
9390 ++it)
9391 {
9392 const settings::GuestProperty &prop = *it;
9393 uint32_t fFlags = guestProp::NILFLAG;
9394 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9395 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9396 mHWData->mGuestProperties[prop.strName] = property;
9397 }
9398
9399 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9400#endif /* VBOX_WITH_GUEST_PROPS defined */
9401
9402 rc = loadDebugging(pDbg);
9403 if (FAILED(rc))
9404 return rc;
9405
9406 mHWData->mAutostart = *pAutostart;
9407
9408 /* default frontend */
9409 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9410 }
9411 catch(std::bad_alloc &)
9412 {
9413 return E_OUTOFMEMORY;
9414 }
9415
9416 AssertComRC(rc);
9417 return rc;
9418}
9419
9420/**
9421 * Called from Machine::loadHardware() to load the debugging settings of the
9422 * machine.
9423 *
9424 * @param pDbg Pointer to the settings.
9425 */
9426HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9427{
9428 mHWData->mDebugging = *pDbg;
9429 /* no more processing currently required, this will probably change. */
9430 return S_OK;
9431}
9432
9433/**
9434 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9435 *
9436 * @param data
9437 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9438 * @param puuidSnapshot
9439 * @return
9440 */
9441HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9442 const Guid *puuidRegistry,
9443 const Guid *puuidSnapshot)
9444{
9445 AssertReturn(!isSessionMachine(), E_FAIL);
9446
9447 HRESULT rc = S_OK;
9448
9449 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9450 it != data.llStorageControllers.end();
9451 ++it)
9452 {
9453 const settings::StorageController &ctlData = *it;
9454
9455 ComObjPtr<StorageController> pCtl;
9456 /* Try to find one with the name first. */
9457 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9458 if (SUCCEEDED(rc))
9459 return setError(VBOX_E_OBJECT_IN_USE,
9460 tr("Storage controller named '%s' already exists"),
9461 ctlData.strName.c_str());
9462
9463 pCtl.createObject();
9464 rc = pCtl->init(this,
9465 ctlData.strName,
9466 ctlData.storageBus,
9467 ctlData.ulInstance,
9468 ctlData.fBootable);
9469 if (FAILED(rc)) return rc;
9470
9471 mStorageControllers->push_back(pCtl);
9472
9473 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9474 if (FAILED(rc)) return rc;
9475
9476 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9477 if (FAILED(rc)) return rc;
9478
9479 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9480 if (FAILED(rc)) return rc;
9481
9482 /* Set IDE emulation settings (only for AHCI controller). */
9483 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9484 {
9485 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9486 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9487 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9488 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9489 )
9490 return rc;
9491 }
9492
9493 /* Load the attached devices now. */
9494 rc = loadStorageDevices(pCtl,
9495 ctlData,
9496 puuidRegistry,
9497 puuidSnapshot);
9498 if (FAILED(rc)) return rc;
9499 }
9500
9501 return S_OK;
9502}
9503
9504/**
9505 * Called from loadStorageControllers for a controller's devices.
9506 *
9507 * @param aStorageController
9508 * @param data
9509 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9510 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9511 * @return
9512 */
9513HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9514 const settings::StorageController &data,
9515 const Guid *puuidRegistry,
9516 const Guid *puuidSnapshot)
9517{
9518 HRESULT rc = S_OK;
9519
9520 /* paranoia: detect duplicate attachments */
9521 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9522 it != data.llAttachedDevices.end();
9523 ++it)
9524 {
9525 const settings::AttachedDevice &ad = *it;
9526
9527 for (settings::AttachedDevicesList::const_iterator it2 = it;
9528 it2 != data.llAttachedDevices.end();
9529 ++it2)
9530 {
9531 if (it == it2)
9532 continue;
9533
9534 const settings::AttachedDevice &ad2 = *it2;
9535
9536 if ( ad.lPort == ad2.lPort
9537 && ad.lDevice == ad2.lDevice)
9538 {
9539 return setError(E_FAIL,
9540 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9541 aStorageController->getName().c_str(),
9542 ad.lPort,
9543 ad.lDevice,
9544 mUserData->s.strName.c_str());
9545 }
9546 }
9547 }
9548
9549 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9550 it != data.llAttachedDevices.end();
9551 ++it)
9552 {
9553 const settings::AttachedDevice &dev = *it;
9554 ComObjPtr<Medium> medium;
9555
9556 switch (dev.deviceType)
9557 {
9558 case DeviceType_Floppy:
9559 case DeviceType_DVD:
9560 if (dev.strHostDriveSrc.isNotEmpty())
9561 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9562 else
9563 rc = mParent->findRemoveableMedium(dev.deviceType,
9564 dev.uuid,
9565 false /* fRefresh */,
9566 false /* aSetError */,
9567 medium);
9568 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9569 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9570 rc = S_OK;
9571 break;
9572
9573 case DeviceType_HardDisk:
9574 {
9575 /* find a hard disk by UUID */
9576 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9577 if (FAILED(rc))
9578 {
9579 if (isSnapshotMachine())
9580 {
9581 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9582 // so the user knows that the bad disk is in a snapshot somewhere
9583 com::ErrorInfo info;
9584 return setError(E_FAIL,
9585 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9586 puuidSnapshot->raw(),
9587 info.getText().raw());
9588 }
9589 else
9590 return rc;
9591 }
9592
9593 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9594
9595 if (medium->getType() == MediumType_Immutable)
9596 {
9597 if (isSnapshotMachine())
9598 return setError(E_FAIL,
9599 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9600 "of the virtual machine '%s' ('%s')"),
9601 medium->getLocationFull().c_str(),
9602 dev.uuid.raw(),
9603 puuidSnapshot->raw(),
9604 mUserData->s.strName.c_str(),
9605 mData->m_strConfigFileFull.c_str());
9606
9607 return setError(E_FAIL,
9608 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9609 medium->getLocationFull().c_str(),
9610 dev.uuid.raw(),
9611 mUserData->s.strName.c_str(),
9612 mData->m_strConfigFileFull.c_str());
9613 }
9614
9615 if (medium->getType() == MediumType_MultiAttach)
9616 {
9617 if (isSnapshotMachine())
9618 return setError(E_FAIL,
9619 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9620 "of the virtual machine '%s' ('%s')"),
9621 medium->getLocationFull().c_str(),
9622 dev.uuid.raw(),
9623 puuidSnapshot->raw(),
9624 mUserData->s.strName.c_str(),
9625 mData->m_strConfigFileFull.c_str());
9626
9627 return setError(E_FAIL,
9628 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9629 medium->getLocationFull().c_str(),
9630 dev.uuid.raw(),
9631 mUserData->s.strName.c_str(),
9632 mData->m_strConfigFileFull.c_str());
9633 }
9634
9635 if ( !isSnapshotMachine()
9636 && medium->getChildren().size() != 0
9637 )
9638 return setError(E_FAIL,
9639 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9640 "because it has %d differencing child hard disks"),
9641 medium->getLocationFull().c_str(),
9642 dev.uuid.raw(),
9643 mUserData->s.strName.c_str(),
9644 mData->m_strConfigFileFull.c_str(),
9645 medium->getChildren().size());
9646
9647 if (findAttachment(mMediaData->mAttachments,
9648 medium))
9649 return setError(E_FAIL,
9650 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9651 medium->getLocationFull().c_str(),
9652 dev.uuid.raw(),
9653 mUserData->s.strName.c_str(),
9654 mData->m_strConfigFileFull.c_str());
9655
9656 break;
9657 }
9658
9659 default:
9660 return setError(E_FAIL,
9661 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9662 medium->getLocationFull().c_str(),
9663 mUserData->s.strName.c_str(),
9664 mData->m_strConfigFileFull.c_str());
9665 }
9666
9667 if (FAILED(rc))
9668 break;
9669
9670 /* Bandwidth groups are loaded at this point. */
9671 ComObjPtr<BandwidthGroup> pBwGroup;
9672
9673 if (!dev.strBwGroup.isEmpty())
9674 {
9675 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9676 if (FAILED(rc))
9677 return setError(E_FAIL,
9678 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9679 medium->getLocationFull().c_str(),
9680 dev.strBwGroup.c_str(),
9681 mUserData->s.strName.c_str(),
9682 mData->m_strConfigFileFull.c_str());
9683 pBwGroup->reference();
9684 }
9685
9686 const Bstr controllerName = aStorageController->getName();
9687 ComObjPtr<MediumAttachment> pAttachment;
9688 pAttachment.createObject();
9689 rc = pAttachment->init(this,
9690 medium,
9691 controllerName,
9692 dev.lPort,
9693 dev.lDevice,
9694 dev.deviceType,
9695 false,
9696 dev.fPassThrough,
9697 dev.fTempEject,
9698 dev.fNonRotational,
9699 dev.fDiscard,
9700 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9701 if (FAILED(rc)) break;
9702
9703 /* associate the medium with this machine and snapshot */
9704 if (!medium.isNull())
9705 {
9706 AutoCaller medCaller(medium);
9707 if (FAILED(medCaller.rc())) return medCaller.rc();
9708 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9709
9710 if (isSnapshotMachine())
9711 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9712 else
9713 rc = medium->addBackReference(mData->mUuid);
9714 /* If the medium->addBackReference fails it sets an appropriate
9715 * error message, so no need to do any guesswork here. */
9716
9717 if (puuidRegistry)
9718 // caller wants registry ID to be set on all attached media (OVF import case)
9719 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9720 }
9721
9722 if (FAILED(rc))
9723 break;
9724
9725 /* back up mMediaData to let registeredInit() properly rollback on failure
9726 * (= limited accessibility) */
9727 setModified(IsModified_Storage);
9728 mMediaData.backup();
9729 mMediaData->mAttachments.push_back(pAttachment);
9730 }
9731
9732 return rc;
9733}
9734
9735/**
9736 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9737 *
9738 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9739 * @param aSnapshot where to return the found snapshot
9740 * @param aSetError true to set extended error info on failure
9741 */
9742HRESULT Machine::findSnapshotById(const Guid &aId,
9743 ComObjPtr<Snapshot> &aSnapshot,
9744 bool aSetError /* = false */)
9745{
9746 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9747
9748 if (!mData->mFirstSnapshot)
9749 {
9750 if (aSetError)
9751 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9752 return E_FAIL;
9753 }
9754
9755 if (aId.isZero())
9756 aSnapshot = mData->mFirstSnapshot;
9757 else
9758 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9759
9760 if (!aSnapshot)
9761 {
9762 if (aSetError)
9763 return setError(E_FAIL,
9764 tr("Could not find a snapshot with UUID {%s}"),
9765 aId.toString().c_str());
9766 return E_FAIL;
9767 }
9768
9769 return S_OK;
9770}
9771
9772/**
9773 * Returns the snapshot with the given name or fails of no such snapshot.
9774 *
9775 * @param aName snapshot name to find
9776 * @param aSnapshot where to return the found snapshot
9777 * @param aSetError true to set extended error info on failure
9778 */
9779HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9780 ComObjPtr<Snapshot> &aSnapshot,
9781 bool aSetError /* = false */)
9782{
9783 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9784
9785 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9786
9787 if (!mData->mFirstSnapshot)
9788 {
9789 if (aSetError)
9790 return setError(VBOX_E_OBJECT_NOT_FOUND,
9791 tr("This machine does not have any snapshots"));
9792 return VBOX_E_OBJECT_NOT_FOUND;
9793 }
9794
9795 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9796
9797 if (!aSnapshot)
9798 {
9799 if (aSetError)
9800 return setError(VBOX_E_OBJECT_NOT_FOUND,
9801 tr("Could not find a snapshot named '%s'"), strName.c_str());
9802 return VBOX_E_OBJECT_NOT_FOUND;
9803 }
9804
9805 return S_OK;
9806}
9807
9808/**
9809 * Returns a storage controller object with the given name.
9810 *
9811 * @param aName storage controller name to find
9812 * @param aStorageController where to return the found storage controller
9813 * @param aSetError true to set extended error info on failure
9814 */
9815HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9816 ComObjPtr<StorageController> &aStorageController,
9817 bool aSetError /* = false */)
9818{
9819 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9820
9821 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9822 it != mStorageControllers->end();
9823 ++it)
9824 {
9825 if ((*it)->getName() == aName)
9826 {
9827 aStorageController = (*it);
9828 return S_OK;
9829 }
9830 }
9831
9832 if (aSetError)
9833 return setError(VBOX_E_OBJECT_NOT_FOUND,
9834 tr("Could not find a storage controller named '%s'"),
9835 aName.c_str());
9836 return VBOX_E_OBJECT_NOT_FOUND;
9837}
9838
9839/**
9840 * Returns a USB controller object with the given name.
9841 *
9842 * @param aName USB controller name to find
9843 * @param aUSBController where to return the found USB controller
9844 * @param aSetError true to set extended error info on failure
9845 */
9846HRESULT Machine::getUSBControllerByName(const Utf8Str &aName,
9847 ComObjPtr<USBController> &aUSBController,
9848 bool aSetError /* = false */)
9849{
9850 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9851
9852 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9853 it != mUSBControllers->end();
9854 ++it)
9855 {
9856 if ((*it)->getName() == aName)
9857 {
9858 aUSBController = (*it);
9859 return S_OK;
9860 }
9861 }
9862
9863 if (aSetError)
9864 return setError(VBOX_E_OBJECT_NOT_FOUND,
9865 tr("Could not find a storage controller named '%s'"),
9866 aName.c_str());
9867 return VBOX_E_OBJECT_NOT_FOUND;
9868}
9869
9870/**
9871 * Returns the number of USB controller instance of the given type.
9872 *
9873 * @param enmType USB controller type.
9874 */
9875ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType)
9876{
9877 ULONG cCtrls = 0;
9878
9879 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9880 it != mUSBControllers->end();
9881 ++it)
9882 {
9883 if ((*it)->getControllerType() == enmType)
9884 cCtrls++;
9885 }
9886
9887 return cCtrls;
9888}
9889
9890HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9891 MediaData::AttachmentList &atts)
9892{
9893 AutoCaller autoCaller(this);
9894 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9895
9896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9897
9898 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9899 it != mMediaData->mAttachments.end();
9900 ++it)
9901 {
9902 const ComObjPtr<MediumAttachment> &pAtt = *it;
9903
9904 // should never happen, but deal with NULL pointers in the list.
9905 AssertStmt(!pAtt.isNull(), continue);
9906
9907 // getControllerName() needs caller+read lock
9908 AutoCaller autoAttCaller(pAtt);
9909 if (FAILED(autoAttCaller.rc()))
9910 {
9911 atts.clear();
9912 return autoAttCaller.rc();
9913 }
9914 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9915
9916 if (pAtt->getControllerName() == aName)
9917 atts.push_back(pAtt);
9918 }
9919
9920 return S_OK;
9921}
9922
9923/**
9924 * Helper for #saveSettings. Cares about renaming the settings directory and
9925 * file if the machine name was changed and about creating a new settings file
9926 * if this is a new machine.
9927 *
9928 * @note Must be never called directly but only from #saveSettings().
9929 */
9930HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9931{
9932 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9933
9934 HRESULT rc = S_OK;
9935
9936 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9937
9938 /// @todo need to handle primary group change, too
9939
9940 /* attempt to rename the settings file if machine name is changed */
9941 if ( mUserData->s.fNameSync
9942 && mUserData.isBackedUp()
9943 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9944 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9945 )
9946 {
9947 bool dirRenamed = false;
9948 bool fileRenamed = false;
9949
9950 Utf8Str configFile, newConfigFile;
9951 Utf8Str configFilePrev, newConfigFilePrev;
9952 Utf8Str configDir, newConfigDir;
9953
9954 do
9955 {
9956 int vrc = VINF_SUCCESS;
9957
9958 Utf8Str name = mUserData.backedUpData()->s.strName;
9959 Utf8Str newName = mUserData->s.strName;
9960 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9961 if (group == "/")
9962 group.setNull();
9963 Utf8Str newGroup = mUserData->s.llGroups.front();
9964 if (newGroup == "/")
9965 newGroup.setNull();
9966
9967 configFile = mData->m_strConfigFileFull;
9968
9969 /* first, rename the directory if it matches the group and machine name */
9970 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9971 group.c_str(), RTPATH_DELIMITER, name.c_str());
9972 /** @todo hack, make somehow use of ComposeMachineFilename */
9973 if (mUserData->s.fDirectoryIncludesUUID)
9974 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9975 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9976 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9977 /** @todo hack, make somehow use of ComposeMachineFilename */
9978 if (mUserData->s.fDirectoryIncludesUUID)
9979 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9980 configDir = configFile;
9981 configDir.stripFilename();
9982 newConfigDir = configDir;
9983 if ( configDir.length() >= groupPlusName.length()
9984 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9985 {
9986 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9987 Utf8Str newConfigBaseDir(newConfigDir);
9988 newConfigDir.append(newGroupPlusName);
9989 /* consistency: use \ if appropriate on the platform */
9990 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9991 /* new dir and old dir cannot be equal here because of 'if'
9992 * above and because name != newName */
9993 Assert(configDir != newConfigDir);
9994 if (!fSettingsFileIsNew)
9995 {
9996 /* perform real rename only if the machine is not new */
9997 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9998 if ( vrc == VERR_FILE_NOT_FOUND
9999 || vrc == VERR_PATH_NOT_FOUND)
10000 {
10001 /* create the parent directory, then retry renaming */
10002 Utf8Str parent(newConfigDir);
10003 parent.stripFilename();
10004 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10005 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10006 }
10007 if (RT_FAILURE(vrc))
10008 {
10009 rc = setError(E_FAIL,
10010 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10011 configDir.c_str(),
10012 newConfigDir.c_str(),
10013 vrc);
10014 break;
10015 }
10016 /* delete subdirectories which are no longer needed */
10017 Utf8Str dir(configDir);
10018 dir.stripFilename();
10019 while (dir != newConfigBaseDir && dir != ".")
10020 {
10021 vrc = RTDirRemove(dir.c_str());
10022 if (RT_FAILURE(vrc))
10023 break;
10024 dir.stripFilename();
10025 }
10026 dirRenamed = true;
10027 }
10028 }
10029
10030 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10031 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10032
10033 /* then try to rename the settings file itself */
10034 if (newConfigFile != configFile)
10035 {
10036 /* get the path to old settings file in renamed directory */
10037 configFile = Utf8StrFmt("%s%c%s",
10038 newConfigDir.c_str(),
10039 RTPATH_DELIMITER,
10040 RTPathFilename(configFile.c_str()));
10041 if (!fSettingsFileIsNew)
10042 {
10043 /* perform real rename only if the machine is not new */
10044 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10045 if (RT_FAILURE(vrc))
10046 {
10047 rc = setError(E_FAIL,
10048 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10049 configFile.c_str(),
10050 newConfigFile.c_str(),
10051 vrc);
10052 break;
10053 }
10054 fileRenamed = true;
10055 configFilePrev = configFile;
10056 configFilePrev += "-prev";
10057 newConfigFilePrev = newConfigFile;
10058 newConfigFilePrev += "-prev";
10059 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10060 }
10061 }
10062
10063 // update m_strConfigFileFull amd mConfigFile
10064 mData->m_strConfigFileFull = newConfigFile;
10065 // compute the relative path too
10066 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10067
10068 // store the old and new so that VirtualBox::saveSettings() can update
10069 // the media registry
10070 if ( mData->mRegistered
10071 && configDir != newConfigDir)
10072 {
10073 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
10074
10075 if (pfNeedsGlobalSaveSettings)
10076 *pfNeedsGlobalSaveSettings = true;
10077 }
10078
10079 // in the saved state file path, replace the old directory with the new directory
10080 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10081 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
10082
10083 // and do the same thing for the saved state file paths of all the online snapshots
10084 if (mData->mFirstSnapshot)
10085 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
10086 newConfigDir.c_str());
10087 }
10088 while (0);
10089
10090 if (FAILED(rc))
10091 {
10092 /* silently try to rename everything back */
10093 if (fileRenamed)
10094 {
10095 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10096 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10097 }
10098 if (dirRenamed)
10099 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10100 }
10101
10102 if (FAILED(rc)) return rc;
10103 }
10104
10105 if (fSettingsFileIsNew)
10106 {
10107 /* create a virgin config file */
10108 int vrc = VINF_SUCCESS;
10109
10110 /* ensure the settings directory exists */
10111 Utf8Str path(mData->m_strConfigFileFull);
10112 path.stripFilename();
10113 if (!RTDirExists(path.c_str()))
10114 {
10115 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10116 if (RT_FAILURE(vrc))
10117 {
10118 return setError(E_FAIL,
10119 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10120 path.c_str(),
10121 vrc);
10122 }
10123 }
10124
10125 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10126 path = Utf8Str(mData->m_strConfigFileFull);
10127 RTFILE f = NIL_RTFILE;
10128 vrc = RTFileOpen(&f, path.c_str(),
10129 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10130 if (RT_FAILURE(vrc))
10131 return setError(E_FAIL,
10132 tr("Could not create the settings file '%s' (%Rrc)"),
10133 path.c_str(),
10134 vrc);
10135 RTFileClose(f);
10136 }
10137
10138 return rc;
10139}
10140
10141/**
10142 * Saves and commits machine data, user data and hardware data.
10143 *
10144 * Note that on failure, the data remains uncommitted.
10145 *
10146 * @a aFlags may combine the following flags:
10147 *
10148 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10149 * Used when saving settings after an operation that makes them 100%
10150 * correspond to the settings from the current snapshot.
10151 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
10152 * #isReallyModified() returns false. This is necessary for cases when we
10153 * change machine data directly, not through the backup()/commit() mechanism.
10154 * - SaveS_Force: settings will be saved without doing a deep compare of the
10155 * settings structures. This is used when this is called because snapshots
10156 * have changed to avoid the overhead of the deep compare.
10157 *
10158 * @note Must be called from under this object's write lock. Locks children for
10159 * writing.
10160 *
10161 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10162 * initialized to false and that will be set to true by this function if
10163 * the caller must invoke VirtualBox::saveSettings() because the global
10164 * settings have changed. This will happen if a machine rename has been
10165 * saved and the global machine and media registries will therefore need
10166 * updating.
10167 */
10168HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
10169 int aFlags /*= 0*/)
10170{
10171 LogFlowThisFuncEnter();
10172
10173 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10174
10175 /* make sure child objects are unable to modify the settings while we are
10176 * saving them */
10177 ensureNoStateDependencies();
10178
10179 AssertReturn(!isSnapshotMachine(),
10180 E_FAIL);
10181
10182 HRESULT rc = S_OK;
10183 bool fNeedsWrite = false;
10184
10185 /* First, prepare to save settings. It will care about renaming the
10186 * settings directory and file if the machine name was changed and about
10187 * creating a new settings file if this is a new machine. */
10188 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
10189 if (FAILED(rc)) return rc;
10190
10191 // keep a pointer to the current settings structures
10192 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10193 settings::MachineConfigFile *pNewConfig = NULL;
10194
10195 try
10196 {
10197 // make a fresh one to have everyone write stuff into
10198 pNewConfig = new settings::MachineConfigFile(NULL);
10199 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10200
10201 // now go and copy all the settings data from COM to the settings structures
10202 // (this calles saveSettings() on all the COM objects in the machine)
10203 copyMachineDataToSettings(*pNewConfig);
10204
10205 if (aFlags & SaveS_ResetCurStateModified)
10206 {
10207 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10208 mData->mCurrentStateModified = FALSE;
10209 fNeedsWrite = true; // always, no need to compare
10210 }
10211 else if (aFlags & SaveS_Force)
10212 {
10213 fNeedsWrite = true; // always, no need to compare
10214 }
10215 else
10216 {
10217 if (!mData->mCurrentStateModified)
10218 {
10219 // do a deep compare of the settings that we just saved with the settings
10220 // previously stored in the config file; this invokes MachineConfigFile::operator==
10221 // which does a deep compare of all the settings, which is expensive but less expensive
10222 // than writing out XML in vain
10223 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10224
10225 // could still be modified if any settings changed
10226 mData->mCurrentStateModified = fAnySettingsChanged;
10227
10228 fNeedsWrite = fAnySettingsChanged;
10229 }
10230 else
10231 fNeedsWrite = true;
10232 }
10233
10234 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10235
10236 if (fNeedsWrite)
10237 // now spit it all out!
10238 pNewConfig->write(mData->m_strConfigFileFull);
10239
10240 mData->pMachineConfigFile = pNewConfig;
10241 delete pOldConfig;
10242 commit();
10243
10244 // after saving settings, we are no longer different from the XML on disk
10245 mData->flModifications = 0;
10246 }
10247 catch (HRESULT err)
10248 {
10249 // we assume that error info is set by the thrower
10250 rc = err;
10251
10252 // restore old config
10253 delete pNewConfig;
10254 mData->pMachineConfigFile = pOldConfig;
10255 }
10256 catch (...)
10257 {
10258 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10259 }
10260
10261 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10262 {
10263 /* Fire the data change event, even on failure (since we've already
10264 * committed all data). This is done only for SessionMachines because
10265 * mutable Machine instances are always not registered (i.e. private
10266 * to the client process that creates them) and thus don't need to
10267 * inform callbacks. */
10268 if (isSessionMachine())
10269 mParent->onMachineDataChange(mData->mUuid);
10270 }
10271
10272 LogFlowThisFunc(("rc=%08X\n", rc));
10273 LogFlowThisFuncLeave();
10274 return rc;
10275}
10276
10277/**
10278 * Implementation for saving the machine settings into the given
10279 * settings::MachineConfigFile instance. This copies machine extradata
10280 * from the previous machine config file in the instance data, if any.
10281 *
10282 * This gets called from two locations:
10283 *
10284 * -- Machine::saveSettings(), during the regular XML writing;
10285 *
10286 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10287 * exported to OVF and we write the VirtualBox proprietary XML
10288 * into a <vbox:Machine> tag.
10289 *
10290 * This routine fills all the fields in there, including snapshots, *except*
10291 * for the following:
10292 *
10293 * -- fCurrentStateModified. There is some special logic associated with that.
10294 *
10295 * The caller can then call MachineConfigFile::write() or do something else
10296 * with it.
10297 *
10298 * Caller must hold the machine lock!
10299 *
10300 * This throws XML errors and HRESULT, so the caller must have a catch block!
10301 */
10302void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10303{
10304 // deep copy extradata
10305 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10306
10307 config.uuid = mData->mUuid;
10308
10309 // copy name, description, OS type, teleport, UTC etc.
10310 config.machineUserData = mUserData->s;
10311
10312 // Encode the Icon Override data from Machine and store on config userdata.
10313 com::SafeArray<BYTE> iconByte;
10314 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10315 ssize_t cbData = iconByte.size();
10316 if (cbData > 0)
10317 {
10318 ssize_t cchOut = RTBase64EncodedLength(cbData);
10319 Utf8Str strIconData;
10320 strIconData.reserve(cchOut+1);
10321 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10322 strIconData.mutableRaw(), strIconData.capacity(),
10323 NULL);
10324 if (RT_FAILURE(vrc))
10325 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10326 strIconData.jolt();
10327 config.machineUserData.ovIcon = strIconData;
10328 }
10329 else
10330 config.machineUserData.ovIcon.setNull();
10331
10332 if ( mData->mMachineState == MachineState_Saved
10333 || mData->mMachineState == MachineState_Restoring
10334 // when deleting a snapshot we may or may not have a saved state in the current state,
10335 // so let's not assert here please
10336 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10337 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10338 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10339 && (!mSSData->strStateFilePath.isEmpty())
10340 )
10341 )
10342 {
10343 Assert(!mSSData->strStateFilePath.isEmpty());
10344 /* try to make the file name relative to the settings file dir */
10345 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10346 }
10347 else
10348 {
10349 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10350 config.strStateFile.setNull();
10351 }
10352
10353 if (mData->mCurrentSnapshot)
10354 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10355 else
10356 config.uuidCurrentSnapshot.clear();
10357
10358 config.timeLastStateChange = mData->mLastStateChange;
10359 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10360 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10361
10362 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10363 if (FAILED(rc)) throw rc;
10364
10365 rc = saveStorageControllers(config.storageMachine);
10366 if (FAILED(rc)) throw rc;
10367
10368 // save machine's media registry if this is VirtualBox 4.0 or later
10369 if (config.canHaveOwnMediaRegistry())
10370 {
10371 // determine machine folder
10372 Utf8Str strMachineFolder = getSettingsFileFull();
10373 strMachineFolder.stripFilename();
10374 mParent->saveMediaRegistry(config.mediaRegistry,
10375 getId(), // only media with registry ID == machine UUID
10376 strMachineFolder);
10377 // this throws HRESULT
10378 }
10379
10380 // save snapshots
10381 rc = saveAllSnapshots(config);
10382 if (FAILED(rc)) throw rc;
10383}
10384
10385/**
10386 * Saves all snapshots of the machine into the given machine config file. Called
10387 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10388 * @param config
10389 * @return
10390 */
10391HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10392{
10393 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10394
10395 HRESULT rc = S_OK;
10396
10397 try
10398 {
10399 config.llFirstSnapshot.clear();
10400
10401 if (mData->mFirstSnapshot)
10402 {
10403 settings::Snapshot snapNew;
10404 config.llFirstSnapshot.push_back(snapNew);
10405
10406 // get reference to the fresh copy of the snapshot on the list and
10407 // work on that copy directly to avoid excessive copying later
10408 settings::Snapshot &snap = config.llFirstSnapshot.front();
10409
10410 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10411 if (FAILED(rc)) throw rc;
10412 }
10413
10414// if (mType == IsSessionMachine)
10415// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10416
10417 }
10418 catch (HRESULT err)
10419 {
10420 /* we assume that error info is set by the thrower */
10421 rc = err;
10422 }
10423 catch (...)
10424 {
10425 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10426 }
10427
10428 return rc;
10429}
10430
10431/**
10432 * Saves the VM hardware configuration. It is assumed that the
10433 * given node is empty.
10434 *
10435 * @param data Reference to the settings object for the hardware config.
10436 * @param pDbg Pointer to the settings object for the debugging config
10437 * which happens to live in mHWData.
10438 * @param pAutostart Pointer to the settings object for the autostart config
10439 * which happens to live in mHWData.
10440 */
10441HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10442 settings::Autostart *pAutostart)
10443{
10444 HRESULT rc = S_OK;
10445
10446 try
10447 {
10448 /* The hardware version attribute (optional).
10449 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10450 if ( mHWData->mHWVersion == "1"
10451 && mSSData->strStateFilePath.isEmpty()
10452 )
10453 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. */
10454
10455 data.strVersion = mHWData->mHWVersion;
10456 data.uuid = mHWData->mHardwareUUID;
10457
10458 // CPU
10459 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10460 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
10461 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10462 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10463 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10464 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10465 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10466 data.fPAE = !!mHWData->mPAEEnabled;
10467 data.enmLongMode = mHWData->mLongMode;
10468 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10469
10470 /* Standard and Extended CPUID leafs. */
10471 data.llCpuIdLeafs.clear();
10472 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10473 {
10474 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10475 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10476 }
10477 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10478 {
10479 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10480 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10481 }
10482
10483 data.cCPUs = mHWData->mCPUCount;
10484 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10485 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10486
10487 data.llCpus.clear();
10488 if (data.fCpuHotPlug)
10489 {
10490 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10491 {
10492 if (mHWData->mCPUAttached[idx])
10493 {
10494 settings::Cpu cpu;
10495 cpu.ulId = idx;
10496 data.llCpus.push_back(cpu);
10497 }
10498 }
10499 }
10500
10501 // memory
10502 data.ulMemorySizeMB = mHWData->mMemorySize;
10503 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10504
10505 // firmware
10506 data.firmwareType = mHWData->mFirmwareType;
10507
10508 // HID
10509 data.pointingHIDType = mHWData->mPointingHIDType;
10510 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10511
10512 // chipset
10513 data.chipsetType = mHWData->mChipsetType;
10514
10515 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10516 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10517
10518 // HPET
10519 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10520
10521 // boot order
10522 data.mapBootOrder.clear();
10523 for (size_t i = 0;
10524 i < RT_ELEMENTS(mHWData->mBootOrder);
10525 ++i)
10526 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10527
10528 // display
10529 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10530 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10531 data.cMonitors = mHWData->mMonitorCount;
10532 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10533 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10534 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10535 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10536 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10537 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10538 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10539 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10540 {
10541 if (mHWData->maVideoCaptureScreens[i])
10542 ASMBitSet(&data.u64VideoCaptureScreens, i);
10543 else
10544 ASMBitClear(&data.u64VideoCaptureScreens, i);
10545 }
10546 /* store relative video capture file if possible */
10547 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10548
10549 /* VRDEServer settings (optional) */
10550 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10551 if (FAILED(rc)) throw rc;
10552
10553 /* BIOS (required) */
10554 rc = mBIOSSettings->saveSettings(data.biosSettings);
10555 if (FAILED(rc)) throw rc;
10556
10557 /* USB Controller (required) */
10558 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10559 it != mUSBControllers->end();
10560 ++it)
10561 {
10562 ComObjPtr<USBController> ctrl = *it;
10563 settings::USBController settingsCtrl;
10564
10565 settingsCtrl.strName = ctrl->getName();
10566 settingsCtrl.enmType = ctrl->getControllerType();
10567
10568 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10569 }
10570
10571 /* USB device filters (required) */
10572 rc = mUSBDeviceFilters->saveSettings(data.usbSettings);
10573 if (FAILED(rc)) throw rc;
10574
10575 /* Network adapters (required) */
10576 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10577 data.llNetworkAdapters.clear();
10578 /* Write out only the nominal number of network adapters for this
10579 * chipset type. Since Machine::commit() hasn't been called there
10580 * may be extra NIC settings in the vector. */
10581 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10582 {
10583 settings::NetworkAdapter nic;
10584 nic.ulSlot = slot;
10585 /* paranoia check... must not be NULL, but must not crash either. */
10586 if (mNetworkAdapters[slot])
10587 {
10588 rc = mNetworkAdapters[slot]->saveSettings(nic);
10589 if (FAILED(rc)) throw rc;
10590
10591 data.llNetworkAdapters.push_back(nic);
10592 }
10593 }
10594
10595 /* Serial ports */
10596 data.llSerialPorts.clear();
10597 for (ULONG slot = 0;
10598 slot < RT_ELEMENTS(mSerialPorts);
10599 ++slot)
10600 {
10601 settings::SerialPort s;
10602 s.ulSlot = slot;
10603 rc = mSerialPorts[slot]->saveSettings(s);
10604 if (FAILED(rc)) return rc;
10605
10606 data.llSerialPorts.push_back(s);
10607 }
10608
10609 /* Parallel ports */
10610 data.llParallelPorts.clear();
10611 for (ULONG slot = 0;
10612 slot < RT_ELEMENTS(mParallelPorts);
10613 ++slot)
10614 {
10615 settings::ParallelPort p;
10616 p.ulSlot = slot;
10617 rc = mParallelPorts[slot]->saveSettings(p);
10618 if (FAILED(rc)) return rc;
10619
10620 data.llParallelPorts.push_back(p);
10621 }
10622
10623 /* Audio adapter */
10624 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10625 if (FAILED(rc)) return rc;
10626
10627 /* Shared folders */
10628 data.llSharedFolders.clear();
10629 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10630 it != mHWData->mSharedFolders.end();
10631 ++it)
10632 {
10633 SharedFolder *pSF = *it;
10634 AutoCaller sfCaller(pSF);
10635 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10636 settings::SharedFolder sf;
10637 sf.strName = pSF->getName();
10638 sf.strHostPath = pSF->getHostPath();
10639 sf.fWritable = !!pSF->isWritable();
10640 sf.fAutoMount = !!pSF->isAutoMounted();
10641
10642 data.llSharedFolders.push_back(sf);
10643 }
10644
10645 // clipboard
10646 data.clipboardMode = mHWData->mClipboardMode;
10647
10648 // drag'n'drop
10649 data.dragAndDropMode = mHWData->mDragAndDropMode;
10650
10651 /* Guest */
10652 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10653
10654 // IO settings
10655 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10656 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10657
10658 /* BandwidthControl (required) */
10659 rc = mBandwidthControl->saveSettings(data.ioSettings);
10660 if (FAILED(rc)) throw rc;
10661
10662 /* Host PCI devices */
10663 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10664 it != mHWData->mPCIDeviceAssignments.end();
10665 ++it)
10666 {
10667 ComObjPtr<PCIDeviceAttachment> pda = *it;
10668 settings::HostPCIDeviceAttachment hpda;
10669
10670 rc = pda->saveSettings(hpda);
10671 if (FAILED(rc)) throw rc;
10672
10673 data.pciAttachments.push_back(hpda);
10674 }
10675
10676
10677 // guest properties
10678 data.llGuestProperties.clear();
10679#ifdef VBOX_WITH_GUEST_PROPS
10680 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10681 it != mHWData->mGuestProperties.end();
10682 ++it)
10683 {
10684 HWData::GuestProperty property = it->second;
10685
10686 /* Remove transient guest properties at shutdown unless we
10687 * are saving state */
10688 if ( ( mData->mMachineState == MachineState_PoweredOff
10689 || mData->mMachineState == MachineState_Aborted
10690 || mData->mMachineState == MachineState_Teleported)
10691 && ( property.mFlags & guestProp::TRANSIENT
10692 || property.mFlags & guestProp::TRANSRESET))
10693 continue;
10694 settings::GuestProperty prop;
10695 prop.strName = it->first;
10696 prop.strValue = property.strValue;
10697 prop.timestamp = property.mTimestamp;
10698 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10699 guestProp::writeFlags(property.mFlags, szFlags);
10700 prop.strFlags = szFlags;
10701
10702 data.llGuestProperties.push_back(prop);
10703 }
10704
10705 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10706 /* I presume this doesn't require a backup(). */
10707 mData->mGuestPropertiesModified = FALSE;
10708#endif /* VBOX_WITH_GUEST_PROPS defined */
10709
10710 *pDbg = mHWData->mDebugging;
10711 *pAutostart = mHWData->mAutostart;
10712
10713 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10714 }
10715 catch(std::bad_alloc &)
10716 {
10717 return E_OUTOFMEMORY;
10718 }
10719
10720 AssertComRC(rc);
10721 return rc;
10722}
10723
10724/**
10725 * Saves the storage controller configuration.
10726 *
10727 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10728 */
10729HRESULT Machine::saveStorageControllers(settings::Storage &data)
10730{
10731 data.llStorageControllers.clear();
10732
10733 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10734 it != mStorageControllers->end();
10735 ++it)
10736 {
10737 HRESULT rc;
10738 ComObjPtr<StorageController> pCtl = *it;
10739
10740 settings::StorageController ctl;
10741 ctl.strName = pCtl->getName();
10742 ctl.controllerType = pCtl->getControllerType();
10743 ctl.storageBus = pCtl->getStorageBus();
10744 ctl.ulInstance = pCtl->getInstance();
10745 ctl.fBootable = pCtl->getBootable();
10746
10747 /* Save the port count. */
10748 ULONG portCount;
10749 rc = pCtl->COMGETTER(PortCount)(&portCount);
10750 ComAssertComRCRet(rc, rc);
10751 ctl.ulPortCount = portCount;
10752
10753 /* Save fUseHostIOCache */
10754 BOOL fUseHostIOCache;
10755 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10756 ComAssertComRCRet(rc, rc);
10757 ctl.fUseHostIOCache = !!fUseHostIOCache;
10758
10759 /* Save IDE emulation settings. */
10760 if (ctl.controllerType == StorageControllerType_IntelAhci)
10761 {
10762 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10763 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10764 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10765 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10766 )
10767 ComAssertComRCRet(rc, rc);
10768 }
10769
10770 /* save the devices now. */
10771 rc = saveStorageDevices(pCtl, ctl);
10772 ComAssertComRCRet(rc, rc);
10773
10774 data.llStorageControllers.push_back(ctl);
10775 }
10776
10777 return S_OK;
10778}
10779
10780/**
10781 * Saves the hard disk configuration.
10782 */
10783HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10784 settings::StorageController &data)
10785{
10786 MediaData::AttachmentList atts;
10787
10788 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10789 if (FAILED(rc)) return rc;
10790
10791 data.llAttachedDevices.clear();
10792 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10793 it != atts.end();
10794 ++it)
10795 {
10796 settings::AttachedDevice dev;
10797
10798 MediumAttachment *pAttach = *it;
10799 Medium *pMedium = pAttach->getMedium();
10800
10801 dev.deviceType = pAttach->getType();
10802 dev.lPort = pAttach->getPort();
10803 dev.lDevice = pAttach->getDevice();
10804 if (pMedium)
10805 {
10806 if (pMedium->isHostDrive())
10807 dev.strHostDriveSrc = pMedium->getLocationFull();
10808 else
10809 dev.uuid = pMedium->getId();
10810 dev.fPassThrough = pAttach->getPassthrough();
10811 dev.fTempEject = pAttach->getTempEject();
10812 dev.fNonRotational = pAttach->getNonRotational();
10813 dev.fDiscard = pAttach->getDiscard();
10814 }
10815
10816 dev.strBwGroup = pAttach->getBandwidthGroup();
10817
10818 data.llAttachedDevices.push_back(dev);
10819 }
10820
10821 return S_OK;
10822}
10823
10824/**
10825 * Saves machine state settings as defined by aFlags
10826 * (SaveSTS_* values).
10827 *
10828 * @param aFlags Combination of SaveSTS_* flags.
10829 *
10830 * @note Locks objects for writing.
10831 */
10832HRESULT Machine::saveStateSettings(int aFlags)
10833{
10834 if (aFlags == 0)
10835 return S_OK;
10836
10837 AutoCaller autoCaller(this);
10838 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10839
10840 /* This object's write lock is also necessary to serialize file access
10841 * (prevent concurrent reads and writes) */
10842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10843
10844 HRESULT rc = S_OK;
10845
10846 Assert(mData->pMachineConfigFile);
10847
10848 try
10849 {
10850 if (aFlags & SaveSTS_CurStateModified)
10851 mData->pMachineConfigFile->fCurrentStateModified = true;
10852
10853 if (aFlags & SaveSTS_StateFilePath)
10854 {
10855 if (!mSSData->strStateFilePath.isEmpty())
10856 /* try to make the file name relative to the settings file dir */
10857 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10858 else
10859 mData->pMachineConfigFile->strStateFile.setNull();
10860 }
10861
10862 if (aFlags & SaveSTS_StateTimeStamp)
10863 {
10864 Assert( mData->mMachineState != MachineState_Aborted
10865 || mSSData->strStateFilePath.isEmpty());
10866
10867 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10868
10869 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10870//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10871 }
10872
10873 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10874 }
10875 catch (...)
10876 {
10877 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10878 }
10879
10880 return rc;
10881}
10882
10883/**
10884 * Ensures that the given medium is added to a media registry. If this machine
10885 * was created with 4.0 or later, then the machine registry is used. Otherwise
10886 * the global VirtualBox media registry is used.
10887 *
10888 * Caller must NOT hold machine lock, media tree or any medium locks!
10889 *
10890 * @param pMedium
10891 */
10892void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10893{
10894 /* Paranoia checks: do not hold machine or media tree locks. */
10895 AssertReturnVoid(!isWriteLockOnCurrentThread());
10896 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10897
10898 ComObjPtr<Medium> pBase;
10899 {
10900 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10901 pBase = pMedium->getBase();
10902 }
10903
10904 /* Paranoia checks: do not hold medium locks. */
10905 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10906 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10907
10908 // decide which medium registry to use now that the medium is attached:
10909 Guid uuid;
10910 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10911 // machine XML is VirtualBox 4.0 or higher:
10912 uuid = getId(); // machine UUID
10913 else
10914 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10915
10916 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10917 mParent->markRegistryModified(uuid);
10918
10919 /* For more complex hard disk structures it can happen that the base
10920 * medium isn't yet associated with any medium registry. Do that now. */
10921 if (pMedium != pBase)
10922 {
10923 if (pBase->addRegistry(uuid, true /* fRecurse */))
10924 mParent->markRegistryModified(uuid);
10925 }
10926}
10927
10928/**
10929 * Creates differencing hard disks for all normal hard disks attached to this
10930 * machine and a new set of attachments to refer to created disks.
10931 *
10932 * Used when taking a snapshot or when deleting the current state. Gets called
10933 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10934 *
10935 * This method assumes that mMediaData contains the original hard disk attachments
10936 * it needs to create diffs for. On success, these attachments will be replaced
10937 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10938 * called to delete created diffs which will also rollback mMediaData and restore
10939 * whatever was backed up before calling this method.
10940 *
10941 * Attachments with non-normal hard disks are left as is.
10942 *
10943 * If @a aOnline is @c false then the original hard disks that require implicit
10944 * diffs will be locked for reading. Otherwise it is assumed that they are
10945 * already locked for writing (when the VM was started). Note that in the latter
10946 * case it is responsibility of the caller to lock the newly created diffs for
10947 * writing if this method succeeds.
10948 *
10949 * @param aProgress Progress object to run (must contain at least as
10950 * many operations left as the number of hard disks
10951 * attached).
10952 * @param aOnline Whether the VM was online prior to this operation.
10953 *
10954 * @note The progress object is not marked as completed, neither on success nor
10955 * on failure. This is a responsibility of the caller.
10956 *
10957 * @note Locks this object and the media tree for writing.
10958 */
10959HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10960 ULONG aWeight,
10961 bool aOnline)
10962{
10963 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10964
10965 AutoCaller autoCaller(this);
10966 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10967
10968 AutoMultiWriteLock2 alock(this->lockHandle(),
10969 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10970
10971 /* must be in a protective state because we release the lock below */
10972 AssertReturn( mData->mMachineState == MachineState_Saving
10973 || mData->mMachineState == MachineState_LiveSnapshotting
10974 || mData->mMachineState == MachineState_RestoringSnapshot
10975 || mData->mMachineState == MachineState_DeletingSnapshot
10976 , E_FAIL);
10977
10978 HRESULT rc = S_OK;
10979
10980 // use appropriate locked media map (online or offline)
10981 MediumLockListMap lockedMediaOffline;
10982 MediumLockListMap *lockedMediaMap;
10983 if (aOnline)
10984 lockedMediaMap = &mData->mSession.mLockedMedia;
10985 else
10986 lockedMediaMap = &lockedMediaOffline;
10987
10988 try
10989 {
10990 if (!aOnline)
10991 {
10992 /* lock all attached hard disks early to detect "in use"
10993 * situations before creating actual diffs */
10994 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10995 it != mMediaData->mAttachments.end();
10996 ++it)
10997 {
10998 MediumAttachment* pAtt = *it;
10999 if (pAtt->getType() == DeviceType_HardDisk)
11000 {
11001 Medium* pMedium = pAtt->getMedium();
11002 Assert(pMedium);
11003
11004 MediumLockList *pMediumLockList(new MediumLockList());
11005 alock.release();
11006 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11007 false /* fMediumLockWrite */,
11008 NULL,
11009 *pMediumLockList);
11010 alock.acquire();
11011 if (FAILED(rc))
11012 {
11013 delete pMediumLockList;
11014 throw rc;
11015 }
11016 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11017 if (FAILED(rc))
11018 {
11019 throw setError(rc,
11020 tr("Collecting locking information for all attached media failed"));
11021 }
11022 }
11023 }
11024
11025 /* Now lock all media. If this fails, nothing is locked. */
11026 alock.release();
11027 rc = lockedMediaMap->Lock();
11028 alock.acquire();
11029 if (FAILED(rc))
11030 {
11031 throw setError(rc,
11032 tr("Locking of attached media failed"));
11033 }
11034 }
11035
11036 /* remember the current list (note that we don't use backup() since
11037 * mMediaData may be already backed up) */
11038 MediaData::AttachmentList atts = mMediaData->mAttachments;
11039
11040 /* start from scratch */
11041 mMediaData->mAttachments.clear();
11042
11043 /* go through remembered attachments and create diffs for normal hard
11044 * disks and attach them */
11045 for (MediaData::AttachmentList::const_iterator it = atts.begin();
11046 it != atts.end();
11047 ++it)
11048 {
11049 MediumAttachment* pAtt = *it;
11050
11051 DeviceType_T devType = pAtt->getType();
11052 Medium* pMedium = pAtt->getMedium();
11053
11054 if ( devType != DeviceType_HardDisk
11055 || pMedium == NULL
11056 || pMedium->getType() != MediumType_Normal)
11057 {
11058 /* copy the attachment as is */
11059
11060 /** @todo the progress object created in Console::TakeSnaphot
11061 * only expects operations for hard disks. Later other
11062 * device types need to show up in the progress as well. */
11063 if (devType == DeviceType_HardDisk)
11064 {
11065 if (pMedium == NULL)
11066 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11067 aWeight); // weight
11068 else
11069 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11070 pMedium->getBase()->getName().c_str()).raw(),
11071 aWeight); // weight
11072 }
11073
11074 mMediaData->mAttachments.push_back(pAtt);
11075 continue;
11076 }
11077
11078 /* need a diff */
11079 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11080 pMedium->getBase()->getName().c_str()).raw(),
11081 aWeight); // weight
11082
11083 Utf8Str strFullSnapshotFolder;
11084 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11085
11086 ComObjPtr<Medium> diff;
11087 diff.createObject();
11088 // store the diff in the same registry as the parent
11089 // (this cannot fail here because we can't create implicit diffs for
11090 // unregistered images)
11091 Guid uuidRegistryParent;
11092 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
11093 Assert(fInRegistry); NOREF(fInRegistry);
11094 rc = diff->init(mParent,
11095 pMedium->getPreferredDiffFormat(),
11096 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11097 uuidRegistryParent);
11098 if (FAILED(rc)) throw rc;
11099
11100 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11101 * the push_back? Looks like we're going to release medium with the
11102 * wrong kind of lock (general issue with if we fail anywhere at all)
11103 * and an orphaned VDI in the snapshots folder. */
11104
11105 /* update the appropriate lock list */
11106 MediumLockList *pMediumLockList;
11107 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11108 AssertComRCThrowRC(rc);
11109 if (aOnline)
11110 {
11111 alock.release();
11112 /* The currently attached medium will be read-only, change
11113 * the lock type to read. */
11114 rc = pMediumLockList->Update(pMedium, false);
11115 alock.acquire();
11116 AssertComRCThrowRC(rc);
11117 }
11118
11119 /* release the locks before the potentially lengthy operation */
11120 alock.release();
11121 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
11122 pMediumLockList,
11123 NULL /* aProgress */,
11124 true /* aWait */);
11125 alock.acquire();
11126 if (FAILED(rc)) throw rc;
11127
11128 /* actual lock list update is done in Medium::commitMedia */
11129
11130 rc = diff->addBackReference(mData->mUuid);
11131 AssertComRCThrowRC(rc);
11132
11133 /* add a new attachment */
11134 ComObjPtr<MediumAttachment> attachment;
11135 attachment.createObject();
11136 rc = attachment->init(this,
11137 diff,
11138 pAtt->getControllerName(),
11139 pAtt->getPort(),
11140 pAtt->getDevice(),
11141 DeviceType_HardDisk,
11142 true /* aImplicit */,
11143 false /* aPassthrough */,
11144 false /* aTempEject */,
11145 pAtt->getNonRotational(),
11146 pAtt->getDiscard(),
11147 pAtt->getBandwidthGroup());
11148 if (FAILED(rc)) throw rc;
11149
11150 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11151 AssertComRCThrowRC(rc);
11152 mMediaData->mAttachments.push_back(attachment);
11153 }
11154 }
11155 catch (HRESULT aRC) { rc = aRC; }
11156
11157 /* unlock all hard disks we locked when there is no VM */
11158 if (!aOnline)
11159 {
11160 ErrorInfoKeeper eik;
11161
11162 HRESULT rc1 = lockedMediaMap->Clear();
11163 AssertComRC(rc1);
11164 }
11165
11166 return rc;
11167}
11168
11169/**
11170 * Deletes implicit differencing hard disks created either by
11171 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
11172 *
11173 * Note that to delete hard disks created by #AttachDevice() this method is
11174 * called from #fixupMedia() when the changes are rolled back.
11175 *
11176 * @note Locks this object and the media tree for writing.
11177 */
11178HRESULT Machine::deleteImplicitDiffs(bool aOnline)
11179{
11180 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11181
11182 AutoCaller autoCaller(this);
11183 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11184
11185 AutoMultiWriteLock2 alock(this->lockHandle(),
11186 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11187
11188 /* We absolutely must have backed up state. */
11189 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
11190
11191 /* Check if there are any implicitly created diff images. */
11192 bool fImplicitDiffs = false;
11193 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11194 it != mMediaData->mAttachments.end();
11195 ++it)
11196 {
11197 const ComObjPtr<MediumAttachment> &pAtt = *it;
11198 if (pAtt->isImplicit())
11199 {
11200 fImplicitDiffs = true;
11201 break;
11202 }
11203 }
11204 /* If there is nothing to do, leave early. This saves lots of image locking
11205 * effort. It also avoids a MachineStateChanged event without real reason.
11206 * This is important e.g. when loading a VM config, because there should be
11207 * no events. Otherwise API clients can become thoroughly confused for
11208 * inaccessible VMs (the code for loading VM configs uses this method for
11209 * cleanup if the config makes no sense), as they take such events as an
11210 * indication that the VM is alive, and they would force the VM config to
11211 * be reread, leading to an endless loop. */
11212 if (!fImplicitDiffs)
11213 return S_OK;
11214
11215 HRESULT rc = S_OK;
11216 MachineState_T oldState = mData->mMachineState;
11217
11218 /* will release the lock before the potentially lengthy operation,
11219 * so protect with the special state (unless already protected) */
11220 if ( oldState != MachineState_Saving
11221 && oldState != MachineState_LiveSnapshotting
11222 && oldState != MachineState_RestoringSnapshot
11223 && oldState != MachineState_DeletingSnapshot
11224 && oldState != MachineState_DeletingSnapshotOnline
11225 && oldState != MachineState_DeletingSnapshotPaused
11226 )
11227 setMachineState(MachineState_SettingUp);
11228
11229 // use appropriate locked media map (online or offline)
11230 MediumLockListMap lockedMediaOffline;
11231 MediumLockListMap *lockedMediaMap;
11232 if (aOnline)
11233 lockedMediaMap = &mData->mSession.mLockedMedia;
11234 else
11235 lockedMediaMap = &lockedMediaOffline;
11236
11237 try
11238 {
11239 if (!aOnline)
11240 {
11241 /* lock all attached hard disks early to detect "in use"
11242 * situations before deleting actual diffs */
11243 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11244 it != mMediaData->mAttachments.end();
11245 ++it)
11246 {
11247 MediumAttachment* pAtt = *it;
11248 if (pAtt->getType() == DeviceType_HardDisk)
11249 {
11250 Medium* pMedium = pAtt->getMedium();
11251 Assert(pMedium);
11252
11253 MediumLockList *pMediumLockList(new MediumLockList());
11254 alock.release();
11255 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11256 false /* fMediumLockWrite */,
11257 NULL,
11258 *pMediumLockList);
11259 alock.acquire();
11260
11261 if (FAILED(rc))
11262 {
11263 delete pMediumLockList;
11264 throw rc;
11265 }
11266
11267 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11268 if (FAILED(rc))
11269 throw rc;
11270 }
11271 }
11272
11273 if (FAILED(rc))
11274 throw rc;
11275 } // end of offline
11276
11277 /* Lock lists are now up to date and include implicitly created media */
11278
11279 /* Go through remembered attachments and delete all implicitly created
11280 * diffs and fix up the attachment information */
11281 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11282 MediaData::AttachmentList implicitAtts;
11283 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11284 it != mMediaData->mAttachments.end();
11285 ++it)
11286 {
11287 ComObjPtr<MediumAttachment> pAtt = *it;
11288 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11289 if (pMedium.isNull())
11290 continue;
11291
11292 // Implicit attachments go on the list for deletion and back references are removed.
11293 if (pAtt->isImplicit())
11294 {
11295 /* Deassociate and mark for deletion */
11296 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
11297 rc = pMedium->removeBackReference(mData->mUuid);
11298 if (FAILED(rc))
11299 throw rc;
11300 implicitAtts.push_back(pAtt);
11301 continue;
11302 }
11303
11304 /* Was this medium attached before? */
11305 if (!findAttachment(oldAtts, pMedium))
11306 {
11307 /* no: de-associate */
11308 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
11309 rc = pMedium->removeBackReference(mData->mUuid);
11310 if (FAILED(rc))
11311 throw rc;
11312 continue;
11313 }
11314 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
11315 }
11316
11317 /* If there are implicit attachments to delete, throw away the lock
11318 * map contents (which will unlock all media) since the medium
11319 * attachments will be rolled back. Below we need to completely
11320 * recreate the lock map anyway since it is infinitely complex to
11321 * do this incrementally (would need reconstructing each attachment
11322 * change, which would be extremely hairy). */
11323 if (implicitAtts.size() != 0)
11324 {
11325 ErrorInfoKeeper eik;
11326
11327 HRESULT rc1 = lockedMediaMap->Clear();
11328 AssertComRC(rc1);
11329 }
11330
11331 /* rollback hard disk changes */
11332 mMediaData.rollback();
11333
11334 MultiResult mrc(S_OK);
11335
11336 // Delete unused implicit diffs.
11337 if (implicitAtts.size() != 0)
11338 {
11339 alock.release();
11340
11341 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11342 it != implicitAtts.end();
11343 ++it)
11344 {
11345 // Remove medium associated with this attachment.
11346 ComObjPtr<MediumAttachment> pAtt = *it;
11347 Assert(pAtt);
11348 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11349 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11350 Assert(pMedium);
11351
11352 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11353 // continue on delete failure, just collect error messages
11354 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11355 mrc = rc;
11356 }
11357
11358 alock.acquire();
11359
11360 /* if there is a VM recreate media lock map as mentioned above,
11361 * otherwise it is a waste of time and we leave things unlocked */
11362 if (aOnline)
11363 {
11364 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11365 /* must never be NULL, but better safe than sorry */
11366 if (!pMachine.isNull())
11367 {
11368 alock.release();
11369 rc = mData->mSession.mMachine->lockMedia();
11370 alock.acquire();
11371 if (FAILED(rc))
11372 throw rc;
11373 }
11374 }
11375 }
11376 }
11377 catch (HRESULT aRC) {rc = aRC;}
11378
11379 if (mData->mMachineState == MachineState_SettingUp)
11380 setMachineState(oldState);
11381
11382 /* unlock all hard disks we locked when there is no VM */
11383 if (!aOnline)
11384 {
11385 ErrorInfoKeeper eik;
11386
11387 HRESULT rc1 = lockedMediaMap->Clear();
11388 AssertComRC(rc1);
11389 }
11390
11391 return rc;
11392}
11393
11394
11395/**
11396 * Looks through the given list of media attachments for one with the given parameters
11397 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11398 * can be searched as well if needed.
11399 *
11400 * @param list
11401 * @param aControllerName
11402 * @param aControllerPort
11403 * @param aDevice
11404 * @return
11405 */
11406MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11407 IN_BSTR aControllerName,
11408 LONG aControllerPort,
11409 LONG aDevice)
11410{
11411 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11412 it != ll.end();
11413 ++it)
11414 {
11415 MediumAttachment *pAttach = *it;
11416 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11417 return pAttach;
11418 }
11419
11420 return NULL;
11421}
11422
11423/**
11424 * Looks through the given list of media attachments for one with the given parameters
11425 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11426 * can be searched as well if needed.
11427 *
11428 * @param list
11429 * @param aControllerName
11430 * @param aControllerPort
11431 * @param aDevice
11432 * @return
11433 */
11434MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11435 ComObjPtr<Medium> pMedium)
11436{
11437 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11438 it != ll.end();
11439 ++it)
11440 {
11441 MediumAttachment *pAttach = *it;
11442 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11443 if (pMediumThis == pMedium)
11444 return pAttach;
11445 }
11446
11447 return NULL;
11448}
11449
11450/**
11451 * Looks through the given list of media attachments for one with the given parameters
11452 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11453 * can be searched as well if needed.
11454 *
11455 * @param list
11456 * @param aControllerName
11457 * @param aControllerPort
11458 * @param aDevice
11459 * @return
11460 */
11461MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11462 Guid &id)
11463{
11464 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11465 it != ll.end();
11466 ++it)
11467 {
11468 MediumAttachment *pAttach = *it;
11469 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11470 if (pMediumThis->getId() == id)
11471 return pAttach;
11472 }
11473
11474 return NULL;
11475}
11476
11477/**
11478 * Main implementation for Machine::DetachDevice. This also gets called
11479 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11480 *
11481 * @param pAttach Medium attachment to detach.
11482 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11483 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11484 * @return
11485 */
11486HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11487 AutoWriteLock &writeLock,
11488 Snapshot *pSnapshot)
11489{
11490 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11491 DeviceType_T mediumType = pAttach->getType();
11492
11493 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11494
11495 if (pAttach->isImplicit())
11496 {
11497 /* attempt to implicitly delete the implicitly created diff */
11498
11499 /// @todo move the implicit flag from MediumAttachment to Medium
11500 /// and forbid any hard disk operation when it is implicit. Or maybe
11501 /// a special media state for it to make it even more simple.
11502
11503 Assert(mMediaData.isBackedUp());
11504
11505 /* will release the lock before the potentially lengthy operation, so
11506 * protect with the special state */
11507 MachineState_T oldState = mData->mMachineState;
11508 setMachineState(MachineState_SettingUp);
11509
11510 writeLock.release();
11511
11512 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11513 true /*aWait*/);
11514
11515 writeLock.acquire();
11516
11517 setMachineState(oldState);
11518
11519 if (FAILED(rc)) return rc;
11520 }
11521
11522 setModified(IsModified_Storage);
11523 mMediaData.backup();
11524 mMediaData->mAttachments.remove(pAttach);
11525
11526 if (!oldmedium.isNull())
11527 {
11528 // if this is from a snapshot, do not defer detachment to commitMedia()
11529 if (pSnapshot)
11530 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11531 // else if non-hard disk media, do not defer detachment to commitMedia() either
11532 else if (mediumType != DeviceType_HardDisk)
11533 oldmedium->removeBackReference(mData->mUuid);
11534 }
11535
11536 return S_OK;
11537}
11538
11539/**
11540 * Goes thru all media of the given list and
11541 *
11542 * 1) calls detachDevice() on each of them for this machine and
11543 * 2) adds all Medium objects found in the process to the given list,
11544 * depending on cleanupMode.
11545 *
11546 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11547 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11548 * media to the list.
11549 *
11550 * This gets called from Machine::Unregister, both for the actual Machine and
11551 * the SnapshotMachine objects that might be found in the snapshots.
11552 *
11553 * Requires caller and locking. The machine lock must be passed in because it
11554 * will be passed on to detachDevice which needs it for temporary unlocking.
11555 *
11556 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11557 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11558 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11559 * otherwise no media get added.
11560 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11561 * @return
11562 */
11563HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11564 Snapshot *pSnapshot,
11565 CleanupMode_T cleanupMode,
11566 MediaList &llMedia)
11567{
11568 Assert(isWriteLockOnCurrentThread());
11569
11570 HRESULT rc;
11571
11572 // make a temporary list because detachDevice invalidates iterators into
11573 // mMediaData->mAttachments
11574 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11575
11576 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11577 it != llAttachments2.end();
11578 ++it)
11579 {
11580 ComObjPtr<MediumAttachment> &pAttach = *it;
11581 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11582
11583 if (!pMedium.isNull())
11584 {
11585 AutoCaller mac(pMedium);
11586 if (FAILED(mac.rc())) return mac.rc();
11587 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11588 DeviceType_T devType = pMedium->getDeviceType();
11589 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11590 && devType == DeviceType_HardDisk)
11591 || (cleanupMode == CleanupMode_Full)
11592 )
11593 {
11594 llMedia.push_back(pMedium);
11595 ComObjPtr<Medium> pParent = pMedium->getParent();
11596 /*
11597 * Search for medias which are not attached to any machine, but
11598 * in the chain to an attached disk. Mediums are only consided
11599 * if they are:
11600 * - have only one child
11601 * - no references to any machines
11602 * - are of normal medium type
11603 */
11604 while (!pParent.isNull())
11605 {
11606 AutoCaller mac1(pParent);
11607 if (FAILED(mac1.rc())) return mac1.rc();
11608 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11609 if (pParent->getChildren().size() == 1)
11610 {
11611 if ( pParent->getMachineBackRefCount() == 0
11612 && pParent->getType() == MediumType_Normal
11613 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11614 llMedia.push_back(pParent);
11615 }
11616 else
11617 break;
11618 pParent = pParent->getParent();
11619 }
11620 }
11621 }
11622
11623 // real machine: then we need to use the proper method
11624 rc = detachDevice(pAttach, writeLock, pSnapshot);
11625
11626 if (FAILED(rc))
11627 return rc;
11628 }
11629
11630 return S_OK;
11631}
11632
11633/**
11634 * Perform deferred hard disk detachments.
11635 *
11636 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11637 * backed up).
11638 *
11639 * If @a aOnline is @c true then this method will also unlock the old hard disks
11640 * for which the new implicit diffs were created and will lock these new diffs for
11641 * writing.
11642 *
11643 * @param aOnline Whether the VM was online prior to this operation.
11644 *
11645 * @note Locks this object for writing!
11646 */
11647void Machine::commitMedia(bool aOnline /*= false*/)
11648{
11649 AutoCaller autoCaller(this);
11650 AssertComRCReturnVoid(autoCaller.rc());
11651
11652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11653
11654 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11655
11656 HRESULT rc = S_OK;
11657
11658 /* no attach/detach operations -- nothing to do */
11659 if (!mMediaData.isBackedUp())
11660 return;
11661
11662 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11663 bool fMediaNeedsLocking = false;
11664
11665 /* enumerate new attachments */
11666 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11667 it != mMediaData->mAttachments.end();
11668 ++it)
11669 {
11670 MediumAttachment *pAttach = *it;
11671
11672 pAttach->commit();
11673
11674 Medium* pMedium = pAttach->getMedium();
11675 bool fImplicit = pAttach->isImplicit();
11676
11677 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11678 (pMedium) ? pMedium->getName().c_str() : "NULL",
11679 fImplicit));
11680
11681 /** @todo convert all this Machine-based voodoo to MediumAttachment
11682 * based commit logic. */
11683 if (fImplicit)
11684 {
11685 /* convert implicit attachment to normal */
11686 pAttach->setImplicit(false);
11687
11688 if ( aOnline
11689 && pMedium
11690 && pAttach->getType() == DeviceType_HardDisk
11691 )
11692 {
11693 ComObjPtr<Medium> parent = pMedium->getParent();
11694 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11695
11696 /* update the appropriate lock list */
11697 MediumLockList *pMediumLockList;
11698 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11699 AssertComRC(rc);
11700 if (pMediumLockList)
11701 {
11702 /* unlock if there's a need to change the locking */
11703 if (!fMediaNeedsLocking)
11704 {
11705 rc = mData->mSession.mLockedMedia.Unlock();
11706 AssertComRC(rc);
11707 fMediaNeedsLocking = true;
11708 }
11709 rc = pMediumLockList->Update(parent, false);
11710 AssertComRC(rc);
11711 rc = pMediumLockList->Append(pMedium, true);
11712 AssertComRC(rc);
11713 }
11714 }
11715
11716 continue;
11717 }
11718
11719 if (pMedium)
11720 {
11721 /* was this medium attached before? */
11722 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11723 oldIt != oldAtts.end();
11724 ++oldIt)
11725 {
11726 MediumAttachment *pOldAttach = *oldIt;
11727 if (pOldAttach->getMedium() == pMedium)
11728 {
11729 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11730
11731 /* yes: remove from old to avoid de-association */
11732 oldAtts.erase(oldIt);
11733 break;
11734 }
11735 }
11736 }
11737 }
11738
11739 /* enumerate remaining old attachments and de-associate from the
11740 * current machine state */
11741 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11742 it != oldAtts.end();
11743 ++it)
11744 {
11745 MediumAttachment *pAttach = *it;
11746 Medium* pMedium = pAttach->getMedium();
11747
11748 /* Detach only hard disks, since DVD/floppy media is detached
11749 * instantly in MountMedium. */
11750 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11751 {
11752 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11753
11754 /* now de-associate from the current machine state */
11755 rc = pMedium->removeBackReference(mData->mUuid);
11756 AssertComRC(rc);
11757
11758 if (aOnline)
11759 {
11760 /* unlock since medium is not used anymore */
11761 MediumLockList *pMediumLockList;
11762 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11763 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11764 {
11765 /* this happens for online snapshots, there the attachment
11766 * is changing, but only to a diff image created under
11767 * the old one, so there is no separate lock list */
11768 Assert(!pMediumLockList);
11769 }
11770 else
11771 {
11772 AssertComRC(rc);
11773 if (pMediumLockList)
11774 {
11775 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11776 AssertComRC(rc);
11777 }
11778 }
11779 }
11780 }
11781 }
11782
11783 /* take media locks again so that the locking state is consistent */
11784 if (fMediaNeedsLocking)
11785 {
11786 Assert(aOnline);
11787 rc = mData->mSession.mLockedMedia.Lock();
11788 AssertComRC(rc);
11789 }
11790
11791 /* commit the hard disk changes */
11792 mMediaData.commit();
11793
11794 if (isSessionMachine())
11795 {
11796 /*
11797 * Update the parent machine to point to the new owner.
11798 * This is necessary because the stored parent will point to the
11799 * session machine otherwise and cause crashes or errors later
11800 * when the session machine gets invalid.
11801 */
11802 /** @todo Change the MediumAttachment class to behave like any other
11803 * class in this regard by creating peer MediumAttachment
11804 * objects for session machines and share the data with the peer
11805 * machine.
11806 */
11807 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11808 it != mMediaData->mAttachments.end();
11809 ++it)
11810 {
11811 (*it)->updateParentMachine(mPeer);
11812 }
11813
11814 /* attach new data to the primary machine and reshare it */
11815 mPeer->mMediaData.attach(mMediaData);
11816 }
11817
11818 return;
11819}
11820
11821/**
11822 * Perform deferred deletion of implicitly created diffs.
11823 *
11824 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11825 * backed up).
11826 *
11827 * @note Locks this object for writing!
11828 */
11829void Machine::rollbackMedia()
11830{
11831 AutoCaller autoCaller(this);
11832 AssertComRCReturnVoid(autoCaller.rc());
11833
11834 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11835 LogFlowThisFunc(("Entering rollbackMedia\n"));
11836
11837 HRESULT rc = S_OK;
11838
11839 /* no attach/detach operations -- nothing to do */
11840 if (!mMediaData.isBackedUp())
11841 return;
11842
11843 /* enumerate new attachments */
11844 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11845 it != mMediaData->mAttachments.end();
11846 ++it)
11847 {
11848 MediumAttachment *pAttach = *it;
11849 /* Fix up the backrefs for DVD/floppy media. */
11850 if (pAttach->getType() != DeviceType_HardDisk)
11851 {
11852 Medium* pMedium = pAttach->getMedium();
11853 if (pMedium)
11854 {
11855 rc = pMedium->removeBackReference(mData->mUuid);
11856 AssertComRC(rc);
11857 }
11858 }
11859
11860 (*it)->rollback();
11861
11862 pAttach = *it;
11863 /* Fix up the backrefs for DVD/floppy media. */
11864 if (pAttach->getType() != DeviceType_HardDisk)
11865 {
11866 Medium* pMedium = pAttach->getMedium();
11867 if (pMedium)
11868 {
11869 rc = pMedium->addBackReference(mData->mUuid);
11870 AssertComRC(rc);
11871 }
11872 }
11873 }
11874
11875 /** @todo convert all this Machine-based voodoo to MediumAttachment
11876 * based rollback logic. */
11877 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11878
11879 return;
11880}
11881
11882/**
11883 * Returns true if the settings file is located in the directory named exactly
11884 * as the machine; this means, among other things, that the machine directory
11885 * should be auto-renamed.
11886 *
11887 * @param aSettingsDir if not NULL, the full machine settings file directory
11888 * name will be assigned there.
11889 *
11890 * @note Doesn't lock anything.
11891 * @note Not thread safe (must be called from this object's lock).
11892 */
11893bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11894{
11895 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11896 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11897 if (aSettingsDir)
11898 *aSettingsDir = strMachineDirName;
11899 strMachineDirName.stripPath(); // vmname
11900 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11901 strConfigFileOnly.stripPath() // vmname.vbox
11902 .stripExt(); // vmname
11903 /** @todo hack, make somehow use of ComposeMachineFilename */
11904 if (mUserData->s.fDirectoryIncludesUUID)
11905 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11906
11907 AssertReturn(!strMachineDirName.isEmpty(), false);
11908 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11909
11910 return strMachineDirName == strConfigFileOnly;
11911}
11912
11913/**
11914 * Discards all changes to machine settings.
11915 *
11916 * @param aNotify Whether to notify the direct session about changes or not.
11917 *
11918 * @note Locks objects for writing!
11919 */
11920void Machine::rollback(bool aNotify)
11921{
11922 AutoCaller autoCaller(this);
11923 AssertComRCReturn(autoCaller.rc(), (void)0);
11924
11925 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11926
11927 if (!mStorageControllers.isNull())
11928 {
11929 if (mStorageControllers.isBackedUp())
11930 {
11931 /* unitialize all new devices (absent in the backed up list). */
11932 StorageControllerList::const_iterator it = mStorageControllers->begin();
11933 StorageControllerList *backedList = mStorageControllers.backedUpData();
11934 while (it != mStorageControllers->end())
11935 {
11936 if ( std::find(backedList->begin(), backedList->end(), *it)
11937 == backedList->end()
11938 )
11939 {
11940 (*it)->uninit();
11941 }
11942 ++it;
11943 }
11944
11945 /* restore the list */
11946 mStorageControllers.rollback();
11947 }
11948
11949 /* rollback any changes to devices after restoring the list */
11950 if (mData->flModifications & IsModified_Storage)
11951 {
11952 StorageControllerList::const_iterator it = mStorageControllers->begin();
11953 while (it != mStorageControllers->end())
11954 {
11955 (*it)->rollback();
11956 ++it;
11957 }
11958 }
11959 }
11960
11961 if (!mUSBControllers.isNull())
11962 {
11963 if (mUSBControllers.isBackedUp())
11964 {
11965 /* unitialize all new devices (absent in the backed up list). */
11966 USBControllerList::const_iterator it = mUSBControllers->begin();
11967 USBControllerList *backedList = mUSBControllers.backedUpData();
11968 while (it != mUSBControllers->end())
11969 {
11970 if ( std::find(backedList->begin(), backedList->end(), *it)
11971 == backedList->end()
11972 )
11973 {
11974 (*it)->uninit();
11975 }
11976 ++it;
11977 }
11978
11979 /* restore the list */
11980 mUSBControllers.rollback();
11981 }
11982
11983 /* rollback any changes to devices after restoring the list */
11984 if (mData->flModifications & IsModified_USB)
11985 {
11986 USBControllerList::const_iterator it = mUSBControllers->begin();
11987 while (it != mUSBControllers->end())
11988 {
11989 (*it)->rollback();
11990 ++it;
11991 }
11992 }
11993 }
11994
11995 mUserData.rollback();
11996
11997 mHWData.rollback();
11998
11999 if (mData->flModifications & IsModified_Storage)
12000 rollbackMedia();
12001
12002 if (mBIOSSettings)
12003 mBIOSSettings->rollback();
12004
12005 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12006 mVRDEServer->rollback();
12007
12008 if (mAudioAdapter)
12009 mAudioAdapter->rollback();
12010
12011 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12012 mUSBDeviceFilters->rollback();
12013
12014 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12015 mBandwidthControl->rollback();
12016
12017 if (!mHWData.isNull())
12018 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12019 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12020 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12021 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12022
12023 if (mData->flModifications & IsModified_NetworkAdapters)
12024 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12025 if ( mNetworkAdapters[slot]
12026 && mNetworkAdapters[slot]->isModified())
12027 {
12028 mNetworkAdapters[slot]->rollback();
12029 networkAdapters[slot] = mNetworkAdapters[slot];
12030 }
12031
12032 if (mData->flModifications & IsModified_SerialPorts)
12033 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12034 if ( mSerialPorts[slot]
12035 && mSerialPorts[slot]->isModified())
12036 {
12037 mSerialPorts[slot]->rollback();
12038 serialPorts[slot] = mSerialPorts[slot];
12039 }
12040
12041 if (mData->flModifications & IsModified_ParallelPorts)
12042 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12043 if ( mParallelPorts[slot]
12044 && mParallelPorts[slot]->isModified())
12045 {
12046 mParallelPorts[slot]->rollback();
12047 parallelPorts[slot] = mParallelPorts[slot];
12048 }
12049
12050 if (aNotify)
12051 {
12052 /* inform the direct session about changes */
12053
12054 ComObjPtr<Machine> that = this;
12055 uint32_t flModifications = mData->flModifications;
12056 alock.release();
12057
12058 if (flModifications & IsModified_SharedFolders)
12059 that->onSharedFolderChange();
12060
12061 if (flModifications & IsModified_VRDEServer)
12062 that->onVRDEServerChange(/* aRestart */ TRUE);
12063 if (flModifications & IsModified_USB)
12064 that->onUSBControllerChange();
12065
12066 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
12067 if (networkAdapters[slot])
12068 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
12069 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
12070 if (serialPorts[slot])
12071 that->onSerialPortChange(serialPorts[slot]);
12072 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
12073 if (parallelPorts[slot])
12074 that->onParallelPortChange(parallelPorts[slot]);
12075
12076 if (flModifications & IsModified_Storage)
12077 that->onStorageControllerChange();
12078
12079#if 0
12080 if (flModifications & IsModified_BandwidthControl)
12081 that->onBandwidthControlChange();
12082#endif
12083 }
12084}
12085
12086/**
12087 * Commits all the changes to machine settings.
12088 *
12089 * Note that this operation is supposed to never fail.
12090 *
12091 * @note Locks this object and children for writing.
12092 */
12093void Machine::commit()
12094{
12095 AutoCaller autoCaller(this);
12096 AssertComRCReturnVoid(autoCaller.rc());
12097
12098 AutoCaller peerCaller(mPeer);
12099 AssertComRCReturnVoid(peerCaller.rc());
12100
12101 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12102
12103 /*
12104 * use safe commit to ensure Snapshot machines (that share mUserData)
12105 * will still refer to a valid memory location
12106 */
12107 mUserData.commitCopy();
12108
12109 mHWData.commit();
12110
12111 if (mMediaData.isBackedUp())
12112 commitMedia(Global::IsOnline(mData->mMachineState));
12113
12114 mBIOSSettings->commit();
12115 mVRDEServer->commit();
12116 mAudioAdapter->commit();
12117 mUSBDeviceFilters->commit();
12118 mBandwidthControl->commit();
12119
12120 /* Since mNetworkAdapters is a list which might have been changed (resized)
12121 * without using the Backupable<> template we need to handle the copying
12122 * of the list entries manually, including the creation of peers for the
12123 * new objects. */
12124 bool commitNetworkAdapters = false;
12125 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12126 if (mPeer)
12127 {
12128 /* commit everything, even the ones which will go away */
12129 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12130 mNetworkAdapters[slot]->commit();
12131 /* copy over the new entries, creating a peer and uninit the original */
12132 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12133 for (size_t slot = 0; slot < newSize; slot++)
12134 {
12135 /* look if this adapter has a peer device */
12136 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
12137 if (!peer)
12138 {
12139 /* no peer means the adapter is a newly created one;
12140 * create a peer owning data this data share it with */
12141 peer.createObject();
12142 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12143 }
12144 mPeer->mNetworkAdapters[slot] = peer;
12145 }
12146 /* uninit any no longer needed network adapters */
12147 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
12148 mNetworkAdapters[slot]->uninit();
12149 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
12150 {
12151 if (mPeer->mNetworkAdapters[slot])
12152 mPeer->mNetworkAdapters[slot]->uninit();
12153 }
12154 /* Keep the original network adapter count until this point, so that
12155 * discarding a chipset type change will not lose settings. */
12156 mNetworkAdapters.resize(newSize);
12157 mPeer->mNetworkAdapters.resize(newSize);
12158 }
12159 else
12160 {
12161 /* we have no peer (our parent is the newly created machine);
12162 * just commit changes to the network adapters */
12163 commitNetworkAdapters = true;
12164 }
12165 if (commitNetworkAdapters)
12166 {
12167 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12168 mNetworkAdapters[slot]->commit();
12169 }
12170
12171 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12172 mSerialPorts[slot]->commit();
12173 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12174 mParallelPorts[slot]->commit();
12175
12176 bool commitStorageControllers = false;
12177
12178 if (mStorageControllers.isBackedUp())
12179 {
12180 mStorageControllers.commit();
12181
12182 if (mPeer)
12183 {
12184 /* Commit all changes to new controllers (this will reshare data with
12185 * peers for those who have peers) */
12186 StorageControllerList *newList = new StorageControllerList();
12187 StorageControllerList::const_iterator it = mStorageControllers->begin();
12188 while (it != mStorageControllers->end())
12189 {
12190 (*it)->commit();
12191
12192 /* look if this controller has a peer device */
12193 ComObjPtr<StorageController> peer = (*it)->getPeer();
12194 if (!peer)
12195 {
12196 /* no peer means the device is a newly created one;
12197 * create a peer owning data this device share it with */
12198 peer.createObject();
12199 peer->init(mPeer, *it, true /* aReshare */);
12200 }
12201 else
12202 {
12203 /* remove peer from the old list */
12204 mPeer->mStorageControllers->remove(peer);
12205 }
12206 /* and add it to the new list */
12207 newList->push_back(peer);
12208
12209 ++it;
12210 }
12211
12212 /* uninit old peer's controllers that are left */
12213 it = mPeer->mStorageControllers->begin();
12214 while (it != mPeer->mStorageControllers->end())
12215 {
12216 (*it)->uninit();
12217 ++it;
12218 }
12219
12220 /* attach new list of controllers to our peer */
12221 mPeer->mStorageControllers.attach(newList);
12222 }
12223 else
12224 {
12225 /* we have no peer (our parent is the newly created machine);
12226 * just commit changes to devices */
12227 commitStorageControllers = true;
12228 }
12229 }
12230 else
12231 {
12232 /* the list of controllers itself is not changed,
12233 * just commit changes to controllers themselves */
12234 commitStorageControllers = true;
12235 }
12236
12237 if (commitStorageControllers)
12238 {
12239 StorageControllerList::const_iterator it = mStorageControllers->begin();
12240 while (it != mStorageControllers->end())
12241 {
12242 (*it)->commit();
12243 ++it;
12244 }
12245 }
12246
12247 bool commitUSBControllers = false;
12248
12249 if (mUSBControllers.isBackedUp())
12250 {
12251 mUSBControllers.commit();
12252
12253 if (mPeer)
12254 {
12255 /* Commit all changes to new controllers (this will reshare data with
12256 * peers for those who have peers) */
12257 USBControllerList *newList = new USBControllerList();
12258 USBControllerList::const_iterator it = mUSBControllers->begin();
12259 while (it != mUSBControllers->end())
12260 {
12261 (*it)->commit();
12262
12263 /* look if this controller has a peer device */
12264 ComObjPtr<USBController> peer = (*it)->getPeer();
12265 if (!peer)
12266 {
12267 /* no peer means the device is a newly created one;
12268 * create a peer owning data this device share it with */
12269 peer.createObject();
12270 peer->init(mPeer, *it, true /* aReshare */);
12271 }
12272 else
12273 {
12274 /* remove peer from the old list */
12275 mPeer->mUSBControllers->remove(peer);
12276 }
12277 /* and add it to the new list */
12278 newList->push_back(peer);
12279
12280 ++it;
12281 }
12282
12283 /* uninit old peer's controllers that are left */
12284 it = mPeer->mUSBControllers->begin();
12285 while (it != mPeer->mUSBControllers->end())
12286 {
12287 (*it)->uninit();
12288 ++it;
12289 }
12290
12291 /* attach new list of controllers to our peer */
12292 mPeer->mUSBControllers.attach(newList);
12293 }
12294 else
12295 {
12296 /* we have no peer (our parent is the newly created machine);
12297 * just commit changes to devices */
12298 commitUSBControllers = true;
12299 }
12300 }
12301 else
12302 {
12303 /* the list of controllers itself is not changed,
12304 * just commit changes to controllers themselves */
12305 commitUSBControllers = true;
12306 }
12307
12308 if (commitUSBControllers)
12309 {
12310 USBControllerList::const_iterator it = mUSBControllers->begin();
12311 while (it != mUSBControllers->end())
12312 {
12313 (*it)->commit();
12314 ++it;
12315 }
12316 }
12317
12318 if (isSessionMachine())
12319 {
12320 /* attach new data to the primary machine and reshare it */
12321 mPeer->mUserData.attach(mUserData);
12322 mPeer->mHWData.attach(mHWData);
12323 /* mMediaData is reshared by fixupMedia */
12324 // mPeer->mMediaData.attach(mMediaData);
12325 Assert(mPeer->mMediaData.data() == mMediaData.data());
12326 }
12327}
12328
12329/**
12330 * Copies all the hardware data from the given machine.
12331 *
12332 * Currently, only called when the VM is being restored from a snapshot. In
12333 * particular, this implies that the VM is not running during this method's
12334 * call.
12335 *
12336 * @note This method must be called from under this object's lock.
12337 *
12338 * @note This method doesn't call #commit(), so all data remains backed up and
12339 * unsaved.
12340 */
12341void Machine::copyFrom(Machine *aThat)
12342{
12343 AssertReturnVoid(!isSnapshotMachine());
12344 AssertReturnVoid(aThat->isSnapshotMachine());
12345
12346 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12347
12348 mHWData.assignCopy(aThat->mHWData);
12349
12350 // create copies of all shared folders (mHWData after attaching a copy
12351 // contains just references to original objects)
12352 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12353 it != mHWData->mSharedFolders.end();
12354 ++it)
12355 {
12356 ComObjPtr<SharedFolder> folder;
12357 folder.createObject();
12358 HRESULT rc = folder->initCopy(getMachine(), *it);
12359 AssertComRC(rc);
12360 *it = folder;
12361 }
12362
12363 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
12364 mVRDEServer->copyFrom(aThat->mVRDEServer);
12365 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
12366 mUSBDeviceFilters->copyFrom(aThat->mUSBDeviceFilters);
12367 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
12368
12369 /* create private copies of all controllers */
12370 mStorageControllers.backup();
12371 mStorageControllers->clear();
12372 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12373 it != aThat->mStorageControllers->end();
12374 ++it)
12375 {
12376 ComObjPtr<StorageController> ctrl;
12377 ctrl.createObject();
12378 ctrl->initCopy(this, *it);
12379 mStorageControllers->push_back(ctrl);
12380 }
12381
12382 /* create private copies of all USB controllers */
12383 mUSBControllers.backup();
12384 mUSBControllers->clear();
12385 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12386 it != aThat->mUSBControllers->end();
12387 ++it)
12388 {
12389 ComObjPtr<USBController> ctrl;
12390 ctrl.createObject();
12391 ctrl->initCopy(this, *it);
12392 mUSBControllers->push_back(ctrl);
12393 }
12394
12395 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12396 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12397 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
12398 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12399 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
12400 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12401 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
12402}
12403
12404/**
12405 * Returns whether the given storage controller is hotplug capable.
12406 *
12407 * @returns true if the controller supports hotplugging
12408 * false otherwise.
12409 * @param enmCtrlType The controller type to check for.
12410 */
12411bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12412{
12413 switch (enmCtrlType)
12414 {
12415 case StorageControllerType_IntelAhci:
12416 return true;
12417 case StorageControllerType_LsiLogic:
12418 case StorageControllerType_LsiLogicSas:
12419 case StorageControllerType_BusLogic:
12420 case StorageControllerType_PIIX3:
12421 case StorageControllerType_PIIX4:
12422 case StorageControllerType_ICH6:
12423 case StorageControllerType_I82078:
12424 default:
12425 return false;
12426 }
12427}
12428
12429#ifdef VBOX_WITH_RESOURCE_USAGE_API
12430
12431void Machine::getDiskList(MediaList &list)
12432{
12433 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12434 it != mMediaData->mAttachments.end();
12435 ++it)
12436 {
12437 MediumAttachment* pAttach = *it;
12438 /* just in case */
12439 AssertStmt(pAttach, continue);
12440
12441 AutoCaller localAutoCallerA(pAttach);
12442 if (FAILED(localAutoCallerA.rc())) continue;
12443
12444 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12445
12446 if (pAttach->getType() == DeviceType_HardDisk)
12447 list.push_back(pAttach->getMedium());
12448 }
12449}
12450
12451void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12452{
12453 AssertReturnVoid(isWriteLockOnCurrentThread());
12454 AssertPtrReturnVoid(aCollector);
12455
12456 pm::CollectorHAL *hal = aCollector->getHAL();
12457 /* Create sub metrics */
12458 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12459 "Percentage of processor time spent in user mode by the VM process.");
12460 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12461 "Percentage of processor time spent in kernel mode by the VM process.");
12462 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12463 "Size of resident portion of VM process in memory.");
12464 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12465 "Actual size of all VM disks combined.");
12466 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12467 "Network receive rate.");
12468 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12469 "Network transmit rate.");
12470 /* Create and register base metrics */
12471 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12472 cpuLoadUser, cpuLoadKernel);
12473 aCollector->registerBaseMetric(cpuLoad);
12474 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12475 ramUsageUsed);
12476 aCollector->registerBaseMetric(ramUsage);
12477 MediaList disks;
12478 getDiskList(disks);
12479 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12480 diskUsageUsed);
12481 aCollector->registerBaseMetric(diskUsage);
12482
12483 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12484 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12485 new pm::AggregateAvg()));
12486 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12487 new pm::AggregateMin()));
12488 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12489 new pm::AggregateMax()));
12490 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12491 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12492 new pm::AggregateAvg()));
12493 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12494 new pm::AggregateMin()));
12495 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12496 new pm::AggregateMax()));
12497
12498 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12499 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12500 new pm::AggregateAvg()));
12501 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12502 new pm::AggregateMin()));
12503 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12504 new pm::AggregateMax()));
12505
12506 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12507 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12508 new pm::AggregateAvg()));
12509 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12510 new pm::AggregateMin()));
12511 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12512 new pm::AggregateMax()));
12513
12514
12515 /* Guest metrics collector */
12516 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12517 aCollector->registerGuest(mCollectorGuest);
12518 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12519 this, __PRETTY_FUNCTION__, mCollectorGuest));
12520
12521 /* Create sub metrics */
12522 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12523 "Percentage of processor time spent in user mode as seen by the guest.");
12524 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12525 "Percentage of processor time spent in kernel mode as seen by the guest.");
12526 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12527 "Percentage of processor time spent idling as seen by the guest.");
12528
12529 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12530 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12531 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12532 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12533 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12534 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12535
12536 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12537
12538 /* Create and register base metrics */
12539 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12540 machineNetRx, machineNetTx);
12541 aCollector->registerBaseMetric(machineNetRate);
12542
12543 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12544 guestLoadUser, guestLoadKernel, guestLoadIdle);
12545 aCollector->registerBaseMetric(guestCpuLoad);
12546
12547 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12548 guestMemTotal, guestMemFree,
12549 guestMemBalloon, guestMemShared,
12550 guestMemCache, guestPagedTotal);
12551 aCollector->registerBaseMetric(guestCpuMem);
12552
12553 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12554 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12555 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12556 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12557
12558 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12559 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12560 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12561 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12562
12563 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12564 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12565 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12566 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12567
12568 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12569 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12570 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12571 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12572
12573 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12574 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12575 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12576 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12577
12578 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12579 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12580 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12581 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12582
12583 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12584 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12585 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12586 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12587
12588 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12589 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12590 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12591 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12592
12593 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12594 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12595 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12596 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12597
12598 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12599 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12600 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12601 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12602
12603 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12604 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12605 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12606 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12607}
12608
12609void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12610{
12611 AssertReturnVoid(isWriteLockOnCurrentThread());
12612
12613 if (aCollector)
12614 {
12615 aCollector->unregisterMetricsFor(aMachine);
12616 aCollector->unregisterBaseMetricsFor(aMachine);
12617 }
12618}
12619
12620#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12621
12622
12623////////////////////////////////////////////////////////////////////////////////
12624
12625DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12626
12627HRESULT SessionMachine::FinalConstruct()
12628{
12629 LogFlowThisFunc(("\n"));
12630
12631 mClientToken = NULL;
12632
12633 return BaseFinalConstruct();
12634}
12635
12636void SessionMachine::FinalRelease()
12637{
12638 LogFlowThisFunc(("\n"));
12639
12640 Assert(!mClientToken);
12641 /* paranoia, should not hang around any more */
12642 if (mClientToken)
12643 {
12644 delete mClientToken;
12645 mClientToken = NULL;
12646 }
12647
12648 uninit(Uninit::Unexpected);
12649
12650 BaseFinalRelease();
12651}
12652
12653/**
12654 * @note Must be called only by Machine::LockMachine() from its own write lock.
12655 */
12656HRESULT SessionMachine::init(Machine *aMachine)
12657{
12658 LogFlowThisFuncEnter();
12659 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12660
12661 AssertReturn(aMachine, E_INVALIDARG);
12662
12663 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12664
12665 /* Enclose the state transition NotReady->InInit->Ready */
12666 AutoInitSpan autoInitSpan(this);
12667 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12668
12669 HRESULT rc = S_OK;
12670
12671 /* create the machine client token */
12672 try
12673 {
12674 mClientToken = new ClientToken(aMachine);
12675 if (!mClientToken->isReady())
12676 {
12677 delete mClientToken;
12678 mClientToken = NULL;
12679 rc = E_FAIL;
12680 }
12681 }
12682 catch (std::bad_alloc &)
12683 {
12684 rc = E_OUTOFMEMORY;
12685 }
12686 if (FAILED(rc))
12687 return rc;
12688
12689 /* memorize the peer Machine */
12690 unconst(mPeer) = aMachine;
12691 /* share the parent pointer */
12692 unconst(mParent) = aMachine->mParent;
12693
12694 /* take the pointers to data to share */
12695 mData.share(aMachine->mData);
12696 mSSData.share(aMachine->mSSData);
12697
12698 mUserData.share(aMachine->mUserData);
12699 mHWData.share(aMachine->mHWData);
12700 mMediaData.share(aMachine->mMediaData);
12701
12702 mStorageControllers.allocate();
12703 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12704 it != aMachine->mStorageControllers->end();
12705 ++it)
12706 {
12707 ComObjPtr<StorageController> ctl;
12708 ctl.createObject();
12709 ctl->init(this, *it);
12710 mStorageControllers->push_back(ctl);
12711 }
12712
12713 mUSBControllers.allocate();
12714 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12715 it != aMachine->mUSBControllers->end();
12716 ++it)
12717 {
12718 ComObjPtr<USBController> ctl;
12719 ctl.createObject();
12720 ctl->init(this, *it);
12721 mUSBControllers->push_back(ctl);
12722 }
12723
12724 unconst(mBIOSSettings).createObject();
12725 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12726 /* create another VRDEServer object that will be mutable */
12727 unconst(mVRDEServer).createObject();
12728 mVRDEServer->init(this, aMachine->mVRDEServer);
12729 /* create another audio adapter object that will be mutable */
12730 unconst(mAudioAdapter).createObject();
12731 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12732 /* create a list of serial ports that will be mutable */
12733 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12734 {
12735 unconst(mSerialPorts[slot]).createObject();
12736 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12737 }
12738 /* create a list of parallel ports that will be mutable */
12739 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12740 {
12741 unconst(mParallelPorts[slot]).createObject();
12742 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12743 }
12744
12745 /* create another USB device filters object that will be mutable */
12746 unconst(mUSBDeviceFilters).createObject();
12747 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12748
12749 /* create a list of network adapters that will be mutable */
12750 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12751 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12752 {
12753 unconst(mNetworkAdapters[slot]).createObject();
12754 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12755 }
12756
12757 /* create another bandwidth control object that will be mutable */
12758 unconst(mBandwidthControl).createObject();
12759 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12760
12761 /* default is to delete saved state on Saved -> PoweredOff transition */
12762 mRemoveSavedState = true;
12763
12764 /* Confirm a successful initialization when it's the case */
12765 autoInitSpan.setSucceeded();
12766
12767 LogFlowThisFuncLeave();
12768 return rc;
12769}
12770
12771/**
12772 * Uninitializes this session object. If the reason is other than
12773 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12774 *
12775 * @param aReason uninitialization reason
12776 *
12777 * @note Locks mParent + this object for writing.
12778 */
12779void SessionMachine::uninit(Uninit::Reason aReason)
12780{
12781 LogFlowThisFuncEnter();
12782 LogFlowThisFunc(("reason=%d\n", aReason));
12783
12784 /*
12785 * Strongly reference ourselves to prevent this object deletion after
12786 * mData->mSession.mMachine.setNull() below (which can release the last
12787 * reference and call the destructor). Important: this must be done before
12788 * accessing any members (and before AutoUninitSpan that does it as well).
12789 * This self reference will be released as the very last step on return.
12790 */
12791 ComObjPtr<SessionMachine> selfRef = this;
12792
12793 /* Enclose the state transition Ready->InUninit->NotReady */
12794 AutoUninitSpan autoUninitSpan(this);
12795 if (autoUninitSpan.uninitDone())
12796 {
12797 LogFlowThisFunc(("Already uninitialized\n"));
12798 LogFlowThisFuncLeave();
12799 return;
12800 }
12801
12802 if (autoUninitSpan.initFailed())
12803 {
12804 /* We've been called by init() because it's failed. It's not really
12805 * necessary (nor it's safe) to perform the regular uninit sequence
12806 * below, the following is enough.
12807 */
12808 LogFlowThisFunc(("Initialization failed.\n"));
12809 /* destroy the machine client token */
12810 if (mClientToken)
12811 {
12812 delete mClientToken;
12813 mClientToken = NULL;
12814 }
12815 uninitDataAndChildObjects();
12816 mData.free();
12817 unconst(mParent) = NULL;
12818 unconst(mPeer) = NULL;
12819 LogFlowThisFuncLeave();
12820 return;
12821 }
12822
12823 MachineState_T lastState;
12824 {
12825 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12826 lastState = mData->mMachineState;
12827 }
12828 NOREF(lastState);
12829
12830#ifdef VBOX_WITH_USB
12831 // release all captured USB devices, but do this before requesting the locks below
12832 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12833 {
12834 /* Console::captureUSBDevices() is called in the VM process only after
12835 * setting the machine state to Starting or Restoring.
12836 * Console::detachAllUSBDevices() will be called upon successful
12837 * termination. So, we need to release USB devices only if there was
12838 * an abnormal termination of a running VM.
12839 *
12840 * This is identical to SessionMachine::DetachAllUSBDevices except
12841 * for the aAbnormal argument. */
12842 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
12843 AssertComRC(rc);
12844 NOREF(rc);
12845
12846 USBProxyService *service = mParent->host()->usbProxyService();
12847 if (service)
12848 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12849 }
12850#endif /* VBOX_WITH_USB */
12851
12852 // we need to lock this object in uninit() because the lock is shared
12853 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12854 // and others need mParent lock, and USB needs host lock.
12855 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12856
12857#ifdef VBOX_WITH_RESOURCE_USAGE_API
12858 /*
12859 * It is safe to call Machine::unregisterMetrics() here because
12860 * PerformanceCollector::samplerCallback no longer accesses guest methods
12861 * holding the lock.
12862 */
12863 unregisterMetrics(mParent->performanceCollector(), mPeer);
12864 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12865 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12866 this, __PRETTY_FUNCTION__, mCollectorGuest));
12867 if (mCollectorGuest)
12868 {
12869 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12870 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12871 mCollectorGuest = NULL;
12872 }
12873#endif
12874
12875 if (aReason == Uninit::Abnormal)
12876 {
12877 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12878 Global::IsOnlineOrTransient(lastState)));
12879
12880 /* reset the state to Aborted */
12881 if (mData->mMachineState != MachineState_Aborted)
12882 setMachineState(MachineState_Aborted);
12883 }
12884
12885 // any machine settings modified?
12886 if (mData->flModifications)
12887 {
12888 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12889 rollback(false /* aNotify */);
12890 }
12891
12892 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12893 || !mConsoleTaskData.mSnapshot);
12894 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12895 {
12896 LogWarningThisFunc(("canceling failed save state request!\n"));
12897 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12898 }
12899 else if (!mConsoleTaskData.mSnapshot.isNull())
12900 {
12901 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12902
12903 /* delete all differencing hard disks created (this will also attach
12904 * their parents back by rolling back mMediaData) */
12905 rollbackMedia();
12906
12907 // delete the saved state file (it might have been already created)
12908 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12909 // think it's still in use
12910 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12911 mConsoleTaskData.mSnapshot->uninit();
12912 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12913 }
12914
12915 if (!mData->mSession.mType.isEmpty())
12916 {
12917 /* mType is not null when this machine's process has been started by
12918 * Machine::LaunchVMProcess(), therefore it is our child. We
12919 * need to queue the PID to reap the process (and avoid zombies on
12920 * Linux). */
12921 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12922 mParent->addProcessToReap(mData->mSession.mPID);
12923 }
12924
12925 mData->mSession.mPID = NIL_RTPROCESS;
12926
12927 if (aReason == Uninit::Unexpected)
12928 {
12929 /* Uninitialization didn't come from #checkForDeath(), so tell the
12930 * client watcher thread to update the set of machines that have open
12931 * sessions. */
12932 mParent->updateClientWatcher();
12933 }
12934
12935 /* uninitialize all remote controls */
12936 if (mData->mSession.mRemoteControls.size())
12937 {
12938 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12939 mData->mSession.mRemoteControls.size()));
12940
12941 Data::Session::RemoteControlList::iterator it =
12942 mData->mSession.mRemoteControls.begin();
12943 while (it != mData->mSession.mRemoteControls.end())
12944 {
12945 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12946 HRESULT rc = (*it)->Uninitialize();
12947 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12948 if (FAILED(rc))
12949 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12950 ++it;
12951 }
12952 mData->mSession.mRemoteControls.clear();
12953 }
12954
12955 /*
12956 * An expected uninitialization can come only from #checkForDeath().
12957 * Otherwise it means that something's gone really wrong (for example,
12958 * the Session implementation has released the VirtualBox reference
12959 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12960 * etc). However, it's also possible, that the client releases the IPC
12961 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12962 * but the VirtualBox release event comes first to the server process.
12963 * This case is practically possible, so we should not assert on an
12964 * unexpected uninit, just log a warning.
12965 */
12966
12967 if ((aReason == Uninit::Unexpected))
12968 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12969
12970 if (aReason != Uninit::Normal)
12971 {
12972 mData->mSession.mDirectControl.setNull();
12973 }
12974 else
12975 {
12976 /* this must be null here (see #OnSessionEnd()) */
12977 Assert(mData->mSession.mDirectControl.isNull());
12978 Assert(mData->mSession.mState == SessionState_Unlocking);
12979 Assert(!mData->mSession.mProgress.isNull());
12980 }
12981 if (mData->mSession.mProgress)
12982 {
12983 if (aReason == Uninit::Normal)
12984 mData->mSession.mProgress->notifyComplete(S_OK);
12985 else
12986 mData->mSession.mProgress->notifyComplete(E_FAIL,
12987 COM_IIDOF(ISession),
12988 getComponentName(),
12989 tr("The VM session was aborted"));
12990 mData->mSession.mProgress.setNull();
12991 }
12992
12993 /* remove the association between the peer machine and this session machine */
12994 Assert( (SessionMachine*)mData->mSession.mMachine == this
12995 || aReason == Uninit::Unexpected);
12996
12997 /* reset the rest of session data */
12998 mData->mSession.mMachine.setNull();
12999 mData->mSession.mState = SessionState_Unlocked;
13000 mData->mSession.mType.setNull();
13001
13002 /* destroy the machine client token before leaving the exclusive lock */
13003 if (mClientToken)
13004 {
13005 delete mClientToken;
13006 mClientToken = NULL;
13007 }
13008
13009 /* fire an event */
13010 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13011
13012 uninitDataAndChildObjects();
13013
13014 /* free the essential data structure last */
13015 mData.free();
13016
13017 /* release the exclusive lock before setting the below two to NULL */
13018 multilock.release();
13019
13020 RTThreadSleep(500);
13021 mParent->AddRef();
13022 LONG c = mParent->Release();
13023 LogFlowThisFunc(("vbox ref=%d\n", c));
13024 unconst(mParent) = NULL;
13025 unconst(mPeer) = NULL;
13026
13027 LogFlowThisFuncLeave();
13028}
13029
13030// util::Lockable interface
13031////////////////////////////////////////////////////////////////////////////////
13032
13033/**
13034 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13035 * with the primary Machine instance (mPeer).
13036 */
13037RWLockHandle *SessionMachine::lockHandle() const
13038{
13039 AssertReturn(mPeer != NULL, NULL);
13040 return mPeer->lockHandle();
13041}
13042
13043// IInternalMachineControl methods
13044////////////////////////////////////////////////////////////////////////////////
13045
13046/**
13047 * Passes collected guest statistics to performance collector object
13048 */
13049STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13050 ULONG aCpuKernel, ULONG aCpuIdle,
13051 ULONG aMemTotal, ULONG aMemFree,
13052 ULONG aMemBalloon, ULONG aMemShared,
13053 ULONG aMemCache, ULONG aPageTotal,
13054 ULONG aAllocVMM, ULONG aFreeVMM,
13055 ULONG aBalloonedVMM, ULONG aSharedVMM,
13056 ULONG aVmNetRx, ULONG aVmNetTx)
13057{
13058#ifdef VBOX_WITH_RESOURCE_USAGE_API
13059 if (mCollectorGuest)
13060 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13061 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13062 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13063 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13064
13065 return S_OK;
13066#else
13067 NOREF(aValidStats);
13068 NOREF(aCpuUser);
13069 NOREF(aCpuKernel);
13070 NOREF(aCpuIdle);
13071 NOREF(aMemTotal);
13072 NOREF(aMemFree);
13073 NOREF(aMemBalloon);
13074 NOREF(aMemShared);
13075 NOREF(aMemCache);
13076 NOREF(aPageTotal);
13077 NOREF(aAllocVMM);
13078 NOREF(aFreeVMM);
13079 NOREF(aBalloonedVMM);
13080 NOREF(aSharedVMM);
13081 NOREF(aVmNetRx);
13082 NOREF(aVmNetTx);
13083 return E_NOTIMPL;
13084#endif
13085}
13086
13087/**
13088 * @note Locks this object for writing.
13089 */
13090STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
13091{
13092 AutoCaller autoCaller(this);
13093 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13094
13095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13096
13097 mRemoveSavedState = aRemove;
13098
13099 return S_OK;
13100}
13101
13102/**
13103 * @note Locks the same as #setMachineState() does.
13104 */
13105STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
13106{
13107 return setMachineState(aMachineState);
13108}
13109
13110/**
13111 * @note Locks this object for writing.
13112 */
13113STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
13114{
13115 LogFlowThisFunc(("aProgress=%p\n", aProgress));
13116 AutoCaller autoCaller(this);
13117 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13118
13119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13120
13121 if (mData->mSession.mState != SessionState_Locked)
13122 return VBOX_E_INVALID_OBJECT_STATE;
13123
13124 if (!mData->mSession.mProgress.isNull())
13125 mData->mSession.mProgress->setOtherProgressObject(aProgress);
13126
13127 LogFlowThisFunc(("returns S_OK.\n"));
13128 return S_OK;
13129}
13130
13131/**
13132 * @note Locks this object for writing.
13133 */
13134STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
13135{
13136 AutoCaller autoCaller(this);
13137 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13138
13139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13140
13141 if (mData->mSession.mState != SessionState_Locked)
13142 return VBOX_E_INVALID_OBJECT_STATE;
13143
13144 /* Finalize the LaunchVMProcess progress object. */
13145 if (mData->mSession.mProgress)
13146 {
13147 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
13148 mData->mSession.mProgress.setNull();
13149 }
13150
13151 if (SUCCEEDED((HRESULT)iResult))
13152 {
13153#ifdef VBOX_WITH_RESOURCE_USAGE_API
13154 /* The VM has been powered up successfully, so it makes sense
13155 * now to offer the performance metrics for a running machine
13156 * object. Doing it earlier wouldn't be safe. */
13157 registerMetrics(mParent->performanceCollector(), mPeer,
13158 mData->mSession.mPID);
13159#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13160 }
13161
13162 return S_OK;
13163}
13164
13165/**
13166 * @note Locks this object for writing.
13167 */
13168STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
13169{
13170 LogFlowThisFuncEnter();
13171
13172 CheckComArgOutPointerValid(aProgress);
13173
13174 AutoCaller autoCaller(this);
13175 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13176
13177 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13178
13179 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13180 E_FAIL);
13181
13182 /* create a progress object to track operation completion */
13183 ComObjPtr<Progress> pProgress;
13184 pProgress.createObject();
13185 pProgress->init(getVirtualBox(),
13186 static_cast<IMachine *>(this) /* aInitiator */,
13187 Bstr(tr("Stopping the virtual machine")).raw(),
13188 FALSE /* aCancelable */);
13189
13190 /* fill in the console task data */
13191 mConsoleTaskData.mLastState = mData->mMachineState;
13192 mConsoleTaskData.mProgress = pProgress;
13193
13194 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13195 setMachineState(MachineState_Stopping);
13196
13197 pProgress.queryInterfaceTo(aProgress);
13198
13199 return S_OK;
13200}
13201
13202/**
13203 * @note Locks this object for writing.
13204 */
13205STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
13206{
13207 LogFlowThisFuncEnter();
13208
13209 AutoCaller autoCaller(this);
13210 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13211
13212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13213
13214 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
13215 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
13216 && mConsoleTaskData.mLastState != MachineState_Null,
13217 E_FAIL);
13218
13219 /*
13220 * On failure, set the state to the state we had when BeginPoweringDown()
13221 * was called (this is expected by Console::PowerDown() and the associated
13222 * task). On success the VM process already changed the state to
13223 * MachineState_PoweredOff, so no need to do anything.
13224 */
13225 if (FAILED(iResult))
13226 setMachineState(mConsoleTaskData.mLastState);
13227
13228 /* notify the progress object about operation completion */
13229 Assert(mConsoleTaskData.mProgress);
13230 if (SUCCEEDED(iResult))
13231 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13232 else
13233 {
13234 Utf8Str strErrMsg(aErrMsg);
13235 if (strErrMsg.length())
13236 mConsoleTaskData.mProgress->notifyComplete(iResult,
13237 COM_IIDOF(ISession),
13238 getComponentName(),
13239 strErrMsg.c_str());
13240 else
13241 mConsoleTaskData.mProgress->notifyComplete(iResult);
13242 }
13243
13244 /* clear out the temporary saved state data */
13245 mConsoleTaskData.mLastState = MachineState_Null;
13246 mConsoleTaskData.mProgress.setNull();
13247
13248 LogFlowThisFuncLeave();
13249 return S_OK;
13250}
13251
13252
13253/**
13254 * Goes through the USB filters of the given machine to see if the given
13255 * device matches any filter or not.
13256 *
13257 * @note Locks the same as USBController::hasMatchingFilter() does.
13258 */
13259STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13260 BOOL *aMatched,
13261 ULONG *aMaskedIfs)
13262{
13263 LogFlowThisFunc(("\n"));
13264
13265 CheckComArgNotNull(aUSBDevice);
13266 CheckComArgOutPointerValid(aMatched);
13267
13268 AutoCaller autoCaller(this);
13269 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13270
13271#ifdef VBOX_WITH_USB
13272 *aMatched = mUSBDeviceFilters->hasMatchingFilter(aUSBDevice, aMaskedIfs);
13273#else
13274 NOREF(aUSBDevice);
13275 NOREF(aMaskedIfs);
13276 *aMatched = FALSE;
13277#endif
13278
13279 return S_OK;
13280}
13281
13282/**
13283 * @note Locks the same as Host::captureUSBDevice() does.
13284 */
13285STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13286{
13287 LogFlowThisFunc(("\n"));
13288
13289 AutoCaller autoCaller(this);
13290 AssertComRCReturnRC(autoCaller.rc());
13291
13292#ifdef VBOX_WITH_USB
13293 /* if captureDeviceForVM() fails, it must have set extended error info */
13294 clearError();
13295 MultiResult rc = mParent->host()->checkUSBProxyService();
13296 if (FAILED(rc)) return rc;
13297
13298 USBProxyService *service = mParent->host()->usbProxyService();
13299 AssertReturn(service, E_FAIL);
13300 return service->captureDeviceForVM(this, Guid(aId).ref());
13301#else
13302 NOREF(aId);
13303 return E_NOTIMPL;
13304#endif
13305}
13306
13307/**
13308 * @note Locks the same as Host::detachUSBDevice() does.
13309 */
13310STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13311{
13312 LogFlowThisFunc(("\n"));
13313
13314 AutoCaller autoCaller(this);
13315 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13316
13317#ifdef VBOX_WITH_USB
13318 USBProxyService *service = mParent->host()->usbProxyService();
13319 AssertReturn(service, E_FAIL);
13320 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13321#else
13322 NOREF(aId);
13323 NOREF(aDone);
13324 return E_NOTIMPL;
13325#endif
13326}
13327
13328/**
13329 * Inserts all machine filters to the USB proxy service and then calls
13330 * Host::autoCaptureUSBDevices().
13331 *
13332 * Called by Console from the VM process upon VM startup.
13333 *
13334 * @note Locks what called methods lock.
13335 */
13336STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13337{
13338 LogFlowThisFunc(("\n"));
13339
13340 AutoCaller autoCaller(this);
13341 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13342
13343#ifdef VBOX_WITH_USB
13344 HRESULT rc = mUSBDeviceFilters->notifyProxy(true /* aInsertFilters */);
13345 AssertComRC(rc);
13346 NOREF(rc);
13347
13348 USBProxyService *service = mParent->host()->usbProxyService();
13349 AssertReturn(service, E_FAIL);
13350 return service->autoCaptureDevicesForVM(this);
13351#else
13352 return S_OK;
13353#endif
13354}
13355
13356/**
13357 * Removes all machine filters from the USB proxy service and then calls
13358 * Host::detachAllUSBDevices().
13359 *
13360 * Called by Console from the VM process upon normal VM termination or by
13361 * SessionMachine::uninit() upon abnormal VM termination (from under the
13362 * Machine/SessionMachine lock).
13363 *
13364 * @note Locks what called methods lock.
13365 */
13366STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13367{
13368 LogFlowThisFunc(("\n"));
13369
13370 AutoCaller autoCaller(this);
13371 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13372
13373#ifdef VBOX_WITH_USB
13374 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
13375 AssertComRC(rc);
13376 NOREF(rc);
13377
13378 USBProxyService *service = mParent->host()->usbProxyService();
13379 AssertReturn(service, E_FAIL);
13380 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13381#else
13382 NOREF(aDone);
13383 return S_OK;
13384#endif
13385}
13386
13387/**
13388 * @note Locks this object for writing.
13389 */
13390STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13391 IProgress **aProgress)
13392{
13393 LogFlowThisFuncEnter();
13394
13395 AssertReturn(aSession, E_INVALIDARG);
13396 AssertReturn(aProgress, E_INVALIDARG);
13397
13398 AutoCaller autoCaller(this);
13399
13400 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13401 /*
13402 * We don't assert below because it might happen that a non-direct session
13403 * informs us it is closed right after we've been uninitialized -- it's ok.
13404 */
13405 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13406
13407 /* get IInternalSessionControl interface */
13408 ComPtr<IInternalSessionControl> control(aSession);
13409
13410 ComAssertRet(!control.isNull(), E_INVALIDARG);
13411
13412 /* Creating a Progress object requires the VirtualBox lock, and
13413 * thus locking it here is required by the lock order rules. */
13414 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13415
13416 if (control == mData->mSession.mDirectControl)
13417 {
13418 ComAssertRet(aProgress, E_POINTER);
13419
13420 /* The direct session is being normally closed by the client process
13421 * ----------------------------------------------------------------- */
13422
13423 /* go to the closing state (essential for all open*Session() calls and
13424 * for #checkForDeath()) */
13425 Assert(mData->mSession.mState == SessionState_Locked);
13426 mData->mSession.mState = SessionState_Unlocking;
13427
13428 /* set direct control to NULL to release the remote instance */
13429 mData->mSession.mDirectControl.setNull();
13430 LogFlowThisFunc(("Direct control is set to NULL\n"));
13431
13432 if (mData->mSession.mProgress)
13433 {
13434 /* finalize the progress, someone might wait if a frontend
13435 * closes the session before powering on the VM. */
13436 mData->mSession.mProgress->notifyComplete(E_FAIL,
13437 COM_IIDOF(ISession),
13438 getComponentName(),
13439 tr("The VM session was closed before any attempt to power it on"));
13440 mData->mSession.mProgress.setNull();
13441 }
13442
13443 /* Create the progress object the client will use to wait until
13444 * #checkForDeath() is called to uninitialize this session object after
13445 * it releases the IPC semaphore.
13446 * Note! Because we're "reusing" mProgress here, this must be a proxy
13447 * object just like for LaunchVMProcess. */
13448 Assert(mData->mSession.mProgress.isNull());
13449 ComObjPtr<ProgressProxy> progress;
13450 progress.createObject();
13451 ComPtr<IUnknown> pPeer(mPeer);
13452 progress->init(mParent, pPeer,
13453 Bstr(tr("Closing session")).raw(),
13454 FALSE /* aCancelable */);
13455 progress.queryInterfaceTo(aProgress);
13456 mData->mSession.mProgress = progress;
13457 }
13458 else
13459 {
13460 /* the remote session is being normally closed */
13461 Data::Session::RemoteControlList::iterator it =
13462 mData->mSession.mRemoteControls.begin();
13463 while (it != mData->mSession.mRemoteControls.end())
13464 {
13465 if (control == *it)
13466 break;
13467 ++it;
13468 }
13469 BOOL found = it != mData->mSession.mRemoteControls.end();
13470 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13471 E_INVALIDARG);
13472 // This MUST be erase(it), not remove(*it) as the latter triggers a
13473 // very nasty use after free due to the place where the value "lives".
13474 mData->mSession.mRemoteControls.erase(it);
13475 }
13476
13477 /* signal the client watcher thread, because the client is going away */
13478 mParent->updateClientWatcher();
13479
13480 LogFlowThisFuncLeave();
13481 return S_OK;
13482}
13483
13484/**
13485 * @note Locks this object for writing.
13486 */
13487STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13488{
13489 LogFlowThisFuncEnter();
13490
13491 CheckComArgOutPointerValid(aProgress);
13492 CheckComArgOutPointerValid(aStateFilePath);
13493
13494 AutoCaller autoCaller(this);
13495 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13496
13497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13498
13499 AssertReturn( mData->mMachineState == MachineState_Paused
13500 && mConsoleTaskData.mLastState == MachineState_Null
13501 && mConsoleTaskData.strStateFilePath.isEmpty(),
13502 E_FAIL);
13503
13504 /* create a progress object to track operation completion */
13505 ComObjPtr<Progress> pProgress;
13506 pProgress.createObject();
13507 pProgress->init(getVirtualBox(),
13508 static_cast<IMachine *>(this) /* aInitiator */,
13509 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13510 FALSE /* aCancelable */);
13511
13512 Utf8Str strStateFilePath;
13513 /* stateFilePath is null when the machine is not running */
13514 if (mData->mMachineState == MachineState_Paused)
13515 composeSavedStateFilename(strStateFilePath);
13516
13517 /* fill in the console task data */
13518 mConsoleTaskData.mLastState = mData->mMachineState;
13519 mConsoleTaskData.strStateFilePath = strStateFilePath;
13520 mConsoleTaskData.mProgress = pProgress;
13521
13522 /* set the state to Saving (this is expected by Console::SaveState()) */
13523 setMachineState(MachineState_Saving);
13524
13525 strStateFilePath.cloneTo(aStateFilePath);
13526 pProgress.queryInterfaceTo(aProgress);
13527
13528 return S_OK;
13529}
13530
13531/**
13532 * @note Locks mParent + this object for writing.
13533 */
13534STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13535{
13536 LogFlowThisFunc(("\n"));
13537
13538 AutoCaller autoCaller(this);
13539 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13540
13541 /* endSavingState() need mParent lock */
13542 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13543
13544 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13545 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13546 && mConsoleTaskData.mLastState != MachineState_Null
13547 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13548 E_FAIL);
13549
13550 /*
13551 * On failure, set the state to the state we had when BeginSavingState()
13552 * was called (this is expected by Console::SaveState() and the associated
13553 * task). On success the VM process already changed the state to
13554 * MachineState_Saved, so no need to do anything.
13555 */
13556 if (FAILED(iResult))
13557 setMachineState(mConsoleTaskData.mLastState);
13558
13559 return endSavingState(iResult, aErrMsg);
13560}
13561
13562/**
13563 * @note Locks this object for writing.
13564 */
13565STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13566{
13567 LogFlowThisFunc(("\n"));
13568
13569 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13570
13571 AutoCaller autoCaller(this);
13572 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13573
13574 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13575
13576 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13577 || mData->mMachineState == MachineState_Teleported
13578 || mData->mMachineState == MachineState_Aborted
13579 , E_FAIL); /** @todo setError. */
13580
13581 Utf8Str stateFilePathFull = aSavedStateFile;
13582 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13583 if (RT_FAILURE(vrc))
13584 return setError(VBOX_E_FILE_ERROR,
13585 tr("Invalid saved state file path '%ls' (%Rrc)"),
13586 aSavedStateFile,
13587 vrc);
13588
13589 mSSData->strStateFilePath = stateFilePathFull;
13590
13591 /* The below setMachineState() will detect the state transition and will
13592 * update the settings file */
13593
13594 return setMachineState(MachineState_Saved);
13595}
13596
13597STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13598 ComSafeArrayOut(BSTR, aValues),
13599 ComSafeArrayOut(LONG64, aTimestamps),
13600 ComSafeArrayOut(BSTR, aFlags))
13601{
13602 LogFlowThisFunc(("\n"));
13603
13604#ifdef VBOX_WITH_GUEST_PROPS
13605 using namespace guestProp;
13606
13607 AutoCaller autoCaller(this);
13608 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13609
13610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13611
13612 CheckComArgOutSafeArrayPointerValid(aNames);
13613 CheckComArgOutSafeArrayPointerValid(aValues);
13614 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13615 CheckComArgOutSafeArrayPointerValid(aFlags);
13616
13617 size_t cEntries = mHWData->mGuestProperties.size();
13618 com::SafeArray<BSTR> names(cEntries);
13619 com::SafeArray<BSTR> values(cEntries);
13620 com::SafeArray<LONG64> timestamps(cEntries);
13621 com::SafeArray<BSTR> flags(cEntries);
13622 unsigned i = 0;
13623 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13624 it != mHWData->mGuestProperties.end();
13625 ++it)
13626 {
13627 char szFlags[MAX_FLAGS_LEN + 1];
13628 it->first.cloneTo(&names[i]);
13629 it->second.strValue.cloneTo(&values[i]);
13630 timestamps[i] = it->second.mTimestamp;
13631 /* If it is NULL, keep it NULL. */
13632 if (it->second.mFlags)
13633 {
13634 writeFlags(it->second.mFlags, szFlags);
13635 Bstr(szFlags).cloneTo(&flags[i]);
13636 }
13637 else
13638 flags[i] = NULL;
13639 ++i;
13640 }
13641 names.detachTo(ComSafeArrayOutArg(aNames));
13642 values.detachTo(ComSafeArrayOutArg(aValues));
13643 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13644 flags.detachTo(ComSafeArrayOutArg(aFlags));
13645 return S_OK;
13646#else
13647 ReturnComNotImplemented();
13648#endif
13649}
13650
13651STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13652 IN_BSTR aValue,
13653 LONG64 aTimestamp,
13654 IN_BSTR aFlags)
13655{
13656 LogFlowThisFunc(("\n"));
13657
13658#ifdef VBOX_WITH_GUEST_PROPS
13659 using namespace guestProp;
13660
13661 CheckComArgStrNotEmptyOrNull(aName);
13662 CheckComArgNotNull(aValue);
13663 CheckComArgNotNull(aFlags);
13664
13665 try
13666 {
13667 /*
13668 * Convert input up front.
13669 */
13670 Utf8Str utf8Name(aName);
13671 uint32_t fFlags = NILFLAG;
13672 if (aFlags)
13673 {
13674 Utf8Str utf8Flags(aFlags);
13675 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13676 AssertRCReturn(vrc, E_INVALIDARG);
13677 }
13678
13679 /*
13680 * Now grab the object lock, validate the state and do the update.
13681 */
13682 AutoCaller autoCaller(this);
13683 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13684
13685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13686
13687 switch (mData->mMachineState)
13688 {
13689 case MachineState_Paused:
13690 case MachineState_Running:
13691 case MachineState_Teleporting:
13692 case MachineState_TeleportingPausedVM:
13693 case MachineState_LiveSnapshotting:
13694 case MachineState_DeletingSnapshotOnline:
13695 case MachineState_DeletingSnapshotPaused:
13696 case MachineState_Saving:
13697 case MachineState_Stopping:
13698 break;
13699
13700 default:
13701 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13702 VBOX_E_INVALID_VM_STATE);
13703 }
13704
13705 setModified(IsModified_MachineData);
13706 mHWData.backup();
13707
13708 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13709 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13710 if (it != mHWData->mGuestProperties.end())
13711 {
13712 if (!fDelete)
13713 {
13714 it->second.strValue = aValue;
13715 it->second.mTimestamp = aTimestamp;
13716 it->second.mFlags = fFlags;
13717 }
13718 else
13719 mHWData->mGuestProperties.erase(it);
13720
13721 mData->mGuestPropertiesModified = TRUE;
13722 }
13723 else if (!fDelete)
13724 {
13725 HWData::GuestProperty prop;
13726 prop.strValue = aValue;
13727 prop.mTimestamp = aTimestamp;
13728 prop.mFlags = fFlags;
13729
13730 mHWData->mGuestProperties[utf8Name] = prop;
13731 mData->mGuestPropertiesModified = TRUE;
13732 }
13733
13734 /*
13735 * Send a callback notification if appropriate
13736 */
13737 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13738 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13739 RTSTR_MAX,
13740 utf8Name.c_str(),
13741 RTSTR_MAX, NULL)
13742 )
13743 {
13744 alock.release();
13745
13746 mParent->onGuestPropertyChange(mData->mUuid,
13747 aName,
13748 aValue,
13749 aFlags);
13750 }
13751 }
13752 catch (...)
13753 {
13754 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13755 }
13756 return S_OK;
13757#else
13758 ReturnComNotImplemented();
13759#endif
13760}
13761
13762STDMETHODIMP SessionMachine::LockMedia()
13763{
13764 AutoCaller autoCaller(this);
13765 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13766
13767 AutoMultiWriteLock2 alock(this->lockHandle(),
13768 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13769
13770 AssertReturn( mData->mMachineState == MachineState_Starting
13771 || mData->mMachineState == MachineState_Restoring
13772 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13773
13774 clearError();
13775 alock.release();
13776 return lockMedia();
13777}
13778
13779STDMETHODIMP SessionMachine::UnlockMedia()
13780{
13781 unlockMedia();
13782 return S_OK;
13783}
13784
13785STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13786 IMediumAttachment **aNewAttachment)
13787{
13788 CheckComArgNotNull(aAttachment);
13789 CheckComArgOutPointerValid(aNewAttachment);
13790
13791 AutoCaller autoCaller(this);
13792 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13793
13794 // request the host lock first, since might be calling Host methods for getting host drives;
13795 // next, protect the media tree all the while we're in here, as well as our member variables
13796 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13797 this->lockHandle(),
13798 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13799
13800 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13801
13802 Bstr ctrlName;
13803 LONG lPort;
13804 LONG lDevice;
13805 bool fTempEject;
13806 {
13807 AutoCaller autoAttachCaller(this);
13808 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13809
13810 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13811
13812 /* Need to query the details first, as the IMediumAttachment reference
13813 * might be to the original settings, which we are going to change. */
13814 ctrlName = pAttach->getControllerName();
13815 lPort = pAttach->getPort();
13816 lDevice = pAttach->getDevice();
13817 fTempEject = pAttach->getTempEject();
13818 }
13819
13820 if (!fTempEject)
13821 {
13822 /* Remember previously mounted medium. The medium before taking the
13823 * backup is not necessarily the same thing. */
13824 ComObjPtr<Medium> oldmedium;
13825 oldmedium = pAttach->getMedium();
13826
13827 setModified(IsModified_Storage);
13828 mMediaData.backup();
13829
13830 // The backup operation makes the pAttach reference point to the
13831 // old settings. Re-get the correct reference.
13832 pAttach = findAttachment(mMediaData->mAttachments,
13833 ctrlName.raw(),
13834 lPort,
13835 lDevice);
13836
13837 {
13838 AutoCaller autoAttachCaller(this);
13839 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13840
13841 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13842 if (!oldmedium.isNull())
13843 oldmedium->removeBackReference(mData->mUuid);
13844
13845 pAttach->updateMedium(NULL);
13846 pAttach->updateEjected();
13847 }
13848
13849 setModified(IsModified_Storage);
13850 }
13851 else
13852 {
13853 {
13854 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13855 pAttach->updateEjected();
13856 }
13857 }
13858
13859 pAttach.queryInterfaceTo(aNewAttachment);
13860
13861 return S_OK;
13862}
13863
13864// public methods only for internal purposes
13865/////////////////////////////////////////////////////////////////////////////
13866
13867/**
13868 * Called from the client watcher thread to check for expected or unexpected
13869 * death of the client process that has a direct session to this machine.
13870 *
13871 * On Win32 and on OS/2, this method is called only when we've got the
13872 * mutex (i.e. the client has either died or terminated normally) so it always
13873 * returns @c true (the client is terminated, the session machine is
13874 * uninitialized).
13875 *
13876 * On other platforms, the method returns @c true if the client process has
13877 * terminated normally or abnormally and the session machine was uninitialized,
13878 * and @c false if the client process is still alive.
13879 *
13880 * @note Locks this object for writing.
13881 */
13882bool SessionMachine::checkForDeath()
13883{
13884 Uninit::Reason reason;
13885 bool terminated = false;
13886
13887 /* Enclose autoCaller with a block because calling uninit() from under it
13888 * will deadlock. */
13889 {
13890 AutoCaller autoCaller(this);
13891 if (!autoCaller.isOk())
13892 {
13893 /* return true if not ready, to cause the client watcher to exclude
13894 * the corresponding session from watching */
13895 LogFlowThisFunc(("Already uninitialized!\n"));
13896 return true;
13897 }
13898
13899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13900
13901 /* Determine the reason of death: if the session state is Closing here,
13902 * everything is fine. Otherwise it means that the client did not call
13903 * OnSessionEnd() before it released the IPC semaphore. This may happen
13904 * either because the client process has abnormally terminated, or
13905 * because it simply forgot to call ISession::Close() before exiting. We
13906 * threat the latter also as an abnormal termination (see
13907 * Session::uninit() for details). */
13908 reason = mData->mSession.mState == SessionState_Unlocking ?
13909 Uninit::Normal :
13910 Uninit::Abnormal;
13911
13912 if (mClientToken)
13913 terminated = mClientToken->release();
13914 } /* AutoCaller block */
13915
13916 if (terminated)
13917 uninit(reason);
13918
13919 return terminated;
13920}
13921
13922void SessionMachine::getTokenId(Utf8Str &strTokenId)
13923{
13924 LogFlowThisFunc(("\n"));
13925
13926 strTokenId.setNull();
13927
13928 AutoCaller autoCaller(this);
13929 AssertComRCReturnVoid(autoCaller.rc());
13930
13931 Assert(mClientToken);
13932 if (mClientToken)
13933 mClientToken->getId(strTokenId);
13934}
13935
13936Machine::ClientToken *SessionMachine::getClientToken()
13937{
13938 LogFlowThisFunc(("\n"));
13939
13940 AutoCaller autoCaller(this);
13941 AssertComRCReturn(autoCaller.rc(), NULL);
13942
13943 return mClientToken;
13944}
13945
13946
13947/**
13948 * @note Locks this object for reading.
13949 */
13950HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13951{
13952 LogFlowThisFunc(("\n"));
13953
13954 AutoCaller autoCaller(this);
13955 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13956
13957 ComPtr<IInternalSessionControl> directControl;
13958 {
13959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13960 directControl = mData->mSession.mDirectControl;
13961 }
13962
13963 /* ignore notifications sent after #OnSessionEnd() is called */
13964 if (!directControl)
13965 return S_OK;
13966
13967 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13968}
13969
13970/**
13971 * @note Locks this object for reading.
13972 */
13973HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13974 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13975{
13976 LogFlowThisFunc(("\n"));
13977
13978 AutoCaller autoCaller(this);
13979 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13980
13981 ComPtr<IInternalSessionControl> directControl;
13982 {
13983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13984 directControl = mData->mSession.mDirectControl;
13985 }
13986
13987 /* ignore notifications sent after #OnSessionEnd() is called */
13988 if (!directControl)
13989 return S_OK;
13990 /*
13991 * instead acting like callback we ask IVirtualBox deliver corresponding event
13992 */
13993
13994 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13995 return S_OK;
13996}
13997
13998/**
13999 * @note Locks this object for reading.
14000 */
14001HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
14002{
14003 LogFlowThisFunc(("\n"));
14004
14005 AutoCaller autoCaller(this);
14006 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14007
14008 ComPtr<IInternalSessionControl> directControl;
14009 {
14010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14011 directControl = mData->mSession.mDirectControl;
14012 }
14013
14014 /* ignore notifications sent after #OnSessionEnd() is called */
14015 if (!directControl)
14016 return S_OK;
14017
14018 return directControl->OnSerialPortChange(serialPort);
14019}
14020
14021/**
14022 * @note Locks this object for reading.
14023 */
14024HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
14025{
14026 LogFlowThisFunc(("\n"));
14027
14028 AutoCaller autoCaller(this);
14029 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14030
14031 ComPtr<IInternalSessionControl> directControl;
14032 {
14033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14034 directControl = mData->mSession.mDirectControl;
14035 }
14036
14037 /* ignore notifications sent after #OnSessionEnd() is called */
14038 if (!directControl)
14039 return S_OK;
14040
14041 return directControl->OnParallelPortChange(parallelPort);
14042}
14043
14044/**
14045 * @note Locks this object for reading.
14046 */
14047HRESULT SessionMachine::onStorageControllerChange()
14048{
14049 LogFlowThisFunc(("\n"));
14050
14051 AutoCaller autoCaller(this);
14052 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14053
14054 ComPtr<IInternalSessionControl> directControl;
14055 {
14056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14057 directControl = mData->mSession.mDirectControl;
14058 }
14059
14060 /* ignore notifications sent after #OnSessionEnd() is called */
14061 if (!directControl)
14062 return S_OK;
14063
14064 return directControl->OnStorageControllerChange();
14065}
14066
14067/**
14068 * @note Locks this object for reading.
14069 */
14070HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14071{
14072 LogFlowThisFunc(("\n"));
14073
14074 AutoCaller autoCaller(this);
14075 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14076
14077 ComPtr<IInternalSessionControl> directControl;
14078 {
14079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14080 directControl = mData->mSession.mDirectControl;
14081 }
14082
14083 /* ignore notifications sent after #OnSessionEnd() is called */
14084 if (!directControl)
14085 return S_OK;
14086
14087 return directControl->OnMediumChange(aAttachment, aForce);
14088}
14089
14090/**
14091 * @note Locks this object for reading.
14092 */
14093HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
14094{
14095 LogFlowThisFunc(("\n"));
14096
14097 AutoCaller autoCaller(this);
14098 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14099
14100 ComPtr<IInternalSessionControl> directControl;
14101 {
14102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14103 directControl = mData->mSession.mDirectControl;
14104 }
14105
14106 /* ignore notifications sent after #OnSessionEnd() is called */
14107 if (!directControl)
14108 return S_OK;
14109
14110 return directControl->OnCPUChange(aCPU, aRemove);
14111}
14112
14113HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
14114{
14115 LogFlowThisFunc(("\n"));
14116
14117 AutoCaller autoCaller(this);
14118 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14119
14120 ComPtr<IInternalSessionControl> directControl;
14121 {
14122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14123 directControl = mData->mSession.mDirectControl;
14124 }
14125
14126 /* ignore notifications sent after #OnSessionEnd() is called */
14127 if (!directControl)
14128 return S_OK;
14129
14130 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14131}
14132
14133/**
14134 * @note Locks this object for reading.
14135 */
14136HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
14137{
14138 LogFlowThisFunc(("\n"));
14139
14140 AutoCaller autoCaller(this);
14141 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14142
14143 ComPtr<IInternalSessionControl> directControl;
14144 {
14145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14146 directControl = mData->mSession.mDirectControl;
14147 }
14148
14149 /* ignore notifications sent after #OnSessionEnd() is called */
14150 if (!directControl)
14151 return S_OK;
14152
14153 return directControl->OnVRDEServerChange(aRestart);
14154}
14155
14156/**
14157 * @note Locks this object for reading.
14158 */
14159HRESULT SessionMachine::onVideoCaptureChange()
14160{
14161 LogFlowThisFunc(("\n"));
14162
14163 AutoCaller autoCaller(this);
14164 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14165
14166 ComPtr<IInternalSessionControl> directControl;
14167 {
14168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14169 directControl = mData->mSession.mDirectControl;
14170 }
14171
14172 /* ignore notifications sent after #OnSessionEnd() is called */
14173 if (!directControl)
14174 return S_OK;
14175
14176 return directControl->OnVideoCaptureChange();
14177}
14178
14179/**
14180 * @note Locks this object for reading.
14181 */
14182HRESULT SessionMachine::onUSBControllerChange()
14183{
14184 LogFlowThisFunc(("\n"));
14185
14186 AutoCaller autoCaller(this);
14187 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14188
14189 ComPtr<IInternalSessionControl> directControl;
14190 {
14191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14192 directControl = mData->mSession.mDirectControl;
14193 }
14194
14195 /* ignore notifications sent after #OnSessionEnd() is called */
14196 if (!directControl)
14197 return S_OK;
14198
14199 return directControl->OnUSBControllerChange();
14200}
14201
14202/**
14203 * @note Locks this object for reading.
14204 */
14205HRESULT SessionMachine::onSharedFolderChange()
14206{
14207 LogFlowThisFunc(("\n"));
14208
14209 AutoCaller autoCaller(this);
14210 AssertComRCReturnRC(autoCaller.rc());
14211
14212 ComPtr<IInternalSessionControl> directControl;
14213 {
14214 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14215 directControl = mData->mSession.mDirectControl;
14216 }
14217
14218 /* ignore notifications sent after #OnSessionEnd() is called */
14219 if (!directControl)
14220 return S_OK;
14221
14222 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14223}
14224
14225/**
14226 * @note Locks this object for reading.
14227 */
14228HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14229{
14230 LogFlowThisFunc(("\n"));
14231
14232 AutoCaller autoCaller(this);
14233 AssertComRCReturnRC(autoCaller.rc());
14234
14235 ComPtr<IInternalSessionControl> directControl;
14236 {
14237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14238 directControl = mData->mSession.mDirectControl;
14239 }
14240
14241 /* ignore notifications sent after #OnSessionEnd() is called */
14242 if (!directControl)
14243 return S_OK;
14244
14245 return directControl->OnClipboardModeChange(aClipboardMode);
14246}
14247
14248/**
14249 * @note Locks this object for reading.
14250 */
14251HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14252{
14253 LogFlowThisFunc(("\n"));
14254
14255 AutoCaller autoCaller(this);
14256 AssertComRCReturnRC(autoCaller.rc());
14257
14258 ComPtr<IInternalSessionControl> directControl;
14259 {
14260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14261 directControl = mData->mSession.mDirectControl;
14262 }
14263
14264 /* ignore notifications sent after #OnSessionEnd() is called */
14265 if (!directControl)
14266 return S_OK;
14267
14268 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14269}
14270
14271/**
14272 * @note Locks this object for reading.
14273 */
14274HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14275{
14276 LogFlowThisFunc(("\n"));
14277
14278 AutoCaller autoCaller(this);
14279 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14280
14281 ComPtr<IInternalSessionControl> directControl;
14282 {
14283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14284 directControl = mData->mSession.mDirectControl;
14285 }
14286
14287 /* ignore notifications sent after #OnSessionEnd() is called */
14288 if (!directControl)
14289 return S_OK;
14290
14291 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14292}
14293
14294/**
14295 * @note Locks this object for reading.
14296 */
14297HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14298{
14299 LogFlowThisFunc(("\n"));
14300
14301 AutoCaller autoCaller(this);
14302 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14303
14304 ComPtr<IInternalSessionControl> directControl;
14305 {
14306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14307 directControl = mData->mSession.mDirectControl;
14308 }
14309
14310 /* ignore notifications sent after #OnSessionEnd() is called */
14311 if (!directControl)
14312 return S_OK;
14313
14314 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14315}
14316
14317/**
14318 * Returns @c true if this machine's USB controller reports it has a matching
14319 * filter for the given USB device and @c false otherwise.
14320 *
14321 * @note locks this object for reading.
14322 */
14323bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14324{
14325 AutoCaller autoCaller(this);
14326 /* silently return if not ready -- this method may be called after the
14327 * direct machine session has been called */
14328 if (!autoCaller.isOk())
14329 return false;
14330
14331#ifdef VBOX_WITH_USB
14332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14333
14334 switch (mData->mMachineState)
14335 {
14336 case MachineState_Starting:
14337 case MachineState_Restoring:
14338 case MachineState_TeleportingIn:
14339 case MachineState_Paused:
14340 case MachineState_Running:
14341 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14342 * elsewhere... */
14343 alock.release();
14344 return mUSBDeviceFilters->hasMatchingFilter(aDevice, aMaskedIfs);
14345 default: break;
14346 }
14347#else
14348 NOREF(aDevice);
14349 NOREF(aMaskedIfs);
14350#endif
14351 return false;
14352}
14353
14354/**
14355 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14356 */
14357HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14358 IVirtualBoxErrorInfo *aError,
14359 ULONG aMaskedIfs)
14360{
14361 LogFlowThisFunc(("\n"));
14362
14363 AutoCaller autoCaller(this);
14364
14365 /* This notification may happen after the machine object has been
14366 * uninitialized (the session was closed), so don't assert. */
14367 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14368
14369 ComPtr<IInternalSessionControl> directControl;
14370 {
14371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14372 directControl = mData->mSession.mDirectControl;
14373 }
14374
14375 /* fail on notifications sent after #OnSessionEnd() is called, it is
14376 * expected by the caller */
14377 if (!directControl)
14378 return E_FAIL;
14379
14380 /* No locks should be held at this point. */
14381 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14382 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14383
14384 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14385}
14386
14387/**
14388 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14389 */
14390HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14391 IVirtualBoxErrorInfo *aError)
14392{
14393 LogFlowThisFunc(("\n"));
14394
14395 AutoCaller autoCaller(this);
14396
14397 /* This notification may happen after the machine object has been
14398 * uninitialized (the session was closed), so don't assert. */
14399 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14400
14401 ComPtr<IInternalSessionControl> directControl;
14402 {
14403 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14404 directControl = mData->mSession.mDirectControl;
14405 }
14406
14407 /* fail on notifications sent after #OnSessionEnd() is called, it is
14408 * expected by the caller */
14409 if (!directControl)
14410 return E_FAIL;
14411
14412 /* No locks should be held at this point. */
14413 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14414 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14415
14416 return directControl->OnUSBDeviceDetach(aId, aError);
14417}
14418
14419// protected methods
14420/////////////////////////////////////////////////////////////////////////////
14421
14422/**
14423 * Helper method to finalize saving the state.
14424 *
14425 * @note Must be called from under this object's lock.
14426 *
14427 * @param aRc S_OK if the snapshot has been taken successfully
14428 * @param aErrMsg human readable error message for failure
14429 *
14430 * @note Locks mParent + this objects for writing.
14431 */
14432HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14433{
14434 LogFlowThisFuncEnter();
14435
14436 AutoCaller autoCaller(this);
14437 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14438
14439 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14440
14441 HRESULT rc = S_OK;
14442
14443 if (SUCCEEDED(aRc))
14444 {
14445 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14446
14447 /* save all VM settings */
14448 rc = saveSettings(NULL);
14449 // no need to check whether VirtualBox.xml needs saving also since
14450 // we can't have a name change pending at this point
14451 }
14452 else
14453 {
14454 // delete the saved state file (it might have been already created);
14455 // we need not check whether this is shared with a snapshot here because
14456 // we certainly created this saved state file here anew
14457 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14458 }
14459
14460 /* notify the progress object about operation completion */
14461 Assert(mConsoleTaskData.mProgress);
14462 if (SUCCEEDED(aRc))
14463 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14464 else
14465 {
14466 if (aErrMsg.length())
14467 mConsoleTaskData.mProgress->notifyComplete(aRc,
14468 COM_IIDOF(ISession),
14469 getComponentName(),
14470 aErrMsg.c_str());
14471 else
14472 mConsoleTaskData.mProgress->notifyComplete(aRc);
14473 }
14474
14475 /* clear out the temporary saved state data */
14476 mConsoleTaskData.mLastState = MachineState_Null;
14477 mConsoleTaskData.strStateFilePath.setNull();
14478 mConsoleTaskData.mProgress.setNull();
14479
14480 LogFlowThisFuncLeave();
14481 return rc;
14482}
14483
14484/**
14485 * Deletes the given file if it is no longer in use by either the current machine state
14486 * (if the machine is "saved") or any of the machine's snapshots.
14487 *
14488 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14489 * but is different for each SnapshotMachine. When calling this, the order of calling this
14490 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14491 * is therefore critical. I know, it's all rather messy.
14492 *
14493 * @param strStateFile
14494 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14495 */
14496void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14497 Snapshot *pSnapshotToIgnore)
14498{
14499 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14500 if ( (strStateFile.isNotEmpty())
14501 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14502 )
14503 // ... and it must also not be shared with other snapshots
14504 if ( !mData->mFirstSnapshot
14505 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14506 // this checks the SnapshotMachine's state file paths
14507 )
14508 RTFileDelete(strStateFile.c_str());
14509}
14510
14511/**
14512 * Locks the attached media.
14513 *
14514 * All attached hard disks are locked for writing and DVD/floppy are locked for
14515 * reading. Parents of attached hard disks (if any) are locked for reading.
14516 *
14517 * This method also performs accessibility check of all media it locks: if some
14518 * media is inaccessible, the method will return a failure and a bunch of
14519 * extended error info objects per each inaccessible medium.
14520 *
14521 * Note that this method is atomic: if it returns a success, all media are
14522 * locked as described above; on failure no media is locked at all (all
14523 * succeeded individual locks will be undone).
14524 *
14525 * The caller is responsible for doing the necessary state sanity checks.
14526 *
14527 * The locks made by this method must be undone by calling #unlockMedia() when
14528 * no more needed.
14529 */
14530HRESULT SessionMachine::lockMedia()
14531{
14532 AutoCaller autoCaller(this);
14533 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14534
14535 AutoMultiWriteLock2 alock(this->lockHandle(),
14536 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14537
14538 /* bail out if trying to lock things with already set up locking */
14539 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14540
14541 MultiResult mrc(S_OK);
14542
14543 /* Collect locking information for all medium objects attached to the VM. */
14544 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14545 it != mMediaData->mAttachments.end();
14546 ++it)
14547 {
14548 MediumAttachment* pAtt = *it;
14549 DeviceType_T devType = pAtt->getType();
14550 Medium *pMedium = pAtt->getMedium();
14551
14552 MediumLockList *pMediumLockList(new MediumLockList());
14553 // There can be attachments without a medium (floppy/dvd), and thus
14554 // it's impossible to create a medium lock list. It still makes sense
14555 // to have the empty medium lock list in the map in case a medium is
14556 // attached later.
14557 if (pMedium != NULL)
14558 {
14559 MediumType_T mediumType = pMedium->getType();
14560 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14561 || mediumType == MediumType_Shareable;
14562 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14563
14564 alock.release();
14565 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14566 !fIsReadOnlyLock /* fMediumLockWrite */,
14567 NULL,
14568 *pMediumLockList);
14569 alock.acquire();
14570 if (FAILED(mrc))
14571 {
14572 delete pMediumLockList;
14573 mData->mSession.mLockedMedia.Clear();
14574 break;
14575 }
14576 }
14577
14578 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14579 if (FAILED(rc))
14580 {
14581 mData->mSession.mLockedMedia.Clear();
14582 mrc = setError(rc,
14583 tr("Collecting locking information for all attached media failed"));
14584 break;
14585 }
14586 }
14587
14588 if (SUCCEEDED(mrc))
14589 {
14590 /* Now lock all media. If this fails, nothing is locked. */
14591 alock.release();
14592 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14593 alock.acquire();
14594 if (FAILED(rc))
14595 {
14596 mrc = setError(rc,
14597 tr("Locking of attached media failed"));
14598 }
14599 }
14600
14601 return mrc;
14602}
14603
14604/**
14605 * Undoes the locks made by by #lockMedia().
14606 */
14607void SessionMachine::unlockMedia()
14608{
14609 AutoCaller autoCaller(this);
14610 AssertComRCReturnVoid(autoCaller.rc());
14611
14612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14613
14614 /* we may be holding important error info on the current thread;
14615 * preserve it */
14616 ErrorInfoKeeper eik;
14617
14618 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14619 AssertComRC(rc);
14620}
14621
14622/**
14623 * Helper to change the machine state (reimplementation).
14624 *
14625 * @note Locks this object for writing.
14626 * @note This method must not call saveSettings or SaveSettings, otherwise
14627 * it can cause crashes in random places due to unexpectedly committing
14628 * the current settings. The caller is responsible for that. The call
14629 * to saveStateSettings is fine, because this method does not commit.
14630 */
14631HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14632{
14633 LogFlowThisFuncEnter();
14634 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14635
14636 AutoCaller autoCaller(this);
14637 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14638
14639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14640
14641 MachineState_T oldMachineState = mData->mMachineState;
14642
14643 AssertMsgReturn(oldMachineState != aMachineState,
14644 ("oldMachineState=%s, aMachineState=%s\n",
14645 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14646 E_FAIL);
14647
14648 HRESULT rc = S_OK;
14649
14650 int stsFlags = 0;
14651 bool deleteSavedState = false;
14652
14653 /* detect some state transitions */
14654
14655 if ( ( oldMachineState == MachineState_Saved
14656 && aMachineState == MachineState_Restoring)
14657 || ( ( oldMachineState == MachineState_PoweredOff
14658 || oldMachineState == MachineState_Teleported
14659 || oldMachineState == MachineState_Aborted
14660 )
14661 && ( aMachineState == MachineState_TeleportingIn
14662 || aMachineState == MachineState_Starting
14663 )
14664 )
14665 )
14666 {
14667 /* The EMT thread is about to start */
14668
14669 /* Nothing to do here for now... */
14670
14671 /// @todo NEWMEDIA don't let mDVDDrive and other children
14672 /// change anything when in the Starting/Restoring state
14673 }
14674 else if ( ( oldMachineState == MachineState_Running
14675 || oldMachineState == MachineState_Paused
14676 || oldMachineState == MachineState_Teleporting
14677 || oldMachineState == MachineState_LiveSnapshotting
14678 || oldMachineState == MachineState_Stuck
14679 || oldMachineState == MachineState_Starting
14680 || oldMachineState == MachineState_Stopping
14681 || oldMachineState == MachineState_Saving
14682 || oldMachineState == MachineState_Restoring
14683 || oldMachineState == MachineState_TeleportingPausedVM
14684 || oldMachineState == MachineState_TeleportingIn
14685 )
14686 && ( aMachineState == MachineState_PoweredOff
14687 || aMachineState == MachineState_Saved
14688 || aMachineState == MachineState_Teleported
14689 || aMachineState == MachineState_Aborted
14690 )
14691 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14692 * snapshot */
14693 && ( mConsoleTaskData.mSnapshot.isNull()
14694 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14695 )
14696 )
14697 {
14698 /* The EMT thread has just stopped, unlock attached media. Note that as
14699 * opposed to locking that is done from Console, we do unlocking here
14700 * because the VM process may have aborted before having a chance to
14701 * properly unlock all media it locked. */
14702
14703 unlockMedia();
14704 }
14705
14706 if (oldMachineState == MachineState_Restoring)
14707 {
14708 if (aMachineState != MachineState_Saved)
14709 {
14710 /*
14711 * delete the saved state file once the machine has finished
14712 * restoring from it (note that Console sets the state from
14713 * Restoring to Saved if the VM couldn't restore successfully,
14714 * to give the user an ability to fix an error and retry --
14715 * we keep the saved state file in this case)
14716 */
14717 deleteSavedState = true;
14718 }
14719 }
14720 else if ( oldMachineState == MachineState_Saved
14721 && ( aMachineState == MachineState_PoweredOff
14722 || aMachineState == MachineState_Aborted
14723 || aMachineState == MachineState_Teleported
14724 )
14725 )
14726 {
14727 /*
14728 * delete the saved state after Console::ForgetSavedState() is called
14729 * or if the VM process (owning a direct VM session) crashed while the
14730 * VM was Saved
14731 */
14732
14733 /// @todo (dmik)
14734 // Not sure that deleting the saved state file just because of the
14735 // client death before it attempted to restore the VM is a good
14736 // thing. But when it crashes we need to go to the Aborted state
14737 // which cannot have the saved state file associated... The only
14738 // way to fix this is to make the Aborted condition not a VM state
14739 // but a bool flag: i.e., when a crash occurs, set it to true and
14740 // change the state to PoweredOff or Saved depending on the
14741 // saved state presence.
14742
14743 deleteSavedState = true;
14744 mData->mCurrentStateModified = TRUE;
14745 stsFlags |= SaveSTS_CurStateModified;
14746 }
14747
14748 if ( aMachineState == MachineState_Starting
14749 || aMachineState == MachineState_Restoring
14750 || aMachineState == MachineState_TeleportingIn
14751 )
14752 {
14753 /* set the current state modified flag to indicate that the current
14754 * state is no more identical to the state in the
14755 * current snapshot */
14756 if (!mData->mCurrentSnapshot.isNull())
14757 {
14758 mData->mCurrentStateModified = TRUE;
14759 stsFlags |= SaveSTS_CurStateModified;
14760 }
14761 }
14762
14763 if (deleteSavedState)
14764 {
14765 if (mRemoveSavedState)
14766 {
14767 Assert(!mSSData->strStateFilePath.isEmpty());
14768
14769 // it is safe to delete the saved state file if ...
14770 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14771 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14772 // ... none of the snapshots share the saved state file
14773 )
14774 RTFileDelete(mSSData->strStateFilePath.c_str());
14775 }
14776
14777 mSSData->strStateFilePath.setNull();
14778 stsFlags |= SaveSTS_StateFilePath;
14779 }
14780
14781 /* redirect to the underlying peer machine */
14782 mPeer->setMachineState(aMachineState);
14783
14784 if ( aMachineState == MachineState_PoweredOff
14785 || aMachineState == MachineState_Teleported
14786 || aMachineState == MachineState_Aborted
14787 || aMachineState == MachineState_Saved)
14788 {
14789 /* the machine has stopped execution
14790 * (or the saved state file was adopted) */
14791 stsFlags |= SaveSTS_StateTimeStamp;
14792 }
14793
14794 if ( ( oldMachineState == MachineState_PoweredOff
14795 || oldMachineState == MachineState_Aborted
14796 || oldMachineState == MachineState_Teleported
14797 )
14798 && aMachineState == MachineState_Saved)
14799 {
14800 /* the saved state file was adopted */
14801 Assert(!mSSData->strStateFilePath.isEmpty());
14802 stsFlags |= SaveSTS_StateFilePath;
14803 }
14804
14805#ifdef VBOX_WITH_GUEST_PROPS
14806 if ( aMachineState == MachineState_PoweredOff
14807 || aMachineState == MachineState_Aborted
14808 || aMachineState == MachineState_Teleported)
14809 {
14810 /* Make sure any transient guest properties get removed from the
14811 * property store on shutdown. */
14812
14813 HWData::GuestPropertyMap::const_iterator it;
14814 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14815 if (!fNeedsSaving)
14816 for (it = mHWData->mGuestProperties.begin();
14817 it != mHWData->mGuestProperties.end(); ++it)
14818 if ( (it->second.mFlags & guestProp::TRANSIENT)
14819 || (it->second.mFlags & guestProp::TRANSRESET))
14820 {
14821 fNeedsSaving = true;
14822 break;
14823 }
14824 if (fNeedsSaving)
14825 {
14826 mData->mCurrentStateModified = TRUE;
14827 stsFlags |= SaveSTS_CurStateModified;
14828 }
14829 }
14830#endif
14831
14832 rc = saveStateSettings(stsFlags);
14833
14834 if ( ( oldMachineState != MachineState_PoweredOff
14835 && oldMachineState != MachineState_Aborted
14836 && oldMachineState != MachineState_Teleported
14837 )
14838 && ( aMachineState == MachineState_PoweredOff
14839 || aMachineState == MachineState_Aborted
14840 || aMachineState == MachineState_Teleported
14841 )
14842 )
14843 {
14844 /* we've been shut down for any reason */
14845 /* no special action so far */
14846 }
14847
14848 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14849 LogFlowThisFuncLeave();
14850 return rc;
14851}
14852
14853/**
14854 * Sends the current machine state value to the VM process.
14855 *
14856 * @note Locks this object for reading, then calls a client process.
14857 */
14858HRESULT SessionMachine::updateMachineStateOnClient()
14859{
14860 AutoCaller autoCaller(this);
14861 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14862
14863 ComPtr<IInternalSessionControl> directControl;
14864 {
14865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14866 AssertReturn(!!mData, E_FAIL);
14867 directControl = mData->mSession.mDirectControl;
14868
14869 /* directControl may be already set to NULL here in #OnSessionEnd()
14870 * called too early by the direct session process while there is still
14871 * some operation (like deleting the snapshot) in progress. The client
14872 * process in this case is waiting inside Session::close() for the
14873 * "end session" process object to complete, while #uninit() called by
14874 * #checkForDeath() on the Watcher thread is waiting for the pending
14875 * operation to complete. For now, we accept this inconsistent behavior
14876 * and simply do nothing here. */
14877
14878 if (mData->mSession.mState == SessionState_Unlocking)
14879 return S_OK;
14880
14881 AssertReturn(!directControl.isNull(), E_FAIL);
14882 }
14883
14884 return directControl->UpdateMachineState(mData->mMachineState);
14885}
Note: See TracBrowser for help on using the repository browser.

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