VirtualBox

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

Last change on this file since 48015 was 47991, checked in by vboxsync, 11 years ago

Main: Made the exclusive HW virtualization use setting global rather than per-VM.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 499.6 KB
Line 
1/* $Id: MachineImpl.cpp 47991 2013-08-22 14:31:52Z 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 HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
184 mPAEEnabled = true;
185#else
186 mPAEEnabled = false;
187#endif
188 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
189 mSyntheticCpu = false;
190 mHPETEnabled = false;
191
192 /* default boot order: floppy - DVD - HDD */
193 mBootOrder[0] = DeviceType_Floppy;
194 mBootOrder[1] = DeviceType_DVD;
195 mBootOrder[2] = DeviceType_HardDisk;
196 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
197 mBootOrder[i] = DeviceType_Null;
198
199 mClipboardMode = ClipboardMode_Disabled;
200 mDragAndDropMode = DragAndDropMode_Disabled;
201 mGuestPropertyNotificationPatterns = "";
202
203 mFirmwareType = FirmwareType_BIOS;
204 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
205 mPointingHIDType = PointingHIDType_PS2Mouse;
206 mChipsetType = ChipsetType_PIIX3;
207 mEmulatedUSBWebcamEnabled = FALSE;
208 mEmulatedUSBCardReaderEnabled = FALSE;
209
210 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
211 mCPUAttached[i] = false;
212
213 mIOCacheEnabled = true;
214 mIOCacheSize = 5; /* 5MB */
215
216 /* Maximum CPU execution cap by default. */
217 mCpuExecutionCap = 100;
218}
219
220Machine::HWData::~HWData()
221{
222}
223
224/////////////////////////////////////////////////////////////////////////////
225// Machine::HDData structure
226/////////////////////////////////////////////////////////////////////////////
227
228Machine::MediaData::MediaData()
229{
230}
231
232Machine::MediaData::~MediaData()
233{
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine class
238/////////////////////////////////////////////////////////////////////////////
239
240// constructor / destructor
241/////////////////////////////////////////////////////////////////////////////
242
243Machine::Machine() :
244#ifdef VBOX_WITH_RESOURCE_USAGE_API
245 mCollectorGuest(NULL),
246#endif
247 mPeer(NULL),
248 mParent(NULL),
249 mSerialPorts(),
250 mParallelPorts(),
251 uRegistryNeedsSaving(0)
252{}
253
254Machine::~Machine()
255{}
256
257HRESULT Machine::FinalConstruct()
258{
259 LogFlowThisFunc(("\n"));
260 return BaseFinalConstruct();
261}
262
263void Machine::FinalRelease()
264{
265 LogFlowThisFunc(("\n"));
266 uninit();
267 BaseFinalRelease();
268}
269
270/**
271 * Initializes a new machine instance; this init() variant creates a new, empty machine.
272 * This gets called from VirtualBox::CreateMachine().
273 *
274 * @param aParent Associated parent object
275 * @param strConfigFile Local file system path to the VM settings file (can
276 * be relative to the VirtualBox config directory).
277 * @param strName name for the machine
278 * @param llGroups list of groups for the machine
279 * @param aOsType OS Type of this machine or NULL.
280 * @param aId UUID for the new machine.
281 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
282 *
283 * @return Success indicator. if not S_OK, the machine object is invalid
284 */
285HRESULT Machine::init(VirtualBox *aParent,
286 const Utf8Str &strConfigFile,
287 const Utf8Str &strName,
288 const StringsList &llGroups,
289 GuestOSType *aOsType,
290 const Guid &aId,
291 bool fForceOverwrite,
292 bool fDirectoryIncludesUUID)
293{
294 LogFlowThisFuncEnter();
295 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
296
297 /* Enclose the state transition NotReady->InInit->Ready */
298 AutoInitSpan autoInitSpan(this);
299 AssertReturn(autoInitSpan.isOk(), E_FAIL);
300
301 HRESULT rc = initImpl(aParent, strConfigFile);
302 if (FAILED(rc)) return rc;
303
304 rc = tryCreateMachineConfigFile(fForceOverwrite);
305 if (FAILED(rc)) return rc;
306
307 if (SUCCEEDED(rc))
308 {
309 // create an empty machine config
310 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
311
312 rc = initDataAndChildObjects();
313 }
314
315 if (SUCCEEDED(rc))
316 {
317 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
318 mData->mAccessible = TRUE;
319
320 unconst(mData->mUuid) = aId;
321
322 mUserData->s.strName = strName;
323
324 mUserData->s.llGroups = llGroups;
325
326 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
327 // the "name sync" flag determines whether the machine directory gets renamed along
328 // with the machine file; say so if the settings file name is the same as the
329 // settings file parent directory (machine directory)
330 mUserData->s.fNameSync = isInOwnDir();
331
332 // initialize the default snapshots folder
333 rc = COMSETTER(SnapshotFolder)(NULL);
334 AssertComRC(rc);
335
336 if (aOsType)
337 {
338 /* Store OS type */
339 mUserData->s.strOsType = aOsType->id();
340
341 /* Apply BIOS defaults */
342 mBIOSSettings->applyDefaults(aOsType);
343
344 /* Apply network adapters defaults */
345 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
346 mNetworkAdapters[slot]->applyDefaults(aOsType);
347
348 /* Apply serial port defaults */
349 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
350 mSerialPorts[slot]->applyDefaults(aOsType);
351
352 /* Let the OS type select 64-bit ness. */
353 mHWData->mLongMode = aOsType->is64Bit()
354 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
355 }
356
357 /* At this point the changing of the current state modification
358 * flag is allowed. */
359 allowStateModification();
360
361 /* commit all changes made during the initialization */
362 commit();
363 }
364
365 /* Confirm a successful initialization when it's the case */
366 if (SUCCEEDED(rc))
367 {
368 if (mData->mAccessible)
369 autoInitSpan.setSucceeded();
370 else
371 autoInitSpan.setLimited();
372 }
373
374 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
375 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
376 mData->mRegistered,
377 mData->mAccessible,
378 rc));
379
380 LogFlowThisFuncLeave();
381
382 return rc;
383}
384
385/**
386 * Initializes a new instance with data from machine XML (formerly Init_Registered).
387 * Gets called in two modes:
388 *
389 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
390 * UUID is specified and we mark the machine as "registered";
391 *
392 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
393 * and the machine remains unregistered until RegisterMachine() is called.
394 *
395 * @param aParent Associated parent object
396 * @param aConfigFile Local file system path to the VM settings file (can
397 * be relative to the VirtualBox config directory).
398 * @param aId UUID of the machine or NULL (see above).
399 *
400 * @return Success indicator. if not S_OK, the machine object is invalid
401 */
402HRESULT Machine::initFromSettings(VirtualBox *aParent,
403 const Utf8Str &strConfigFile,
404 const Guid *aId)
405{
406 LogFlowThisFuncEnter();
407 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
408
409 /* Enclose the state transition NotReady->InInit->Ready */
410 AutoInitSpan autoInitSpan(this);
411 AssertReturn(autoInitSpan.isOk(), E_FAIL);
412
413 HRESULT rc = initImpl(aParent, strConfigFile);
414 if (FAILED(rc)) return rc;
415
416 if (aId)
417 {
418 // loading a registered VM:
419 unconst(mData->mUuid) = *aId;
420 mData->mRegistered = TRUE;
421 // now load the settings from XML:
422 rc = registeredInit();
423 // this calls initDataAndChildObjects() and loadSettings()
424 }
425 else
426 {
427 // opening an unregistered VM (VirtualBox::OpenMachine()):
428 rc = initDataAndChildObjects();
429
430 if (SUCCEEDED(rc))
431 {
432 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
433 mData->mAccessible = TRUE;
434
435 try
436 {
437 // load and parse machine XML; this will throw on XML or logic errors
438 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
439
440 // reject VM UUID duplicates, they can happen if someone
441 // tries to register an already known VM config again
442 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
443 true /* fPermitInaccessible */,
444 false /* aDoSetError */,
445 NULL) != VBOX_E_OBJECT_NOT_FOUND)
446 {
447 throw setError(E_FAIL,
448 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
449 mData->m_strConfigFile.c_str());
450 }
451
452 // use UUID from machine config
453 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
454
455 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
456 NULL /* puuidRegistry */);
457 if (FAILED(rc)) throw rc;
458
459 /* At this point the changing of the current state modification
460 * flag is allowed. */
461 allowStateModification();
462
463 commit();
464 }
465 catch (HRESULT err)
466 {
467 /* we assume that error info is set by the thrower */
468 rc = err;
469 }
470 catch (...)
471 {
472 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
473 }
474 }
475 }
476
477 /* Confirm a successful initialization when it's the case */
478 if (SUCCEEDED(rc))
479 {
480 if (mData->mAccessible)
481 autoInitSpan.setSucceeded();
482 else
483 {
484 autoInitSpan.setLimited();
485
486 // uninit media from this machine's media registry, or else
487 // reloading the settings will fail
488 mParent->unregisterMachineMedia(getId());
489 }
490 }
491
492 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
493 "rc=%08X\n",
494 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
495 mData->mRegistered, mData->mAccessible, rc));
496
497 LogFlowThisFuncLeave();
498
499 return rc;
500}
501
502/**
503 * Initializes a new instance from a machine config that is already in memory
504 * (import OVF case). Since we are importing, the UUID in the machine
505 * config is ignored and we always generate a fresh one.
506 *
507 * @param strName Name for the new machine; this overrides what is specified in config and is used
508 * for the settings file as well.
509 * @param config Machine configuration loaded and parsed from XML.
510 *
511 * @return Success indicator. if not S_OK, the machine object is invalid
512 */
513HRESULT Machine::init(VirtualBox *aParent,
514 const Utf8Str &strName,
515 const settings::MachineConfigFile &config)
516{
517 LogFlowThisFuncEnter();
518
519 /* Enclose the state transition NotReady->InInit->Ready */
520 AutoInitSpan autoInitSpan(this);
521 AssertReturn(autoInitSpan.isOk(), E_FAIL);
522
523 Utf8Str strConfigFile;
524 aParent->getDefaultMachineFolder(strConfigFile);
525 strConfigFile.append(RTPATH_DELIMITER);
526 strConfigFile.append(strName);
527 strConfigFile.append(RTPATH_DELIMITER);
528 strConfigFile.append(strName);
529 strConfigFile.append(".vbox");
530
531 HRESULT rc = initImpl(aParent, strConfigFile);
532 if (FAILED(rc)) return rc;
533
534 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
535 if (FAILED(rc)) return rc;
536
537 rc = initDataAndChildObjects();
538
539 if (SUCCEEDED(rc))
540 {
541 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
542 mData->mAccessible = TRUE;
543
544 // create empty machine config for instance data
545 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
546
547 // generate fresh UUID, ignore machine config
548 unconst(mData->mUuid).create();
549
550 rc = loadMachineDataFromSettings(config,
551 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
552
553 // override VM name as well, it may be different
554 mUserData->s.strName = strName;
555
556 if (SUCCEEDED(rc))
557 {
558 /* At this point the changing of the current state modification
559 * flag is allowed. */
560 allowStateModification();
561
562 /* commit all changes made during the initialization */
563 commit();
564 }
565 }
566
567 /* Confirm a successful initialization when it's the case */
568 if (SUCCEEDED(rc))
569 {
570 if (mData->mAccessible)
571 autoInitSpan.setSucceeded();
572 else
573 {
574 autoInitSpan.setLimited();
575
576 // uninit media from this machine's media registry, or else
577 // reloading the settings will fail
578 mParent->unregisterMachineMedia(getId());
579 }
580 }
581
582 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
583 "rc=%08X\n",
584 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
585 mData->mRegistered, mData->mAccessible, rc));
586
587 LogFlowThisFuncLeave();
588
589 return rc;
590}
591
592/**
593 * Shared code between the various init() implementations.
594 * @param aParent
595 * @return
596 */
597HRESULT Machine::initImpl(VirtualBox *aParent,
598 const Utf8Str &strConfigFile)
599{
600 LogFlowThisFuncEnter();
601
602 AssertReturn(aParent, E_INVALIDARG);
603 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
604
605 HRESULT rc = S_OK;
606
607 /* share the parent weakly */
608 unconst(mParent) = aParent;
609
610 /* allocate the essential machine data structure (the rest will be
611 * allocated later by initDataAndChildObjects() */
612 mData.allocate();
613
614 /* memorize the config file name (as provided) */
615 mData->m_strConfigFile = strConfigFile;
616
617 /* get the full file name */
618 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
619 if (RT_FAILURE(vrc1))
620 return setError(VBOX_E_FILE_ERROR,
621 tr("Invalid machine settings file name '%s' (%Rrc)"),
622 strConfigFile.c_str(),
623 vrc1);
624
625 LogFlowThisFuncLeave();
626
627 return rc;
628}
629
630/**
631 * Tries to create a machine settings file in the path stored in the machine
632 * instance data. Used when a new machine is created to fail gracefully if
633 * the settings file could not be written (e.g. because machine dir is read-only).
634 * @return
635 */
636HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
637{
638 HRESULT rc = S_OK;
639
640 // when we create a new machine, we must be able to create the settings file
641 RTFILE f = NIL_RTFILE;
642 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
643 if ( RT_SUCCESS(vrc)
644 || vrc == VERR_SHARING_VIOLATION
645 )
646 {
647 if (RT_SUCCESS(vrc))
648 RTFileClose(f);
649 if (!fForceOverwrite)
650 rc = setError(VBOX_E_FILE_ERROR,
651 tr("Machine settings file '%s' already exists"),
652 mData->m_strConfigFileFull.c_str());
653 else
654 {
655 /* try to delete the config file, as otherwise the creation
656 * of a new settings file will fail. */
657 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
658 if (RT_FAILURE(vrc2))
659 rc = setError(VBOX_E_FILE_ERROR,
660 tr("Could not delete the existing settings file '%s' (%Rrc)"),
661 mData->m_strConfigFileFull.c_str(), vrc2);
662 }
663 }
664 else if ( vrc != VERR_FILE_NOT_FOUND
665 && vrc != VERR_PATH_NOT_FOUND
666 )
667 rc = setError(VBOX_E_FILE_ERROR,
668 tr("Invalid machine settings file name '%s' (%Rrc)"),
669 mData->m_strConfigFileFull.c_str(),
670 vrc);
671 return rc;
672}
673
674/**
675 * Initializes the registered machine by loading the settings file.
676 * This method is separated from #init() in order to make it possible to
677 * retry the operation after VirtualBox startup instead of refusing to
678 * startup the whole VirtualBox server in case if the settings file of some
679 * registered VM is invalid or inaccessible.
680 *
681 * @note Must be always called from this object's write lock
682 * (unless called from #init() that doesn't need any locking).
683 * @note Locks the mUSBController method for writing.
684 * @note Subclasses must not call this method.
685 */
686HRESULT Machine::registeredInit()
687{
688 AssertReturn(!isSessionMachine(), E_FAIL);
689 AssertReturn(!isSnapshotMachine(), E_FAIL);
690 AssertReturn(mData->mUuid.isValid(), E_FAIL);
691 AssertReturn(!mData->mAccessible, E_FAIL);
692
693 HRESULT rc = initDataAndChildObjects();
694
695 if (SUCCEEDED(rc))
696 {
697 /* Temporarily reset the registered flag in order to let setters
698 * potentially called from loadSettings() succeed (isMutable() used in
699 * all setters will return FALSE for a Machine instance if mRegistered
700 * is TRUE). */
701 mData->mRegistered = FALSE;
702
703 try
704 {
705 // load and parse machine XML; this will throw on XML or logic errors
706 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
707
708 if (mData->mUuid != mData->pMachineConfigFile->uuid)
709 throw setError(E_FAIL,
710 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
711 mData->pMachineConfigFile->uuid.raw(),
712 mData->m_strConfigFileFull.c_str(),
713 mData->mUuid.toString().c_str(),
714 mParent->settingsFilePath().c_str());
715
716 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
717 NULL /* const Guid *puuidRegistry */);
718 if (FAILED(rc)) throw rc;
719 }
720 catch (HRESULT err)
721 {
722 /* we assume that error info is set by the thrower */
723 rc = err;
724 }
725 catch (...)
726 {
727 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
728 }
729
730 /* Restore the registered flag (even on failure) */
731 mData->mRegistered = TRUE;
732 }
733
734 if (SUCCEEDED(rc))
735 {
736 /* Set mAccessible to TRUE only if we successfully locked and loaded
737 * the settings file */
738 mData->mAccessible = TRUE;
739
740 /* commit all changes made during loading the settings file */
741 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
742 /// @todo r=klaus for some reason the settings loading logic backs up
743 // the settings, and therefore a commit is needed. Should probably be changed.
744 }
745 else
746 {
747 /* If the machine is registered, then, instead of returning a
748 * failure, we mark it as inaccessible and set the result to
749 * success to give it a try later */
750
751 /* fetch the current error info */
752 mData->mAccessError = com::ErrorInfo();
753 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
754 mData->mUuid.raw(),
755 mData->mAccessError.getText().raw()));
756
757 /* rollback all changes */
758 rollback(false /* aNotify */);
759
760 // uninit media from this machine's media registry, or else
761 // reloading the settings will fail
762 mParent->unregisterMachineMedia(getId());
763
764 /* uninitialize the common part to make sure all data is reset to
765 * default (null) values */
766 uninitDataAndChildObjects();
767
768 rc = S_OK;
769 }
770
771 return rc;
772}
773
774/**
775 * Uninitializes the instance.
776 * Called either from FinalRelease() or by the parent when it gets destroyed.
777 *
778 * @note The caller of this method must make sure that this object
779 * a) doesn't have active callers on the current thread and b) is not locked
780 * by the current thread; otherwise uninit() will hang either a) due to
781 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
782 * a dead-lock caused by this thread waiting for all callers on the other
783 * threads are done but preventing them from doing so by holding a lock.
784 */
785void Machine::uninit()
786{
787 LogFlowThisFuncEnter();
788
789 Assert(!isWriteLockOnCurrentThread());
790
791 Assert(!uRegistryNeedsSaving);
792 if (uRegistryNeedsSaving)
793 {
794 AutoCaller autoCaller(this);
795 if (SUCCEEDED(autoCaller.rc()))
796 {
797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
798 saveSettings(NULL, Machine::SaveS_Force);
799 }
800 }
801
802 /* Enclose the state transition Ready->InUninit->NotReady */
803 AutoUninitSpan autoUninitSpan(this);
804 if (autoUninitSpan.uninitDone())
805 return;
806
807 Assert(!isSnapshotMachine());
808 Assert(!isSessionMachine());
809 Assert(!!mData);
810
811 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
812 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
813
814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
815
816 if (!mData->mSession.mMachine.isNull())
817 {
818 /* Theoretically, this can only happen if the VirtualBox server has been
819 * terminated while there were clients running that owned open direct
820 * sessions. Since in this case we are definitely called by
821 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
822 * won't happen on the client watcher thread (because it does
823 * VirtualBox::addCaller() for the duration of the
824 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
825 * cannot happen until the VirtualBox caller is released). This is
826 * important, because SessionMachine::uninit() cannot correctly operate
827 * after we return from this method (it expects the Machine instance is
828 * still valid). We'll call it ourselves below.
829 */
830 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
831 (SessionMachine*)mData->mSession.mMachine));
832
833 if (Global::IsOnlineOrTransient(mData->mMachineState))
834 {
835 LogWarningThisFunc(("Setting state to Aborted!\n"));
836 /* set machine state using SessionMachine reimplementation */
837 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
838 }
839
840 /*
841 * Uninitialize SessionMachine using public uninit() to indicate
842 * an unexpected uninitialization.
843 */
844 mData->mSession.mMachine->uninit();
845 /* SessionMachine::uninit() must set mSession.mMachine to null */
846 Assert(mData->mSession.mMachine.isNull());
847 }
848
849 // uninit media from this machine's media registry, if they're still there
850 Guid uuidMachine(getId());
851
852 /* the lock is no more necessary (SessionMachine is uninitialized) */
853 alock.release();
854
855 /* XXX This will fail with
856 * "cannot be closed because it is still attached to 1 virtual machines"
857 * because at this point we did not call uninitDataAndChildObjects() yet
858 * and therefore also removeBackReference() for all these mediums was not called! */
859
860 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
861 mParent->unregisterMachineMedia(uuidMachine);
862
863 // has machine been modified?
864 if (mData->flModifications)
865 {
866 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
867 rollback(false /* aNotify */);
868 }
869
870 if (mData->mAccessible)
871 uninitDataAndChildObjects();
872
873 /* free the essential data structure last */
874 mData.free();
875
876 LogFlowThisFuncLeave();
877}
878
879// IMachine properties
880/////////////////////////////////////////////////////////////////////////////
881
882STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
883{
884 CheckComArgOutPointerValid(aParent);
885
886 AutoLimitedCaller autoCaller(this);
887 if (FAILED(autoCaller.rc())) return autoCaller.rc();
888
889 /* mParent is constant during life time, no need to lock */
890 ComObjPtr<VirtualBox> pVirtualBox(mParent);
891 pVirtualBox.queryInterfaceTo(aParent);
892
893 return S_OK;
894}
895
896STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
897{
898 CheckComArgOutPointerValid(aAccessible);
899
900 AutoLimitedCaller autoCaller(this);
901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
902
903 LogFlowThisFunc(("ENTER\n"));
904
905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
906
907 HRESULT rc = S_OK;
908
909 if (!mData->mAccessible)
910 {
911 /* try to initialize the VM once more if not accessible */
912
913 AutoReinitSpan autoReinitSpan(this);
914 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
915
916#ifdef DEBUG
917 LogFlowThisFunc(("Dumping media backreferences\n"));
918 mParent->dumpAllBackRefs();
919#endif
920
921 if (mData->pMachineConfigFile)
922 {
923 // reset the XML file to force loadSettings() (called from registeredInit())
924 // to parse it again; the file might have changed
925 delete mData->pMachineConfigFile;
926 mData->pMachineConfigFile = NULL;
927 }
928
929 rc = registeredInit();
930
931 if (SUCCEEDED(rc) && mData->mAccessible)
932 {
933 autoReinitSpan.setSucceeded();
934
935 /* make sure interesting parties will notice the accessibility
936 * state change */
937 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
938 mParent->onMachineDataChange(mData->mUuid);
939 }
940 }
941
942 if (SUCCEEDED(rc))
943 *aAccessible = mData->mAccessible;
944
945 LogFlowThisFuncLeave();
946
947 return rc;
948}
949
950STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
951{
952 CheckComArgOutPointerValid(aAccessError);
953
954 AutoLimitedCaller autoCaller(this);
955 if (FAILED(autoCaller.rc())) return autoCaller.rc();
956
957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
958
959 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
960 {
961 /* return shortly */
962 aAccessError = NULL;
963 return S_OK;
964 }
965
966 HRESULT rc = S_OK;
967
968 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
969 rc = errorInfo.createObject();
970 if (SUCCEEDED(rc))
971 {
972 errorInfo->init(mData->mAccessError.getResultCode(),
973 mData->mAccessError.getInterfaceID().ref(),
974 Utf8Str(mData->mAccessError.getComponent()).c_str(),
975 Utf8Str(mData->mAccessError.getText()));
976 rc = errorInfo.queryInterfaceTo(aAccessError);
977 }
978
979 return rc;
980}
981
982STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
983{
984 CheckComArgOutPointerValid(aName);
985
986 AutoCaller autoCaller(this);
987 if (FAILED(autoCaller.rc())) return autoCaller.rc();
988
989 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
990
991 mUserData->s.strName.cloneTo(aName);
992
993 return S_OK;
994}
995
996STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
997{
998 CheckComArgStrNotEmptyOrNull(aName);
999
1000 AutoCaller autoCaller(this);
1001 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1002
1003 // prohibit setting a UUID only as the machine name, or else it can
1004 // never be found by findMachine()
1005 Guid test(aName);
1006
1007 if (test.isValid())
1008 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1009
1010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1011
1012 HRESULT rc = checkStateDependency(MutableStateDep);
1013 if (FAILED(rc)) return rc;
1014
1015 setModified(IsModified_MachineData);
1016 mUserData.backup();
1017 mUserData->s.strName = aName;
1018
1019 return S_OK;
1020}
1021
1022STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1023{
1024 CheckComArgOutPointerValid(aDescription);
1025
1026 AutoCaller autoCaller(this);
1027 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1028
1029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1030
1031 mUserData->s.strDescription.cloneTo(aDescription);
1032
1033 return S_OK;
1034}
1035
1036STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1037{
1038 AutoCaller autoCaller(this);
1039 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1040
1041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1042
1043 // this can be done in principle in any state as it doesn't affect the VM
1044 // significantly, but play safe by not messing around while complex
1045 // activities are going on
1046 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1047 if (FAILED(rc)) return rc;
1048
1049 setModified(IsModified_MachineData);
1050 mUserData.backup();
1051 mUserData->s.strDescription = aDescription;
1052
1053 return S_OK;
1054}
1055
1056STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1057{
1058 CheckComArgOutPointerValid(aId);
1059
1060 AutoLimitedCaller autoCaller(this);
1061 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1062
1063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1064
1065 mData->mUuid.toUtf16().cloneTo(aId);
1066
1067 return S_OK;
1068}
1069
1070STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1071{
1072 CheckComArgOutSafeArrayPointerValid(aGroups);
1073
1074 AutoCaller autoCaller(this);
1075 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1076
1077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1078 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1079 size_t i = 0;
1080 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1081 it != mUserData->s.llGroups.end();
1082 ++it, i++)
1083 {
1084 Bstr tmp = *it;
1085 tmp.cloneTo(&groups[i]);
1086 }
1087 groups.detachTo(ComSafeArrayOutArg(aGroups));
1088
1089 return S_OK;
1090}
1091
1092STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1093{
1094 AutoCaller autoCaller(this);
1095 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1096
1097 StringsList llGroups;
1098 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1099 if (FAILED(rc))
1100 return rc;
1101
1102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1103
1104 // changing machine groups is possible while the VM is offline
1105 rc = checkStateDependency(OfflineStateDep);
1106 if (FAILED(rc)) return rc;
1107
1108 setModified(IsModified_MachineData);
1109 mUserData.backup();
1110 mUserData->s.llGroups = llGroups;
1111
1112 return S_OK;
1113}
1114
1115STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1116{
1117 CheckComArgOutPointerValid(aOSTypeId);
1118
1119 AutoCaller autoCaller(this);
1120 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1121
1122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1123
1124 mUserData->s.strOsType.cloneTo(aOSTypeId);
1125
1126 return S_OK;
1127}
1128
1129STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1130{
1131 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1132
1133 AutoCaller autoCaller(this);
1134 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1135
1136 /* look up the object by Id to check it is valid */
1137 ComPtr<IGuestOSType> guestOSType;
1138 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1139 if (FAILED(rc)) return rc;
1140
1141 /* when setting, always use the "etalon" value for consistency -- lookup
1142 * by ID is case-insensitive and the input value may have different case */
1143 Bstr osTypeId;
1144 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1145 if (FAILED(rc)) return rc;
1146
1147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1148
1149 rc = checkStateDependency(MutableStateDep);
1150 if (FAILED(rc)) return rc;
1151
1152 setModified(IsModified_MachineData);
1153 mUserData.backup();
1154 mUserData->s.strOsType = osTypeId;
1155
1156 return S_OK;
1157}
1158
1159
1160STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1161{
1162 CheckComArgOutPointerValid(aFirmwareType);
1163
1164 AutoCaller autoCaller(this);
1165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1166
1167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1168
1169 *aFirmwareType = mHWData->mFirmwareType;
1170
1171 return S_OK;
1172}
1173
1174STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1175{
1176 AutoCaller autoCaller(this);
1177 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1178 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1179
1180 HRESULT rc = checkStateDependency(MutableStateDep);
1181 if (FAILED(rc)) return rc;
1182
1183 setModified(IsModified_MachineData);
1184 mHWData.backup();
1185 mHWData->mFirmwareType = aFirmwareType;
1186
1187 return S_OK;
1188}
1189
1190STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1191{
1192 CheckComArgOutPointerValid(aKeyboardHIDType);
1193
1194 AutoCaller autoCaller(this);
1195 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1196
1197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1198
1199 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1200
1201 return S_OK;
1202}
1203
1204STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1205{
1206 AutoCaller autoCaller(this);
1207 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1209
1210 HRESULT rc = checkStateDependency(MutableStateDep);
1211 if (FAILED(rc)) return rc;
1212
1213 setModified(IsModified_MachineData);
1214 mHWData.backup();
1215 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1216
1217 return S_OK;
1218}
1219
1220STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1221{
1222 CheckComArgOutPointerValid(aPointingHIDType);
1223
1224 AutoCaller autoCaller(this);
1225 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1226
1227 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1228
1229 *aPointingHIDType = mHWData->mPointingHIDType;
1230
1231 return S_OK;
1232}
1233
1234STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1235{
1236 AutoCaller autoCaller(this);
1237 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1239
1240 HRESULT rc = checkStateDependency(MutableStateDep);
1241 if (FAILED(rc)) return rc;
1242
1243 setModified(IsModified_MachineData);
1244 mHWData.backup();
1245 mHWData->mPointingHIDType = aPointingHIDType;
1246
1247 return S_OK;
1248}
1249
1250STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1251{
1252 CheckComArgOutPointerValid(aChipsetType);
1253
1254 AutoCaller autoCaller(this);
1255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1256
1257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1258
1259 *aChipsetType = mHWData->mChipsetType;
1260
1261 return S_OK;
1262}
1263
1264STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1265{
1266 AutoCaller autoCaller(this);
1267 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1269
1270 HRESULT rc = checkStateDependency(MutableStateDep);
1271 if (FAILED(rc)) return rc;
1272
1273 if (aChipsetType != mHWData->mChipsetType)
1274 {
1275 setModified(IsModified_MachineData);
1276 mHWData.backup();
1277 mHWData->mChipsetType = aChipsetType;
1278
1279 // Resize network adapter array, to be finalized on commit/rollback.
1280 // We must not throw away entries yet, otherwise settings are lost
1281 // without a way to roll back.
1282 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1283 size_t oldCount = mNetworkAdapters.size();
1284 if (newCount > oldCount)
1285 {
1286 mNetworkAdapters.resize(newCount);
1287 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1288 {
1289 unconst(mNetworkAdapters[slot]).createObject();
1290 mNetworkAdapters[slot]->init(this, slot);
1291 }
1292 }
1293 }
1294
1295 return S_OK;
1296}
1297
1298STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1299{
1300 CheckComArgOutPointerValid(aHWVersion);
1301
1302 AutoCaller autoCaller(this);
1303 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1304
1305 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1306
1307 mHWData->mHWVersion.cloneTo(aHWVersion);
1308
1309 return S_OK;
1310}
1311
1312STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1313{
1314 /* check known version */
1315 Utf8Str hwVersion = aHWVersion;
1316 if ( hwVersion.compare("1") != 0
1317 && hwVersion.compare("2") != 0)
1318 return setError(E_INVALIDARG,
1319 tr("Invalid hardware version: %ls\n"), aHWVersion);
1320
1321 AutoCaller autoCaller(this);
1322 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1323
1324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1325
1326 HRESULT rc = checkStateDependency(MutableStateDep);
1327 if (FAILED(rc)) return rc;
1328
1329 setModified(IsModified_MachineData);
1330 mHWData.backup();
1331 mHWData->mHWVersion = hwVersion;
1332
1333 return S_OK;
1334}
1335
1336STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1337{
1338 CheckComArgOutPointerValid(aUUID);
1339
1340 AutoCaller autoCaller(this);
1341 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1342
1343 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1344
1345 if (mHWData->mHardwareUUID.isValid())
1346 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1347 else
1348 mData->mUuid.toUtf16().cloneTo(aUUID);
1349
1350 return S_OK;
1351}
1352
1353STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1354{
1355 Guid hardwareUUID(aUUID);
1356 if (!hardwareUUID.isValid())
1357 return E_INVALIDARG;
1358
1359 AutoCaller autoCaller(this);
1360 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1361
1362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1363
1364 HRESULT rc = checkStateDependency(MutableStateDep);
1365 if (FAILED(rc)) return rc;
1366
1367 setModified(IsModified_MachineData);
1368 mHWData.backup();
1369 if (hardwareUUID == mData->mUuid)
1370 mHWData->mHardwareUUID.clear();
1371 else
1372 mHWData->mHardwareUUID = hardwareUUID;
1373
1374 return S_OK;
1375}
1376
1377STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1378{
1379 CheckComArgOutPointerValid(memorySize);
1380
1381 AutoCaller autoCaller(this);
1382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1383
1384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1385
1386 *memorySize = mHWData->mMemorySize;
1387
1388 return S_OK;
1389}
1390
1391STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1392{
1393 /* check RAM limits */
1394 if ( memorySize < MM_RAM_MIN_IN_MB
1395 || memorySize > MM_RAM_MAX_IN_MB
1396 )
1397 return setError(E_INVALIDARG,
1398 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1399 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1400
1401 AutoCaller autoCaller(this);
1402 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1403
1404 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1405
1406 HRESULT rc = checkStateDependency(MutableStateDep);
1407 if (FAILED(rc)) return rc;
1408
1409 setModified(IsModified_MachineData);
1410 mHWData.backup();
1411 mHWData->mMemorySize = memorySize;
1412
1413 return S_OK;
1414}
1415
1416STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1417{
1418 CheckComArgOutPointerValid(CPUCount);
1419
1420 AutoCaller autoCaller(this);
1421 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1422
1423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1424
1425 *CPUCount = mHWData->mCPUCount;
1426
1427 return S_OK;
1428}
1429
1430STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1431{
1432 /* check CPU limits */
1433 if ( CPUCount < SchemaDefs::MinCPUCount
1434 || CPUCount > SchemaDefs::MaxCPUCount
1435 )
1436 return setError(E_INVALIDARG,
1437 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1438 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1439
1440 AutoCaller autoCaller(this);
1441 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1442
1443 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1444
1445 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1446 if (mHWData->mCPUHotPlugEnabled)
1447 {
1448 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1449 {
1450 if (mHWData->mCPUAttached[idx])
1451 return setError(E_INVALIDARG,
1452 tr("There is still a CPU attached to socket %lu."
1453 "Detach the CPU before removing the socket"),
1454 CPUCount, idx+1);
1455 }
1456 }
1457
1458 HRESULT rc = checkStateDependency(MutableStateDep);
1459 if (FAILED(rc)) return rc;
1460
1461 setModified(IsModified_MachineData);
1462 mHWData.backup();
1463 mHWData->mCPUCount = CPUCount;
1464
1465 return S_OK;
1466}
1467
1468STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1469{
1470 CheckComArgOutPointerValid(aExecutionCap);
1471
1472 AutoCaller autoCaller(this);
1473 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1474
1475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1476
1477 *aExecutionCap = mHWData->mCpuExecutionCap;
1478
1479 return S_OK;
1480}
1481
1482STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1483{
1484 HRESULT rc = S_OK;
1485
1486 /* check throttle limits */
1487 if ( aExecutionCap < 1
1488 || aExecutionCap > 100
1489 )
1490 return setError(E_INVALIDARG,
1491 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1492 aExecutionCap, 1, 100);
1493
1494 AutoCaller autoCaller(this);
1495 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1496
1497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1498
1499 alock.release();
1500 rc = onCPUExecutionCapChange(aExecutionCap);
1501 alock.acquire();
1502 if (FAILED(rc)) return rc;
1503
1504 setModified(IsModified_MachineData);
1505 mHWData.backup();
1506 mHWData->mCpuExecutionCap = aExecutionCap;
1507
1508 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1509 if (Global::IsOnline(mData->mMachineState))
1510 saveSettings(NULL);
1511
1512 return S_OK;
1513}
1514
1515
1516STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1517{
1518 CheckComArgOutPointerValid(aEnabled);
1519
1520 AutoCaller autoCaller(this);
1521 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1522
1523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1524
1525 *aEnabled = mHWData->mCPUHotPlugEnabled;
1526
1527 return S_OK;
1528}
1529
1530STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1531{
1532 HRESULT rc = S_OK;
1533
1534 AutoCaller autoCaller(this);
1535 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1536
1537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1538
1539 rc = checkStateDependency(MutableStateDep);
1540 if (FAILED(rc)) return rc;
1541
1542 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1543 {
1544 if (aEnabled)
1545 {
1546 setModified(IsModified_MachineData);
1547 mHWData.backup();
1548
1549 /* Add the amount of CPUs currently attached */
1550 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1551 {
1552 mHWData->mCPUAttached[i] = true;
1553 }
1554 }
1555 else
1556 {
1557 /*
1558 * We can disable hotplug only if the amount of maximum CPUs is equal
1559 * to the amount of attached CPUs
1560 */
1561 unsigned cCpusAttached = 0;
1562 unsigned iHighestId = 0;
1563
1564 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1565 {
1566 if (mHWData->mCPUAttached[i])
1567 {
1568 cCpusAttached++;
1569 iHighestId = i;
1570 }
1571 }
1572
1573 if ( (cCpusAttached != mHWData->mCPUCount)
1574 || (iHighestId >= mHWData->mCPUCount))
1575 return setError(E_INVALIDARG,
1576 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1577
1578 setModified(IsModified_MachineData);
1579 mHWData.backup();
1580 }
1581 }
1582
1583 mHWData->mCPUHotPlugEnabled = aEnabled;
1584
1585 return rc;
1586}
1587
1588STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1589{
1590#ifdef VBOX_WITH_USB_CARDREADER
1591 CheckComArgOutPointerValid(aEnabled);
1592
1593 AutoCaller autoCaller(this);
1594 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1595
1596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1597
1598 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1599
1600 return S_OK;
1601#else
1602 NOREF(aEnabled);
1603 return E_NOTIMPL;
1604#endif
1605}
1606
1607STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1608{
1609#ifdef VBOX_WITH_USB_CARDREADER
1610 AutoCaller autoCaller(this);
1611 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1613
1614 HRESULT rc = checkStateDependency(MutableStateDep);
1615 if (FAILED(rc)) return rc;
1616
1617 setModified(IsModified_MachineData);
1618 mHWData.backup();
1619 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1620
1621 return S_OK;
1622#else
1623 NOREF(aEnabled);
1624 return E_NOTIMPL;
1625#endif
1626}
1627
1628STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled)
1629{
1630#ifdef VBOX_WITH_USB_VIDEO
1631 CheckComArgOutPointerValid(aEnabled);
1632
1633 AutoCaller autoCaller(this);
1634 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1635
1636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1637
1638 *aEnabled = mHWData->mEmulatedUSBWebcamEnabled;
1639
1640 return S_OK;
1641#else
1642 NOREF(aEnabled);
1643 return E_NOTIMPL;
1644#endif
1645}
1646
1647STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled)
1648{
1649#ifdef VBOX_WITH_USB_VIDEO
1650 AutoCaller autoCaller(this);
1651 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1653
1654 HRESULT rc = checkStateDependency(MutableStateDep);
1655 if (FAILED(rc)) return rc;
1656
1657 setModified(IsModified_MachineData);
1658 mHWData.backup();
1659 mHWData->mEmulatedUSBWebcamEnabled = aEnabled;
1660
1661 return S_OK;
1662#else
1663 NOREF(aEnabled);
1664 return E_NOTIMPL;
1665#endif
1666}
1667
1668STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1669{
1670 CheckComArgOutPointerValid(aEnabled);
1671
1672 AutoCaller autoCaller(this);
1673 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1675
1676 *aEnabled = mHWData->mHPETEnabled;
1677
1678 return S_OK;
1679}
1680
1681STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1682{
1683 HRESULT rc = S_OK;
1684
1685 AutoCaller autoCaller(this);
1686 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1687 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1688
1689 rc = checkStateDependency(MutableStateDep);
1690 if (FAILED(rc)) return rc;
1691
1692 setModified(IsModified_MachineData);
1693 mHWData.backup();
1694
1695 mHWData->mHPETEnabled = aEnabled;
1696
1697 return rc;
1698}
1699
1700STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1701{
1702 AutoCaller autoCaller(this);
1703 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1704
1705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1706
1707 *fEnabled = mHWData->mVideoCaptureEnabled;
1708 return S_OK;
1709}
1710
1711STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1712{
1713 HRESULT rc = S_OK;
1714
1715 AutoCaller autoCaller(this);
1716 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1717 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1718
1719 setModified(IsModified_MachineData);
1720 mHWData.backup();
1721 mHWData->mVideoCaptureEnabled = fEnabled;
1722
1723 alock.release();
1724 rc = onVideoCaptureChange();
1725 alock.acquire();
1726 if (FAILED(rc))
1727 {
1728 /*
1729 * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded.
1730 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1731 * determine if it should start or stop capturing. Therefore we need to manually
1732 * undo change.
1733 */
1734 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1735 return rc;
1736 }
1737
1738 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1739 if (Global::IsOnline(mData->mMachineState))
1740 saveSettings(NULL);
1741
1742 return rc;
1743}
1744
1745STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1746{
1747 CheckComArgOutSafeArrayPointerValid(aScreens);
1748
1749 AutoCaller autoCaller(this);
1750 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1751
1752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1753
1754 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1755 for (unsigned i = 0; i < screens.size(); i++)
1756 screens[i] = mHWData->maVideoCaptureScreens[i];
1757 screens.detachTo(ComSafeArrayOutArg(aScreens));
1758 return S_OK;
1759}
1760
1761STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1762{
1763 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1764 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1765 bool fChanged = false;
1766
1767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1768
1769 for (unsigned i = 0; i < screens.size(); i++)
1770 {
1771 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1772 {
1773 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1774 fChanged = true;
1775 }
1776 }
1777 if (fChanged)
1778 {
1779 alock.release();
1780 HRESULT rc = onVideoCaptureChange();
1781 alock.acquire();
1782 if (FAILED(rc)) return rc;
1783 setModified(IsModified_MachineData);
1784
1785 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1786 if (Global::IsOnline(mData->mMachineState))
1787 saveSettings(NULL);
1788 }
1789
1790 return S_OK;
1791}
1792
1793STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1794{
1795 AutoCaller autoCaller(this);
1796 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1797
1798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1799 if (mHWData->mVideoCaptureFile.isEmpty())
1800 {
1801 Utf8Str defaultFile;
1802 getDefaultVideoCaptureFile(defaultFile);
1803 defaultFile.cloneTo(apFile);
1804 }
1805 else
1806 mHWData->mVideoCaptureFile.cloneTo(apFile);
1807 return S_OK;
1808}
1809
1810STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1811{
1812 Utf8Str strFile(aFile);
1813 AutoCaller autoCaller(this);
1814 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1815
1816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1817
1818 if ( Global::IsOnline(mData->mMachineState)
1819 && mHWData->mVideoCaptureEnabled)
1820 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1821
1822 if (!RTPathStartsWithRoot(strFile.c_str()))
1823 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1824
1825 if (!strFile.isEmpty())
1826 {
1827 Utf8Str defaultFile;
1828 getDefaultVideoCaptureFile(defaultFile);
1829 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1830 strFile.setNull();
1831 }
1832
1833 setModified(IsModified_MachineData);
1834 mHWData.backup();
1835 mHWData->mVideoCaptureFile = strFile;
1836
1837 return S_OK;
1838}
1839
1840STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1841{
1842 AutoCaller autoCaller(this);
1843 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1844
1845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1846 *aHorzRes = mHWData->mVideoCaptureWidth;
1847 return S_OK;
1848}
1849
1850STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1851{
1852 AutoCaller autoCaller(this);
1853 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1854
1855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1856
1857 if ( Global::IsOnline(mData->mMachineState)
1858 && mHWData->mVideoCaptureEnabled)
1859 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1860
1861 setModified(IsModified_MachineData);
1862 mHWData.backup();
1863 mHWData->mVideoCaptureWidth = aHorzRes;
1864
1865 return S_OK;
1866}
1867
1868STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1869{
1870 AutoCaller autoCaller(this);
1871 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1872
1873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1874 *aVertRes = mHWData->mVideoCaptureHeight;
1875 return S_OK;
1876}
1877
1878STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1879{
1880 AutoCaller autoCaller(this);
1881 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1882
1883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1884
1885 if ( Global::IsOnline(mData->mMachineState)
1886 && mHWData->mVideoCaptureEnabled)
1887 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1888
1889 setModified(IsModified_MachineData);
1890 mHWData.backup();
1891 mHWData->mVideoCaptureHeight = aVertRes;
1892
1893 return S_OK;
1894}
1895
1896STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1897{
1898 AutoCaller autoCaller(this);
1899 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1900
1901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1902 *aRate = mHWData->mVideoCaptureRate;
1903 return S_OK;
1904}
1905
1906STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1907{
1908 AutoCaller autoCaller(this);
1909 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1910
1911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1912
1913 if ( Global::IsOnline(mData->mMachineState)
1914 && mHWData->mVideoCaptureEnabled)
1915 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1916
1917 setModified(IsModified_MachineData);
1918 mHWData.backup();
1919 mHWData->mVideoCaptureRate = aRate;
1920
1921 return S_OK;
1922}
1923
1924STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1925{
1926 AutoCaller autoCaller(this);
1927 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1928
1929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1930 *aFPS = mHWData->mVideoCaptureFPS;
1931 return S_OK;
1932}
1933
1934STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1935{
1936 AutoCaller autoCaller(this);
1937 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1938
1939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1940
1941 if ( Global::IsOnline(mData->mMachineState)
1942 && mHWData->mVideoCaptureEnabled)
1943 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1944
1945 setModified(IsModified_MachineData);
1946 mHWData.backup();
1947 mHWData->mVideoCaptureFPS = aFPS;
1948
1949 return S_OK;
1950}
1951
1952STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1953{
1954 CheckComArgOutPointerValid(aGraphicsControllerType);
1955
1956 AutoCaller autoCaller(this);
1957 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1958
1959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1960
1961 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1962
1963 return S_OK;
1964}
1965
1966STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1967{
1968 switch (aGraphicsControllerType)
1969 {
1970 case GraphicsControllerType_Null:
1971 case GraphicsControllerType_VBoxVGA:
1972 break;
1973 default:
1974 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1975 }
1976
1977 AutoCaller autoCaller(this);
1978 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1979
1980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1981
1982 HRESULT rc = checkStateDependency(MutableStateDep);
1983 if (FAILED(rc)) return rc;
1984
1985 setModified(IsModified_MachineData);
1986 mHWData.backup();
1987 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1988
1989 return S_OK;
1990}
1991
1992STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1993{
1994 CheckComArgOutPointerValid(memorySize);
1995
1996 AutoCaller autoCaller(this);
1997 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1998
1999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2000
2001 *memorySize = mHWData->mVRAMSize;
2002
2003 return S_OK;
2004}
2005
2006STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
2007{
2008 /* check VRAM limits */
2009 if (memorySize < SchemaDefs::MinGuestVRAM ||
2010 memorySize > SchemaDefs::MaxGuestVRAM)
2011 return setError(E_INVALIDARG,
2012 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2013 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2014
2015 AutoCaller autoCaller(this);
2016 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2017
2018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2019
2020 HRESULT rc = checkStateDependency(MutableStateDep);
2021 if (FAILED(rc)) return rc;
2022
2023 setModified(IsModified_MachineData);
2024 mHWData.backup();
2025 mHWData->mVRAMSize = memorySize;
2026
2027 return S_OK;
2028}
2029
2030/** @todo this method should not be public */
2031STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
2032{
2033 CheckComArgOutPointerValid(memoryBalloonSize);
2034
2035 AutoCaller autoCaller(this);
2036 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2037
2038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2039
2040 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2041
2042 return S_OK;
2043}
2044
2045/**
2046 * Set the memory balloon size.
2047 *
2048 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2049 * we have to make sure that we never call IGuest from here.
2050 */
2051STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2052{
2053 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2054#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2055 /* check limits */
2056 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2057 return setError(E_INVALIDARG,
2058 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2059 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2060
2061 AutoCaller autoCaller(this);
2062 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2063
2064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2065
2066 setModified(IsModified_MachineData);
2067 mHWData.backup();
2068 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2069
2070 return S_OK;
2071#else
2072 NOREF(memoryBalloonSize);
2073 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2074#endif
2075}
2076
2077STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2078{
2079 CheckComArgOutPointerValid(aEnabled);
2080
2081 AutoCaller autoCaller(this);
2082 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2083
2084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2085
2086 *aEnabled = mHWData->mPageFusionEnabled;
2087 return S_OK;
2088}
2089
2090STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2091{
2092#ifdef VBOX_WITH_PAGE_SHARING
2093 AutoCaller autoCaller(this);
2094 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2095
2096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2097
2098 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2099 setModified(IsModified_MachineData);
2100 mHWData.backup();
2101 mHWData->mPageFusionEnabled = aEnabled;
2102 return S_OK;
2103#else
2104 NOREF(aEnabled);
2105 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2106#endif
2107}
2108
2109STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2110{
2111 CheckComArgOutPointerValid(aEnabled);
2112
2113 AutoCaller autoCaller(this);
2114 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2115
2116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2117
2118 *aEnabled = mHWData->mAccelerate3DEnabled;
2119
2120 return S_OK;
2121}
2122
2123STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2124{
2125 AutoCaller autoCaller(this);
2126 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2127
2128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2129
2130 HRESULT rc = checkStateDependency(MutableStateDep);
2131 if (FAILED(rc)) return rc;
2132
2133 /** @todo check validity! */
2134
2135 setModified(IsModified_MachineData);
2136 mHWData.backup();
2137 mHWData->mAccelerate3DEnabled = enable;
2138
2139 return S_OK;
2140}
2141
2142
2143STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2144{
2145 CheckComArgOutPointerValid(aEnabled);
2146
2147 AutoCaller autoCaller(this);
2148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2149
2150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2151
2152 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2153
2154 return S_OK;
2155}
2156
2157STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2158{
2159 AutoCaller autoCaller(this);
2160 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2161
2162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2163
2164 HRESULT rc = checkStateDependency(MutableStateDep);
2165 if (FAILED(rc)) return rc;
2166
2167 /** @todo check validity! */
2168
2169 setModified(IsModified_MachineData);
2170 mHWData.backup();
2171 mHWData->mAccelerate2DVideoEnabled = enable;
2172
2173 return S_OK;
2174}
2175
2176STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2177{
2178 CheckComArgOutPointerValid(monitorCount);
2179
2180 AutoCaller autoCaller(this);
2181 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2182
2183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2184
2185 *monitorCount = mHWData->mMonitorCount;
2186
2187 return S_OK;
2188}
2189
2190STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2191{
2192 /* make sure monitor count is a sensible number */
2193 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2194 return setError(E_INVALIDARG,
2195 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2196 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2197
2198 AutoCaller autoCaller(this);
2199 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2200
2201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2202
2203 HRESULT rc = checkStateDependency(MutableStateDep);
2204 if (FAILED(rc)) return rc;
2205
2206 setModified(IsModified_MachineData);
2207 mHWData.backup();
2208 mHWData->mMonitorCount = monitorCount;
2209
2210 return S_OK;
2211}
2212
2213STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2214{
2215 CheckComArgOutPointerValid(biosSettings);
2216
2217 AutoCaller autoCaller(this);
2218 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2219
2220 /* mBIOSSettings is constant during life time, no need to lock */
2221 mBIOSSettings.queryInterfaceTo(biosSettings);
2222
2223 return S_OK;
2224}
2225
2226STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2227{
2228 CheckComArgOutPointerValid(aVal);
2229
2230 AutoCaller autoCaller(this);
2231 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2232
2233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2234
2235 switch (property)
2236 {
2237 case CPUPropertyType_PAE:
2238 *aVal = mHWData->mPAEEnabled;
2239 break;
2240
2241 case CPUPropertyType_Synthetic:
2242 *aVal = mHWData->mSyntheticCpu;
2243 break;
2244
2245 case CPUPropertyType_LongMode:
2246 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2247 *aVal = TRUE;
2248 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2249 *aVal = FALSE;
2250#if HC_ARCH_BITS == 64
2251 else
2252 *aVal = TRUE;
2253#else
2254 else
2255 {
2256 *aVal = FALSE;
2257
2258 ComPtr<IGuestOSType> ptrGuestOSType;
2259 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2260 if (SUCCEEDED(hrc2))
2261 {
2262 BOOL fIs64Bit = FALSE;
2263 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2264 if (SUCCEEDED(hrc2) && fIs64Bit)
2265 {
2266 ComObjPtr<Host> ptrHost = mParent->host();
2267 alock.release();
2268
2269 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2270 if (FAILED(hrc2))
2271 *aVal = FALSE;
2272 }
2273 }
2274 }
2275#endif
2276 break;
2277
2278 default:
2279 return E_INVALIDARG;
2280 }
2281 return S_OK;
2282}
2283
2284STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2285{
2286 AutoCaller autoCaller(this);
2287 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2288
2289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2290
2291 HRESULT rc = checkStateDependency(MutableStateDep);
2292 if (FAILED(rc)) return rc;
2293
2294 switch (property)
2295 {
2296 case CPUPropertyType_PAE:
2297 setModified(IsModified_MachineData);
2298 mHWData.backup();
2299 mHWData->mPAEEnabled = !!aVal;
2300 break;
2301
2302 case CPUPropertyType_Synthetic:
2303 setModified(IsModified_MachineData);
2304 mHWData.backup();
2305 mHWData->mSyntheticCpu = !!aVal;
2306 break;
2307
2308 case CPUPropertyType_LongMode:
2309 setModified(IsModified_MachineData);
2310 mHWData.backup();
2311 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2312 break;
2313
2314 default:
2315 return E_INVALIDARG;
2316 }
2317 return S_OK;
2318}
2319
2320STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2321{
2322 CheckComArgOutPointerValid(aValEax);
2323 CheckComArgOutPointerValid(aValEbx);
2324 CheckComArgOutPointerValid(aValEcx);
2325 CheckComArgOutPointerValid(aValEdx);
2326
2327 AutoCaller autoCaller(this);
2328 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2329
2330 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2331
2332 switch(aId)
2333 {
2334 case 0x0:
2335 case 0x1:
2336 case 0x2:
2337 case 0x3:
2338 case 0x4:
2339 case 0x5:
2340 case 0x6:
2341 case 0x7:
2342 case 0x8:
2343 case 0x9:
2344 case 0xA:
2345 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2346 return E_INVALIDARG;
2347
2348 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2349 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2350 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2351 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2352 break;
2353
2354 case 0x80000000:
2355 case 0x80000001:
2356 case 0x80000002:
2357 case 0x80000003:
2358 case 0x80000004:
2359 case 0x80000005:
2360 case 0x80000006:
2361 case 0x80000007:
2362 case 0x80000008:
2363 case 0x80000009:
2364 case 0x8000000A:
2365 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2366 return E_INVALIDARG;
2367
2368 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2369 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2370 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2371 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2372 break;
2373
2374 default:
2375 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2376 }
2377 return S_OK;
2378}
2379
2380STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2381{
2382 AutoCaller autoCaller(this);
2383 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2384
2385 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2386
2387 HRESULT rc = checkStateDependency(MutableStateDep);
2388 if (FAILED(rc)) return rc;
2389
2390 switch(aId)
2391 {
2392 case 0x0:
2393 case 0x1:
2394 case 0x2:
2395 case 0x3:
2396 case 0x4:
2397 case 0x5:
2398 case 0x6:
2399 case 0x7:
2400 case 0x8:
2401 case 0x9:
2402 case 0xA:
2403 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2404 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2405 setModified(IsModified_MachineData);
2406 mHWData.backup();
2407 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2408 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2409 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2410 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2411 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2412 break;
2413
2414 case 0x80000000:
2415 case 0x80000001:
2416 case 0x80000002:
2417 case 0x80000003:
2418 case 0x80000004:
2419 case 0x80000005:
2420 case 0x80000006:
2421 case 0x80000007:
2422 case 0x80000008:
2423 case 0x80000009:
2424 case 0x8000000A:
2425 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2426 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2427 setModified(IsModified_MachineData);
2428 mHWData.backup();
2429 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2430 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2431 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2432 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2433 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2434 break;
2435
2436 default:
2437 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2438 }
2439 return S_OK;
2440}
2441
2442STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2443{
2444 AutoCaller autoCaller(this);
2445 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2446
2447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2448
2449 HRESULT rc = checkStateDependency(MutableStateDep);
2450 if (FAILED(rc)) return rc;
2451
2452 switch(aId)
2453 {
2454 case 0x0:
2455 case 0x1:
2456 case 0x2:
2457 case 0x3:
2458 case 0x4:
2459 case 0x5:
2460 case 0x6:
2461 case 0x7:
2462 case 0x8:
2463 case 0x9:
2464 case 0xA:
2465 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2466 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2467 setModified(IsModified_MachineData);
2468 mHWData.backup();
2469 /* Invalidate leaf. */
2470 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2471 break;
2472
2473 case 0x80000000:
2474 case 0x80000001:
2475 case 0x80000002:
2476 case 0x80000003:
2477 case 0x80000004:
2478 case 0x80000005:
2479 case 0x80000006:
2480 case 0x80000007:
2481 case 0x80000008:
2482 case 0x80000009:
2483 case 0x8000000A:
2484 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2485 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2486 setModified(IsModified_MachineData);
2487 mHWData.backup();
2488 /* Invalidate leaf. */
2489 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2490 break;
2491
2492 default:
2493 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2494 }
2495 return S_OK;
2496}
2497
2498STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2499{
2500 AutoCaller autoCaller(this);
2501 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2502
2503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2504
2505 HRESULT rc = checkStateDependency(MutableStateDep);
2506 if (FAILED(rc)) return rc;
2507
2508 setModified(IsModified_MachineData);
2509 mHWData.backup();
2510
2511 /* Invalidate all standard leafs. */
2512 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2513 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2514
2515 /* Invalidate all extended leafs. */
2516 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2517 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2518
2519 return S_OK;
2520}
2521
2522STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2523{
2524 CheckComArgOutPointerValid(aVal);
2525
2526 AutoCaller autoCaller(this);
2527 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2528
2529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2530
2531 switch(property)
2532 {
2533 case HWVirtExPropertyType_Enabled:
2534 *aVal = mHWData->mHWVirtExEnabled;
2535 break;
2536
2537 case HWVirtExPropertyType_VPID:
2538 *aVal = mHWData->mHWVirtExVPIDEnabled;
2539 break;
2540
2541 case HWVirtExPropertyType_NestedPaging:
2542 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2543 break;
2544
2545 case HWVirtExPropertyType_UnrestrictedExecution:
2546 *aVal = mHWData->mHWVirtExUXEnabled;
2547 break;
2548
2549 case HWVirtExPropertyType_LargePages:
2550 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2551#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2552 *aVal = FALSE;
2553#endif
2554 break;
2555
2556 case HWVirtExPropertyType_Force:
2557 *aVal = mHWData->mHWVirtExForceEnabled;
2558 break;
2559
2560 default:
2561 return E_INVALIDARG;
2562 }
2563 return S_OK;
2564}
2565
2566STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2567{
2568 AutoCaller autoCaller(this);
2569 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2570
2571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2572
2573 HRESULT rc = checkStateDependency(MutableStateDep);
2574 if (FAILED(rc)) return rc;
2575
2576 switch(property)
2577 {
2578 case HWVirtExPropertyType_Enabled:
2579 setModified(IsModified_MachineData);
2580 mHWData.backup();
2581 mHWData->mHWVirtExEnabled = !!aVal;
2582 break;
2583
2584 case HWVirtExPropertyType_VPID:
2585 setModified(IsModified_MachineData);
2586 mHWData.backup();
2587 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2588 break;
2589
2590 case HWVirtExPropertyType_NestedPaging:
2591 setModified(IsModified_MachineData);
2592 mHWData.backup();
2593 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2594 break;
2595
2596 case HWVirtExPropertyType_UnrestrictedExecution:
2597 setModified(IsModified_MachineData);
2598 mHWData.backup();
2599 mHWData->mHWVirtExUXEnabled = !!aVal;
2600 break;
2601
2602 case HWVirtExPropertyType_LargePages:
2603 setModified(IsModified_MachineData);
2604 mHWData.backup();
2605 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2606 break;
2607
2608 case HWVirtExPropertyType_Force:
2609 setModified(IsModified_MachineData);
2610 mHWData.backup();
2611 mHWData->mHWVirtExForceEnabled = !!aVal;
2612 break;
2613
2614 default:
2615 return E_INVALIDARG;
2616 }
2617
2618 return S_OK;
2619}
2620
2621STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2622{
2623 CheckComArgOutPointerValid(aSnapshotFolder);
2624
2625 AutoCaller autoCaller(this);
2626 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2627
2628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2629
2630 Utf8Str strFullSnapshotFolder;
2631 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2632 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2633
2634 return S_OK;
2635}
2636
2637STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2638{
2639 /* @todo (r=dmik):
2640 * 1. Allow to change the name of the snapshot folder containing snapshots
2641 * 2. Rename the folder on disk instead of just changing the property
2642 * value (to be smart and not to leave garbage). Note that it cannot be
2643 * done here because the change may be rolled back. Thus, the right
2644 * place is #saveSettings().
2645 */
2646
2647 AutoCaller autoCaller(this);
2648 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2649
2650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2651
2652 HRESULT rc = checkStateDependency(MutableStateDep);
2653 if (FAILED(rc)) return rc;
2654
2655 if (!mData->mCurrentSnapshot.isNull())
2656 return setError(E_FAIL,
2657 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2658
2659 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2660
2661 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2662 if (strSnapshotFolder.isEmpty())
2663 strSnapshotFolder = "Snapshots";
2664 int vrc = calculateFullPath(strSnapshotFolder,
2665 strSnapshotFolder);
2666 if (RT_FAILURE(vrc))
2667 return setError(E_FAIL,
2668 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2669 aSnapshotFolder, vrc);
2670
2671 setModified(IsModified_MachineData);
2672 mUserData.backup();
2673
2674 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2675
2676 return S_OK;
2677}
2678
2679STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2680{
2681 CheckComArgOutSafeArrayPointerValid(aAttachments);
2682
2683 AutoCaller autoCaller(this);
2684 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2685
2686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2687
2688 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2689 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2690
2691 return S_OK;
2692}
2693
2694STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2695{
2696 CheckComArgOutPointerValid(vrdeServer);
2697
2698 AutoCaller autoCaller(this);
2699 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2700
2701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2702
2703 Assert(!!mVRDEServer);
2704 mVRDEServer.queryInterfaceTo(vrdeServer);
2705
2706 return S_OK;
2707}
2708
2709STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2710{
2711 CheckComArgOutPointerValid(audioAdapter);
2712
2713 AutoCaller autoCaller(this);
2714 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2715
2716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2717
2718 mAudioAdapter.queryInterfaceTo(audioAdapter);
2719 return S_OK;
2720}
2721
2722STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers))
2723{
2724#ifdef VBOX_WITH_VUSB
2725 CheckComArgOutPointerValid(aUSBControllers);
2726
2727 AutoCaller autoCaller(this);
2728 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2729
2730 clearError();
2731 MultiResult rc(S_OK);
2732
2733# ifdef VBOX_WITH_USB
2734 rc = mParent->host()->checkUSBProxyService();
2735 if (FAILED(rc)) return rc;
2736# endif
2737
2738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2739
2740 SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data());
2741 ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers));
2742 return S_OK;
2743#else
2744 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2745 * extended error info to indicate that USB is simply not available
2746 * (w/o treating it as a failure), for example, as in OSE */
2747 NOREF(aUSBControllers);
2748 ReturnComNotImplemented();
2749#endif /* VBOX_WITH_VUSB */
2750}
2751
2752STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters)
2753{
2754#ifdef VBOX_WITH_VUSB
2755 CheckComArgOutPointerValid(aUSBDeviceFilters);
2756
2757 AutoCaller autoCaller(this);
2758 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2759
2760 clearError();
2761 MultiResult rc(S_OK);
2762
2763# ifdef VBOX_WITH_USB
2764 rc = mParent->host()->checkUSBProxyService();
2765 if (FAILED(rc)) return rc;
2766# endif
2767
2768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2769
2770 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters);
2771#else
2772 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2773 * extended error info to indicate that USB is simply not available
2774 * (w/o treating it as a failure), for example, as in OSE */
2775 NOREF(aUSBDeviceFilters);
2776 ReturnComNotImplemented();
2777#endif /* VBOX_WITH_VUSB */
2778}
2779
2780STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2781{
2782 CheckComArgOutPointerValid(aFilePath);
2783
2784 AutoLimitedCaller autoCaller(this);
2785 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2786
2787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2788
2789 mData->m_strConfigFileFull.cloneTo(aFilePath);
2790 return S_OK;
2791}
2792
2793STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2794{
2795 CheckComArgOutPointerValid(aModified);
2796
2797 AutoCaller autoCaller(this);
2798 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2799
2800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2801
2802 HRESULT rc = checkStateDependency(MutableStateDep);
2803 if (FAILED(rc)) return rc;
2804
2805 if (!mData->pMachineConfigFile->fileExists())
2806 // this is a new machine, and no config file exists yet:
2807 *aModified = TRUE;
2808 else
2809 *aModified = (mData->flModifications != 0);
2810
2811 return S_OK;
2812}
2813
2814STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2815{
2816 CheckComArgOutPointerValid(aSessionState);
2817
2818 AutoCaller autoCaller(this);
2819 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2820
2821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2822
2823 *aSessionState = mData->mSession.mState;
2824
2825 return S_OK;
2826}
2827
2828STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2829{
2830 CheckComArgOutPointerValid(aSessionType);
2831
2832 AutoCaller autoCaller(this);
2833 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2834
2835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2836
2837 mData->mSession.mType.cloneTo(aSessionType);
2838
2839 return S_OK;
2840}
2841
2842STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2843{
2844 CheckComArgOutPointerValid(aSessionPID);
2845
2846 AutoCaller autoCaller(this);
2847 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2848
2849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2850
2851 *aSessionPID = mData->mSession.mPID;
2852
2853 return S_OK;
2854}
2855
2856STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2857{
2858 CheckComArgOutPointerValid(machineState);
2859
2860 AutoCaller autoCaller(this);
2861 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2862
2863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2864
2865 *machineState = mData->mMachineState;
2866
2867 return S_OK;
2868}
2869
2870STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2871{
2872 CheckComArgOutPointerValid(aLastStateChange);
2873
2874 AutoCaller autoCaller(this);
2875 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2876
2877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2878
2879 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2880
2881 return S_OK;
2882}
2883
2884STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2885{
2886 CheckComArgOutPointerValid(aStateFilePath);
2887
2888 AutoCaller autoCaller(this);
2889 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2890
2891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2892
2893 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2894
2895 return S_OK;
2896}
2897
2898STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2899{
2900 CheckComArgOutPointerValid(aLogFolder);
2901
2902 AutoCaller autoCaller(this);
2903 AssertComRCReturnRC(autoCaller.rc());
2904
2905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2906
2907 Utf8Str logFolder;
2908 getLogFolder(logFolder);
2909 logFolder.cloneTo(aLogFolder);
2910
2911 return S_OK;
2912}
2913
2914STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2915{
2916 CheckComArgOutPointerValid(aCurrentSnapshot);
2917
2918 AutoCaller autoCaller(this);
2919 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2920
2921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2922
2923 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2924
2925 return S_OK;
2926}
2927
2928STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2929{
2930 CheckComArgOutPointerValid(aSnapshotCount);
2931
2932 AutoCaller autoCaller(this);
2933 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2934
2935 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2936
2937 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2938 ? 0
2939 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2940
2941 return S_OK;
2942}
2943
2944STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2945{
2946 CheckComArgOutPointerValid(aCurrentStateModified);
2947
2948 AutoCaller autoCaller(this);
2949 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2950
2951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2952
2953 /* Note: for machines with no snapshots, we always return FALSE
2954 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2955 * reasons :) */
2956
2957 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2958 ? FALSE
2959 : mData->mCurrentStateModified;
2960
2961 return S_OK;
2962}
2963
2964STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2965{
2966 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2967
2968 AutoCaller autoCaller(this);
2969 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2970
2971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2972
2973 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2974 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2975
2976 return S_OK;
2977}
2978
2979STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2980{
2981 CheckComArgOutPointerValid(aClipboardMode);
2982
2983 AutoCaller autoCaller(this);
2984 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2985
2986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2987
2988 *aClipboardMode = mHWData->mClipboardMode;
2989
2990 return S_OK;
2991}
2992
2993STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2994{
2995 HRESULT rc = S_OK;
2996
2997 AutoCaller autoCaller(this);
2998 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2999
3000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3001
3002 alock.release();
3003 rc = onClipboardModeChange(aClipboardMode);
3004 alock.acquire();
3005 if (FAILED(rc)) return rc;
3006
3007 setModified(IsModified_MachineData);
3008 mHWData.backup();
3009 mHWData->mClipboardMode = aClipboardMode;
3010
3011 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3012 if (Global::IsOnline(mData->mMachineState))
3013 saveSettings(NULL);
3014
3015 return S_OK;
3016}
3017
3018STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
3019{
3020 CheckComArgOutPointerValid(aDragAndDropMode);
3021
3022 AutoCaller autoCaller(this);
3023 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3024
3025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3026
3027 *aDragAndDropMode = mHWData->mDragAndDropMode;
3028
3029 return S_OK;
3030}
3031
3032STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3033{
3034 HRESULT rc = S_OK;
3035
3036 AutoCaller autoCaller(this);
3037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3038
3039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3040
3041 alock.release();
3042 rc = onDragAndDropModeChange(aDragAndDropMode);
3043 alock.acquire();
3044 if (FAILED(rc)) return rc;
3045
3046 setModified(IsModified_MachineData);
3047 mHWData.backup();
3048 mHWData->mDragAndDropMode = aDragAndDropMode;
3049
3050 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3051 if (Global::IsOnline(mData->mMachineState))
3052 saveSettings(NULL);
3053
3054 return S_OK;
3055}
3056
3057STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3058{
3059 CheckComArgOutPointerValid(aPatterns);
3060
3061 AutoCaller autoCaller(this);
3062 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3063
3064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3065
3066 try
3067 {
3068 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3069 }
3070 catch (...)
3071 {
3072 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3073 }
3074
3075 return S_OK;
3076}
3077
3078STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3079{
3080 AutoCaller autoCaller(this);
3081 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3082
3083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3084
3085 HRESULT rc = checkStateDependency(MutableStateDep);
3086 if (FAILED(rc)) return rc;
3087
3088 setModified(IsModified_MachineData);
3089 mHWData.backup();
3090 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3091 return rc;
3092}
3093
3094STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3095{
3096 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3097
3098 AutoCaller autoCaller(this);
3099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3100
3101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3102
3103 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3104 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3105
3106 return S_OK;
3107}
3108
3109STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3110{
3111 CheckComArgOutPointerValid(aEnabled);
3112
3113 AutoCaller autoCaller(this);
3114 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3115
3116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3117
3118 *aEnabled = mUserData->s.fTeleporterEnabled;
3119
3120 return S_OK;
3121}
3122
3123STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3124{
3125 AutoCaller autoCaller(this);
3126 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3127
3128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3129
3130 /* Only allow it to be set to true when PoweredOff or Aborted.
3131 (Clearing it is always permitted.) */
3132 if ( aEnabled
3133 && mData->mRegistered
3134 && ( !isSessionMachine()
3135 || ( mData->mMachineState != MachineState_PoweredOff
3136 && mData->mMachineState != MachineState_Teleported
3137 && mData->mMachineState != MachineState_Aborted
3138 )
3139 )
3140 )
3141 return setError(VBOX_E_INVALID_VM_STATE,
3142 tr("The machine is not powered off (state is %s)"),
3143 Global::stringifyMachineState(mData->mMachineState));
3144
3145 setModified(IsModified_MachineData);
3146 mUserData.backup();
3147 mUserData->s.fTeleporterEnabled = !!aEnabled;
3148
3149 return S_OK;
3150}
3151
3152STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3153{
3154 CheckComArgOutPointerValid(aPort);
3155
3156 AutoCaller autoCaller(this);
3157 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3158
3159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3160
3161 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3162
3163 return S_OK;
3164}
3165
3166STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3167{
3168 if (aPort >= _64K)
3169 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3170
3171 AutoCaller autoCaller(this);
3172 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3173
3174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3175
3176 HRESULT rc = checkStateDependency(MutableStateDep);
3177 if (FAILED(rc)) return rc;
3178
3179 setModified(IsModified_MachineData);
3180 mUserData.backup();
3181 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3182
3183 return S_OK;
3184}
3185
3186STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3187{
3188 CheckComArgOutPointerValid(aAddress);
3189
3190 AutoCaller autoCaller(this);
3191 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3192
3193 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3194
3195 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3196
3197 return S_OK;
3198}
3199
3200STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3201{
3202 AutoCaller autoCaller(this);
3203 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3204
3205 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3206
3207 HRESULT rc = checkStateDependency(MutableStateDep);
3208 if (FAILED(rc)) return rc;
3209
3210 setModified(IsModified_MachineData);
3211 mUserData.backup();
3212 mUserData->s.strTeleporterAddress = aAddress;
3213
3214 return S_OK;
3215}
3216
3217STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3218{
3219 CheckComArgOutPointerValid(aPassword);
3220
3221 AutoCaller autoCaller(this);
3222 HRESULT hrc = autoCaller.rc();
3223 if (SUCCEEDED(hrc))
3224 {
3225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3226 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3227 }
3228
3229 return hrc;
3230}
3231
3232STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3233{
3234 /*
3235 * Hash the password first.
3236 */
3237 Utf8Str strPassword(aPassword);
3238 if (!strPassword.isEmpty())
3239 {
3240 if (VBoxIsPasswordHashed(&strPassword))
3241 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3242 VBoxHashPassword(&strPassword);
3243 }
3244
3245 /*
3246 * Do the update.
3247 */
3248 AutoCaller autoCaller(this);
3249 HRESULT hrc = autoCaller.rc();
3250 if (SUCCEEDED(hrc))
3251 {
3252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3253 hrc = checkStateDependency(MutableStateDep);
3254 if (SUCCEEDED(hrc))
3255 {
3256 setModified(IsModified_MachineData);
3257 mUserData.backup();
3258 mUserData->s.strTeleporterPassword = strPassword;
3259 }
3260 }
3261
3262 return hrc;
3263}
3264
3265STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3266{
3267 CheckComArgOutPointerValid(aState);
3268
3269 AutoCaller autoCaller(this);
3270 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3271
3272 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3273
3274 *aState = mUserData->s.enmFaultToleranceState;
3275 return S_OK;
3276}
3277
3278STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3279{
3280 AutoCaller autoCaller(this);
3281 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3282
3283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3284
3285 /* @todo deal with running state change. */
3286 HRESULT rc = checkStateDependency(MutableStateDep);
3287 if (FAILED(rc)) return rc;
3288
3289 setModified(IsModified_MachineData);
3290 mUserData.backup();
3291 mUserData->s.enmFaultToleranceState = aState;
3292 return S_OK;
3293}
3294
3295STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3296{
3297 CheckComArgOutPointerValid(aAddress);
3298
3299 AutoCaller autoCaller(this);
3300 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3301
3302 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3303
3304 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3305 return S_OK;
3306}
3307
3308STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3309{
3310 AutoCaller autoCaller(this);
3311 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3312
3313 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3314
3315 /* @todo deal with running state change. */
3316 HRESULT rc = checkStateDependency(MutableStateDep);
3317 if (FAILED(rc)) return rc;
3318
3319 setModified(IsModified_MachineData);
3320 mUserData.backup();
3321 mUserData->s.strFaultToleranceAddress = aAddress;
3322 return S_OK;
3323}
3324
3325STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3326{
3327 CheckComArgOutPointerValid(aPort);
3328
3329 AutoCaller autoCaller(this);
3330 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3331
3332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3333
3334 *aPort = mUserData->s.uFaultTolerancePort;
3335 return S_OK;
3336}
3337
3338STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3339{
3340 AutoCaller autoCaller(this);
3341 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3342
3343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3344
3345 /* @todo deal with running state change. */
3346 HRESULT rc = checkStateDependency(MutableStateDep);
3347 if (FAILED(rc)) return rc;
3348
3349 setModified(IsModified_MachineData);
3350 mUserData.backup();
3351 mUserData->s.uFaultTolerancePort = aPort;
3352 return S_OK;
3353}
3354
3355STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3356{
3357 CheckComArgOutPointerValid(aPassword);
3358
3359 AutoCaller autoCaller(this);
3360 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3361
3362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3363
3364 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3365
3366 return S_OK;
3367}
3368
3369STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3370{
3371 AutoCaller autoCaller(this);
3372 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3373
3374 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3375
3376 /* @todo deal with running state change. */
3377 HRESULT rc = checkStateDependency(MutableStateDep);
3378 if (FAILED(rc)) return rc;
3379
3380 setModified(IsModified_MachineData);
3381 mUserData.backup();
3382 mUserData->s.strFaultTolerancePassword = aPassword;
3383
3384 return S_OK;
3385}
3386
3387STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3388{
3389 CheckComArgOutPointerValid(aInterval);
3390
3391 AutoCaller autoCaller(this);
3392 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3393
3394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3395
3396 *aInterval = mUserData->s.uFaultToleranceInterval;
3397 return S_OK;
3398}
3399
3400STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3401{
3402 AutoCaller autoCaller(this);
3403 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3404
3405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3406
3407 /* @todo deal with running state change. */
3408 HRESULT rc = checkStateDependency(MutableStateDep);
3409 if (FAILED(rc)) return rc;
3410
3411 setModified(IsModified_MachineData);
3412 mUserData.backup();
3413 mUserData->s.uFaultToleranceInterval = aInterval;
3414 return S_OK;
3415}
3416
3417STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3418{
3419 CheckComArgOutPointerValid(aEnabled);
3420
3421 AutoCaller autoCaller(this);
3422 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3423
3424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3425
3426 *aEnabled = mUserData->s.fRTCUseUTC;
3427
3428 return S_OK;
3429}
3430
3431STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3432{
3433 AutoCaller autoCaller(this);
3434 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3435
3436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3437
3438 /* Only allow it to be set to true when PoweredOff or Aborted.
3439 (Clearing it is always permitted.) */
3440 if ( aEnabled
3441 && mData->mRegistered
3442 && ( !isSessionMachine()
3443 || ( mData->mMachineState != MachineState_PoweredOff
3444 && mData->mMachineState != MachineState_Teleported
3445 && mData->mMachineState != MachineState_Aborted
3446 )
3447 )
3448 )
3449 return setError(VBOX_E_INVALID_VM_STATE,
3450 tr("The machine is not powered off (state is %s)"),
3451 Global::stringifyMachineState(mData->mMachineState));
3452
3453 setModified(IsModified_MachineData);
3454 mUserData.backup();
3455 mUserData->s.fRTCUseUTC = !!aEnabled;
3456
3457 return S_OK;
3458}
3459
3460STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3461{
3462 CheckComArgOutPointerValid(aEnabled);
3463
3464 AutoCaller autoCaller(this);
3465 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3466
3467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3468
3469 *aEnabled = mHWData->mIOCacheEnabled;
3470
3471 return S_OK;
3472}
3473
3474STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3475{
3476 AutoCaller autoCaller(this);
3477 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3478
3479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3480
3481 HRESULT rc = checkStateDependency(MutableStateDep);
3482 if (FAILED(rc)) return rc;
3483
3484 setModified(IsModified_MachineData);
3485 mHWData.backup();
3486 mHWData->mIOCacheEnabled = aEnabled;
3487
3488 return S_OK;
3489}
3490
3491STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3492{
3493 CheckComArgOutPointerValid(aIOCacheSize);
3494
3495 AutoCaller autoCaller(this);
3496 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3497
3498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3499
3500 *aIOCacheSize = mHWData->mIOCacheSize;
3501
3502 return S_OK;
3503}
3504
3505STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3506{
3507 AutoCaller autoCaller(this);
3508 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3509
3510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3511
3512 HRESULT rc = checkStateDependency(MutableStateDep);
3513 if (FAILED(rc)) return rc;
3514
3515 setModified(IsModified_MachineData);
3516 mHWData.backup();
3517 mHWData->mIOCacheSize = aIOCacheSize;
3518
3519 return S_OK;
3520}
3521
3522
3523/**
3524 * @note Locks objects!
3525 */
3526STDMETHODIMP Machine::LockMachine(ISession *aSession,
3527 LockType_T lockType)
3528{
3529 CheckComArgNotNull(aSession);
3530
3531 AutoCaller autoCaller(this);
3532 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3533
3534 /* check the session state */
3535 SessionState_T state;
3536 HRESULT rc = aSession->COMGETTER(State)(&state);
3537 if (FAILED(rc)) return rc;
3538
3539 if (state != SessionState_Unlocked)
3540 return setError(VBOX_E_INVALID_OBJECT_STATE,
3541 tr("The given session is busy"));
3542
3543 // get the client's IInternalSessionControl interface
3544 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3545 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3546 E_INVALIDARG);
3547
3548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3549
3550 if (!mData->mRegistered)
3551 return setError(E_UNEXPECTED,
3552 tr("The machine '%s' is not registered"),
3553 mUserData->s.strName.c_str());
3554
3555 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3556
3557 SessionState_T oldState = mData->mSession.mState;
3558 /* Hack: in case the session is closing and there is a progress object
3559 * which allows waiting for the session to be closed, take the opportunity
3560 * and do a limited wait (max. 1 second). This helps a lot when the system
3561 * is busy and thus session closing can take a little while. */
3562 if ( mData->mSession.mState == SessionState_Unlocking
3563 && mData->mSession.mProgress)
3564 {
3565 alock.release();
3566 mData->mSession.mProgress->WaitForCompletion(1000);
3567 alock.acquire();
3568 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3569 }
3570
3571 // try again now
3572 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3573 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3574 )
3575 {
3576 // OK, share the session... we are now dealing with three processes:
3577 // 1) VBoxSVC (where this code runs);
3578 // 2) process C: the caller's client process (who wants a shared session);
3579 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3580
3581 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3582 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3583 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3584 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3585 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3586
3587 /*
3588 * Release the lock before calling the client process. It's safe here
3589 * since the only thing to do after we get the lock again is to add
3590 * the remote control to the list (which doesn't directly influence
3591 * anything).
3592 */
3593 alock.release();
3594
3595 // get the console of the session holding the write lock (this is a remote call)
3596 ComPtr<IConsole> pConsoleW;
3597 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3598 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3599 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3600 if (FAILED(rc))
3601 // the failure may occur w/o any error info (from RPC), so provide one
3602 return setError(VBOX_E_VM_ERROR,
3603 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3604
3605 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3606
3607 // share the session machine and W's console with the caller's session
3608 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3609 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3610 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3611
3612 if (FAILED(rc))
3613 // the failure may occur w/o any error info (from RPC), so provide one
3614 return setError(VBOX_E_VM_ERROR,
3615 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3616 alock.acquire();
3617
3618 // need to revalidate the state after acquiring the lock again
3619 if (mData->mSession.mState != SessionState_Locked)
3620 {
3621 pSessionControl->Uninitialize();
3622 return setError(VBOX_E_INVALID_SESSION_STATE,
3623 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3624 mUserData->s.strName.c_str());
3625 }
3626
3627 // add the caller's session to the list
3628 mData->mSession.mRemoteControls.push_back(pSessionControl);
3629 }
3630 else if ( mData->mSession.mState == SessionState_Locked
3631 || mData->mSession.mState == SessionState_Unlocking
3632 )
3633 {
3634 // sharing not permitted, or machine still unlocking:
3635 return setError(VBOX_E_INVALID_OBJECT_STATE,
3636 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3637 mUserData->s.strName.c_str());
3638 }
3639 else
3640 {
3641 // machine is not locked: then write-lock the machine (create the session machine)
3642
3643 // must not be busy
3644 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3645
3646 // get the caller's session PID
3647 RTPROCESS pid = NIL_RTPROCESS;
3648 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3649 pSessionControl->GetPID((ULONG*)&pid);
3650 Assert(pid != NIL_RTPROCESS);
3651
3652 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3653
3654 if (fLaunchingVMProcess)
3655 {
3656 // this machine is awaiting for a spawning session to be opened:
3657 // then the calling process must be the one that got started by
3658 // LaunchVMProcess()
3659
3660 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3661 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3662
3663 if (mData->mSession.mPID != pid)
3664 return setError(E_ACCESSDENIED,
3665 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3666 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3667 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3668 }
3669
3670 // create the mutable SessionMachine from the current machine
3671 ComObjPtr<SessionMachine> sessionMachine;
3672 sessionMachine.createObject();
3673 rc = sessionMachine->init(this);
3674 AssertComRC(rc);
3675
3676 /* NOTE: doing return from this function after this point but
3677 * before the end is forbidden since it may call SessionMachine::uninit()
3678 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3679 * lock while still holding the Machine lock in alock so that a deadlock
3680 * is possible due to the wrong lock order. */
3681
3682 if (SUCCEEDED(rc))
3683 {
3684 /*
3685 * Set the session state to Spawning to protect against subsequent
3686 * attempts to open a session and to unregister the machine after
3687 * we release the lock.
3688 */
3689 SessionState_T origState = mData->mSession.mState;
3690 mData->mSession.mState = SessionState_Spawning;
3691
3692 /* Get the client token ID to be passed to the client process */
3693 Utf8Str strTokenId;
3694 sessionMachine->getTokenId(strTokenId);
3695 Assert(!strTokenId.isEmpty());
3696
3697 /*
3698 * Release the lock before calling the client process -- it will call
3699 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3700 * because the state is Spawning, so that LaunchVMProcess() and
3701 * LockMachine() calls will fail. This method, called before we
3702 * acquire the lock again, will fail because of the wrong PID.
3703 *
3704 * Note that mData->mSession.mRemoteControls accessed outside
3705 * the lock may not be modified when state is Spawning, so it's safe.
3706 */
3707 alock.release();
3708
3709 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3710 rc = pSessionControl->AssignMachine(sessionMachine, lockType, Bstr(strTokenId).raw());
3711 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3712
3713 /* The failure may occur w/o any error info (from RPC), so provide one */
3714 if (FAILED(rc))
3715 setError(VBOX_E_VM_ERROR,
3716 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3717
3718 if ( SUCCEEDED(rc)
3719 && fLaunchingVMProcess
3720 )
3721 {
3722 /* complete the remote session initialization */
3723
3724 /* get the console from the direct session */
3725 ComPtr<IConsole> console;
3726 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3727 ComAssertComRC(rc);
3728
3729 if (SUCCEEDED(rc) && !console)
3730 {
3731 ComAssert(!!console);
3732 rc = E_FAIL;
3733 }
3734
3735 /* assign machine & console to the remote session */
3736 if (SUCCEEDED(rc))
3737 {
3738 /*
3739 * after LaunchVMProcess(), the first and the only
3740 * entry in remoteControls is that remote session
3741 */
3742 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3743 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3744 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3745
3746 /* The failure may occur w/o any error info (from RPC), so provide one */
3747 if (FAILED(rc))
3748 setError(VBOX_E_VM_ERROR,
3749 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3750 }
3751
3752 if (FAILED(rc))
3753 pSessionControl->Uninitialize();
3754 }
3755
3756 /* acquire the lock again */
3757 alock.acquire();
3758
3759 /* Restore the session state */
3760 mData->mSession.mState = origState;
3761 }
3762
3763 // finalize spawning anyway (this is why we don't return on errors above)
3764 if (fLaunchingVMProcess)
3765 {
3766 /* Note that the progress object is finalized later */
3767 /** @todo Consider checking mData->mSession.mProgress for cancellation
3768 * around here. */
3769
3770 /* We don't reset mSession.mPID here because it is necessary for
3771 * SessionMachine::uninit() to reap the child process later. */
3772
3773 if (FAILED(rc))
3774 {
3775 /* Close the remote session, remove the remote control from the list
3776 * and reset session state to Closed (@note keep the code in sync
3777 * with the relevant part in checkForSpawnFailure()). */
3778
3779 Assert(mData->mSession.mRemoteControls.size() == 1);
3780 if (mData->mSession.mRemoteControls.size() == 1)
3781 {
3782 ErrorInfoKeeper eik;
3783 mData->mSession.mRemoteControls.front()->Uninitialize();
3784 }
3785
3786 mData->mSession.mRemoteControls.clear();
3787 mData->mSession.mState = SessionState_Unlocked;
3788 }
3789 }
3790 else
3791 {
3792 /* memorize PID of the directly opened session */
3793 if (SUCCEEDED(rc))
3794 mData->mSession.mPID = pid;
3795 }
3796
3797 if (SUCCEEDED(rc))
3798 {
3799 /* memorize the direct session control and cache IUnknown for it */
3800 mData->mSession.mDirectControl = pSessionControl;
3801 mData->mSession.mState = SessionState_Locked;
3802 /* associate the SessionMachine with this Machine */
3803 mData->mSession.mMachine = sessionMachine;
3804
3805 /* request an IUnknown pointer early from the remote party for later
3806 * identity checks (it will be internally cached within mDirectControl
3807 * at least on XPCOM) */
3808 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3809 NOREF(unk);
3810 }
3811
3812 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3813 * would break the lock order */
3814 alock.release();
3815
3816 /* uninitialize the created session machine on failure */
3817 if (FAILED(rc))
3818 sessionMachine->uninit();
3819
3820 }
3821
3822 if (SUCCEEDED(rc))
3823 {
3824 /*
3825 * tell the client watcher thread to update the set of
3826 * machines that have open sessions
3827 */
3828 mParent->updateClientWatcher();
3829
3830 if (oldState != SessionState_Locked)
3831 /* fire an event */
3832 mParent->onSessionStateChange(getId(), SessionState_Locked);
3833 }
3834
3835 return rc;
3836}
3837
3838/**
3839 * @note Locks objects!
3840 */
3841STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3842 IN_BSTR aFrontend,
3843 IN_BSTR aEnvironment,
3844 IProgress **aProgress)
3845{
3846 CheckComArgStr(aFrontend);
3847 Utf8Str strFrontend(aFrontend);
3848 Utf8Str strEnvironment(aEnvironment);
3849 /* "emergencystop" doesn't need the session, so skip the checks/interface
3850 * retrieval. This code doesn't quite fit in here, but introducing a
3851 * special API method would be even more effort, and would require explicit
3852 * support by every API client. It's better to hide the feature a bit. */
3853 if (strFrontend != "emergencystop")
3854 CheckComArgNotNull(aSession);
3855 CheckComArgOutPointerValid(aProgress);
3856
3857 AutoCaller autoCaller(this);
3858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3859
3860 HRESULT rc = S_OK;
3861 if (strFrontend.isEmpty())
3862 {
3863 Bstr bstrFrontend;
3864 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3865 if (FAILED(rc))
3866 return rc;
3867 strFrontend = bstrFrontend;
3868 if (strFrontend.isEmpty())
3869 {
3870 ComPtr<ISystemProperties> systemProperties;
3871 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3872 if (FAILED(rc))
3873 return rc;
3874 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3875 if (FAILED(rc))
3876 return rc;
3877 strFrontend = bstrFrontend;
3878 }
3879 /* paranoia - emergencystop is not a valid default */
3880 if (strFrontend == "emergencystop")
3881 strFrontend = Utf8Str::Empty;
3882 }
3883 /* default frontend: Qt GUI */
3884 if (strFrontend.isEmpty())
3885 strFrontend = "GUI/Qt";
3886
3887 if (strFrontend != "emergencystop")
3888 {
3889 /* check the session state */
3890 SessionState_T state;
3891 rc = aSession->COMGETTER(State)(&state);
3892 if (FAILED(rc))
3893 return rc;
3894
3895 if (state != SessionState_Unlocked)
3896 return setError(VBOX_E_INVALID_OBJECT_STATE,
3897 tr("The given session is busy"));
3898
3899 /* get the IInternalSessionControl interface */
3900 ComPtr<IInternalSessionControl> control(aSession);
3901 ComAssertMsgRet(!control.isNull(),
3902 ("No IInternalSessionControl interface"),
3903 E_INVALIDARG);
3904
3905 /* get the teleporter enable state for the progress object init. */
3906 BOOL fTeleporterEnabled;
3907 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3908 if (FAILED(rc))
3909 return rc;
3910
3911 /* create a progress object */
3912 ComObjPtr<ProgressProxy> progress;
3913 progress.createObject();
3914 rc = progress->init(mParent,
3915 static_cast<IMachine*>(this),
3916 Bstr(tr("Starting VM")).raw(),
3917 TRUE /* aCancelable */,
3918 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3919 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3920 2 /* uFirstOperationWeight */,
3921 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3922
3923 if (SUCCEEDED(rc))
3924 {
3925 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3926 if (SUCCEEDED(rc))
3927 {
3928 progress.queryInterfaceTo(aProgress);
3929
3930 /* signal the client watcher thread */
3931 mParent->updateClientWatcher();
3932
3933 /* fire an event */
3934 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3935 }
3936 }
3937 }
3938 else
3939 {
3940 /* no progress object - either instant success or failure */
3941 *aProgress = NULL;
3942
3943 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3944
3945 if (mData->mSession.mState != SessionState_Locked)
3946 return setError(VBOX_E_INVALID_OBJECT_STATE,
3947 tr("The machine '%s' is not locked by a session"),
3948 mUserData->s.strName.c_str());
3949
3950 /* must have a VM process associated - do not kill normal API clients
3951 * with an open session */
3952 if (!Global::IsOnline(mData->mMachineState))
3953 return setError(VBOX_E_INVALID_OBJECT_STATE,
3954 tr("The machine '%s' does not have a VM process"),
3955 mUserData->s.strName.c_str());
3956
3957 /* forcibly terminate the VM process */
3958 if (mData->mSession.mPID != NIL_RTPROCESS)
3959 RTProcTerminate(mData->mSession.mPID);
3960
3961 /* signal the client watcher thread, as most likely the client has
3962 * been terminated */
3963 mParent->updateClientWatcher();
3964 }
3965
3966 return rc;
3967}
3968
3969STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3970{
3971 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3972 return setError(E_INVALIDARG,
3973 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3974 aPosition, SchemaDefs::MaxBootPosition);
3975
3976 if (aDevice == DeviceType_USB)
3977 return setError(E_NOTIMPL,
3978 tr("Booting from USB device is currently not supported"));
3979
3980 AutoCaller autoCaller(this);
3981 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3982
3983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3984
3985 HRESULT rc = checkStateDependency(MutableStateDep);
3986 if (FAILED(rc)) return rc;
3987
3988 setModified(IsModified_MachineData);
3989 mHWData.backup();
3990 mHWData->mBootOrder[aPosition - 1] = aDevice;
3991
3992 return S_OK;
3993}
3994
3995STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3996{
3997 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3998 return setError(E_INVALIDARG,
3999 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
4000 aPosition, SchemaDefs::MaxBootPosition);
4001
4002 AutoCaller autoCaller(this);
4003 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4004
4005 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4006
4007 *aDevice = mHWData->mBootOrder[aPosition - 1];
4008
4009 return S_OK;
4010}
4011
4012STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
4013 LONG aControllerPort,
4014 LONG aDevice,
4015 DeviceType_T aType,
4016 IMedium *aMedium)
4017{
4018 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4019 aControllerName, aControllerPort, aDevice, aType, aMedium));
4020
4021 CheckComArgStrNotEmptyOrNull(aControllerName);
4022
4023 AutoCaller autoCaller(this);
4024 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4025
4026 // request the host lock first, since might be calling Host methods for getting host drives;
4027 // next, protect the media tree all the while we're in here, as well as our member variables
4028 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
4029 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4030
4031 HRESULT rc = checkStateDependency(MutableStateDep);
4032 if (FAILED(rc)) return rc;
4033
4034 /// @todo NEWMEDIA implicit machine registration
4035 if (!mData->mRegistered)
4036 return setError(VBOX_E_INVALID_OBJECT_STATE,
4037 tr("Cannot attach storage devices to an unregistered machine"));
4038
4039 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4040
4041 /* Check for an existing controller. */
4042 ComObjPtr<StorageController> ctl;
4043 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4044 if (FAILED(rc)) return rc;
4045
4046 StorageControllerType_T ctrlType;
4047 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4048 if (FAILED(rc))
4049 return setError(E_FAIL,
4050 tr("Could not get type of controller '%ls'"),
4051 aControllerName);
4052
4053 bool fSilent = false;
4054 Utf8Str strReconfig;
4055
4056 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4057 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4058 if ( mData->mMachineState == MachineState_Paused
4059 && strReconfig == "1")
4060 fSilent = true;
4061
4062 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4063 bool fHotplug = false;
4064 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4065 fHotplug = true;
4066
4067 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4068 return setError(VBOX_E_INVALID_VM_STATE,
4069 tr("Controller '%ls' does not support hotplugging"),
4070 aControllerName);
4071
4072 // check that the port and device are not out of range
4073 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
4074 if (FAILED(rc)) return rc;
4075
4076 /* check if the device slot is already busy */
4077 MediumAttachment *pAttachTemp;
4078 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4079 aControllerName,
4080 aControllerPort,
4081 aDevice)))
4082 {
4083 Medium *pMedium = pAttachTemp->getMedium();
4084 if (pMedium)
4085 {
4086 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4087 return setError(VBOX_E_OBJECT_IN_USE,
4088 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4089 pMedium->getLocationFull().c_str(),
4090 aControllerPort,
4091 aDevice,
4092 aControllerName);
4093 }
4094 else
4095 return setError(VBOX_E_OBJECT_IN_USE,
4096 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4097 aControllerPort, aDevice, aControllerName);
4098 }
4099
4100 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4101 if (aMedium && medium.isNull())
4102 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4103
4104 AutoCaller mediumCaller(medium);
4105 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4106
4107 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4108
4109 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4110 && !medium.isNull()
4111 )
4112 return setError(VBOX_E_OBJECT_IN_USE,
4113 tr("Medium '%s' is already attached to this virtual machine"),
4114 medium->getLocationFull().c_str());
4115
4116 if (!medium.isNull())
4117 {
4118 MediumType_T mtype = medium->getType();
4119 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4120 // For DVDs it's not written to the config file, so needs no global config
4121 // version bump. For floppies it's a new attribute "type", which is ignored
4122 // by older VirtualBox version, so needs no global config version bump either.
4123 // For hard disks this type is not accepted.
4124 if (mtype == MediumType_MultiAttach)
4125 {
4126 // This type is new with VirtualBox 4.0 and therefore requires settings
4127 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4128 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4129 // two reasons: The medium type is a property of the media registry tree, which
4130 // can reside in the global config file (for pre-4.0 media); we would therefore
4131 // possibly need to bump the global config version. We don't want to do that though
4132 // because that might make downgrading to pre-4.0 impossible.
4133 // As a result, we can only use these two new types if the medium is NOT in the
4134 // global registry:
4135 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4136 if ( medium->isInRegistry(uuidGlobalRegistry)
4137 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4138 )
4139 return setError(VBOX_E_INVALID_OBJECT_STATE,
4140 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4141 "to machines that were created with VirtualBox 4.0 or later"),
4142 medium->getLocationFull().c_str());
4143 }
4144 }
4145
4146 bool fIndirect = false;
4147 if (!medium.isNull())
4148 fIndirect = medium->isReadOnly();
4149 bool associate = true;
4150
4151 do
4152 {
4153 if ( aType == DeviceType_HardDisk
4154 && mMediaData.isBackedUp())
4155 {
4156 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4157
4158 /* check if the medium was attached to the VM before we started
4159 * changing attachments in which case the attachment just needs to
4160 * be restored */
4161 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4162 {
4163 AssertReturn(!fIndirect, E_FAIL);
4164
4165 /* see if it's the same bus/channel/device */
4166 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4167 {
4168 /* the simplest case: restore the whole attachment
4169 * and return, nothing else to do */
4170 mMediaData->mAttachments.push_back(pAttachTemp);
4171
4172 /* Reattach the medium to the VM. */
4173 if (fHotplug || fSilent)
4174 {
4175 mediumLock.release();
4176 treeLock.release();
4177 alock.release();
4178
4179 MediumLockList *pMediumLockList(new MediumLockList());
4180
4181 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4182 true /* fMediumLockWrite */,
4183 NULL,
4184 *pMediumLockList);
4185 alock.acquire();
4186 if (FAILED(rc))
4187 delete pMediumLockList;
4188 else
4189 {
4190 mData->mSession.mLockedMedia.Unlock();
4191 alock.release();
4192 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4193 mData->mSession.mLockedMedia.Lock();
4194 alock.acquire();
4195 }
4196 alock.release();
4197
4198 if (SUCCEEDED(rc))
4199 {
4200 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4201 /* Remove lock list in case of error. */
4202 if (FAILED(rc))
4203 {
4204 mData->mSession.mLockedMedia.Unlock();
4205 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4206 mData->mSession.mLockedMedia.Lock();
4207 }
4208 }
4209 }
4210
4211 return S_OK;
4212 }
4213
4214 /* bus/channel/device differ; we need a new attachment object,
4215 * but don't try to associate it again */
4216 associate = false;
4217 break;
4218 }
4219 }
4220
4221 /* go further only if the attachment is to be indirect */
4222 if (!fIndirect)
4223 break;
4224
4225 /* perform the so called smart attachment logic for indirect
4226 * attachments. Note that smart attachment is only applicable to base
4227 * hard disks. */
4228
4229 if (medium->getParent().isNull())
4230 {
4231 /* first, investigate the backup copy of the current hard disk
4232 * attachments to make it possible to re-attach existing diffs to
4233 * another device slot w/o losing their contents */
4234 if (mMediaData.isBackedUp())
4235 {
4236 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4237
4238 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4239 uint32_t foundLevel = 0;
4240
4241 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4242 it != oldAtts.end();
4243 ++it)
4244 {
4245 uint32_t level = 0;
4246 MediumAttachment *pAttach = *it;
4247 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4248 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4249 if (pMedium.isNull())
4250 continue;
4251
4252 if (pMedium->getBase(&level) == medium)
4253 {
4254 /* skip the hard disk if its currently attached (we
4255 * cannot attach the same hard disk twice) */
4256 if (findAttachment(mMediaData->mAttachments,
4257 pMedium))
4258 continue;
4259
4260 /* matched device, channel and bus (i.e. attached to the
4261 * same place) will win and immediately stop the search;
4262 * otherwise the attachment that has the youngest
4263 * descendant of medium will be used
4264 */
4265 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4266 {
4267 /* the simplest case: restore the whole attachment
4268 * and return, nothing else to do */
4269 mMediaData->mAttachments.push_back(*it);
4270
4271 /* Reattach the medium to the VM. */
4272 if (fHotplug || fSilent)
4273 {
4274 mediumLock.release();
4275 treeLock.release();
4276 alock.release();
4277
4278 MediumLockList *pMediumLockList(new MediumLockList());
4279
4280 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4281 true /* fMediumLockWrite */,
4282 NULL,
4283 *pMediumLockList);
4284 alock.acquire();
4285 if (FAILED(rc))
4286 delete pMediumLockList;
4287 else
4288 {
4289 mData->mSession.mLockedMedia.Unlock();
4290 alock.release();
4291 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4292 mData->mSession.mLockedMedia.Lock();
4293 alock.acquire();
4294 }
4295 alock.release();
4296
4297 if (SUCCEEDED(rc))
4298 {
4299 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4300 /* Remove lock list in case of error. */
4301 if (FAILED(rc))
4302 {
4303 mData->mSession.mLockedMedia.Unlock();
4304 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4305 mData->mSession.mLockedMedia.Lock();
4306 }
4307 }
4308 }
4309
4310 return S_OK;
4311 }
4312 else if ( foundIt == oldAtts.end()
4313 || level > foundLevel /* prefer younger */
4314 )
4315 {
4316 foundIt = it;
4317 foundLevel = level;
4318 }
4319 }
4320 }
4321
4322 if (foundIt != oldAtts.end())
4323 {
4324 /* use the previously attached hard disk */
4325 medium = (*foundIt)->getMedium();
4326 mediumCaller.attach(medium);
4327 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4328 mediumLock.attach(medium);
4329 /* not implicit, doesn't require association with this VM */
4330 fIndirect = false;
4331 associate = false;
4332 /* go right to the MediumAttachment creation */
4333 break;
4334 }
4335 }
4336
4337 /* must give up the medium lock and medium tree lock as below we
4338 * go over snapshots, which needs a lock with higher lock order. */
4339 mediumLock.release();
4340 treeLock.release();
4341
4342 /* then, search through snapshots for the best diff in the given
4343 * hard disk's chain to base the new diff on */
4344
4345 ComObjPtr<Medium> base;
4346 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4347 while (snap)
4348 {
4349 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4350
4351 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4352
4353 MediumAttachment *pAttachFound = NULL;
4354 uint32_t foundLevel = 0;
4355
4356 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4357 it != snapAtts.end();
4358 ++it)
4359 {
4360 MediumAttachment *pAttach = *it;
4361 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4362 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4363 if (pMedium.isNull())
4364 continue;
4365
4366 uint32_t level = 0;
4367 if (pMedium->getBase(&level) == medium)
4368 {
4369 /* matched device, channel and bus (i.e. attached to the
4370 * same place) will win and immediately stop the search;
4371 * otherwise the attachment that has the youngest
4372 * descendant of medium will be used
4373 */
4374 if ( pAttach->getDevice() == aDevice
4375 && pAttach->getPort() == aControllerPort
4376 && pAttach->getControllerName() == aControllerName
4377 )
4378 {
4379 pAttachFound = pAttach;
4380 break;
4381 }
4382 else if ( !pAttachFound
4383 || level > foundLevel /* prefer younger */
4384 )
4385 {
4386 pAttachFound = pAttach;
4387 foundLevel = level;
4388 }
4389 }
4390 }
4391
4392 if (pAttachFound)
4393 {
4394 base = pAttachFound->getMedium();
4395 break;
4396 }
4397
4398 snap = snap->getParent();
4399 }
4400
4401 /* re-lock medium tree and the medium, as we need it below */
4402 treeLock.acquire();
4403 mediumLock.acquire();
4404
4405 /* found a suitable diff, use it as a base */
4406 if (!base.isNull())
4407 {
4408 medium = base;
4409 mediumCaller.attach(medium);
4410 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4411 mediumLock.attach(medium);
4412 }
4413 }
4414
4415 Utf8Str strFullSnapshotFolder;
4416 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4417
4418 ComObjPtr<Medium> diff;
4419 diff.createObject();
4420 // store this diff in the same registry as the parent
4421 Guid uuidRegistryParent;
4422 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4423 {
4424 // parent image has no registry: this can happen if we're attaching a new immutable
4425 // image that has not yet been attached (medium then points to the base and we're
4426 // creating the diff image for the immutable, and the parent is not yet registered);
4427 // put the parent in the machine registry then
4428 mediumLock.release();
4429 treeLock.release();
4430 alock.release();
4431 addMediumToRegistry(medium);
4432 alock.acquire();
4433 treeLock.acquire();
4434 mediumLock.acquire();
4435 medium->getFirstRegistryMachineId(uuidRegistryParent);
4436 }
4437 rc = diff->init(mParent,
4438 medium->getPreferredDiffFormat(),
4439 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4440 uuidRegistryParent);
4441 if (FAILED(rc)) return rc;
4442
4443 /* Apply the normal locking logic to the entire chain. */
4444 MediumLockList *pMediumLockList(new MediumLockList());
4445 mediumLock.release();
4446 treeLock.release();
4447 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4448 true /* fMediumLockWrite */,
4449 medium,
4450 *pMediumLockList);
4451 treeLock.acquire();
4452 mediumLock.acquire();
4453 if (SUCCEEDED(rc))
4454 {
4455 mediumLock.release();
4456 treeLock.release();
4457 rc = pMediumLockList->Lock();
4458 treeLock.acquire();
4459 mediumLock.acquire();
4460 if (FAILED(rc))
4461 setError(rc,
4462 tr("Could not lock medium when creating diff '%s'"),
4463 diff->getLocationFull().c_str());
4464 else
4465 {
4466 /* will release the lock before the potentially lengthy
4467 * operation, so protect with the special state */
4468 MachineState_T oldState = mData->mMachineState;
4469 setMachineState(MachineState_SettingUp);
4470
4471 mediumLock.release();
4472 treeLock.release();
4473 alock.release();
4474
4475 rc = medium->createDiffStorage(diff,
4476 MediumVariant_Standard,
4477 pMediumLockList,
4478 NULL /* aProgress */,
4479 true /* aWait */);
4480
4481 alock.acquire();
4482 treeLock.acquire();
4483 mediumLock.acquire();
4484
4485 setMachineState(oldState);
4486 }
4487 }
4488
4489 /* Unlock the media and free the associated memory. */
4490 delete pMediumLockList;
4491
4492 if (FAILED(rc)) return rc;
4493
4494 /* use the created diff for the actual attachment */
4495 medium = diff;
4496 mediumCaller.attach(medium);
4497 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4498 mediumLock.attach(medium);
4499 }
4500 while (0);
4501
4502 ComObjPtr<MediumAttachment> attachment;
4503 attachment.createObject();
4504 rc = attachment->init(this,
4505 medium,
4506 aControllerName,
4507 aControllerPort,
4508 aDevice,
4509 aType,
4510 fIndirect,
4511 false /* fPassthrough */,
4512 false /* fTempEject */,
4513 false /* fNonRotational */,
4514 false /* fDiscard */,
4515 Utf8Str::Empty);
4516 if (FAILED(rc)) return rc;
4517
4518 if (associate && !medium.isNull())
4519 {
4520 // as the last step, associate the medium to the VM
4521 rc = medium->addBackReference(mData->mUuid);
4522 // here we can fail because of Deleting, or being in process of creating a Diff
4523 if (FAILED(rc)) return rc;
4524
4525 mediumLock.release();
4526 treeLock.release();
4527 alock.release();
4528 addMediumToRegistry(medium);
4529 alock.acquire();
4530 treeLock.acquire();
4531 mediumLock.acquire();
4532 }
4533
4534 /* success: finally remember the attachment */
4535 setModified(IsModified_Storage);
4536 mMediaData.backup();
4537 mMediaData->mAttachments.push_back(attachment);
4538
4539 mediumLock.release();
4540 treeLock.release();
4541 alock.release();
4542
4543 if (fHotplug || fSilent)
4544 {
4545 if (!medium.isNull())
4546 {
4547 MediumLockList *pMediumLockList(new MediumLockList());
4548
4549 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4550 true /* fMediumLockWrite */,
4551 NULL,
4552 *pMediumLockList);
4553 alock.acquire();
4554 if (FAILED(rc))
4555 delete pMediumLockList;
4556 else
4557 {
4558 mData->mSession.mLockedMedia.Unlock();
4559 alock.release();
4560 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4561 mData->mSession.mLockedMedia.Lock();
4562 alock.acquire();
4563 }
4564 alock.release();
4565 }
4566
4567 if (SUCCEEDED(rc))
4568 {
4569 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4570 /* Remove lock list in case of error. */
4571 if (FAILED(rc))
4572 {
4573 mData->mSession.mLockedMedia.Unlock();
4574 mData->mSession.mLockedMedia.Remove(attachment);
4575 mData->mSession.mLockedMedia.Lock();
4576 }
4577 }
4578 }
4579
4580 mParent->saveModifiedRegistries();
4581
4582 return rc;
4583}
4584
4585STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4586 LONG aDevice)
4587{
4588 CheckComArgStrNotEmptyOrNull(aControllerName);
4589
4590 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4591 aControllerName, aControllerPort, aDevice));
4592
4593 AutoCaller autoCaller(this);
4594 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4595
4596 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4597
4598 HRESULT rc = checkStateDependency(MutableStateDep);
4599 if (FAILED(rc)) return rc;
4600
4601 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4602
4603 /* Check for an existing controller. */
4604 ComObjPtr<StorageController> ctl;
4605 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4606 if (FAILED(rc)) return rc;
4607
4608 StorageControllerType_T ctrlType;
4609 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4610 if (FAILED(rc))
4611 return setError(E_FAIL,
4612 tr("Could not get type of controller '%ls'"),
4613 aControllerName);
4614
4615 bool fSilent = false;
4616 Utf8Str strReconfig;
4617
4618 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4619 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4620 if ( mData->mMachineState == MachineState_Paused
4621 && strReconfig == "1")
4622 fSilent = true;
4623
4624 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4625 bool fHotplug = false;
4626 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4627 fHotplug = true;
4628
4629 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4630 return setError(VBOX_E_INVALID_VM_STATE,
4631 tr("Controller '%ls' does not support hotplugging"),
4632 aControllerName);
4633
4634 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4635 aControllerName,
4636 aControllerPort,
4637 aDevice);
4638 if (!pAttach)
4639 return setError(VBOX_E_OBJECT_NOT_FOUND,
4640 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4641 aDevice, aControllerPort, aControllerName);
4642
4643 /*
4644 * The VM has to detach the device before we delete any implicit diffs.
4645 * If this fails we can roll back without loosing data.
4646 */
4647 if (fHotplug || fSilent)
4648 {
4649 alock.release();
4650 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4651 alock.acquire();
4652 }
4653 if (FAILED(rc)) return rc;
4654
4655 /* If we are here everything went well and we can delete the implicit now. */
4656 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4657
4658 alock.release();
4659
4660 mParent->saveModifiedRegistries();
4661
4662 return rc;
4663}
4664
4665STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4666 LONG aDevice, BOOL aPassthrough)
4667{
4668 CheckComArgStrNotEmptyOrNull(aControllerName);
4669
4670 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4671 aControllerName, aControllerPort, aDevice, aPassthrough));
4672
4673 AutoCaller autoCaller(this);
4674 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4675
4676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4677
4678 HRESULT rc = checkStateDependency(MutableStateDep);
4679 if (FAILED(rc)) return rc;
4680
4681 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4682
4683 if (Global::IsOnlineOrTransient(mData->mMachineState))
4684 return setError(VBOX_E_INVALID_VM_STATE,
4685 tr("Invalid machine state: %s"),
4686 Global::stringifyMachineState(mData->mMachineState));
4687
4688 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4689 aControllerName,
4690 aControllerPort,
4691 aDevice);
4692 if (!pAttach)
4693 return setError(VBOX_E_OBJECT_NOT_FOUND,
4694 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4695 aDevice, aControllerPort, aControllerName);
4696
4697
4698 setModified(IsModified_Storage);
4699 mMediaData.backup();
4700
4701 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4702
4703 if (pAttach->getType() != DeviceType_DVD)
4704 return setError(E_INVALIDARG,
4705 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4706 aDevice, aControllerPort, aControllerName);
4707 pAttach->updatePassthrough(!!aPassthrough);
4708
4709 return S_OK;
4710}
4711
4712STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4713 LONG aDevice, BOOL aTemporaryEject)
4714{
4715 CheckComArgStrNotEmptyOrNull(aControllerName);
4716
4717 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4718 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4719
4720 AutoCaller autoCaller(this);
4721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4722
4723 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4724
4725 HRESULT rc = checkStateDependency(MutableStateDep);
4726 if (FAILED(rc)) return rc;
4727
4728 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4729 aControllerName,
4730 aControllerPort,
4731 aDevice);
4732 if (!pAttach)
4733 return setError(VBOX_E_OBJECT_NOT_FOUND,
4734 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4735 aDevice, aControllerPort, aControllerName);
4736
4737
4738 setModified(IsModified_Storage);
4739 mMediaData.backup();
4740
4741 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4742
4743 if (pAttach->getType() != DeviceType_DVD)
4744 return setError(E_INVALIDARG,
4745 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4746 aDevice, aControllerPort, aControllerName);
4747 pAttach->updateTempEject(!!aTemporaryEject);
4748
4749 return S_OK;
4750}
4751
4752STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4753 LONG aDevice, BOOL aNonRotational)
4754{
4755 CheckComArgStrNotEmptyOrNull(aControllerName);
4756
4757 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4758 aControllerName, aControllerPort, aDevice, aNonRotational));
4759
4760 AutoCaller autoCaller(this);
4761 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4762
4763 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4764
4765 HRESULT rc = checkStateDependency(MutableStateDep);
4766 if (FAILED(rc)) return rc;
4767
4768 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4769
4770 if (Global::IsOnlineOrTransient(mData->mMachineState))
4771 return setError(VBOX_E_INVALID_VM_STATE,
4772 tr("Invalid machine state: %s"),
4773 Global::stringifyMachineState(mData->mMachineState));
4774
4775 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4776 aControllerName,
4777 aControllerPort,
4778 aDevice);
4779 if (!pAttach)
4780 return setError(VBOX_E_OBJECT_NOT_FOUND,
4781 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4782 aDevice, aControllerPort, aControllerName);
4783
4784
4785 setModified(IsModified_Storage);
4786 mMediaData.backup();
4787
4788 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4789
4790 if (pAttach->getType() != DeviceType_HardDisk)
4791 return setError(E_INVALIDARG,
4792 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"),
4793 aDevice, aControllerPort, aControllerName);
4794 pAttach->updateNonRotational(!!aNonRotational);
4795
4796 return S_OK;
4797}
4798
4799STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4800 LONG aDevice, BOOL aDiscard)
4801{
4802 CheckComArgStrNotEmptyOrNull(aControllerName);
4803
4804 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4805 aControllerName, aControllerPort, aDevice, aDiscard));
4806
4807 AutoCaller autoCaller(this);
4808 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4809
4810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4811
4812 HRESULT rc = checkStateDependency(MutableStateDep);
4813 if (FAILED(rc)) return rc;
4814
4815 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4816
4817 if (Global::IsOnlineOrTransient(mData->mMachineState))
4818 return setError(VBOX_E_INVALID_VM_STATE,
4819 tr("Invalid machine state: %s"),
4820 Global::stringifyMachineState(mData->mMachineState));
4821
4822 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4823 aControllerName,
4824 aControllerPort,
4825 aDevice);
4826 if (!pAttach)
4827 return setError(VBOX_E_OBJECT_NOT_FOUND,
4828 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4829 aDevice, aControllerPort, aControllerName);
4830
4831
4832 setModified(IsModified_Storage);
4833 mMediaData.backup();
4834
4835 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4836
4837 if (pAttach->getType() != DeviceType_HardDisk)
4838 return setError(E_INVALIDARG,
4839 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"),
4840 aDevice, aControllerPort, aControllerName);
4841 pAttach->updateDiscard(!!aDiscard);
4842
4843 return S_OK;
4844}
4845
4846STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4847 LONG aDevice)
4848{
4849 int rc = S_OK;
4850 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4851 aControllerName, aControllerPort, aDevice));
4852
4853 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4854
4855 return rc;
4856}
4857
4858STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4859 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4860{
4861 CheckComArgStrNotEmptyOrNull(aControllerName);
4862
4863 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4864 aControllerName, aControllerPort, aDevice));
4865
4866 AutoCaller autoCaller(this);
4867 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4868
4869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4870
4871 HRESULT rc = checkStateDependency(MutableStateDep);
4872 if (FAILED(rc)) return rc;
4873
4874 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4875
4876 if (Global::IsOnlineOrTransient(mData->mMachineState))
4877 return setError(VBOX_E_INVALID_VM_STATE,
4878 tr("Invalid machine state: %s"),
4879 Global::stringifyMachineState(mData->mMachineState));
4880
4881 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4882 aControllerName,
4883 aControllerPort,
4884 aDevice);
4885 if (!pAttach)
4886 return setError(VBOX_E_OBJECT_NOT_FOUND,
4887 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4888 aDevice, aControllerPort, aControllerName);
4889
4890
4891 setModified(IsModified_Storage);
4892 mMediaData.backup();
4893
4894 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4895 if (aBandwidthGroup && group.isNull())
4896 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4897
4898 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4899
4900 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4901 if (strBandwidthGroupOld.isNotEmpty())
4902 {
4903 /* Get the bandwidth group object and release it - this must not fail. */
4904 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4905 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4906 Assert(SUCCEEDED(rc));
4907
4908 pBandwidthGroupOld->release();
4909 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4910 }
4911
4912 if (!group.isNull())
4913 {
4914 group->reference();
4915 pAttach->updateBandwidthGroup(group->getName());
4916 }
4917
4918 return S_OK;
4919}
4920
4921STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4922 LONG aControllerPort,
4923 LONG aDevice,
4924 DeviceType_T aType)
4925{
4926 HRESULT rc = S_OK;
4927
4928 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4929 aControllerName, aControllerPort, aDevice, aType));
4930
4931 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4932
4933 return rc;
4934}
4935
4936
4937
4938STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4939 LONG aControllerPort,
4940 LONG aDevice,
4941 BOOL aForce)
4942{
4943 int rc = S_OK;
4944 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4945 aControllerName, aControllerPort, aForce));
4946
4947 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4948
4949 return rc;
4950}
4951
4952STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4953 LONG aControllerPort,
4954 LONG aDevice,
4955 IMedium *aMedium,
4956 BOOL aForce)
4957{
4958 int rc = S_OK;
4959 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4960 aControllerName, aControllerPort, aDevice, aForce));
4961
4962 CheckComArgStrNotEmptyOrNull(aControllerName);
4963
4964 AutoCaller autoCaller(this);
4965 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4966
4967 // request the host lock first, since might be calling Host methods for getting host drives;
4968 // next, protect the media tree all the while we're in here, as well as our member variables
4969 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4970 this->lockHandle(),
4971 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4972
4973 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4974 aControllerName,
4975 aControllerPort,
4976 aDevice);
4977 if (pAttach.isNull())
4978 return setError(VBOX_E_OBJECT_NOT_FOUND,
4979 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4980 aDevice, aControllerPort, aControllerName);
4981
4982 /* Remember previously mounted medium. The medium before taking the
4983 * backup is not necessarily the same thing. */
4984 ComObjPtr<Medium> oldmedium;
4985 oldmedium = pAttach->getMedium();
4986
4987 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4988 if (aMedium && pMedium.isNull())
4989 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4990
4991 AutoCaller mediumCaller(pMedium);
4992 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4993
4994 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4995 if (pMedium)
4996 {
4997 DeviceType_T mediumType = pAttach->getType();
4998 switch (mediumType)
4999 {
5000 case DeviceType_DVD:
5001 case DeviceType_Floppy:
5002 break;
5003
5004 default:
5005 return setError(VBOX_E_INVALID_OBJECT_STATE,
5006 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
5007 aControllerPort,
5008 aDevice,
5009 aControllerName);
5010 }
5011 }
5012
5013 setModified(IsModified_Storage);
5014 mMediaData.backup();
5015
5016 {
5017 // The backup operation makes the pAttach reference point to the
5018 // old settings. Re-get the correct reference.
5019 pAttach = findAttachment(mMediaData->mAttachments,
5020 aControllerName,
5021 aControllerPort,
5022 aDevice);
5023 if (!oldmedium.isNull())
5024 oldmedium->removeBackReference(mData->mUuid);
5025 if (!pMedium.isNull())
5026 {
5027 pMedium->addBackReference(mData->mUuid);
5028
5029 mediumLock.release();
5030 multiLock.release();
5031 addMediumToRegistry(pMedium);
5032 multiLock.acquire();
5033 mediumLock.acquire();
5034 }
5035
5036 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5037 pAttach->updateMedium(pMedium);
5038 }
5039
5040 setModified(IsModified_Storage);
5041
5042 mediumLock.release();
5043 multiLock.release();
5044 rc = onMediumChange(pAttach, aForce);
5045 multiLock.acquire();
5046 mediumLock.acquire();
5047
5048 /* On error roll back this change only. */
5049 if (FAILED(rc))
5050 {
5051 if (!pMedium.isNull())
5052 pMedium->removeBackReference(mData->mUuid);
5053 pAttach = findAttachment(mMediaData->mAttachments,
5054 aControllerName,
5055 aControllerPort,
5056 aDevice);
5057 /* If the attachment is gone in the meantime, bail out. */
5058 if (pAttach.isNull())
5059 return rc;
5060 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5061 if (!oldmedium.isNull())
5062 oldmedium->addBackReference(mData->mUuid);
5063 pAttach->updateMedium(oldmedium);
5064 }
5065
5066 mediumLock.release();
5067 multiLock.release();
5068
5069 mParent->saveModifiedRegistries();
5070
5071 return rc;
5072}
5073
5074STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5075 LONG aControllerPort,
5076 LONG aDevice,
5077 IMedium **aMedium)
5078{
5079 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5080 aControllerName, aControllerPort, aDevice));
5081
5082 CheckComArgStrNotEmptyOrNull(aControllerName);
5083 CheckComArgOutPointerValid(aMedium);
5084
5085 AutoCaller autoCaller(this);
5086 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5087
5088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5089
5090 *aMedium = NULL;
5091
5092 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5093 aControllerName,
5094 aControllerPort,
5095 aDevice);
5096 if (pAttach.isNull())
5097 return setError(VBOX_E_OBJECT_NOT_FOUND,
5098 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5099 aDevice, aControllerPort, aControllerName);
5100
5101 pAttach->getMedium().queryInterfaceTo(aMedium);
5102
5103 return S_OK;
5104}
5105
5106STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5107{
5108 CheckComArgOutPointerValid(port);
5109 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5110
5111 AutoCaller autoCaller(this);
5112 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5113
5114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5115
5116 mSerialPorts[slot].queryInterfaceTo(port);
5117
5118 return S_OK;
5119}
5120
5121STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5122{
5123 CheckComArgOutPointerValid(port);
5124 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5125
5126 AutoCaller autoCaller(this);
5127 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5128
5129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5130
5131 mParallelPorts[slot].queryInterfaceTo(port);
5132
5133 return S_OK;
5134}
5135
5136STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5137{
5138 CheckComArgOutPointerValid(adapter);
5139 /* Do not assert if slot is out of range, just return the advertised
5140 status. testdriver/vbox.py triggers this in logVmInfo. */
5141 if (slot >= mNetworkAdapters.size())
5142 return setError(E_INVALIDARG,
5143 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5144 slot, mNetworkAdapters.size());
5145
5146 AutoCaller autoCaller(this);
5147 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5148
5149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5150
5151 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5152
5153 return S_OK;
5154}
5155
5156STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5157{
5158 CheckComArgOutSafeArrayPointerValid(aKeys);
5159
5160 AutoCaller autoCaller(this);
5161 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5162
5163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5164
5165 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5166 int i = 0;
5167 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5168 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5169 ++it, ++i)
5170 {
5171 const Utf8Str &strKey = it->first;
5172 strKey.cloneTo(&saKeys[i]);
5173 }
5174 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5175
5176 return S_OK;
5177 }
5178
5179 /**
5180 * @note Locks this object for reading.
5181 */
5182STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5183 BSTR *aValue)
5184{
5185 CheckComArgStrNotEmptyOrNull(aKey);
5186 CheckComArgOutPointerValid(aValue);
5187
5188 AutoCaller autoCaller(this);
5189 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5190
5191 /* start with nothing found */
5192 Bstr bstrResult("");
5193
5194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5195
5196 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5197 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5198 // found:
5199 bstrResult = it->second; // source is a Utf8Str
5200
5201 /* return the result to caller (may be empty) */
5202 bstrResult.cloneTo(aValue);
5203
5204 return S_OK;
5205}
5206
5207 /**
5208 * @note Locks mParent for writing + this object for writing.
5209 */
5210STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5211{
5212 CheckComArgStrNotEmptyOrNull(aKey);
5213
5214 AutoCaller autoCaller(this);
5215 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5216
5217 Utf8Str strKey(aKey);
5218 Utf8Str strValue(aValue);
5219 Utf8Str strOldValue; // empty
5220
5221 // locking note: we only hold the read lock briefly to look up the old value,
5222 // then release it and call the onExtraCanChange callbacks. There is a small
5223 // chance of a race insofar as the callback might be called twice if two callers
5224 // change the same key at the same time, but that's a much better solution
5225 // than the deadlock we had here before. The actual changing of the extradata
5226 // is then performed under the write lock and race-free.
5227
5228 // look up the old value first; if nothing has changed then we need not do anything
5229 {
5230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5231 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5232 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5233 strOldValue = it->second;
5234 }
5235
5236 bool fChanged;
5237 if ((fChanged = (strOldValue != strValue)))
5238 {
5239 // ask for permission from all listeners outside the locks;
5240 // onExtraDataCanChange() only briefly requests the VirtualBox
5241 // lock to copy the list of callbacks to invoke
5242 Bstr error;
5243 Bstr bstrValue(aValue);
5244
5245 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5246 {
5247 const char *sep = error.isEmpty() ? "" : ": ";
5248 CBSTR err = error.raw();
5249 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5250 sep, err));
5251 return setError(E_ACCESSDENIED,
5252 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5253 aKey,
5254 bstrValue.raw(),
5255 sep,
5256 err);
5257 }
5258
5259 // data is changing and change not vetoed: then write it out under the lock
5260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5261
5262 if (isSnapshotMachine())
5263 {
5264 HRESULT rc = checkStateDependency(MutableStateDep);
5265 if (FAILED(rc)) return rc;
5266 }
5267
5268 if (strValue.isEmpty())
5269 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5270 else
5271 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5272 // creates a new key if needed
5273
5274 bool fNeedsGlobalSaveSettings = false;
5275 saveSettings(&fNeedsGlobalSaveSettings);
5276
5277 if (fNeedsGlobalSaveSettings)
5278 {
5279 // save the global settings; for that we should hold only the VirtualBox lock
5280 alock.release();
5281 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5282 mParent->saveSettings();
5283 }
5284 }
5285
5286 // fire notification outside the lock
5287 if (fChanged)
5288 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5289
5290 return S_OK;
5291}
5292
5293STDMETHODIMP Machine::SaveSettings()
5294{
5295 AutoCaller autoCaller(this);
5296 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5297
5298 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5299
5300 /* when there was auto-conversion, we want to save the file even if
5301 * the VM is saved */
5302 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5303 if (FAILED(rc)) return rc;
5304
5305 /* the settings file path may never be null */
5306 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5307
5308 /* save all VM data excluding snapshots */
5309 bool fNeedsGlobalSaveSettings = false;
5310 rc = saveSettings(&fNeedsGlobalSaveSettings);
5311 mlock.release();
5312
5313 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5314 {
5315 // save the global settings; for that we should hold only the VirtualBox lock
5316 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5317 rc = mParent->saveSettings();
5318 }
5319
5320 return rc;
5321}
5322
5323STDMETHODIMP Machine::DiscardSettings()
5324{
5325 AutoCaller autoCaller(this);
5326 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5327
5328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5329
5330 HRESULT rc = checkStateDependency(MutableStateDep);
5331 if (FAILED(rc)) return rc;
5332
5333 /*
5334 * during this rollback, the session will be notified if data has
5335 * been actually changed
5336 */
5337 rollback(true /* aNotify */);
5338
5339 return S_OK;
5340}
5341
5342/** @note Locks objects! */
5343STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5344 ComSafeArrayOut(IMedium*, aMedia))
5345{
5346 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5347 AutoLimitedCaller autoCaller(this);
5348 AssertComRCReturnRC(autoCaller.rc());
5349
5350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5351
5352 Guid id(getId());
5353
5354 if (mData->mSession.mState != SessionState_Unlocked)
5355 return setError(VBOX_E_INVALID_OBJECT_STATE,
5356 tr("Cannot unregister the machine '%s' while it is locked"),
5357 mUserData->s.strName.c_str());
5358
5359 // wait for state dependents to drop to zero
5360 ensureNoStateDependencies();
5361
5362 if (!mData->mAccessible)
5363 {
5364 // inaccessible maschines can only be unregistered; uninitialize ourselves
5365 // here because currently there may be no unregistered that are inaccessible
5366 // (this state combination is not supported). Note releasing the caller and
5367 // leaving the lock before calling uninit()
5368 alock.release();
5369 autoCaller.release();
5370
5371 uninit();
5372
5373 mParent->unregisterMachine(this, id);
5374 // calls VirtualBox::saveSettings()
5375
5376 return S_OK;
5377 }
5378
5379 HRESULT rc = S_OK;
5380
5381 // discard saved state
5382 if (mData->mMachineState == MachineState_Saved)
5383 {
5384 // add the saved state file to the list of files the caller should delete
5385 Assert(!mSSData->strStateFilePath.isEmpty());
5386 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5387
5388 mSSData->strStateFilePath.setNull();
5389
5390 // unconditionally set the machine state to powered off, we now
5391 // know no session has locked the machine
5392 mData->mMachineState = MachineState_PoweredOff;
5393 }
5394
5395 size_t cSnapshots = 0;
5396 if (mData->mFirstSnapshot)
5397 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5398 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5399 // fail now before we start detaching media
5400 return setError(VBOX_E_INVALID_OBJECT_STATE,
5401 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5402 mUserData->s.strName.c_str(), cSnapshots);
5403
5404 // This list collects the medium objects from all medium attachments
5405 // which we will detach from the machine and its snapshots, in a specific
5406 // order which allows for closing all media without getting "media in use"
5407 // errors, simply by going through the list from the front to the back:
5408 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5409 // and must be closed before the parent media from the snapshots, or closing the parents
5410 // will fail because they still have children);
5411 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5412 // the root ("first") snapshot of the machine.
5413 MediaList llMedia;
5414
5415 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5416 && mMediaData->mAttachments.size()
5417 )
5418 {
5419 // we have media attachments: detach them all and add the Medium objects to our list
5420 if (cleanupMode != CleanupMode_UnregisterOnly)
5421 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5422 else
5423 return setError(VBOX_E_INVALID_OBJECT_STATE,
5424 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5425 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5426 }
5427
5428 if (cSnapshots)
5429 {
5430 // autoCleanup must be true here, or we would have failed above
5431
5432 // add the media from the medium attachments of the snapshots to llMedia
5433 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5434 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5435 // into the children first
5436
5437 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5438 MachineState_T oldState = mData->mMachineState;
5439 mData->mMachineState = MachineState_DeletingSnapshot;
5440
5441 // make a copy of the first snapshot so the refcount does not drop to 0
5442 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5443 // because of the AutoCaller voodoo)
5444 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5445
5446 // GO!
5447 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5448
5449 mData->mMachineState = oldState;
5450 }
5451
5452 if (FAILED(rc))
5453 {
5454 rollbackMedia();
5455 return rc;
5456 }
5457
5458 // commit all the media changes made above
5459 commitMedia();
5460
5461 mData->mRegistered = false;
5462
5463 // machine lock no longer needed
5464 alock.release();
5465
5466 // return media to caller
5467 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5468 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5469
5470 mParent->unregisterMachine(this, id);
5471 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5472
5473 return S_OK;
5474}
5475
5476struct Machine::DeleteTask
5477{
5478 ComObjPtr<Machine> pMachine;
5479 RTCList<ComPtr<IMedium> > llMediums;
5480 StringsList llFilesToDelete;
5481 ComObjPtr<Progress> pProgress;
5482};
5483
5484STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5485{
5486 LogFlowFuncEnter();
5487
5488 AutoCaller autoCaller(this);
5489 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5490
5491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5492
5493 HRESULT rc = checkStateDependency(MutableStateDep);
5494 if (FAILED(rc)) return rc;
5495
5496 if (mData->mRegistered)
5497 return setError(VBOX_E_INVALID_VM_STATE,
5498 tr("Cannot delete settings of a registered machine"));
5499
5500 DeleteTask *pTask = new DeleteTask;
5501 pTask->pMachine = this;
5502 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5503
5504 // collect files to delete
5505 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5506
5507 for (size_t i = 0; i < sfaMedia.size(); ++i)
5508 {
5509 IMedium *pIMedium(sfaMedia[i]);
5510 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5511 if (pMedium.isNull())
5512 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5513 SafeArray<BSTR> ids;
5514 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5515 if (FAILED(rc)) return rc;
5516 /* At this point the medium should not have any back references
5517 * anymore. If it has it is attached to another VM and *must* not
5518 * deleted. */
5519 if (ids.size() < 1)
5520 pTask->llMediums.append(pMedium);
5521 }
5522 if (mData->pMachineConfigFile->fileExists())
5523 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5524
5525 pTask->pProgress.createObject();
5526 pTask->pProgress->init(getVirtualBox(),
5527 static_cast<IMachine*>(this) /* aInitiator */,
5528 Bstr(tr("Deleting files")).raw(),
5529 true /* fCancellable */,
5530 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5531 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5532
5533 int vrc = RTThreadCreate(NULL,
5534 Machine::deleteThread,
5535 (void*)pTask,
5536 0,
5537 RTTHREADTYPE_MAIN_WORKER,
5538 0,
5539 "MachineDelete");
5540
5541 pTask->pProgress.queryInterfaceTo(aProgress);
5542
5543 if (RT_FAILURE(vrc))
5544 {
5545 delete pTask;
5546 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5547 }
5548
5549 LogFlowFuncLeave();
5550
5551 return S_OK;
5552}
5553
5554/**
5555 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5556 * calls Machine::deleteTaskWorker() on the actual machine object.
5557 * @param Thread
5558 * @param pvUser
5559 * @return
5560 */
5561/*static*/
5562DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5563{
5564 LogFlowFuncEnter();
5565
5566 DeleteTask *pTask = (DeleteTask*)pvUser;
5567 Assert(pTask);
5568 Assert(pTask->pMachine);
5569 Assert(pTask->pProgress);
5570
5571 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5572 pTask->pProgress->notifyComplete(rc);
5573
5574 delete pTask;
5575
5576 LogFlowFuncLeave();
5577
5578 NOREF(Thread);
5579
5580 return VINF_SUCCESS;
5581}
5582
5583/**
5584 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5585 * @param task
5586 * @return
5587 */
5588HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5589{
5590 AutoCaller autoCaller(this);
5591 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5592
5593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5594
5595 HRESULT rc = S_OK;
5596
5597 try
5598 {
5599 ULONG uLogHistoryCount = 3;
5600 ComPtr<ISystemProperties> systemProperties;
5601 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5602 if (FAILED(rc)) throw rc;
5603
5604 if (!systemProperties.isNull())
5605 {
5606 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5607 if (FAILED(rc)) throw rc;
5608 }
5609
5610 MachineState_T oldState = mData->mMachineState;
5611 setMachineState(MachineState_SettingUp);
5612 alock.release();
5613 for (size_t i = 0; i < task.llMediums.size(); ++i)
5614 {
5615 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5616 {
5617 AutoCaller mac(pMedium);
5618 if (FAILED(mac.rc())) throw mac.rc();
5619 Utf8Str strLocation = pMedium->getLocationFull();
5620 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5621 if (FAILED(rc)) throw rc;
5622 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5623 }
5624 ComPtr<IProgress> pProgress2;
5625 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5626 if (FAILED(rc)) throw rc;
5627 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5628 if (FAILED(rc)) throw rc;
5629 /* Check the result of the asynchrony process. */
5630 LONG iRc;
5631 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5632 if (FAILED(rc)) throw rc;
5633 /* If the thread of the progress object has an error, then
5634 * retrieve the error info from there, or it'll be lost. */
5635 if (FAILED(iRc))
5636 throw setError(ProgressErrorInfo(pProgress2));
5637 }
5638 setMachineState(oldState);
5639 alock.acquire();
5640
5641 // delete the files pushed on the task list by Machine::Delete()
5642 // (this includes saved states of the machine and snapshots and
5643 // medium storage files from the IMedium list passed in, and the
5644 // machine XML file)
5645 StringsList::const_iterator it = task.llFilesToDelete.begin();
5646 while (it != task.llFilesToDelete.end())
5647 {
5648 const Utf8Str &strFile = *it;
5649 LogFunc(("Deleting file %s\n", strFile.c_str()));
5650 int vrc = RTFileDelete(strFile.c_str());
5651 if (RT_FAILURE(vrc))
5652 throw setError(VBOX_E_IPRT_ERROR,
5653 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5654
5655 ++it;
5656 if (it == task.llFilesToDelete.end())
5657 {
5658 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5659 if (FAILED(rc)) throw rc;
5660 break;
5661 }
5662
5663 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5664 if (FAILED(rc)) throw rc;
5665 }
5666
5667 /* delete the settings only when the file actually exists */
5668 if (mData->pMachineConfigFile->fileExists())
5669 {
5670 /* Delete any backup or uncommitted XML files. Ignore failures.
5671 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5672 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5673 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5674 RTFileDelete(otherXml.c_str());
5675 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5676 RTFileDelete(otherXml.c_str());
5677
5678 /* delete the Logs folder, nothing important should be left
5679 * there (we don't check for errors because the user might have
5680 * some private files there that we don't want to delete) */
5681 Utf8Str logFolder;
5682 getLogFolder(logFolder);
5683 Assert(logFolder.length());
5684 if (RTDirExists(logFolder.c_str()))
5685 {
5686 /* Delete all VBox.log[.N] files from the Logs folder
5687 * (this must be in sync with the rotation logic in
5688 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5689 * files that may have been created by the GUI. */
5690 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5691 logFolder.c_str(), RTPATH_DELIMITER);
5692 RTFileDelete(log.c_str());
5693 log = Utf8StrFmt("%s%cVBox.png",
5694 logFolder.c_str(), RTPATH_DELIMITER);
5695 RTFileDelete(log.c_str());
5696 for (int i = uLogHistoryCount; i > 0; i--)
5697 {
5698 log = Utf8StrFmt("%s%cVBox.log.%d",
5699 logFolder.c_str(), RTPATH_DELIMITER, i);
5700 RTFileDelete(log.c_str());
5701 log = Utf8StrFmt("%s%cVBox.png.%d",
5702 logFolder.c_str(), RTPATH_DELIMITER, i);
5703 RTFileDelete(log.c_str());
5704 }
5705
5706 RTDirRemove(logFolder.c_str());
5707 }
5708
5709 /* delete the Snapshots folder, nothing important should be left
5710 * there (we don't check for errors because the user might have
5711 * some private files there that we don't want to delete) */
5712 Utf8Str strFullSnapshotFolder;
5713 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5714 Assert(!strFullSnapshotFolder.isEmpty());
5715 if (RTDirExists(strFullSnapshotFolder.c_str()))
5716 RTDirRemove(strFullSnapshotFolder.c_str());
5717
5718 // delete the directory that contains the settings file, but only
5719 // if it matches the VM name
5720 Utf8Str settingsDir;
5721 if (isInOwnDir(&settingsDir))
5722 RTDirRemove(settingsDir.c_str());
5723 }
5724
5725 alock.release();
5726
5727 mParent->saveModifiedRegistries();
5728 }
5729 catch (HRESULT aRC) { rc = aRC; }
5730
5731 return rc;
5732}
5733
5734STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5735{
5736 CheckComArgOutPointerValid(aSnapshot);
5737
5738 AutoCaller autoCaller(this);
5739 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5740
5741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5742
5743 ComObjPtr<Snapshot> pSnapshot;
5744 HRESULT rc;
5745
5746 if (!aNameOrId || !*aNameOrId)
5747 // null case (caller wants root snapshot): findSnapshotById() handles this
5748 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5749 else
5750 {
5751 Guid uuid(aNameOrId);
5752 if (uuid.isValid())
5753 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5754 else
5755 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5756 }
5757 pSnapshot.queryInterfaceTo(aSnapshot);
5758
5759 return rc;
5760}
5761
5762STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5763{
5764 CheckComArgStrNotEmptyOrNull(aName);
5765 CheckComArgStrNotEmptyOrNull(aHostPath);
5766
5767 AutoCaller autoCaller(this);
5768 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5769
5770 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5771
5772 HRESULT rc = checkStateDependency(MutableStateDep);
5773 if (FAILED(rc)) return rc;
5774
5775 Utf8Str strName(aName);
5776
5777 ComObjPtr<SharedFolder> sharedFolder;
5778 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5779 if (SUCCEEDED(rc))
5780 return setError(VBOX_E_OBJECT_IN_USE,
5781 tr("Shared folder named '%s' already exists"),
5782 strName.c_str());
5783
5784 sharedFolder.createObject();
5785 rc = sharedFolder->init(getMachine(),
5786 strName,
5787 aHostPath,
5788 !!aWritable,
5789 !!aAutoMount,
5790 true /* fFailOnError */);
5791 if (FAILED(rc)) return rc;
5792
5793 setModified(IsModified_SharedFolders);
5794 mHWData.backup();
5795 mHWData->mSharedFolders.push_back(sharedFolder);
5796
5797 /* inform the direct session if any */
5798 alock.release();
5799 onSharedFolderChange();
5800
5801 return S_OK;
5802}
5803
5804STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5805{
5806 CheckComArgStrNotEmptyOrNull(aName);
5807
5808 AutoCaller autoCaller(this);
5809 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5810
5811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5812
5813 HRESULT rc = checkStateDependency(MutableStateDep);
5814 if (FAILED(rc)) return rc;
5815
5816 ComObjPtr<SharedFolder> sharedFolder;
5817 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5818 if (FAILED(rc)) return rc;
5819
5820 setModified(IsModified_SharedFolders);
5821 mHWData.backup();
5822 mHWData->mSharedFolders.remove(sharedFolder);
5823
5824 /* inform the direct session if any */
5825 alock.release();
5826 onSharedFolderChange();
5827
5828 return S_OK;
5829}
5830
5831STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5832{
5833 CheckComArgOutPointerValid(aCanShow);
5834
5835 /* start with No */
5836 *aCanShow = FALSE;
5837
5838 AutoCaller autoCaller(this);
5839 AssertComRCReturnRC(autoCaller.rc());
5840
5841 ComPtr<IInternalSessionControl> directControl;
5842 {
5843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5844
5845 if (mData->mSession.mState != SessionState_Locked)
5846 return setError(VBOX_E_INVALID_VM_STATE,
5847 tr("Machine is not locked for session (session state: %s)"),
5848 Global::stringifySessionState(mData->mSession.mState));
5849
5850 directControl = mData->mSession.mDirectControl;
5851 }
5852
5853 /* ignore calls made after #OnSessionEnd() is called */
5854 if (!directControl)
5855 return S_OK;
5856
5857 LONG64 dummy;
5858 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5859}
5860
5861STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5862{
5863 CheckComArgOutPointerValid(aWinId);
5864
5865 AutoCaller autoCaller(this);
5866 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5867
5868 ComPtr<IInternalSessionControl> directControl;
5869 {
5870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5871
5872 if (mData->mSession.mState != SessionState_Locked)
5873 return setError(E_FAIL,
5874 tr("Machine is not locked for session (session state: %s)"),
5875 Global::stringifySessionState(mData->mSession.mState));
5876
5877 directControl = mData->mSession.mDirectControl;
5878 }
5879
5880 /* ignore calls made after #OnSessionEnd() is called */
5881 if (!directControl)
5882 return S_OK;
5883
5884 BOOL dummy;
5885 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5886}
5887
5888#ifdef VBOX_WITH_GUEST_PROPS
5889/**
5890 * Look up a guest property in VBoxSVC's internal structures.
5891 */
5892HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5893 BSTR *aValue,
5894 LONG64 *aTimestamp,
5895 BSTR *aFlags) const
5896{
5897 using namespace guestProp;
5898
5899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5900 Utf8Str strName(aName);
5901 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5902
5903 if (it != mHWData->mGuestProperties.end())
5904 {
5905 char szFlags[MAX_FLAGS_LEN + 1];
5906 it->second.strValue.cloneTo(aValue);
5907 *aTimestamp = it->second.mTimestamp;
5908 writeFlags(it->second.mFlags, szFlags);
5909 Bstr(szFlags).cloneTo(aFlags);
5910 }
5911
5912 return S_OK;
5913}
5914
5915/**
5916 * Query the VM that a guest property belongs to for the property.
5917 * @returns E_ACCESSDENIED if the VM process is not available or not
5918 * currently handling queries and the lookup should then be done in
5919 * VBoxSVC.
5920 */
5921HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5922 BSTR *aValue,
5923 LONG64 *aTimestamp,
5924 BSTR *aFlags) const
5925{
5926 HRESULT rc;
5927 ComPtr<IInternalSessionControl> directControl;
5928 directControl = mData->mSession.mDirectControl;
5929
5930 /* fail if we were called after #OnSessionEnd() is called. This is a
5931 * silly race condition. */
5932
5933 if (!directControl)
5934 rc = E_ACCESSDENIED;
5935 else
5936 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5937 false /* isSetter */,
5938 aValue, aTimestamp, aFlags);
5939 return rc;
5940}
5941#endif // VBOX_WITH_GUEST_PROPS
5942
5943STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5944 BSTR *aValue,
5945 LONG64 *aTimestamp,
5946 BSTR *aFlags)
5947{
5948#ifndef VBOX_WITH_GUEST_PROPS
5949 ReturnComNotImplemented();
5950#else // VBOX_WITH_GUEST_PROPS
5951 CheckComArgStrNotEmptyOrNull(aName);
5952 CheckComArgOutPointerValid(aValue);
5953 CheckComArgOutPointerValid(aTimestamp);
5954 CheckComArgOutPointerValid(aFlags);
5955
5956 AutoCaller autoCaller(this);
5957 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5958
5959 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5960 if (rc == E_ACCESSDENIED)
5961 /* The VM is not running or the service is not (yet) accessible */
5962 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5963 return rc;
5964#endif // VBOX_WITH_GUEST_PROPS
5965}
5966
5967STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5968{
5969 LONG64 dummyTimestamp;
5970 Bstr dummyFlags;
5971 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5972}
5973
5974STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5975{
5976 Bstr dummyValue;
5977 Bstr dummyFlags;
5978 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5979}
5980
5981#ifdef VBOX_WITH_GUEST_PROPS
5982/**
5983 * Set a guest property in VBoxSVC's internal structures.
5984 */
5985HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5986 IN_BSTR aFlags)
5987{
5988 using namespace guestProp;
5989
5990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5991 HRESULT rc = S_OK;
5992
5993 rc = checkStateDependency(MutableStateDep);
5994 if (FAILED(rc)) return rc;
5995
5996 try
5997 {
5998 Utf8Str utf8Name(aName);
5999 Utf8Str utf8Flags(aFlags);
6000 uint32_t fFlags = NILFLAG;
6001 if ( aFlags != NULL
6002 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
6003 return setError(E_INVALIDARG,
6004 tr("Invalid guest property flag values: '%ls'"),
6005 aFlags);
6006
6007 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
6008 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
6009 if (it == mHWData->mGuestProperties.end())
6010 {
6011 if (!fDelete)
6012 {
6013 setModified(IsModified_MachineData);
6014 mHWData.backupEx();
6015
6016 RTTIMESPEC time;
6017 HWData::GuestProperty prop;
6018 prop.strValue = aValue;
6019 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6020 prop.mFlags = fFlags;
6021 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
6022 }
6023 }
6024 else
6025 {
6026 if (it->second.mFlags & (RDONLYHOST))
6027 {
6028 rc = setError(E_ACCESSDENIED,
6029 tr("The property '%ls' cannot be changed by the host"),
6030 aName);
6031 }
6032 else
6033 {
6034 setModified(IsModified_MachineData);
6035 mHWData.backupEx();
6036
6037 /* The backupEx() operation invalidates our iterator,
6038 * so get a new one. */
6039 it = mHWData->mGuestProperties.find(utf8Name);
6040 Assert(it != mHWData->mGuestProperties.end());
6041
6042 if (!fDelete)
6043 {
6044 RTTIMESPEC time;
6045 it->second.strValue = aValue;
6046 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6047 it->second.mFlags = fFlags;
6048 }
6049 else
6050 mHWData->mGuestProperties.erase(it);
6051 }
6052 }
6053
6054 if ( SUCCEEDED(rc)
6055 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6056 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6057 RTSTR_MAX,
6058 utf8Name.c_str(),
6059 RTSTR_MAX,
6060 NULL)
6061 )
6062 )
6063 {
6064 alock.release();
6065
6066 mParent->onGuestPropertyChange(mData->mUuid, aName,
6067 aValue ? aValue : Bstr("").raw(),
6068 aFlags ? aFlags : Bstr("").raw());
6069 }
6070 }
6071 catch (std::bad_alloc &)
6072 {
6073 rc = E_OUTOFMEMORY;
6074 }
6075
6076 return rc;
6077}
6078
6079/**
6080 * Set a property on the VM that that property belongs to.
6081 * @returns E_ACCESSDENIED if the VM process is not available or not
6082 * currently handling queries and the setting should then be done in
6083 * VBoxSVC.
6084 */
6085HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6086 IN_BSTR aFlags)
6087{
6088 HRESULT rc;
6089
6090 try
6091 {
6092 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6093
6094 BSTR dummy = NULL; /* will not be changed (setter) */
6095 LONG64 dummy64;
6096 if (!directControl)
6097 rc = E_ACCESSDENIED;
6098 else
6099 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6100 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6101 true /* isSetter */,
6102 &dummy, &dummy64, &dummy);
6103 }
6104 catch (std::bad_alloc &)
6105 {
6106 rc = E_OUTOFMEMORY;
6107 }
6108
6109 return rc;
6110}
6111#endif // VBOX_WITH_GUEST_PROPS
6112
6113STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6114 IN_BSTR aFlags)
6115{
6116#ifndef VBOX_WITH_GUEST_PROPS
6117 ReturnComNotImplemented();
6118#else // VBOX_WITH_GUEST_PROPS
6119 CheckComArgStrNotEmptyOrNull(aName);
6120 CheckComArgMaybeNull(aFlags);
6121 CheckComArgMaybeNull(aValue);
6122
6123 AutoCaller autoCaller(this);
6124 if (FAILED(autoCaller.rc()))
6125 return autoCaller.rc();
6126
6127 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6128 if (rc == E_ACCESSDENIED)
6129 /* The VM is not running or the service is not (yet) accessible */
6130 rc = setGuestPropertyToService(aName, aValue, aFlags);
6131 return rc;
6132#endif // VBOX_WITH_GUEST_PROPS
6133}
6134
6135STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6136{
6137 return SetGuestProperty(aName, aValue, NULL);
6138}
6139
6140STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6141{
6142 return SetGuestProperty(aName, NULL, NULL);
6143}
6144
6145#ifdef VBOX_WITH_GUEST_PROPS
6146/**
6147 * Enumerate the guest properties in VBoxSVC's internal structures.
6148 */
6149HRESULT Machine::enumerateGuestPropertiesInService
6150 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6151 ComSafeArrayOut(BSTR, aValues),
6152 ComSafeArrayOut(LONG64, aTimestamps),
6153 ComSafeArrayOut(BSTR, aFlags))
6154{
6155 using namespace guestProp;
6156
6157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6158 Utf8Str strPatterns(aPatterns);
6159
6160 HWData::GuestPropertyMap propMap;
6161
6162 /*
6163 * Look for matching patterns and build up a list.
6164 */
6165 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6166 while (it != mHWData->mGuestProperties.end())
6167 {
6168 if ( strPatterns.isEmpty()
6169 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6170 RTSTR_MAX,
6171 it->first.c_str(),
6172 RTSTR_MAX,
6173 NULL)
6174 )
6175 {
6176 propMap.insert(*it);
6177 }
6178
6179 it++;
6180 }
6181
6182 alock.release();
6183
6184 /*
6185 * And build up the arrays for returning the property information.
6186 */
6187 size_t cEntries = propMap.size();
6188 SafeArray<BSTR> names(cEntries);
6189 SafeArray<BSTR> values(cEntries);
6190 SafeArray<LONG64> timestamps(cEntries);
6191 SafeArray<BSTR> flags(cEntries);
6192 size_t iProp = 0;
6193
6194 it = propMap.begin();
6195 while (it != propMap.end())
6196 {
6197 char szFlags[MAX_FLAGS_LEN + 1];
6198 it->first.cloneTo(&names[iProp]);
6199 it->second.strValue.cloneTo(&values[iProp]);
6200 timestamps[iProp] = it->second.mTimestamp;
6201 writeFlags(it->second.mFlags, szFlags);
6202 Bstr(szFlags).cloneTo(&flags[iProp++]);
6203 it++;
6204 }
6205 names.detachTo(ComSafeArrayOutArg(aNames));
6206 values.detachTo(ComSafeArrayOutArg(aValues));
6207 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6208 flags.detachTo(ComSafeArrayOutArg(aFlags));
6209 return S_OK;
6210}
6211
6212/**
6213 * Enumerate the properties managed by a VM.
6214 * @returns E_ACCESSDENIED if the VM process is not available or not
6215 * currently handling queries and the setting should then be done in
6216 * VBoxSVC.
6217 */
6218HRESULT Machine::enumerateGuestPropertiesOnVM
6219 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6220 ComSafeArrayOut(BSTR, aValues),
6221 ComSafeArrayOut(LONG64, aTimestamps),
6222 ComSafeArrayOut(BSTR, aFlags))
6223{
6224 HRESULT rc;
6225 ComPtr<IInternalSessionControl> directControl;
6226 directControl = mData->mSession.mDirectControl;
6227
6228 if (!directControl)
6229 rc = E_ACCESSDENIED;
6230 else
6231 rc = directControl->EnumerateGuestProperties
6232 (aPatterns, ComSafeArrayOutArg(aNames),
6233 ComSafeArrayOutArg(aValues),
6234 ComSafeArrayOutArg(aTimestamps),
6235 ComSafeArrayOutArg(aFlags));
6236 return rc;
6237}
6238#endif // VBOX_WITH_GUEST_PROPS
6239
6240STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6241 ComSafeArrayOut(BSTR, aNames),
6242 ComSafeArrayOut(BSTR, aValues),
6243 ComSafeArrayOut(LONG64, aTimestamps),
6244 ComSafeArrayOut(BSTR, aFlags))
6245{
6246#ifndef VBOX_WITH_GUEST_PROPS
6247 ReturnComNotImplemented();
6248#else // VBOX_WITH_GUEST_PROPS
6249 CheckComArgMaybeNull(aPatterns);
6250 CheckComArgOutSafeArrayPointerValid(aNames);
6251 CheckComArgOutSafeArrayPointerValid(aValues);
6252 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6253 CheckComArgOutSafeArrayPointerValid(aFlags);
6254
6255 AutoCaller autoCaller(this);
6256 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6257
6258 HRESULT rc = enumerateGuestPropertiesOnVM
6259 (aPatterns, ComSafeArrayOutArg(aNames),
6260 ComSafeArrayOutArg(aValues),
6261 ComSafeArrayOutArg(aTimestamps),
6262 ComSafeArrayOutArg(aFlags));
6263 if (rc == E_ACCESSDENIED)
6264 /* The VM is not running or the service is not (yet) accessible */
6265 rc = enumerateGuestPropertiesInService
6266 (aPatterns, ComSafeArrayOutArg(aNames),
6267 ComSafeArrayOutArg(aValues),
6268 ComSafeArrayOutArg(aTimestamps),
6269 ComSafeArrayOutArg(aFlags));
6270 return rc;
6271#endif // VBOX_WITH_GUEST_PROPS
6272}
6273
6274STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6275 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6276{
6277 MediaData::AttachmentList atts;
6278
6279 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6280 if (FAILED(rc)) return rc;
6281
6282 SafeIfaceArray<IMediumAttachment> attachments(atts);
6283 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6284
6285 return S_OK;
6286}
6287
6288STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6289 LONG aControllerPort,
6290 LONG aDevice,
6291 IMediumAttachment **aAttachment)
6292{
6293 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6294 aControllerName, aControllerPort, aDevice));
6295
6296 CheckComArgStrNotEmptyOrNull(aControllerName);
6297 CheckComArgOutPointerValid(aAttachment);
6298
6299 AutoCaller autoCaller(this);
6300 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6301
6302 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6303
6304 *aAttachment = NULL;
6305
6306 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6307 aControllerName,
6308 aControllerPort,
6309 aDevice);
6310 if (pAttach.isNull())
6311 return setError(VBOX_E_OBJECT_NOT_FOUND,
6312 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6313 aDevice, aControllerPort, aControllerName);
6314
6315 pAttach.queryInterfaceTo(aAttachment);
6316
6317 return S_OK;
6318}
6319
6320STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6321 StorageBus_T aConnectionType,
6322 IStorageController **controller)
6323{
6324 CheckComArgStrNotEmptyOrNull(aName);
6325
6326 if ( (aConnectionType <= StorageBus_Null)
6327 || (aConnectionType > StorageBus_SAS))
6328 return setError(E_INVALIDARG,
6329 tr("Invalid connection type: %d"),
6330 aConnectionType);
6331
6332 AutoCaller autoCaller(this);
6333 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6334
6335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6336
6337 HRESULT rc = checkStateDependency(MutableStateDep);
6338 if (FAILED(rc)) return rc;
6339
6340 /* try to find one with the name first. */
6341 ComObjPtr<StorageController> ctrl;
6342
6343 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6344 if (SUCCEEDED(rc))
6345 return setError(VBOX_E_OBJECT_IN_USE,
6346 tr("Storage controller named '%ls' already exists"),
6347 aName);
6348
6349 ctrl.createObject();
6350
6351 /* get a new instance number for the storage controller */
6352 ULONG ulInstance = 0;
6353 bool fBootable = true;
6354 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6355 it != mStorageControllers->end();
6356 ++it)
6357 {
6358 if ((*it)->getStorageBus() == aConnectionType)
6359 {
6360 ULONG ulCurInst = (*it)->getInstance();
6361
6362 if (ulCurInst >= ulInstance)
6363 ulInstance = ulCurInst + 1;
6364
6365 /* Only one controller of each type can be marked as bootable. */
6366 if ((*it)->getBootable())
6367 fBootable = false;
6368 }
6369 }
6370
6371 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6372 if (FAILED(rc)) return rc;
6373
6374 setModified(IsModified_Storage);
6375 mStorageControllers.backup();
6376 mStorageControllers->push_back(ctrl);
6377
6378 ctrl.queryInterfaceTo(controller);
6379
6380 /* inform the direct session if any */
6381 alock.release();
6382 onStorageControllerChange();
6383
6384 return S_OK;
6385}
6386
6387STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6388 IStorageController **aStorageController)
6389{
6390 CheckComArgStrNotEmptyOrNull(aName);
6391
6392 AutoCaller autoCaller(this);
6393 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6394
6395 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6396
6397 ComObjPtr<StorageController> ctrl;
6398
6399 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6400 if (SUCCEEDED(rc))
6401 ctrl.queryInterfaceTo(aStorageController);
6402
6403 return rc;
6404}
6405
6406STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6407 IStorageController **aStorageController)
6408{
6409 AutoCaller autoCaller(this);
6410 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6411
6412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6413
6414 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6415 it != mStorageControllers->end();
6416 ++it)
6417 {
6418 if ((*it)->getInstance() == aInstance)
6419 {
6420 (*it).queryInterfaceTo(aStorageController);
6421 return S_OK;
6422 }
6423 }
6424
6425 return setError(VBOX_E_OBJECT_NOT_FOUND,
6426 tr("Could not find a storage controller with instance number '%lu'"),
6427 aInstance);
6428}
6429
6430STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6431{
6432 AutoCaller autoCaller(this);
6433 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6434
6435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6436
6437 HRESULT rc = checkStateDependency(MutableStateDep);
6438 if (FAILED(rc)) return rc;
6439
6440 ComObjPtr<StorageController> ctrl;
6441
6442 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6443 if (SUCCEEDED(rc))
6444 {
6445 /* Ensure that only one controller of each type is marked as bootable. */
6446 if (fBootable == TRUE)
6447 {
6448 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6449 it != mStorageControllers->end();
6450 ++it)
6451 {
6452 ComObjPtr<StorageController> aCtrl = (*it);
6453
6454 if ( (aCtrl->getName() != Utf8Str(aName))
6455 && aCtrl->getBootable() == TRUE
6456 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6457 && aCtrl->getControllerType() == ctrl->getControllerType())
6458 {
6459 aCtrl->setBootable(FALSE);
6460 break;
6461 }
6462 }
6463 }
6464
6465 if (SUCCEEDED(rc))
6466 {
6467 ctrl->setBootable(fBootable);
6468 setModified(IsModified_Storage);
6469 }
6470 }
6471
6472 if (SUCCEEDED(rc))
6473 {
6474 /* inform the direct session if any */
6475 alock.release();
6476 onStorageControllerChange();
6477 }
6478
6479 return rc;
6480}
6481
6482STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6483{
6484 CheckComArgStrNotEmptyOrNull(aName);
6485
6486 AutoCaller autoCaller(this);
6487 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6488
6489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6490
6491 HRESULT rc = checkStateDependency(MutableStateDep);
6492 if (FAILED(rc)) return rc;
6493
6494 ComObjPtr<StorageController> ctrl;
6495 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6496 if (FAILED(rc)) return rc;
6497
6498 {
6499 /* find all attached devices to the appropriate storage controller and detach them all */
6500 // make a temporary list because detachDevice invalidates iterators into
6501 // mMediaData->mAttachments
6502 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6503
6504 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6505 it != llAttachments2.end();
6506 ++it)
6507 {
6508 MediumAttachment *pAttachTemp = *it;
6509
6510 AutoCaller localAutoCaller(pAttachTemp);
6511 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6512
6513 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6514
6515 if (pAttachTemp->getControllerName() == aName)
6516 {
6517 rc = detachDevice(pAttachTemp, alock, NULL);
6518 if (FAILED(rc)) return rc;
6519 }
6520 }
6521 }
6522
6523 /* We can remove it now. */
6524 setModified(IsModified_Storage);
6525 mStorageControllers.backup();
6526
6527 ctrl->unshare();
6528
6529 mStorageControllers->remove(ctrl);
6530
6531 /* inform the direct session if any */
6532 alock.release();
6533 onStorageControllerChange();
6534
6535 return S_OK;
6536}
6537
6538STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType,
6539 IUSBController **controller)
6540{
6541 if ( (aType <= USBControllerType_Null)
6542 || (aType >= USBControllerType_Last))
6543 return setError(E_INVALIDARG,
6544 tr("Invalid USB controller type: %d"),
6545 aType);
6546
6547 AutoCaller autoCaller(this);
6548 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6549
6550 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6551
6552 HRESULT rc = checkStateDependency(MutableStateDep);
6553 if (FAILED(rc)) return rc;
6554
6555 /* try to find one with the same type first. */
6556 ComObjPtr<USBController> ctrl;
6557
6558 rc = getUSBControllerByName(aName, ctrl, false /* aSetError */);
6559 if (SUCCEEDED(rc))
6560 return setError(VBOX_E_OBJECT_IN_USE,
6561 tr("USB controller named '%ls' already exists"),
6562 aName);
6563
6564 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6565 ULONG maxInstances;
6566 rc = mParent->getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6567 if (FAILED(rc))
6568 return rc;
6569
6570 ULONG cInstances = getUSBControllerCountByType(aType);
6571 if (cInstances >= maxInstances)
6572 return setError(E_INVALIDARG,
6573 tr("Too many USB controllers of this type"));
6574
6575 ctrl.createObject();
6576
6577 rc = ctrl->init(this, aName, aType);
6578 if (FAILED(rc)) return rc;
6579
6580 setModified(IsModified_USB);
6581 mUSBControllers.backup();
6582 mUSBControllers->push_back(ctrl);
6583
6584 ctrl.queryInterfaceTo(controller);
6585
6586 /* inform the direct session if any */
6587 alock.release();
6588 onUSBControllerChange();
6589
6590 return S_OK;
6591}
6592
6593STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController)
6594{
6595 CheckComArgStrNotEmptyOrNull(aName);
6596
6597 AutoCaller autoCaller(this);
6598 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6599
6600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6601
6602 ComObjPtr<USBController> ctrl;
6603
6604 HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6605 if (SUCCEEDED(rc))
6606 ctrl.queryInterfaceTo(aUSBController);
6607
6608 return rc;
6609}
6610
6611STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType,
6612 ULONG *aControllers)
6613{
6614 CheckComArgOutPointerValid(aControllers);
6615
6616 if ( (aType <= USBControllerType_Null)
6617 || (aType >= USBControllerType_Last))
6618 return setError(E_INVALIDARG,
6619 tr("Invalid USB controller type: %d"),
6620 aType);
6621
6622 AutoCaller autoCaller(this);
6623 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6624
6625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6626
6627 ComObjPtr<USBController> ctrl;
6628
6629 *aControllers = getUSBControllerCountByType(aType);
6630
6631 return S_OK;
6632}
6633
6634STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName)
6635{
6636 CheckComArgStrNotEmptyOrNull(aName);
6637
6638 AutoCaller autoCaller(this);
6639 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6640
6641 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6642
6643 HRESULT rc = checkStateDependency(MutableStateDep);
6644 if (FAILED(rc)) return rc;
6645
6646 ComObjPtr<USBController> ctrl;
6647 rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6648 if (FAILED(rc)) return rc;
6649
6650 setModified(IsModified_USB);
6651 mUSBControllers.backup();
6652
6653 ctrl->unshare();
6654
6655 mUSBControllers->remove(ctrl);
6656
6657 /* inform the direct session if any */
6658 alock.release();
6659 onUSBControllerChange();
6660
6661 return S_OK;
6662}
6663
6664STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6665 ULONG *puOriginX,
6666 ULONG *puOriginY,
6667 ULONG *puWidth,
6668 ULONG *puHeight,
6669 BOOL *pfEnabled)
6670{
6671 LogFlowThisFunc(("\n"));
6672
6673 CheckComArgNotNull(puOriginX);
6674 CheckComArgNotNull(puOriginY);
6675 CheckComArgNotNull(puWidth);
6676 CheckComArgNotNull(puHeight);
6677 CheckComArgNotNull(pfEnabled);
6678
6679 uint32_t u32OriginX= 0;
6680 uint32_t u32OriginY= 0;
6681 uint32_t u32Width = 0;
6682 uint32_t u32Height = 0;
6683 uint16_t u16Flags = 0;
6684
6685 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6686 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6687 if (RT_FAILURE(vrc))
6688 {
6689#ifdef RT_OS_WINDOWS
6690 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6691 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6692 * So just assign fEnable to TRUE again.
6693 * The right fix would be to change GUI API wrappers to make sure that parameters
6694 * are changed only if API succeeds.
6695 */
6696 *pfEnabled = TRUE;
6697#endif
6698 return setError(VBOX_E_IPRT_ERROR,
6699 tr("Saved guest size is not available (%Rrc)"),
6700 vrc);
6701 }
6702
6703 *puOriginX = u32OriginX;
6704 *puOriginY = u32OriginY;
6705 *puWidth = u32Width;
6706 *puHeight = u32Height;
6707 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6708
6709 return S_OK;
6710}
6711
6712STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6713{
6714 LogFlowThisFunc(("\n"));
6715
6716 CheckComArgNotNull(aSize);
6717 CheckComArgNotNull(aWidth);
6718 CheckComArgNotNull(aHeight);
6719
6720 if (aScreenId != 0)
6721 return E_NOTIMPL;
6722
6723 AutoCaller autoCaller(this);
6724 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6725
6726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6727
6728 uint8_t *pu8Data = NULL;
6729 uint32_t cbData = 0;
6730 uint32_t u32Width = 0;
6731 uint32_t u32Height = 0;
6732
6733 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6734
6735 if (RT_FAILURE(vrc))
6736 return setError(VBOX_E_IPRT_ERROR,
6737 tr("Saved screenshot data is not available (%Rrc)"),
6738 vrc);
6739
6740 *aSize = cbData;
6741 *aWidth = u32Width;
6742 *aHeight = u32Height;
6743
6744 freeSavedDisplayScreenshot(pu8Data);
6745
6746 return S_OK;
6747}
6748
6749STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6750{
6751 LogFlowThisFunc(("\n"));
6752
6753 CheckComArgNotNull(aWidth);
6754 CheckComArgNotNull(aHeight);
6755 CheckComArgOutSafeArrayPointerValid(aData);
6756
6757 if (aScreenId != 0)
6758 return E_NOTIMPL;
6759
6760 AutoCaller autoCaller(this);
6761 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6762
6763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6764
6765 uint8_t *pu8Data = NULL;
6766 uint32_t cbData = 0;
6767 uint32_t u32Width = 0;
6768 uint32_t u32Height = 0;
6769
6770 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6771
6772 if (RT_FAILURE(vrc))
6773 return setError(VBOX_E_IPRT_ERROR,
6774 tr("Saved screenshot data is not available (%Rrc)"),
6775 vrc);
6776
6777 *aWidth = u32Width;
6778 *aHeight = u32Height;
6779
6780 com::SafeArray<BYTE> bitmap(cbData);
6781 /* Convert pixels to format expected by the API caller. */
6782 if (aBGR)
6783 {
6784 /* [0] B, [1] G, [2] R, [3] A. */
6785 for (unsigned i = 0; i < cbData; i += 4)
6786 {
6787 bitmap[i] = pu8Data[i];
6788 bitmap[i + 1] = pu8Data[i + 1];
6789 bitmap[i + 2] = pu8Data[i + 2];
6790 bitmap[i + 3] = 0xff;
6791 }
6792 }
6793 else
6794 {
6795 /* [0] R, [1] G, [2] B, [3] A. */
6796 for (unsigned i = 0; i < cbData; i += 4)
6797 {
6798 bitmap[i] = pu8Data[i + 2];
6799 bitmap[i + 1] = pu8Data[i + 1];
6800 bitmap[i + 2] = pu8Data[i];
6801 bitmap[i + 3] = 0xff;
6802 }
6803 }
6804 bitmap.detachTo(ComSafeArrayOutArg(aData));
6805
6806 freeSavedDisplayScreenshot(pu8Data);
6807
6808 return S_OK;
6809}
6810
6811
6812STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6813{
6814 LogFlowThisFunc(("\n"));
6815
6816 CheckComArgNotNull(aWidth);
6817 CheckComArgNotNull(aHeight);
6818 CheckComArgOutSafeArrayPointerValid(aData);
6819
6820 if (aScreenId != 0)
6821 return E_NOTIMPL;
6822
6823 AutoCaller autoCaller(this);
6824 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6825
6826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6827
6828 uint8_t *pu8Data = NULL;
6829 uint32_t cbData = 0;
6830 uint32_t u32Width = 0;
6831 uint32_t u32Height = 0;
6832
6833 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6834
6835 if (RT_FAILURE(vrc))
6836 return setError(VBOX_E_IPRT_ERROR,
6837 tr("Saved screenshot data is not available (%Rrc)"),
6838 vrc);
6839
6840 *aWidth = u32Width;
6841 *aHeight = u32Height;
6842
6843 HRESULT rc = S_OK;
6844 uint8_t *pu8PNG = NULL;
6845 uint32_t cbPNG = 0;
6846 uint32_t cxPNG = 0;
6847 uint32_t cyPNG = 0;
6848
6849 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6850
6851 if (RT_SUCCESS(vrc))
6852 {
6853 com::SafeArray<BYTE> screenData(cbPNG);
6854 screenData.initFrom(pu8PNG, cbPNG);
6855 if (pu8PNG)
6856 RTMemFree(pu8PNG);
6857 screenData.detachTo(ComSafeArrayOutArg(aData));
6858 }
6859 else
6860 {
6861 if (pu8PNG)
6862 RTMemFree(pu8PNG);
6863 return setError(VBOX_E_IPRT_ERROR,
6864 tr("Could not convert screenshot to PNG (%Rrc)"),
6865 vrc);
6866 }
6867
6868 freeSavedDisplayScreenshot(pu8Data);
6869
6870 return rc;
6871}
6872
6873STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6874{
6875 LogFlowThisFunc(("\n"));
6876
6877 CheckComArgNotNull(aSize);
6878 CheckComArgNotNull(aWidth);
6879 CheckComArgNotNull(aHeight);
6880
6881 if (aScreenId != 0)
6882 return E_NOTIMPL;
6883
6884 AutoCaller autoCaller(this);
6885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6886
6887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6888
6889 uint8_t *pu8Data = NULL;
6890 uint32_t cbData = 0;
6891 uint32_t u32Width = 0;
6892 uint32_t u32Height = 0;
6893
6894 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6895
6896 if (RT_FAILURE(vrc))
6897 return setError(VBOX_E_IPRT_ERROR,
6898 tr("Saved screenshot data is not available (%Rrc)"),
6899 vrc);
6900
6901 *aSize = cbData;
6902 *aWidth = u32Width;
6903 *aHeight = u32Height;
6904
6905 freeSavedDisplayScreenshot(pu8Data);
6906
6907 return S_OK;
6908}
6909
6910STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6911{
6912 LogFlowThisFunc(("\n"));
6913
6914 CheckComArgNotNull(aWidth);
6915 CheckComArgNotNull(aHeight);
6916 CheckComArgOutSafeArrayPointerValid(aData);
6917
6918 if (aScreenId != 0)
6919 return E_NOTIMPL;
6920
6921 AutoCaller autoCaller(this);
6922 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6923
6924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6925
6926 uint8_t *pu8Data = NULL;
6927 uint32_t cbData = 0;
6928 uint32_t u32Width = 0;
6929 uint32_t u32Height = 0;
6930
6931 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6932
6933 if (RT_FAILURE(vrc))
6934 return setError(VBOX_E_IPRT_ERROR,
6935 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6936 vrc);
6937
6938 *aWidth = u32Width;
6939 *aHeight = u32Height;
6940
6941 com::SafeArray<BYTE> png(cbData);
6942 png.initFrom(pu8Data, cbData);
6943 png.detachTo(ComSafeArrayOutArg(aData));
6944
6945 freeSavedDisplayScreenshot(pu8Data);
6946
6947 return S_OK;
6948}
6949
6950STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6951{
6952 HRESULT rc = S_OK;
6953 LogFlowThisFunc(("\n"));
6954
6955 AutoCaller autoCaller(this);
6956 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6957
6958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6959
6960 if (!mHWData->mCPUHotPlugEnabled)
6961 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6962
6963 if (aCpu >= mHWData->mCPUCount)
6964 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6965
6966 if (mHWData->mCPUAttached[aCpu])
6967 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6968
6969 alock.release();
6970 rc = onCPUChange(aCpu, false);
6971 alock.acquire();
6972 if (FAILED(rc)) return rc;
6973
6974 setModified(IsModified_MachineData);
6975 mHWData.backup();
6976 mHWData->mCPUAttached[aCpu] = true;
6977
6978 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6979 if (Global::IsOnline(mData->mMachineState))
6980 saveSettings(NULL);
6981
6982 return S_OK;
6983}
6984
6985STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6986{
6987 HRESULT rc = S_OK;
6988 LogFlowThisFunc(("\n"));
6989
6990 AutoCaller autoCaller(this);
6991 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6992
6993 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6994
6995 if (!mHWData->mCPUHotPlugEnabled)
6996 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6997
6998 if (aCpu >= SchemaDefs::MaxCPUCount)
6999 return setError(E_INVALIDARG,
7000 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
7001 SchemaDefs::MaxCPUCount);
7002
7003 if (!mHWData->mCPUAttached[aCpu])
7004 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
7005
7006 /* CPU 0 can't be detached */
7007 if (aCpu == 0)
7008 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
7009
7010 alock.release();
7011 rc = onCPUChange(aCpu, true);
7012 alock.acquire();
7013 if (FAILED(rc)) return rc;
7014
7015 setModified(IsModified_MachineData);
7016 mHWData.backup();
7017 mHWData->mCPUAttached[aCpu] = false;
7018
7019 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7020 if (Global::IsOnline(mData->mMachineState))
7021 saveSettings(NULL);
7022
7023 return S_OK;
7024}
7025
7026STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
7027{
7028 LogFlowThisFunc(("\n"));
7029
7030 CheckComArgNotNull(aCpuAttached);
7031
7032 *aCpuAttached = false;
7033
7034 AutoCaller autoCaller(this);
7035 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7036
7037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7038
7039 /* If hotplug is enabled the CPU is always enabled. */
7040 if (!mHWData->mCPUHotPlugEnabled)
7041 {
7042 if (aCpu < mHWData->mCPUCount)
7043 *aCpuAttached = true;
7044 }
7045 else
7046 {
7047 if (aCpu < SchemaDefs::MaxCPUCount)
7048 *aCpuAttached = mHWData->mCPUAttached[aCpu];
7049 }
7050
7051 return S_OK;
7052}
7053
7054STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
7055{
7056 CheckComArgOutPointerValid(aName);
7057
7058 AutoCaller autoCaller(this);
7059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7060
7061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7062
7063 Utf8Str log = queryLogFilename(aIdx);
7064 if (!RTFileExists(log.c_str()))
7065 log.setNull();
7066 log.cloneTo(aName);
7067
7068 return S_OK;
7069}
7070
7071STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
7072{
7073 LogFlowThisFunc(("\n"));
7074 CheckComArgOutSafeArrayPointerValid(aData);
7075 if (aSize < 0)
7076 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
7077
7078 AutoCaller autoCaller(this);
7079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7080
7081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7082
7083 HRESULT rc = S_OK;
7084 Utf8Str log = queryLogFilename(aIdx);
7085
7086 /* do not unnecessarily hold the lock while doing something which does
7087 * not need the lock and potentially takes a long time. */
7088 alock.release();
7089
7090 /* Limit the chunk size to 32K for now, as that gives better performance
7091 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
7092 * One byte expands to approx. 25 bytes of breathtaking XML. */
7093 size_t cbData = (size_t)RT_MIN(aSize, 32768);
7094 com::SafeArray<BYTE> logData(cbData);
7095
7096 RTFILE LogFile;
7097 int vrc = RTFileOpen(&LogFile, log.c_str(),
7098 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
7099 if (RT_SUCCESS(vrc))
7100 {
7101 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
7102 if (RT_SUCCESS(vrc))
7103 logData.resize(cbData);
7104 else
7105 rc = setError(VBOX_E_IPRT_ERROR,
7106 tr("Could not read log file '%s' (%Rrc)"),
7107 log.c_str(), vrc);
7108 RTFileClose(LogFile);
7109 }
7110 else
7111 rc = setError(VBOX_E_IPRT_ERROR,
7112 tr("Could not open log file '%s' (%Rrc)"),
7113 log.c_str(), vrc);
7114
7115 if (FAILED(rc))
7116 logData.resize(0);
7117 logData.detachTo(ComSafeArrayOutArg(aData));
7118
7119 return rc;
7120}
7121
7122
7123/**
7124 * Currently this method doesn't attach device to the running VM,
7125 * just makes sure it's plugged on next VM start.
7126 */
7127STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
7128{
7129 AutoCaller autoCaller(this);
7130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7131
7132 // lock scope
7133 {
7134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7135
7136 HRESULT rc = checkStateDependency(MutableStateDep);
7137 if (FAILED(rc)) return rc;
7138
7139 ChipsetType_T aChipset = ChipsetType_PIIX3;
7140 COMGETTER(ChipsetType)(&aChipset);
7141
7142 if (aChipset != ChipsetType_ICH9)
7143 {
7144 return setError(E_INVALIDARG,
7145 tr("Host PCI attachment only supported with ICH9 chipset"));
7146 }
7147
7148 // check if device with this host PCI address already attached
7149 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7150 it != mHWData->mPCIDeviceAssignments.end();
7151 ++it)
7152 {
7153 LONG iHostAddress = -1;
7154 ComPtr<PCIDeviceAttachment> pAttach;
7155 pAttach = *it;
7156 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7157 if (iHostAddress == hostAddress)
7158 return setError(E_INVALIDARG,
7159 tr("Device with host PCI address already attached to this VM"));
7160 }
7161
7162 ComObjPtr<PCIDeviceAttachment> pda;
7163 char name[32];
7164
7165 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7166 Bstr bname(name);
7167 pda.createObject();
7168 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7169 setModified(IsModified_MachineData);
7170 mHWData.backup();
7171 mHWData->mPCIDeviceAssignments.push_back(pda);
7172 }
7173
7174 return S_OK;
7175}
7176
7177/**
7178 * Currently this method doesn't detach device from the running VM,
7179 * just makes sure it's not plugged on next VM start.
7180 */
7181STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7182{
7183 AutoCaller autoCaller(this);
7184 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7185
7186 ComObjPtr<PCIDeviceAttachment> pAttach;
7187 bool fRemoved = false;
7188 HRESULT rc;
7189
7190 // lock scope
7191 {
7192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7193
7194 rc = checkStateDependency(MutableStateDep);
7195 if (FAILED(rc)) return rc;
7196
7197 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7198 it != mHWData->mPCIDeviceAssignments.end();
7199 ++it)
7200 {
7201 LONG iHostAddress = -1;
7202 pAttach = *it;
7203 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7204 if (iHostAddress != -1 && iHostAddress == hostAddress)
7205 {
7206 setModified(IsModified_MachineData);
7207 mHWData.backup();
7208 mHWData->mPCIDeviceAssignments.remove(pAttach);
7209 fRemoved = true;
7210 break;
7211 }
7212 }
7213 }
7214
7215
7216 /* Fire event outside of the lock */
7217 if (fRemoved)
7218 {
7219 Assert(!pAttach.isNull());
7220 ComPtr<IEventSource> es;
7221 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7222 Assert(SUCCEEDED(rc));
7223 Bstr mid;
7224 rc = this->COMGETTER(Id)(mid.asOutParam());
7225 Assert(SUCCEEDED(rc));
7226 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7227 }
7228
7229 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7230 tr("No host PCI device %08x attached"),
7231 hostAddress
7232 );
7233}
7234
7235STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7236{
7237 CheckComArgOutSafeArrayPointerValid(aAssignments);
7238
7239 AutoCaller autoCaller(this);
7240 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7241
7242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7243
7244 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7245 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7246
7247 return S_OK;
7248}
7249
7250STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7251{
7252 CheckComArgOutPointerValid(aBandwidthControl);
7253
7254 AutoCaller autoCaller(this);
7255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7256
7257 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7258
7259 return S_OK;
7260}
7261
7262STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7263{
7264 CheckComArgOutPointerValid(pfEnabled);
7265 AutoCaller autoCaller(this);
7266 HRESULT hrc = autoCaller.rc();
7267 if (SUCCEEDED(hrc))
7268 {
7269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7270 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7271 }
7272 return hrc;
7273}
7274
7275STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7276{
7277 AutoCaller autoCaller(this);
7278 HRESULT hrc = autoCaller.rc();
7279 if (SUCCEEDED(hrc))
7280 {
7281 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7282 hrc = checkStateDependency(MutableStateDep);
7283 if (SUCCEEDED(hrc))
7284 {
7285 hrc = mHWData.backupEx();
7286 if (SUCCEEDED(hrc))
7287 {
7288 setModified(IsModified_MachineData);
7289 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7290 }
7291 }
7292 }
7293 return hrc;
7294}
7295
7296STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7297{
7298 CheckComArgOutPointerValid(pbstrConfig);
7299 AutoCaller autoCaller(this);
7300 HRESULT hrc = autoCaller.rc();
7301 if (SUCCEEDED(hrc))
7302 {
7303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7304 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7305 }
7306 return hrc;
7307}
7308
7309STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7310{
7311 CheckComArgStr(bstrConfig);
7312 AutoCaller autoCaller(this);
7313 HRESULT hrc = autoCaller.rc();
7314 if (SUCCEEDED(hrc))
7315 {
7316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7317 hrc = checkStateDependency(MutableStateDep);
7318 if (SUCCEEDED(hrc))
7319 {
7320 hrc = mHWData.backupEx();
7321 if (SUCCEEDED(hrc))
7322 {
7323 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7324 if (SUCCEEDED(hrc))
7325 setModified(IsModified_MachineData);
7326 }
7327 }
7328 }
7329 return hrc;
7330
7331}
7332
7333STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7334{
7335 CheckComArgOutPointerValid(pfAllow);
7336 AutoCaller autoCaller(this);
7337 HRESULT hrc = autoCaller.rc();
7338 if (SUCCEEDED(hrc))
7339 {
7340 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7341 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7342 }
7343 return hrc;
7344}
7345
7346STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7347{
7348 AutoCaller autoCaller(this);
7349 HRESULT hrc = autoCaller.rc();
7350 if (SUCCEEDED(hrc))
7351 {
7352 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7353 hrc = checkStateDependency(MutableStateDep);
7354 if (SUCCEEDED(hrc))
7355 {
7356 hrc = mHWData.backupEx();
7357 if (SUCCEEDED(hrc))
7358 {
7359 setModified(IsModified_MachineData);
7360 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7361 }
7362 }
7363 }
7364 return hrc;
7365}
7366
7367STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7368{
7369 CheckComArgOutPointerValid(pfEnabled);
7370 AutoCaller autoCaller(this);
7371 HRESULT hrc = autoCaller.rc();
7372 if (SUCCEEDED(hrc))
7373 {
7374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7375 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7376 }
7377 return hrc;
7378}
7379
7380STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7381{
7382 AutoCaller autoCaller(this);
7383 HRESULT hrc = autoCaller.rc();
7384 if (SUCCEEDED(hrc))
7385 {
7386 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7387 hrc = checkStateDependency(MutableStateDep);
7388 if ( SUCCEEDED(hrc)
7389 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7390 {
7391 AutostartDb *autostartDb = mParent->getAutostartDb();
7392 int vrc;
7393
7394 if (fEnabled)
7395 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7396 else
7397 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7398
7399 if (RT_SUCCESS(vrc))
7400 {
7401 hrc = mHWData.backupEx();
7402 if (SUCCEEDED(hrc))
7403 {
7404 setModified(IsModified_MachineData);
7405 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7406 }
7407 }
7408 else if (vrc == VERR_NOT_SUPPORTED)
7409 hrc = setError(VBOX_E_NOT_SUPPORTED,
7410 tr("The VM autostart feature is not supported on this platform"));
7411 else if (vrc == VERR_PATH_NOT_FOUND)
7412 hrc = setError(E_FAIL,
7413 tr("The path to the autostart database is not set"));
7414 else
7415 hrc = setError(E_UNEXPECTED,
7416 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7417 fEnabled ? "Adding" : "Removing",
7418 mUserData->s.strName.c_str(), vrc);
7419 }
7420 }
7421 return hrc;
7422}
7423
7424STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7425{
7426 CheckComArgOutPointerValid(puDelay);
7427 AutoCaller autoCaller(this);
7428 HRESULT hrc = autoCaller.rc();
7429 if (SUCCEEDED(hrc))
7430 {
7431 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7432 *puDelay = mHWData->mAutostart.uAutostartDelay;
7433 }
7434 return hrc;
7435}
7436
7437STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7438{
7439 AutoCaller autoCaller(this);
7440 HRESULT hrc = autoCaller.rc();
7441 if (SUCCEEDED(hrc))
7442 {
7443 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7444 hrc = checkStateDependency(MutableStateDep);
7445 if (SUCCEEDED(hrc))
7446 {
7447 hrc = mHWData.backupEx();
7448 if (SUCCEEDED(hrc))
7449 {
7450 setModified(IsModified_MachineData);
7451 mHWData->mAutostart.uAutostartDelay = uDelay;
7452 }
7453 }
7454 }
7455 return hrc;
7456}
7457
7458STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7459{
7460 CheckComArgOutPointerValid(penmAutostopType);
7461 AutoCaller autoCaller(this);
7462 HRESULT hrc = autoCaller.rc();
7463 if (SUCCEEDED(hrc))
7464 {
7465 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7466 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7467 }
7468 return hrc;
7469}
7470
7471STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7472{
7473 AutoCaller autoCaller(this);
7474 HRESULT hrc = autoCaller.rc();
7475 if (SUCCEEDED(hrc))
7476 {
7477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7478 hrc = checkStateDependency(MutableStateDep);
7479 if ( SUCCEEDED(hrc)
7480 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7481 {
7482 AutostartDb *autostartDb = mParent->getAutostartDb();
7483 int vrc;
7484
7485 if (enmAutostopType != AutostopType_Disabled)
7486 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7487 else
7488 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7489
7490 if (RT_SUCCESS(vrc))
7491 {
7492 hrc = mHWData.backupEx();
7493 if (SUCCEEDED(hrc))
7494 {
7495 setModified(IsModified_MachineData);
7496 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7497 }
7498 }
7499 else if (vrc == VERR_NOT_SUPPORTED)
7500 hrc = setError(VBOX_E_NOT_SUPPORTED,
7501 tr("The VM autostop feature is not supported on this platform"));
7502 else if (vrc == VERR_PATH_NOT_FOUND)
7503 hrc = setError(E_FAIL,
7504 tr("The path to the autostart database is not set"));
7505 else
7506 hrc = setError(E_UNEXPECTED,
7507 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7508 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7509 mUserData->s.strName.c_str(), vrc);
7510 }
7511 }
7512 return hrc;
7513}
7514
7515STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7516{
7517 CheckComArgOutPointerValid(aDefaultFrontend);
7518 AutoCaller autoCaller(this);
7519 HRESULT hrc = autoCaller.rc();
7520 if (SUCCEEDED(hrc))
7521 {
7522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7523 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7524 }
7525 return hrc;
7526}
7527
7528STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7529{
7530 CheckComArgStr(aDefaultFrontend);
7531 AutoCaller autoCaller(this);
7532 HRESULT hrc = autoCaller.rc();
7533 if (SUCCEEDED(hrc))
7534 {
7535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7536 hrc = checkStateDependency(MutableOrSavedStateDep);
7537 if (SUCCEEDED(hrc))
7538 {
7539 hrc = mHWData.backupEx();
7540 if (SUCCEEDED(hrc))
7541 {
7542 setModified(IsModified_MachineData);
7543 mHWData->mDefaultFrontend = aDefaultFrontend;
7544 }
7545 }
7546 }
7547 return hrc;
7548}
7549
7550STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7551{
7552 CheckComArgSafeArrayNotNull(aIcon);
7553 CheckComArgOutSafeArrayPointerValid(aIcon);
7554 AutoCaller autoCaller(this);
7555 HRESULT hrc = autoCaller.rc();
7556 if (SUCCEEDED(hrc))
7557 {
7558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7559 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7560 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7561 icon.detachTo(ComSafeArrayOutArg(aIcon));
7562 }
7563 return hrc;
7564}
7565
7566STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7567{
7568 CheckComArgSafeArrayNotNull(aIcon);
7569 AutoCaller autoCaller(this);
7570 HRESULT hrc = autoCaller.rc();
7571 if (SUCCEEDED(hrc))
7572 {
7573 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7574 hrc = checkStateDependency(MutableOrSavedStateDep);
7575 if (SUCCEEDED(hrc))
7576 {
7577 setModified(IsModified_MachineData);
7578 mUserData.backup();
7579 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7580 mUserData->mIcon.resize(icon.size());
7581 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7582 }
7583 }
7584 return hrc;
7585}
7586
7587STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable)
7588{
7589 CheckComArgOutPointerValid(aAvailable);
7590
7591 AutoCaller autoCaller(this);
7592 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7593
7594#ifdef VBOX_WITH_USB
7595 *aAvailable = true;
7596#else
7597 *aAvailable = false;
7598#endif
7599 return S_OK;
7600}
7601
7602STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7603{
7604 LogFlowFuncEnter();
7605
7606 CheckComArgNotNull(pTarget);
7607 CheckComArgOutPointerValid(pProgress);
7608
7609 /* Convert the options. */
7610 RTCList<CloneOptions_T> optList;
7611 if (options != NULL)
7612 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7613
7614 if (optList.contains(CloneOptions_Link))
7615 {
7616 if (!isSnapshotMachine())
7617 return setError(E_INVALIDARG,
7618 tr("Linked clone can only be created from a snapshot"));
7619 if (mode != CloneMode_MachineState)
7620 return setError(E_INVALIDARG,
7621 tr("Linked clone can only be created for a single machine state"));
7622 }
7623 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7624
7625 AutoCaller autoCaller(this);
7626 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7627
7628
7629 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7630
7631 HRESULT rc = pWorker->start(pProgress);
7632
7633 LogFlowFuncLeave();
7634
7635 return rc;
7636}
7637
7638// public methods for internal purposes
7639/////////////////////////////////////////////////////////////////////////////
7640
7641/**
7642 * Adds the given IsModified_* flag to the dirty flags of the machine.
7643 * This must be called either during loadSettings or under the machine write lock.
7644 * @param fl
7645 */
7646void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7647{
7648 mData->flModifications |= fl;
7649 if (fAllowStateModification && isStateModificationAllowed())
7650 mData->mCurrentStateModified = true;
7651}
7652
7653/**
7654 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7655 * care of the write locking.
7656 *
7657 * @param fModifications The flag to add.
7658 */
7659void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7660{
7661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7662 setModified(fModification, fAllowStateModification);
7663}
7664
7665/**
7666 * Saves the registry entry of this machine to the given configuration node.
7667 *
7668 * @param aEntryNode Node to save the registry entry to.
7669 *
7670 * @note locks this object for reading.
7671 */
7672HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7673{
7674 AutoLimitedCaller autoCaller(this);
7675 AssertComRCReturnRC(autoCaller.rc());
7676
7677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7678
7679 data.uuid = mData->mUuid;
7680 data.strSettingsFile = mData->m_strConfigFile;
7681
7682 return S_OK;
7683}
7684
7685/**
7686 * Calculates the absolute path of the given path taking the directory of the
7687 * machine settings file as the current directory.
7688 *
7689 * @param aPath Path to calculate the absolute path for.
7690 * @param aResult Where to put the result (used only on success, can be the
7691 * same Utf8Str instance as passed in @a aPath).
7692 * @return IPRT result.
7693 *
7694 * @note Locks this object for reading.
7695 */
7696int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7697{
7698 AutoCaller autoCaller(this);
7699 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7700
7701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7702
7703 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7704
7705 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7706
7707 strSettingsDir.stripFilename();
7708 char folder[RTPATH_MAX];
7709 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7710 if (RT_SUCCESS(vrc))
7711 aResult = folder;
7712
7713 return vrc;
7714}
7715
7716/**
7717 * Copies strSource to strTarget, making it relative to the machine folder
7718 * if it is a subdirectory thereof, or simply copying it otherwise.
7719 *
7720 * @param strSource Path to evaluate and copy.
7721 * @param strTarget Buffer to receive target path.
7722 *
7723 * @note Locks this object for reading.
7724 */
7725void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7726 Utf8Str &strTarget)
7727{
7728 AutoCaller autoCaller(this);
7729 AssertComRCReturn(autoCaller.rc(), (void)0);
7730
7731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7732
7733 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7734 // use strTarget as a temporary buffer to hold the machine settings dir
7735 strTarget = mData->m_strConfigFileFull;
7736 strTarget.stripFilename();
7737 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7738 {
7739 // is relative: then append what's left
7740 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7741 // for empty paths (only possible for subdirs) use "." to avoid
7742 // triggering default settings for not present config attributes.
7743 if (strTarget.isEmpty())
7744 strTarget = ".";
7745 }
7746 else
7747 // is not relative: then overwrite
7748 strTarget = strSource;
7749}
7750
7751/**
7752 * Returns the full path to the machine's log folder in the
7753 * \a aLogFolder argument.
7754 */
7755void Machine::getLogFolder(Utf8Str &aLogFolder)
7756{
7757 AutoCaller autoCaller(this);
7758 AssertComRCReturnVoid(autoCaller.rc());
7759
7760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7761
7762 char szTmp[RTPATH_MAX];
7763 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7764 if (RT_SUCCESS(vrc))
7765 {
7766 if (szTmp[0] && !mUserData.isNull())
7767 {
7768 char szTmp2[RTPATH_MAX];
7769 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7770 if (RT_SUCCESS(vrc))
7771 aLogFolder = BstrFmt("%s%c%s",
7772 szTmp2,
7773 RTPATH_DELIMITER,
7774 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7775 }
7776 else
7777 vrc = VERR_PATH_IS_RELATIVE;
7778 }
7779
7780 if (RT_FAILURE(vrc))
7781 {
7782 // fallback if VBOX_USER_LOGHOME is not set or invalid
7783 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7784 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7785 aLogFolder.append(RTPATH_DELIMITER);
7786 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7787 }
7788}
7789
7790/**
7791 * Returns the full path to the machine's log file for an given index.
7792 */
7793Utf8Str Machine::queryLogFilename(ULONG idx)
7794{
7795 Utf8Str logFolder;
7796 getLogFolder(logFolder);
7797 Assert(logFolder.length());
7798 Utf8Str log;
7799 if (idx == 0)
7800 log = Utf8StrFmt("%s%cVBox.log",
7801 logFolder.c_str(), RTPATH_DELIMITER);
7802 else
7803 log = Utf8StrFmt("%s%cVBox.log.%d",
7804 logFolder.c_str(), RTPATH_DELIMITER, idx);
7805 return log;
7806}
7807
7808/**
7809 * Composes a unique saved state filename based on the current system time. The filename is
7810 * granular to the second so this will work so long as no more than one snapshot is taken on
7811 * a machine per second.
7812 *
7813 * Before version 4.1, we used this formula for saved state files:
7814 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7815 * which no longer works because saved state files can now be shared between the saved state of the
7816 * "saved" machine and an online snapshot, and the following would cause problems:
7817 * 1) save machine
7818 * 2) create online snapshot from that machine state --> reusing saved state file
7819 * 3) save machine again --> filename would be reused, breaking the online snapshot
7820 *
7821 * So instead we now use a timestamp.
7822 *
7823 * @param str
7824 */
7825void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7826{
7827 AutoCaller autoCaller(this);
7828 AssertComRCReturnVoid(autoCaller.rc());
7829
7830 {
7831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7832 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7833 }
7834
7835 RTTIMESPEC ts;
7836 RTTimeNow(&ts);
7837 RTTIME time;
7838 RTTimeExplode(&time, &ts);
7839
7840 strStateFilePath += RTPATH_DELIMITER;
7841 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7842 time.i32Year, time.u8Month, time.u8MonthDay,
7843 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7844}
7845
7846/**
7847 * Returns the full path to the default video capture file.
7848 */
7849void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7850{
7851 AutoCaller autoCaller(this);
7852 AssertComRCReturnVoid(autoCaller.rc());
7853
7854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7855
7856 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7857 strFile.stripExt(); // path/to/machinesfolder/vmname/vmname
7858 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7859}
7860
7861/**
7862 * Returns whether at least one USB controller is present for the VM.
7863 */
7864bool Machine::isUSBControllerPresent()
7865{
7866 AutoCaller autoCaller(this);
7867 AssertComRCReturn(autoCaller.rc(), false);
7868
7869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7870
7871 return (mUSBControllers->size() > 0);
7872}
7873
7874/**
7875 * @note Locks this object for writing, calls the client process
7876 * (inside the lock).
7877 */
7878HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7879 const Utf8Str &strFrontend,
7880 const Utf8Str &strEnvironment,
7881 ProgressProxy *aProgress)
7882{
7883 LogFlowThisFuncEnter();
7884
7885 AssertReturn(aControl, E_FAIL);
7886 AssertReturn(aProgress, E_FAIL);
7887 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7888
7889 AutoCaller autoCaller(this);
7890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7891
7892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7893
7894 if (!mData->mRegistered)
7895 return setError(E_UNEXPECTED,
7896 tr("The machine '%s' is not registered"),
7897 mUserData->s.strName.c_str());
7898
7899 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7900
7901 if ( mData->mSession.mState == SessionState_Locked
7902 || mData->mSession.mState == SessionState_Spawning
7903 || mData->mSession.mState == SessionState_Unlocking)
7904 return setError(VBOX_E_INVALID_OBJECT_STATE,
7905 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7906 mUserData->s.strName.c_str());
7907
7908 /* may not be busy */
7909 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7910
7911 /* get the path to the executable */
7912 char szPath[RTPATH_MAX];
7913 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7914 size_t sz = strlen(szPath);
7915 szPath[sz++] = RTPATH_DELIMITER;
7916 szPath[sz] = 0;
7917 char *cmd = szPath + sz;
7918 sz = sizeof(szPath) - sz;
7919
7920 int vrc = VINF_SUCCESS;
7921 RTPROCESS pid = NIL_RTPROCESS;
7922
7923 RTENV env = RTENV_DEFAULT;
7924
7925 if (!strEnvironment.isEmpty())
7926 {
7927 char *newEnvStr = NULL;
7928
7929 do
7930 {
7931 /* clone the current environment */
7932 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7933 AssertRCBreakStmt(vrc2, vrc = vrc2);
7934
7935 newEnvStr = RTStrDup(strEnvironment.c_str());
7936 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7937
7938 /* put new variables to the environment
7939 * (ignore empty variable names here since RTEnv API
7940 * intentionally doesn't do that) */
7941 char *var = newEnvStr;
7942 for (char *p = newEnvStr; *p; ++p)
7943 {
7944 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7945 {
7946 *p = '\0';
7947 if (*var)
7948 {
7949 char *val = strchr(var, '=');
7950 if (val)
7951 {
7952 *val++ = '\0';
7953 vrc2 = RTEnvSetEx(env, var, val);
7954 }
7955 else
7956 vrc2 = RTEnvUnsetEx(env, var);
7957 if (RT_FAILURE(vrc2))
7958 break;
7959 }
7960 var = p + 1;
7961 }
7962 }
7963 if (RT_SUCCESS(vrc2) && *var)
7964 vrc2 = RTEnvPutEx(env, var);
7965
7966 AssertRCBreakStmt(vrc2, vrc = vrc2);
7967 }
7968 while (0);
7969
7970 if (newEnvStr != NULL)
7971 RTStrFree(newEnvStr);
7972 }
7973
7974#ifdef VBOX_WITH_QTGUI
7975 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7976 {
7977# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7978 /* Modify the base path so that we don't need to use ".." below. */
7979 RTPathStripTrailingSlash(szPath);
7980 RTPathStripFilename(szPath);
7981 sz = strlen(szPath);
7982 cmd = szPath + sz;
7983 sz = sizeof(szPath) - sz;
7984
7985#define OSX_APP_NAME "VirtualBoxVM"
7986#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7987
7988 Utf8Str strAppOverride = getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7989 if ( strAppOverride.contains(".")
7990 || strAppOverride.contains("/")
7991 || strAppOverride.contains("\\")
7992 || strAppOverride.contains(":"))
7993 strAppOverride.setNull();
7994 Utf8Str strAppPath;
7995 if (!strAppOverride.isEmpty())
7996 {
7997 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7998 Utf8Str strFullPath(szPath);
7999 strFullPath.append(strAppPath);
8000 /* there is a race, but people using this deserve the failure */
8001 if (!RTFileExists(strFullPath.c_str()))
8002 strAppOverride.setNull();
8003 }
8004 if (strAppOverride.isEmpty())
8005 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
8006 const char *VirtualBox_exe = strAppPath.c_str();
8007 AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED);
8008# else
8009 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
8010 Assert(sz >= sizeof(VirtualBox_exe));
8011# endif
8012 strcpy(cmd, VirtualBox_exe);
8013
8014 Utf8Str idStr = mData->mUuid.toString();
8015 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
8016 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8017 }
8018#else /* !VBOX_WITH_QTGUI */
8019 if (0)
8020 ;
8021#endif /* VBOX_WITH_QTGUI */
8022
8023 else
8024
8025#ifdef VBOX_WITH_VBOXSDL
8026 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
8027 {
8028 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
8029 Assert(sz >= sizeof(VBoxSDL_exe));
8030 strcpy(cmd, VBoxSDL_exe);
8031
8032 Utf8Str idStr = mData->mUuid.toString();
8033 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
8034 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8035 }
8036#else /* !VBOX_WITH_VBOXSDL */
8037 if (0)
8038 ;
8039#endif /* !VBOX_WITH_VBOXSDL */
8040
8041 else
8042
8043#ifdef VBOX_WITH_HEADLESS
8044 if ( strFrontend == "headless"
8045 || strFrontend == "capture"
8046 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
8047 )
8048 {
8049 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
8050 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
8051 * and a VM works even if the server has not been installed.
8052 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
8053 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
8054 * differently in 4.0 and 3.x.
8055 */
8056 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
8057 Assert(sz >= sizeof(VBoxHeadless_exe));
8058 strcpy(cmd, VBoxHeadless_exe);
8059
8060 Utf8Str idStr = mData->mUuid.toString();
8061 /* Leave space for "--capture" arg. */
8062 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
8063 "--startvm", idStr.c_str(),
8064 "--vrde", "config",
8065 0, /* For "--capture". */
8066 0 };
8067 if (strFrontend == "capture")
8068 {
8069 unsigned pos = RT_ELEMENTS(args) - 2;
8070 args[pos] = "--capture";
8071 }
8072 vrc = RTProcCreate(szPath, args, env,
8073#ifdef RT_OS_WINDOWS
8074 RTPROC_FLAGS_NO_WINDOW
8075#else
8076 0
8077#endif
8078 , &pid);
8079 }
8080#else /* !VBOX_WITH_HEADLESS */
8081 if (0)
8082 ;
8083#endif /* !VBOX_WITH_HEADLESS */
8084 else
8085 {
8086 RTEnvDestroy(env);
8087 return setError(E_INVALIDARG,
8088 tr("Invalid frontend name: '%s'"),
8089 strFrontend.c_str());
8090 }
8091
8092 RTEnvDestroy(env);
8093
8094 if (RT_FAILURE(vrc))
8095 return setError(VBOX_E_IPRT_ERROR,
8096 tr("Could not launch a process for the machine '%s' (%Rrc)"),
8097 mUserData->s.strName.c_str(), vrc);
8098
8099 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
8100
8101 /*
8102 * Note that we don't release the lock here before calling the client,
8103 * because it doesn't need to call us back if called with a NULL argument.
8104 * Releasing the lock here is dangerous because we didn't prepare the
8105 * launch data yet, but the client we've just started may happen to be
8106 * too fast and call LockMachine() that will fail (because of PID, etc.),
8107 * so that the Machine will never get out of the Spawning session state.
8108 */
8109
8110 /* inform the session that it will be a remote one */
8111 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8112 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8113 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8114
8115 if (FAILED(rc))
8116 {
8117 /* restore the session state */
8118 mData->mSession.mState = SessionState_Unlocked;
8119 /* The failure may occur w/o any error info (from RPC), so provide one */
8120 return setError(VBOX_E_VM_ERROR,
8121 tr("Failed to assign the machine to the session (%Rrc)"), rc);
8122 }
8123
8124 /* attach launch data to the machine */
8125 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8126 mData->mSession.mRemoteControls.push_back(aControl);
8127 mData->mSession.mProgress = aProgress;
8128 mData->mSession.mPID = pid;
8129 mData->mSession.mState = SessionState_Spawning;
8130 mData->mSession.mType = strFrontend;
8131
8132 LogFlowThisFuncLeave();
8133 return S_OK;
8134}
8135
8136/**
8137 * Returns @c true if the given session machine instance has an open direct
8138 * session (and optionally also for direct sessions which are closing) and
8139 * returns the session control machine instance if so.
8140 *
8141 * Note that when the method returns @c false, the arguments remain unchanged.
8142 *
8143 * @param aMachine Session machine object.
8144 * @param aControl Direct session control object (optional).
8145 * @param aAllowClosing If true then additionally a session which is currently
8146 * being closed will also be allowed.
8147 *
8148 * @note locks this object for reading.
8149 */
8150bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8151 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8152 bool aAllowClosing /*= false*/)
8153{
8154 AutoLimitedCaller autoCaller(this);
8155 AssertComRCReturn(autoCaller.rc(), false);
8156
8157 /* just return false for inaccessible machines */
8158 if (autoCaller.state() != Ready)
8159 return false;
8160
8161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8162
8163 if ( mData->mSession.mState == SessionState_Locked
8164 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8165 )
8166 {
8167 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8168
8169 aMachine = mData->mSession.mMachine;
8170
8171 if (aControl != NULL)
8172 *aControl = mData->mSession.mDirectControl;
8173
8174 return true;
8175 }
8176
8177 return false;
8178}
8179
8180/**
8181 * Returns @c true if the given machine has an spawning direct session.
8182 *
8183 * @note locks this object for reading.
8184 */
8185bool Machine::isSessionSpawning()
8186{
8187 AutoLimitedCaller autoCaller(this);
8188 AssertComRCReturn(autoCaller.rc(), false);
8189
8190 /* just return false for inaccessible machines */
8191 if (autoCaller.state() != Ready)
8192 return false;
8193
8194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8195
8196 if (mData->mSession.mState == SessionState_Spawning)
8197 return true;
8198
8199 return false;
8200}
8201
8202/**
8203 * Called from the client watcher thread to check for unexpected client process
8204 * death during Session_Spawning state (e.g. before it successfully opened a
8205 * direct session).
8206 *
8207 * On Win32 and on OS/2, this method is called only when we've got the
8208 * direct client's process termination notification, so it always returns @c
8209 * true.
8210 *
8211 * On other platforms, this method returns @c true if the client process is
8212 * terminated and @c false if it's still alive.
8213 *
8214 * @note Locks this object for writing.
8215 */
8216bool Machine::checkForSpawnFailure()
8217{
8218 AutoCaller autoCaller(this);
8219 if (!autoCaller.isOk())
8220 {
8221 /* nothing to do */
8222 LogFlowThisFunc(("Already uninitialized!\n"));
8223 return true;
8224 }
8225
8226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8227
8228 if (mData->mSession.mState != SessionState_Spawning)
8229 {
8230 /* nothing to do */
8231 LogFlowThisFunc(("Not spawning any more!\n"));
8232 return true;
8233 }
8234
8235 HRESULT rc = S_OK;
8236
8237 /* PID not yet initialized, skip check. */
8238 if (mData->mSession.mPID == NIL_RTPROCESS)
8239 return false;
8240
8241 RTPROCSTATUS status;
8242 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8243
8244 if (vrc != VERR_PROCESS_RUNNING)
8245 {
8246 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8247 rc = setError(E_FAIL,
8248 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8249 getName().c_str(), status.iStatus);
8250 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8251 rc = setError(E_FAIL,
8252 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8253 getName().c_str(), status.iStatus);
8254 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8255 rc = setError(E_FAIL,
8256 tr("The virtual machine '%s' has terminated abnormally"),
8257 getName().c_str(), status.iStatus);
8258 else
8259 rc = setError(E_FAIL,
8260 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8261 getName().c_str(), rc);
8262 }
8263
8264 if (FAILED(rc))
8265 {
8266 /* Close the remote session, remove the remote control from the list
8267 * and reset session state to Closed (@note keep the code in sync with
8268 * the relevant part in LockMachine()). */
8269
8270 Assert(mData->mSession.mRemoteControls.size() == 1);
8271 if (mData->mSession.mRemoteControls.size() == 1)
8272 {
8273 ErrorInfoKeeper eik;
8274 mData->mSession.mRemoteControls.front()->Uninitialize();
8275 }
8276
8277 mData->mSession.mRemoteControls.clear();
8278 mData->mSession.mState = SessionState_Unlocked;
8279
8280 /* finalize the progress after setting the state */
8281 if (!mData->mSession.mProgress.isNull())
8282 {
8283 mData->mSession.mProgress->notifyComplete(rc);
8284 mData->mSession.mProgress.setNull();
8285 }
8286
8287 mData->mSession.mPID = NIL_RTPROCESS;
8288
8289 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8290 return true;
8291 }
8292
8293 return false;
8294}
8295
8296/**
8297 * Checks whether the machine can be registered. If so, commits and saves
8298 * all settings.
8299 *
8300 * @note Must be called from mParent's write lock. Locks this object and
8301 * children for writing.
8302 */
8303HRESULT Machine::prepareRegister()
8304{
8305 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8306
8307 AutoLimitedCaller autoCaller(this);
8308 AssertComRCReturnRC(autoCaller.rc());
8309
8310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8311
8312 /* wait for state dependents to drop to zero */
8313 ensureNoStateDependencies();
8314
8315 if (!mData->mAccessible)
8316 return setError(VBOX_E_INVALID_OBJECT_STATE,
8317 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8318 mUserData->s.strName.c_str(),
8319 mData->mUuid.toString().c_str());
8320
8321 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8322
8323 if (mData->mRegistered)
8324 return setError(VBOX_E_INVALID_OBJECT_STATE,
8325 tr("The machine '%s' with UUID {%s} is already registered"),
8326 mUserData->s.strName.c_str(),
8327 mData->mUuid.toString().c_str());
8328
8329 HRESULT rc = S_OK;
8330
8331 // Ensure the settings are saved. If we are going to be registered and
8332 // no config file exists yet, create it by calling saveSettings() too.
8333 if ( (mData->flModifications)
8334 || (!mData->pMachineConfigFile->fileExists())
8335 )
8336 {
8337 rc = saveSettings(NULL);
8338 // no need to check whether VirtualBox.xml needs saving too since
8339 // we can't have a machine XML file rename pending
8340 if (FAILED(rc)) return rc;
8341 }
8342
8343 /* more config checking goes here */
8344
8345 if (SUCCEEDED(rc))
8346 {
8347 /* we may have had implicit modifications we want to fix on success */
8348 commit();
8349
8350 mData->mRegistered = true;
8351 }
8352 else
8353 {
8354 /* we may have had implicit modifications we want to cancel on failure*/
8355 rollback(false /* aNotify */);
8356 }
8357
8358 return rc;
8359}
8360
8361/**
8362 * Increases the number of objects dependent on the machine state or on the
8363 * registered state. Guarantees that these two states will not change at least
8364 * until #releaseStateDependency() is called.
8365 *
8366 * Depending on the @a aDepType value, additional state checks may be made.
8367 * These checks will set extended error info on failure. See
8368 * #checkStateDependency() for more info.
8369 *
8370 * If this method returns a failure, the dependency is not added and the caller
8371 * is not allowed to rely on any particular machine state or registration state
8372 * value and may return the failed result code to the upper level.
8373 *
8374 * @param aDepType Dependency type to add.
8375 * @param aState Current machine state (NULL if not interested).
8376 * @param aRegistered Current registered state (NULL if not interested).
8377 *
8378 * @note Locks this object for writing.
8379 */
8380HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8381 MachineState_T *aState /* = NULL */,
8382 BOOL *aRegistered /* = NULL */)
8383{
8384 AutoCaller autoCaller(this);
8385 AssertComRCReturnRC(autoCaller.rc());
8386
8387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8388
8389 HRESULT rc = checkStateDependency(aDepType);
8390 if (FAILED(rc)) return rc;
8391
8392 {
8393 if (mData->mMachineStateChangePending != 0)
8394 {
8395 /* ensureNoStateDependencies() is waiting for state dependencies to
8396 * drop to zero so don't add more. It may make sense to wait a bit
8397 * and retry before reporting an error (since the pending state
8398 * transition should be really quick) but let's just assert for
8399 * now to see if it ever happens on practice. */
8400
8401 AssertFailed();
8402
8403 return setError(E_ACCESSDENIED,
8404 tr("Machine state change is in progress. Please retry the operation later."));
8405 }
8406
8407 ++mData->mMachineStateDeps;
8408 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8409 }
8410
8411 if (aState)
8412 *aState = mData->mMachineState;
8413 if (aRegistered)
8414 *aRegistered = mData->mRegistered;
8415
8416 return S_OK;
8417}
8418
8419/**
8420 * Decreases the number of objects dependent on the machine state.
8421 * Must always complete the #addStateDependency() call after the state
8422 * dependency is no more necessary.
8423 */
8424void Machine::releaseStateDependency()
8425{
8426 AutoCaller autoCaller(this);
8427 AssertComRCReturnVoid(autoCaller.rc());
8428
8429 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8430
8431 /* releaseStateDependency() w/o addStateDependency()? */
8432 AssertReturnVoid(mData->mMachineStateDeps != 0);
8433 -- mData->mMachineStateDeps;
8434
8435 if (mData->mMachineStateDeps == 0)
8436 {
8437 /* inform ensureNoStateDependencies() that there are no more deps */
8438 if (mData->mMachineStateChangePending != 0)
8439 {
8440 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8441 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8442 }
8443 }
8444}
8445
8446Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8447{
8448 /* start with nothing found */
8449 Utf8Str strResult("");
8450
8451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8452
8453 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8454 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8455 // found:
8456 strResult = it->second; // source is a Utf8Str
8457
8458 return strResult;
8459}
8460
8461// protected methods
8462/////////////////////////////////////////////////////////////////////////////
8463
8464/**
8465 * Performs machine state checks based on the @a aDepType value. If a check
8466 * fails, this method will set extended error info, otherwise it will return
8467 * S_OK. It is supposed, that on failure, the caller will immediately return
8468 * the return value of this method to the upper level.
8469 *
8470 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8471 *
8472 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8473 * current state of this machine object allows to change settings of the
8474 * machine (i.e. the machine is not registered, or registered but not running
8475 * and not saved). It is useful to call this method from Machine setters
8476 * before performing any change.
8477 *
8478 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8479 * as for MutableStateDep except that if the machine is saved, S_OK is also
8480 * returned. This is useful in setters which allow changing machine
8481 * properties when it is in the saved state.
8482 *
8483 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8484 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8485 * Aborted).
8486 *
8487 * @param aDepType Dependency type to check.
8488 *
8489 * @note Non Machine based classes should use #addStateDependency() and
8490 * #releaseStateDependency() methods or the smart AutoStateDependency
8491 * template.
8492 *
8493 * @note This method must be called from under this object's read or write
8494 * lock.
8495 */
8496HRESULT Machine::checkStateDependency(StateDependency aDepType)
8497{
8498 switch (aDepType)
8499 {
8500 case AnyStateDep:
8501 {
8502 break;
8503 }
8504 case MutableStateDep:
8505 {
8506 if ( mData->mRegistered
8507 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8508 || ( mData->mMachineState != MachineState_Paused
8509 && mData->mMachineState != MachineState_Running
8510 && mData->mMachineState != MachineState_Aborted
8511 && mData->mMachineState != MachineState_Teleported
8512 && mData->mMachineState != MachineState_PoweredOff
8513 )
8514 )
8515 )
8516 return setError(VBOX_E_INVALID_VM_STATE,
8517 tr("The machine is not mutable (state is %s)"),
8518 Global::stringifyMachineState(mData->mMachineState));
8519 break;
8520 }
8521 case MutableOrSavedStateDep:
8522 {
8523 if ( mData->mRegistered
8524 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8525 || ( mData->mMachineState != MachineState_Paused
8526 && mData->mMachineState != MachineState_Running
8527 && mData->mMachineState != MachineState_Aborted
8528 && mData->mMachineState != MachineState_Teleported
8529 && mData->mMachineState != MachineState_Saved
8530 && mData->mMachineState != MachineState_PoweredOff
8531 )
8532 )
8533 )
8534 return setError(VBOX_E_INVALID_VM_STATE,
8535 tr("The machine is not mutable (state is %s)"),
8536 Global::stringifyMachineState(mData->mMachineState));
8537 break;
8538 }
8539 case OfflineStateDep:
8540 {
8541 if ( mData->mRegistered
8542 && ( !isSessionMachine()
8543 || ( mData->mMachineState != MachineState_PoweredOff
8544 && mData->mMachineState != MachineState_Saved
8545 && mData->mMachineState != MachineState_Aborted
8546 && mData->mMachineState != MachineState_Teleported
8547 )
8548 )
8549 )
8550 return setError(VBOX_E_INVALID_VM_STATE,
8551 tr("The machine is not offline (state is %s)"),
8552 Global::stringifyMachineState(mData->mMachineState));
8553 break;
8554 }
8555 }
8556
8557 return S_OK;
8558}
8559
8560/**
8561 * Helper to initialize all associated child objects and allocate data
8562 * structures.
8563 *
8564 * This method must be called as a part of the object's initialization procedure
8565 * (usually done in the #init() method).
8566 *
8567 * @note Must be called only from #init() or from #registeredInit().
8568 */
8569HRESULT Machine::initDataAndChildObjects()
8570{
8571 AutoCaller autoCaller(this);
8572 AssertComRCReturnRC(autoCaller.rc());
8573 AssertComRCReturn(autoCaller.state() == InInit ||
8574 autoCaller.state() == Limited, E_FAIL);
8575
8576 AssertReturn(!mData->mAccessible, E_FAIL);
8577
8578 /* allocate data structures */
8579 mSSData.allocate();
8580 mUserData.allocate();
8581 mHWData.allocate();
8582 mMediaData.allocate();
8583 mStorageControllers.allocate();
8584 mUSBControllers.allocate();
8585
8586 /* initialize mOSTypeId */
8587 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8588
8589 /* create associated BIOS settings object */
8590 unconst(mBIOSSettings).createObject();
8591 mBIOSSettings->init(this);
8592
8593 /* create an associated VRDE object (default is disabled) */
8594 unconst(mVRDEServer).createObject();
8595 mVRDEServer->init(this);
8596
8597 /* create associated serial port objects */
8598 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8599 {
8600 unconst(mSerialPorts[slot]).createObject();
8601 mSerialPorts[slot]->init(this, slot);
8602 }
8603
8604 /* create associated parallel port objects */
8605 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8606 {
8607 unconst(mParallelPorts[slot]).createObject();
8608 mParallelPorts[slot]->init(this, slot);
8609 }
8610
8611 /* create the audio adapter object (always present, default is disabled) */
8612 unconst(mAudioAdapter).createObject();
8613 mAudioAdapter->init(this);
8614
8615 /* create the USB device filters object (always present) */
8616 unconst(mUSBDeviceFilters).createObject();
8617 mUSBDeviceFilters->init(this);
8618
8619 /* create associated network adapter objects */
8620 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8621 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8622 {
8623 unconst(mNetworkAdapters[slot]).createObject();
8624 mNetworkAdapters[slot]->init(this, slot);
8625 }
8626
8627 /* create the bandwidth control */
8628 unconst(mBandwidthControl).createObject();
8629 mBandwidthControl->init(this);
8630
8631 return S_OK;
8632}
8633
8634/**
8635 * Helper to uninitialize all associated child objects and to free all data
8636 * structures.
8637 *
8638 * This method must be called as a part of the object's uninitialization
8639 * procedure (usually done in the #uninit() method).
8640 *
8641 * @note Must be called only from #uninit() or from #registeredInit().
8642 */
8643void Machine::uninitDataAndChildObjects()
8644{
8645 AutoCaller autoCaller(this);
8646 AssertComRCReturnVoid(autoCaller.rc());
8647 AssertComRCReturnVoid( autoCaller.state() == InUninit
8648 || autoCaller.state() == Limited);
8649
8650 /* tell all our other child objects we've been uninitialized */
8651 if (mBandwidthControl)
8652 {
8653 mBandwidthControl->uninit();
8654 unconst(mBandwidthControl).setNull();
8655 }
8656
8657 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8658 {
8659 if (mNetworkAdapters[slot])
8660 {
8661 mNetworkAdapters[slot]->uninit();
8662 unconst(mNetworkAdapters[slot]).setNull();
8663 }
8664 }
8665
8666 if (mUSBDeviceFilters)
8667 {
8668 mUSBDeviceFilters->uninit();
8669 unconst(mUSBDeviceFilters).setNull();
8670 }
8671
8672 if (mAudioAdapter)
8673 {
8674 mAudioAdapter->uninit();
8675 unconst(mAudioAdapter).setNull();
8676 }
8677
8678 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8679 {
8680 if (mParallelPorts[slot])
8681 {
8682 mParallelPorts[slot]->uninit();
8683 unconst(mParallelPorts[slot]).setNull();
8684 }
8685 }
8686
8687 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8688 {
8689 if (mSerialPorts[slot])
8690 {
8691 mSerialPorts[slot]->uninit();
8692 unconst(mSerialPorts[slot]).setNull();
8693 }
8694 }
8695
8696 if (mVRDEServer)
8697 {
8698 mVRDEServer->uninit();
8699 unconst(mVRDEServer).setNull();
8700 }
8701
8702 if (mBIOSSettings)
8703 {
8704 mBIOSSettings->uninit();
8705 unconst(mBIOSSettings).setNull();
8706 }
8707
8708 /* Deassociate media (only when a real Machine or a SnapshotMachine
8709 * instance is uninitialized; SessionMachine instances refer to real
8710 * Machine media). This is necessary for a clean re-initialization of
8711 * the VM after successfully re-checking the accessibility state. Note
8712 * that in case of normal Machine or SnapshotMachine uninitialization (as
8713 * a result of unregistering or deleting the snapshot), outdated media
8714 * attachments will already be uninitialized and deleted, so this
8715 * code will not affect them. */
8716 if ( !!mMediaData
8717 && (!isSessionMachine())
8718 )
8719 {
8720 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8721 it != mMediaData->mAttachments.end();
8722 ++it)
8723 {
8724 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8725 if (pMedium.isNull())
8726 continue;
8727 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8728 AssertComRC(rc);
8729 }
8730 }
8731
8732 if (!isSessionMachine() && !isSnapshotMachine())
8733 {
8734 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8735 if (mData->mFirstSnapshot)
8736 {
8737 // snapshots tree is protected by machine write lock; strictly
8738 // this isn't necessary here since we're deleting the entire
8739 // machine, but otherwise we assert in Snapshot::uninit()
8740 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8741 mData->mFirstSnapshot->uninit();
8742 mData->mFirstSnapshot.setNull();
8743 }
8744
8745 mData->mCurrentSnapshot.setNull();
8746 }
8747
8748 /* free data structures (the essential mData structure is not freed here
8749 * since it may be still in use) */
8750 mMediaData.free();
8751 mStorageControllers.free();
8752 mUSBControllers.free();
8753 mHWData.free();
8754 mUserData.free();
8755 mSSData.free();
8756}
8757
8758/**
8759 * Returns a pointer to the Machine object for this machine that acts like a
8760 * parent for complex machine data objects such as shared folders, etc.
8761 *
8762 * For primary Machine objects and for SnapshotMachine objects, returns this
8763 * object's pointer itself. For SessionMachine objects, returns the peer
8764 * (primary) machine pointer.
8765 */
8766Machine* Machine::getMachine()
8767{
8768 if (isSessionMachine())
8769 return (Machine*)mPeer;
8770 return this;
8771}
8772
8773/**
8774 * Makes sure that there are no machine state dependents. If necessary, waits
8775 * for the number of dependents to drop to zero.
8776 *
8777 * Make sure this method is called from under this object's write lock to
8778 * guarantee that no new dependents may be added when this method returns
8779 * control to the caller.
8780 *
8781 * @note Locks this object for writing. The lock will be released while waiting
8782 * (if necessary).
8783 *
8784 * @warning To be used only in methods that change the machine state!
8785 */
8786void Machine::ensureNoStateDependencies()
8787{
8788 AssertReturnVoid(isWriteLockOnCurrentThread());
8789
8790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8791
8792 /* Wait for all state dependents if necessary */
8793 if (mData->mMachineStateDeps != 0)
8794 {
8795 /* lazy semaphore creation */
8796 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8797 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8798
8799 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8800 mData->mMachineStateDeps));
8801
8802 ++mData->mMachineStateChangePending;
8803
8804 /* reset the semaphore before waiting, the last dependent will signal
8805 * it */
8806 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8807
8808 alock.release();
8809
8810 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8811
8812 alock.acquire();
8813
8814 -- mData->mMachineStateChangePending;
8815 }
8816}
8817
8818/**
8819 * Changes the machine state and informs callbacks.
8820 *
8821 * This method is not intended to fail so it either returns S_OK or asserts (and
8822 * returns a failure).
8823 *
8824 * @note Locks this object for writing.
8825 */
8826HRESULT Machine::setMachineState(MachineState_T aMachineState)
8827{
8828 LogFlowThisFuncEnter();
8829 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8830
8831 AutoCaller autoCaller(this);
8832 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8833
8834 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8835
8836 /* wait for state dependents to drop to zero */
8837 ensureNoStateDependencies();
8838
8839 if (mData->mMachineState != aMachineState)
8840 {
8841 mData->mMachineState = aMachineState;
8842
8843 RTTimeNow(&mData->mLastStateChange);
8844
8845 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8846 }
8847
8848 LogFlowThisFuncLeave();
8849 return S_OK;
8850}
8851
8852/**
8853 * Searches for a shared folder with the given logical name
8854 * in the collection of shared folders.
8855 *
8856 * @param aName logical name of the shared folder
8857 * @param aSharedFolder where to return the found object
8858 * @param aSetError whether to set the error info if the folder is
8859 * not found
8860 * @return
8861 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8862 *
8863 * @note
8864 * must be called from under the object's lock!
8865 */
8866HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8867 ComObjPtr<SharedFolder> &aSharedFolder,
8868 bool aSetError /* = false */)
8869{
8870 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8871 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8872 it != mHWData->mSharedFolders.end();
8873 ++it)
8874 {
8875 SharedFolder *pSF = *it;
8876 AutoCaller autoCaller(pSF);
8877 if (pSF->getName() == aName)
8878 {
8879 aSharedFolder = pSF;
8880 rc = S_OK;
8881 break;
8882 }
8883 }
8884
8885 if (aSetError && FAILED(rc))
8886 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8887
8888 return rc;
8889}
8890
8891/**
8892 * Initializes all machine instance data from the given settings structures
8893 * from XML. The exception is the machine UUID which needs special handling
8894 * depending on the caller's use case, so the caller needs to set that herself.
8895 *
8896 * This gets called in several contexts during machine initialization:
8897 *
8898 * -- When machine XML exists on disk already and needs to be loaded into memory,
8899 * for example, from registeredInit() to load all registered machines on
8900 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8901 * attached to the machine should be part of some media registry already.
8902 *
8903 * -- During OVF import, when a machine config has been constructed from an
8904 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8905 * ensure that the media listed as attachments in the config (which have
8906 * been imported from the OVF) receive the correct registry ID.
8907 *
8908 * -- During VM cloning.
8909 *
8910 * @param config Machine settings from XML.
8911 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8912 * @return
8913 */
8914HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8915 const Guid *puuidRegistry)
8916{
8917 // copy name, description, OS type, teleporter, UTC etc.
8918 mUserData->s = config.machineUserData;
8919
8920 // Decode the Icon overide data from config userdata and set onto Machine.
8921 #define DECODE_STR_MAX _1M
8922 const char* pszStr = config.machineUserData.ovIcon.c_str();
8923 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8924 if (cbOut > DECODE_STR_MAX)
8925 return setError(E_FAIL,
8926 tr("Icon Data too long.'%d' > '%d'"),
8927 cbOut,
8928 DECODE_STR_MAX);
8929 com::SafeArray<BYTE> iconByte(cbOut);
8930 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8931 if (FAILED(rc))
8932 return setError(E_FAIL,
8933 tr("Failure to Decode Icon Data. '%s' (%d)"),
8934 pszStr,
8935 rc);
8936 mUserData->mIcon.resize(iconByte.size());
8937 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
8938
8939 // look up the object by Id to check it is valid
8940 ComPtr<IGuestOSType> guestOSType;
8941 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8942 guestOSType.asOutParam());
8943 if (FAILED(rc)) return rc;
8944
8945 // stateFile (optional)
8946 if (config.strStateFile.isEmpty())
8947 mSSData->strStateFilePath.setNull();
8948 else
8949 {
8950 Utf8Str stateFilePathFull(config.strStateFile);
8951 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8952 if (RT_FAILURE(vrc))
8953 return setError(E_FAIL,
8954 tr("Invalid saved state file path '%s' (%Rrc)"),
8955 config.strStateFile.c_str(),
8956 vrc);
8957 mSSData->strStateFilePath = stateFilePathFull;
8958 }
8959
8960 // snapshot folder needs special processing so set it again
8961 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8962 if (FAILED(rc)) return rc;
8963
8964 /* Copy the extra data items (Not in any case config is already the same as
8965 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8966 * make sure the extra data map is copied). */
8967 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8968
8969 /* currentStateModified (optional, default is true) */
8970 mData->mCurrentStateModified = config.fCurrentStateModified;
8971
8972 mData->mLastStateChange = config.timeLastStateChange;
8973
8974 /*
8975 * note: all mUserData members must be assigned prior this point because
8976 * we need to commit changes in order to let mUserData be shared by all
8977 * snapshot machine instances.
8978 */
8979 mUserData.commitCopy();
8980
8981 // machine registry, if present (must be loaded before snapshots)
8982 if (config.canHaveOwnMediaRegistry())
8983 {
8984 // determine machine folder
8985 Utf8Str strMachineFolder = getSettingsFileFull();
8986 strMachineFolder.stripFilename();
8987 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8988 config.mediaRegistry,
8989 strMachineFolder);
8990 if (FAILED(rc)) return rc;
8991 }
8992
8993 /* Snapshot node (optional) */
8994 size_t cRootSnapshots;
8995 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8996 {
8997 // there must be only one root snapshot
8998 Assert(cRootSnapshots == 1);
8999
9000 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9001
9002 rc = loadSnapshot(snap,
9003 config.uuidCurrentSnapshot,
9004 NULL); // no parent == first snapshot
9005 if (FAILED(rc)) return rc;
9006 }
9007
9008 // hardware data
9009 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9010 if (FAILED(rc)) return rc;
9011
9012 // load storage controllers
9013 rc = loadStorageControllers(config.storageMachine,
9014 puuidRegistry,
9015 NULL /* puuidSnapshot */);
9016 if (FAILED(rc)) return rc;
9017
9018 /*
9019 * NOTE: the assignment below must be the last thing to do,
9020 * otherwise it will be not possible to change the settings
9021 * somewhere in the code above because all setters will be
9022 * blocked by checkStateDependency(MutableStateDep).
9023 */
9024
9025 /* set the machine state to Aborted or Saved when appropriate */
9026 if (config.fAborted)
9027 {
9028 mSSData->strStateFilePath.setNull();
9029
9030 /* no need to use setMachineState() during init() */
9031 mData->mMachineState = MachineState_Aborted;
9032 }
9033 else if (!mSSData->strStateFilePath.isEmpty())
9034 {
9035 /* no need to use setMachineState() during init() */
9036 mData->mMachineState = MachineState_Saved;
9037 }
9038
9039 // after loading settings, we are no longer different from the XML on disk
9040 mData->flModifications = 0;
9041
9042 return S_OK;
9043}
9044
9045/**
9046 * Recursively loads all snapshots starting from the given.
9047 *
9048 * @param aNode <Snapshot> node.
9049 * @param aCurSnapshotId Current snapshot ID from the settings file.
9050 * @param aParentSnapshot Parent snapshot.
9051 */
9052HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
9053 const Guid &aCurSnapshotId,
9054 Snapshot *aParentSnapshot)
9055{
9056 AssertReturn(!isSnapshotMachine(), E_FAIL);
9057 AssertReturn(!isSessionMachine(), E_FAIL);
9058
9059 HRESULT rc = S_OK;
9060
9061 Utf8Str strStateFile;
9062 if (!data.strStateFile.isEmpty())
9063 {
9064 /* optional */
9065 strStateFile = data.strStateFile;
9066 int vrc = calculateFullPath(strStateFile, strStateFile);
9067 if (RT_FAILURE(vrc))
9068 return setError(E_FAIL,
9069 tr("Invalid saved state file path '%s' (%Rrc)"),
9070 strStateFile.c_str(),
9071 vrc);
9072 }
9073
9074 /* create a snapshot machine object */
9075 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9076 pSnapshotMachine.createObject();
9077 rc = pSnapshotMachine->initFromSettings(this,
9078 data.hardware,
9079 &data.debugging,
9080 &data.autostart,
9081 data.storage,
9082 data.uuid.ref(),
9083 strStateFile);
9084 if (FAILED(rc)) return rc;
9085
9086 /* create a snapshot object */
9087 ComObjPtr<Snapshot> pSnapshot;
9088 pSnapshot.createObject();
9089 /* initialize the snapshot */
9090 rc = pSnapshot->init(mParent, // VirtualBox object
9091 data.uuid,
9092 data.strName,
9093 data.strDescription,
9094 data.timestamp,
9095 pSnapshotMachine,
9096 aParentSnapshot);
9097 if (FAILED(rc)) return rc;
9098
9099 /* memorize the first snapshot if necessary */
9100 if (!mData->mFirstSnapshot)
9101 mData->mFirstSnapshot = pSnapshot;
9102
9103 /* memorize the current snapshot when appropriate */
9104 if ( !mData->mCurrentSnapshot
9105 && pSnapshot->getId() == aCurSnapshotId
9106 )
9107 mData->mCurrentSnapshot = pSnapshot;
9108
9109 // now create the children
9110 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
9111 it != data.llChildSnapshots.end();
9112 ++it)
9113 {
9114 const settings::Snapshot &childData = *it;
9115 // recurse
9116 rc = loadSnapshot(childData,
9117 aCurSnapshotId,
9118 pSnapshot); // parent = the one we created above
9119 if (FAILED(rc)) return rc;
9120 }
9121
9122 return rc;
9123}
9124
9125/**
9126 * Loads settings into mHWData.
9127 *
9128 * @param data Reference to the hardware settings.
9129 * @param pDbg Pointer to the debugging settings.
9130 * @param pAutostart Pointer to the autostart settings.
9131 */
9132HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
9133 const settings::Autostart *pAutostart)
9134{
9135 AssertReturn(!isSessionMachine(), E_FAIL);
9136
9137 HRESULT rc = S_OK;
9138
9139 try
9140 {
9141 /* The hardware version attribute (optional). */
9142 mHWData->mHWVersion = data.strVersion;
9143 mHWData->mHardwareUUID = data.uuid;
9144
9145 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9146 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9147 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9148 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9149 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9150 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9151 mHWData->mPAEEnabled = data.fPAE;
9152 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9153 mHWData->mLongMode = data.enmLongMode;
9154 mHWData->mCPUCount = data.cCPUs;
9155 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9156 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9157
9158 // cpu
9159 if (mHWData->mCPUHotPlugEnabled)
9160 {
9161 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9162 it != data.llCpus.end();
9163 ++it)
9164 {
9165 const settings::Cpu &cpu = *it;
9166
9167 mHWData->mCPUAttached[cpu.ulId] = true;
9168 }
9169 }
9170
9171 // cpuid leafs
9172 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9173 it != data.llCpuIdLeafs.end();
9174 ++it)
9175 {
9176 const settings::CpuIdLeaf &leaf = *it;
9177
9178 switch (leaf.ulId)
9179 {
9180 case 0x0:
9181 case 0x1:
9182 case 0x2:
9183 case 0x3:
9184 case 0x4:
9185 case 0x5:
9186 case 0x6:
9187 case 0x7:
9188 case 0x8:
9189 case 0x9:
9190 case 0xA:
9191 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9192 break;
9193
9194 case 0x80000000:
9195 case 0x80000001:
9196 case 0x80000002:
9197 case 0x80000003:
9198 case 0x80000004:
9199 case 0x80000005:
9200 case 0x80000006:
9201 case 0x80000007:
9202 case 0x80000008:
9203 case 0x80000009:
9204 case 0x8000000A:
9205 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9206 break;
9207
9208 default:
9209 /* just ignore */
9210 break;
9211 }
9212 }
9213
9214 mHWData->mMemorySize = data.ulMemorySizeMB;
9215 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9216
9217 // boot order
9218 for (size_t i = 0;
9219 i < RT_ELEMENTS(mHWData->mBootOrder);
9220 i++)
9221 {
9222 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9223 if (it == data.mapBootOrder.end())
9224 mHWData->mBootOrder[i] = DeviceType_Null;
9225 else
9226 mHWData->mBootOrder[i] = it->second;
9227 }
9228
9229 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9230 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9231 mHWData->mMonitorCount = data.cMonitors;
9232 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9233 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9234 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9235 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9236 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9237 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9238 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9239 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9240 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9241 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9242 if (!data.strVideoCaptureFile.isEmpty())
9243 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9244 else
9245 mHWData->mVideoCaptureFile.setNull();
9246 mHWData->mFirmwareType = data.firmwareType;
9247 mHWData->mPointingHIDType = data.pointingHIDType;
9248 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9249 mHWData->mChipsetType = data.chipsetType;
9250 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
9251 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9252 mHWData->mHPETEnabled = data.fHPETEnabled;
9253
9254 /* VRDEServer */
9255 rc = mVRDEServer->loadSettings(data.vrdeSettings);
9256 if (FAILED(rc)) return rc;
9257
9258 /* BIOS */
9259 rc = mBIOSSettings->loadSettings(data.biosSettings);
9260 if (FAILED(rc)) return rc;
9261
9262 // Bandwidth control (must come before network adapters)
9263 rc = mBandwidthControl->loadSettings(data.ioSettings);
9264 if (FAILED(rc)) return rc;
9265
9266 /* Shared folders */
9267 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9268 it != data.usbSettings.llUSBControllers.end();
9269 ++it)
9270 {
9271 const settings::USBController &settingsCtrl = *it;
9272 ComObjPtr<USBController> newCtrl;
9273
9274 newCtrl.createObject();
9275 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9276 mUSBControllers->push_back(newCtrl);
9277 }
9278
9279 /* USB device filters */
9280 rc = mUSBDeviceFilters->loadSettings(data.usbSettings);
9281 if (FAILED(rc)) return rc;
9282
9283 // network adapters
9284 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9285 uint32_t oldCount = mNetworkAdapters.size();
9286 if (newCount > oldCount)
9287 {
9288 mNetworkAdapters.resize(newCount);
9289 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9290 {
9291 unconst(mNetworkAdapters[slot]).createObject();
9292 mNetworkAdapters[slot]->init(this, slot);
9293 }
9294 }
9295 else if (newCount < oldCount)
9296 mNetworkAdapters.resize(newCount);
9297 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9298 it != data.llNetworkAdapters.end();
9299 ++it)
9300 {
9301 const settings::NetworkAdapter &nic = *it;
9302
9303 /* slot unicity is guaranteed by XML Schema */
9304 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9305 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9306 if (FAILED(rc)) return rc;
9307 }
9308
9309 // serial ports
9310 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9311 it != data.llSerialPorts.end();
9312 ++it)
9313 {
9314 const settings::SerialPort &s = *it;
9315
9316 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9317 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
9318 if (FAILED(rc)) return rc;
9319 }
9320
9321 // parallel ports (optional)
9322 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9323 it != data.llParallelPorts.end();
9324 ++it)
9325 {
9326 const settings::ParallelPort &p = *it;
9327
9328 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9329 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9330 if (FAILED(rc)) return rc;
9331 }
9332
9333 /* AudioAdapter */
9334 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9335 if (FAILED(rc)) return rc;
9336
9337 /* Shared folders */
9338 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9339 it != data.llSharedFolders.end();
9340 ++it)
9341 {
9342 const settings::SharedFolder &sf = *it;
9343
9344 ComObjPtr<SharedFolder> sharedFolder;
9345 /* Check for double entries. Not allowed! */
9346 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9347 if (SUCCEEDED(rc))
9348 return setError(VBOX_E_OBJECT_IN_USE,
9349 tr("Shared folder named '%s' already exists"),
9350 sf.strName.c_str());
9351
9352 /* Create the new shared folder. Don't break on error. This will be
9353 * reported when the machine starts. */
9354 sharedFolder.createObject();
9355 rc = sharedFolder->init(getMachine(),
9356 sf.strName,
9357 sf.strHostPath,
9358 RT_BOOL(sf.fWritable),
9359 RT_BOOL(sf.fAutoMount),
9360 false /* fFailOnError */);
9361 if (FAILED(rc)) return rc;
9362 mHWData->mSharedFolders.push_back(sharedFolder);
9363 }
9364
9365 // Clipboard
9366 mHWData->mClipboardMode = data.clipboardMode;
9367
9368 // drag'n'drop
9369 mHWData->mDragAndDropMode = data.dragAndDropMode;
9370
9371 // guest settings
9372 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9373
9374 // IO settings
9375 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9376 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9377
9378 // Host PCI devices
9379 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9380 it != data.pciAttachments.end();
9381 ++it)
9382 {
9383 const settings::HostPCIDeviceAttachment &hpda = *it;
9384 ComObjPtr<PCIDeviceAttachment> pda;
9385
9386 pda.createObject();
9387 pda->loadSettings(this, hpda);
9388 mHWData->mPCIDeviceAssignments.push_back(pda);
9389 }
9390
9391 /*
9392 * (The following isn't really real hardware, but it lives in HWData
9393 * for reasons of convenience.)
9394 */
9395
9396#ifdef VBOX_WITH_GUEST_PROPS
9397 /* Guest properties (optional) */
9398 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9399 it != data.llGuestProperties.end();
9400 ++it)
9401 {
9402 const settings::GuestProperty &prop = *it;
9403 uint32_t fFlags = guestProp::NILFLAG;
9404 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9405 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9406 mHWData->mGuestProperties[prop.strName] = property;
9407 }
9408
9409 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9410#endif /* VBOX_WITH_GUEST_PROPS defined */
9411
9412 rc = loadDebugging(pDbg);
9413 if (FAILED(rc))
9414 return rc;
9415
9416 mHWData->mAutostart = *pAutostart;
9417
9418 /* default frontend */
9419 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9420 }
9421 catch(std::bad_alloc &)
9422 {
9423 return E_OUTOFMEMORY;
9424 }
9425
9426 AssertComRC(rc);
9427 return rc;
9428}
9429
9430/**
9431 * Called from Machine::loadHardware() to load the debugging settings of the
9432 * machine.
9433 *
9434 * @param pDbg Pointer to the settings.
9435 */
9436HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9437{
9438 mHWData->mDebugging = *pDbg;
9439 /* no more processing currently required, this will probably change. */
9440 return S_OK;
9441}
9442
9443/**
9444 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9445 *
9446 * @param data
9447 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9448 * @param puuidSnapshot
9449 * @return
9450 */
9451HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9452 const Guid *puuidRegistry,
9453 const Guid *puuidSnapshot)
9454{
9455 AssertReturn(!isSessionMachine(), E_FAIL);
9456
9457 HRESULT rc = S_OK;
9458
9459 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9460 it != data.llStorageControllers.end();
9461 ++it)
9462 {
9463 const settings::StorageController &ctlData = *it;
9464
9465 ComObjPtr<StorageController> pCtl;
9466 /* Try to find one with the name first. */
9467 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9468 if (SUCCEEDED(rc))
9469 return setError(VBOX_E_OBJECT_IN_USE,
9470 tr("Storage controller named '%s' already exists"),
9471 ctlData.strName.c_str());
9472
9473 pCtl.createObject();
9474 rc = pCtl->init(this,
9475 ctlData.strName,
9476 ctlData.storageBus,
9477 ctlData.ulInstance,
9478 ctlData.fBootable);
9479 if (FAILED(rc)) return rc;
9480
9481 mStorageControllers->push_back(pCtl);
9482
9483 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9484 if (FAILED(rc)) return rc;
9485
9486 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9487 if (FAILED(rc)) return rc;
9488
9489 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9490 if (FAILED(rc)) return rc;
9491
9492 /* Set IDE emulation settings (only for AHCI controller). */
9493 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9494 {
9495 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9496 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9497 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9498 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9499 )
9500 return rc;
9501 }
9502
9503 /* Load the attached devices now. */
9504 rc = loadStorageDevices(pCtl,
9505 ctlData,
9506 puuidRegistry,
9507 puuidSnapshot);
9508 if (FAILED(rc)) return rc;
9509 }
9510
9511 return S_OK;
9512}
9513
9514/**
9515 * Called from loadStorageControllers for a controller's devices.
9516 *
9517 * @param aStorageController
9518 * @param data
9519 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9520 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9521 * @return
9522 */
9523HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9524 const settings::StorageController &data,
9525 const Guid *puuidRegistry,
9526 const Guid *puuidSnapshot)
9527{
9528 HRESULT rc = S_OK;
9529
9530 /* paranoia: detect duplicate attachments */
9531 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9532 it != data.llAttachedDevices.end();
9533 ++it)
9534 {
9535 const settings::AttachedDevice &ad = *it;
9536
9537 for (settings::AttachedDevicesList::const_iterator it2 = it;
9538 it2 != data.llAttachedDevices.end();
9539 ++it2)
9540 {
9541 if (it == it2)
9542 continue;
9543
9544 const settings::AttachedDevice &ad2 = *it2;
9545
9546 if ( ad.lPort == ad2.lPort
9547 && ad.lDevice == ad2.lDevice)
9548 {
9549 return setError(E_FAIL,
9550 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9551 aStorageController->getName().c_str(),
9552 ad.lPort,
9553 ad.lDevice,
9554 mUserData->s.strName.c_str());
9555 }
9556 }
9557 }
9558
9559 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9560 it != data.llAttachedDevices.end();
9561 ++it)
9562 {
9563 const settings::AttachedDevice &dev = *it;
9564 ComObjPtr<Medium> medium;
9565
9566 switch (dev.deviceType)
9567 {
9568 case DeviceType_Floppy:
9569 case DeviceType_DVD:
9570 if (dev.strHostDriveSrc.isNotEmpty())
9571 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9572 else
9573 rc = mParent->findRemoveableMedium(dev.deviceType,
9574 dev.uuid,
9575 false /* fRefresh */,
9576 false /* aSetError */,
9577 medium);
9578 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9579 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9580 rc = S_OK;
9581 break;
9582
9583 case DeviceType_HardDisk:
9584 {
9585 /* find a hard disk by UUID */
9586 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9587 if (FAILED(rc))
9588 {
9589 if (isSnapshotMachine())
9590 {
9591 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9592 // so the user knows that the bad disk is in a snapshot somewhere
9593 com::ErrorInfo info;
9594 return setError(E_FAIL,
9595 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9596 puuidSnapshot->raw(),
9597 info.getText().raw());
9598 }
9599 else
9600 return rc;
9601 }
9602
9603 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9604
9605 if (medium->getType() == MediumType_Immutable)
9606 {
9607 if (isSnapshotMachine())
9608 return setError(E_FAIL,
9609 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9610 "of the virtual machine '%s' ('%s')"),
9611 medium->getLocationFull().c_str(),
9612 dev.uuid.raw(),
9613 puuidSnapshot->raw(),
9614 mUserData->s.strName.c_str(),
9615 mData->m_strConfigFileFull.c_str());
9616
9617 return setError(E_FAIL,
9618 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9619 medium->getLocationFull().c_str(),
9620 dev.uuid.raw(),
9621 mUserData->s.strName.c_str(),
9622 mData->m_strConfigFileFull.c_str());
9623 }
9624
9625 if (medium->getType() == MediumType_MultiAttach)
9626 {
9627 if (isSnapshotMachine())
9628 return setError(E_FAIL,
9629 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9630 "of the virtual machine '%s' ('%s')"),
9631 medium->getLocationFull().c_str(),
9632 dev.uuid.raw(),
9633 puuidSnapshot->raw(),
9634 mUserData->s.strName.c_str(),
9635 mData->m_strConfigFileFull.c_str());
9636
9637 return setError(E_FAIL,
9638 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9639 medium->getLocationFull().c_str(),
9640 dev.uuid.raw(),
9641 mUserData->s.strName.c_str(),
9642 mData->m_strConfigFileFull.c_str());
9643 }
9644
9645 if ( !isSnapshotMachine()
9646 && medium->getChildren().size() != 0
9647 )
9648 return setError(E_FAIL,
9649 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9650 "because it has %d differencing child hard disks"),
9651 medium->getLocationFull().c_str(),
9652 dev.uuid.raw(),
9653 mUserData->s.strName.c_str(),
9654 mData->m_strConfigFileFull.c_str(),
9655 medium->getChildren().size());
9656
9657 if (findAttachment(mMediaData->mAttachments,
9658 medium))
9659 return setError(E_FAIL,
9660 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9661 medium->getLocationFull().c_str(),
9662 dev.uuid.raw(),
9663 mUserData->s.strName.c_str(),
9664 mData->m_strConfigFileFull.c_str());
9665
9666 break;
9667 }
9668
9669 default:
9670 return setError(E_FAIL,
9671 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9672 medium->getLocationFull().c_str(),
9673 mUserData->s.strName.c_str(),
9674 mData->m_strConfigFileFull.c_str());
9675 }
9676
9677 if (FAILED(rc))
9678 break;
9679
9680 /* Bandwidth groups are loaded at this point. */
9681 ComObjPtr<BandwidthGroup> pBwGroup;
9682
9683 if (!dev.strBwGroup.isEmpty())
9684 {
9685 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9686 if (FAILED(rc))
9687 return setError(E_FAIL,
9688 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9689 medium->getLocationFull().c_str(),
9690 dev.strBwGroup.c_str(),
9691 mUserData->s.strName.c_str(),
9692 mData->m_strConfigFileFull.c_str());
9693 pBwGroup->reference();
9694 }
9695
9696 const Bstr controllerName = aStorageController->getName();
9697 ComObjPtr<MediumAttachment> pAttachment;
9698 pAttachment.createObject();
9699 rc = pAttachment->init(this,
9700 medium,
9701 controllerName,
9702 dev.lPort,
9703 dev.lDevice,
9704 dev.deviceType,
9705 false,
9706 dev.fPassThrough,
9707 dev.fTempEject,
9708 dev.fNonRotational,
9709 dev.fDiscard,
9710 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9711 if (FAILED(rc)) break;
9712
9713 /* associate the medium with this machine and snapshot */
9714 if (!medium.isNull())
9715 {
9716 AutoCaller medCaller(medium);
9717 if (FAILED(medCaller.rc())) return medCaller.rc();
9718 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9719
9720 if (isSnapshotMachine())
9721 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9722 else
9723 rc = medium->addBackReference(mData->mUuid);
9724 /* If the medium->addBackReference fails it sets an appropriate
9725 * error message, so no need to do any guesswork here. */
9726
9727 if (puuidRegistry)
9728 // caller wants registry ID to be set on all attached media (OVF import case)
9729 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9730 }
9731
9732 if (FAILED(rc))
9733 break;
9734
9735 /* back up mMediaData to let registeredInit() properly rollback on failure
9736 * (= limited accessibility) */
9737 setModified(IsModified_Storage);
9738 mMediaData.backup();
9739 mMediaData->mAttachments.push_back(pAttachment);
9740 }
9741
9742 return rc;
9743}
9744
9745/**
9746 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9747 *
9748 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9749 * @param aSnapshot where to return the found snapshot
9750 * @param aSetError true to set extended error info on failure
9751 */
9752HRESULT Machine::findSnapshotById(const Guid &aId,
9753 ComObjPtr<Snapshot> &aSnapshot,
9754 bool aSetError /* = false */)
9755{
9756 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9757
9758 if (!mData->mFirstSnapshot)
9759 {
9760 if (aSetError)
9761 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9762 return E_FAIL;
9763 }
9764
9765 if (aId.isZero())
9766 aSnapshot = mData->mFirstSnapshot;
9767 else
9768 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9769
9770 if (!aSnapshot)
9771 {
9772 if (aSetError)
9773 return setError(E_FAIL,
9774 tr("Could not find a snapshot with UUID {%s}"),
9775 aId.toString().c_str());
9776 return E_FAIL;
9777 }
9778
9779 return S_OK;
9780}
9781
9782/**
9783 * Returns the snapshot with the given name or fails of no such snapshot.
9784 *
9785 * @param aName snapshot name to find
9786 * @param aSnapshot where to return the found snapshot
9787 * @param aSetError true to set extended error info on failure
9788 */
9789HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9790 ComObjPtr<Snapshot> &aSnapshot,
9791 bool aSetError /* = false */)
9792{
9793 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9794
9795 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9796
9797 if (!mData->mFirstSnapshot)
9798 {
9799 if (aSetError)
9800 return setError(VBOX_E_OBJECT_NOT_FOUND,
9801 tr("This machine does not have any snapshots"));
9802 return VBOX_E_OBJECT_NOT_FOUND;
9803 }
9804
9805 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9806
9807 if (!aSnapshot)
9808 {
9809 if (aSetError)
9810 return setError(VBOX_E_OBJECT_NOT_FOUND,
9811 tr("Could not find a snapshot named '%s'"), strName.c_str());
9812 return VBOX_E_OBJECT_NOT_FOUND;
9813 }
9814
9815 return S_OK;
9816}
9817
9818/**
9819 * Returns a storage controller object with the given name.
9820 *
9821 * @param aName storage controller name to find
9822 * @param aStorageController where to return the found storage controller
9823 * @param aSetError true to set extended error info on failure
9824 */
9825HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9826 ComObjPtr<StorageController> &aStorageController,
9827 bool aSetError /* = false */)
9828{
9829 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9830
9831 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9832 it != mStorageControllers->end();
9833 ++it)
9834 {
9835 if ((*it)->getName() == aName)
9836 {
9837 aStorageController = (*it);
9838 return S_OK;
9839 }
9840 }
9841
9842 if (aSetError)
9843 return setError(VBOX_E_OBJECT_NOT_FOUND,
9844 tr("Could not find a storage controller named '%s'"),
9845 aName.c_str());
9846 return VBOX_E_OBJECT_NOT_FOUND;
9847}
9848
9849/**
9850 * Returns a USB controller object with the given name.
9851 *
9852 * @param aName USB controller name to find
9853 * @param aUSBController where to return the found USB controller
9854 * @param aSetError true to set extended error info on failure
9855 */
9856HRESULT Machine::getUSBControllerByName(const Utf8Str &aName,
9857 ComObjPtr<USBController> &aUSBController,
9858 bool aSetError /* = false */)
9859{
9860 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9861
9862 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9863 it != mUSBControllers->end();
9864 ++it)
9865 {
9866 if ((*it)->getName() == aName)
9867 {
9868 aUSBController = (*it);
9869 return S_OK;
9870 }
9871 }
9872
9873 if (aSetError)
9874 return setError(VBOX_E_OBJECT_NOT_FOUND,
9875 tr("Could not find a storage controller named '%s'"),
9876 aName.c_str());
9877 return VBOX_E_OBJECT_NOT_FOUND;
9878}
9879
9880/**
9881 * Returns the number of USB controller instance of the given type.
9882 *
9883 * @param enmType USB controller type.
9884 */
9885ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType)
9886{
9887 ULONG cCtrls = 0;
9888
9889 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9890 it != mUSBControllers->end();
9891 ++it)
9892 {
9893 if ((*it)->getControllerType() == enmType)
9894 cCtrls++;
9895 }
9896
9897 return cCtrls;
9898}
9899
9900HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9901 MediaData::AttachmentList &atts)
9902{
9903 AutoCaller autoCaller(this);
9904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9905
9906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9907
9908 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9909 it != mMediaData->mAttachments.end();
9910 ++it)
9911 {
9912 const ComObjPtr<MediumAttachment> &pAtt = *it;
9913
9914 // should never happen, but deal with NULL pointers in the list.
9915 AssertStmt(!pAtt.isNull(), continue);
9916
9917 // getControllerName() needs caller+read lock
9918 AutoCaller autoAttCaller(pAtt);
9919 if (FAILED(autoAttCaller.rc()))
9920 {
9921 atts.clear();
9922 return autoAttCaller.rc();
9923 }
9924 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9925
9926 if (pAtt->getControllerName() == aName)
9927 atts.push_back(pAtt);
9928 }
9929
9930 return S_OK;
9931}
9932
9933/**
9934 * Helper for #saveSettings. Cares about renaming the settings directory and
9935 * file if the machine name was changed and about creating a new settings file
9936 * if this is a new machine.
9937 *
9938 * @note Must be never called directly but only from #saveSettings().
9939 */
9940HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9941{
9942 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9943
9944 HRESULT rc = S_OK;
9945
9946 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9947
9948 /// @todo need to handle primary group change, too
9949
9950 /* attempt to rename the settings file if machine name is changed */
9951 if ( mUserData->s.fNameSync
9952 && mUserData.isBackedUp()
9953 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9954 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9955 )
9956 {
9957 bool dirRenamed = false;
9958 bool fileRenamed = false;
9959
9960 Utf8Str configFile, newConfigFile;
9961 Utf8Str configFilePrev, newConfigFilePrev;
9962 Utf8Str configDir, newConfigDir;
9963
9964 do
9965 {
9966 int vrc = VINF_SUCCESS;
9967
9968 Utf8Str name = mUserData.backedUpData()->s.strName;
9969 Utf8Str newName = mUserData->s.strName;
9970 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9971 if (group == "/")
9972 group.setNull();
9973 Utf8Str newGroup = mUserData->s.llGroups.front();
9974 if (newGroup == "/")
9975 newGroup.setNull();
9976
9977 configFile = mData->m_strConfigFileFull;
9978
9979 /* first, rename the directory if it matches the group and machine name */
9980 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9981 group.c_str(), RTPATH_DELIMITER, name.c_str());
9982 /** @todo hack, make somehow use of ComposeMachineFilename */
9983 if (mUserData->s.fDirectoryIncludesUUID)
9984 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9985 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9986 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9987 /** @todo hack, make somehow use of ComposeMachineFilename */
9988 if (mUserData->s.fDirectoryIncludesUUID)
9989 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9990 configDir = configFile;
9991 configDir.stripFilename();
9992 newConfigDir = configDir;
9993 if ( configDir.length() >= groupPlusName.length()
9994 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9995 {
9996 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9997 Utf8Str newConfigBaseDir(newConfigDir);
9998 newConfigDir.append(newGroupPlusName);
9999 /* consistency: use \ if appropriate on the platform */
10000 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10001 /* new dir and old dir cannot be equal here because of 'if'
10002 * above and because name != newName */
10003 Assert(configDir != newConfigDir);
10004 if (!fSettingsFileIsNew)
10005 {
10006 /* perform real rename only if the machine is not new */
10007 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10008 if ( vrc == VERR_FILE_NOT_FOUND
10009 || vrc == VERR_PATH_NOT_FOUND)
10010 {
10011 /* create the parent directory, then retry renaming */
10012 Utf8Str parent(newConfigDir);
10013 parent.stripFilename();
10014 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10015 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10016 }
10017 if (RT_FAILURE(vrc))
10018 {
10019 rc = setError(E_FAIL,
10020 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10021 configDir.c_str(),
10022 newConfigDir.c_str(),
10023 vrc);
10024 break;
10025 }
10026 /* delete subdirectories which are no longer needed */
10027 Utf8Str dir(configDir);
10028 dir.stripFilename();
10029 while (dir != newConfigBaseDir && dir != ".")
10030 {
10031 vrc = RTDirRemove(dir.c_str());
10032 if (RT_FAILURE(vrc))
10033 break;
10034 dir.stripFilename();
10035 }
10036 dirRenamed = true;
10037 }
10038 }
10039
10040 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10041 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10042
10043 /* then try to rename the settings file itself */
10044 if (newConfigFile != configFile)
10045 {
10046 /* get the path to old settings file in renamed directory */
10047 configFile = Utf8StrFmt("%s%c%s",
10048 newConfigDir.c_str(),
10049 RTPATH_DELIMITER,
10050 RTPathFilename(configFile.c_str()));
10051 if (!fSettingsFileIsNew)
10052 {
10053 /* perform real rename only if the machine is not new */
10054 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10055 if (RT_FAILURE(vrc))
10056 {
10057 rc = setError(E_FAIL,
10058 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10059 configFile.c_str(),
10060 newConfigFile.c_str(),
10061 vrc);
10062 break;
10063 }
10064 fileRenamed = true;
10065 configFilePrev = configFile;
10066 configFilePrev += "-prev";
10067 newConfigFilePrev = newConfigFile;
10068 newConfigFilePrev += "-prev";
10069 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10070 }
10071 }
10072
10073 // update m_strConfigFileFull amd mConfigFile
10074 mData->m_strConfigFileFull = newConfigFile;
10075 // compute the relative path too
10076 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10077
10078 // store the old and new so that VirtualBox::saveSettings() can update
10079 // the media registry
10080 if ( mData->mRegistered
10081 && configDir != newConfigDir)
10082 {
10083 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
10084
10085 if (pfNeedsGlobalSaveSettings)
10086 *pfNeedsGlobalSaveSettings = true;
10087 }
10088
10089 // in the saved state file path, replace the old directory with the new directory
10090 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10091 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
10092
10093 // and do the same thing for the saved state file paths of all the online snapshots
10094 if (mData->mFirstSnapshot)
10095 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
10096 newConfigDir.c_str());
10097 }
10098 while (0);
10099
10100 if (FAILED(rc))
10101 {
10102 /* silently try to rename everything back */
10103 if (fileRenamed)
10104 {
10105 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10106 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10107 }
10108 if (dirRenamed)
10109 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10110 }
10111
10112 if (FAILED(rc)) return rc;
10113 }
10114
10115 if (fSettingsFileIsNew)
10116 {
10117 /* create a virgin config file */
10118 int vrc = VINF_SUCCESS;
10119
10120 /* ensure the settings directory exists */
10121 Utf8Str path(mData->m_strConfigFileFull);
10122 path.stripFilename();
10123 if (!RTDirExists(path.c_str()))
10124 {
10125 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10126 if (RT_FAILURE(vrc))
10127 {
10128 return setError(E_FAIL,
10129 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10130 path.c_str(),
10131 vrc);
10132 }
10133 }
10134
10135 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10136 path = Utf8Str(mData->m_strConfigFileFull);
10137 RTFILE f = NIL_RTFILE;
10138 vrc = RTFileOpen(&f, path.c_str(),
10139 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10140 if (RT_FAILURE(vrc))
10141 return setError(E_FAIL,
10142 tr("Could not create the settings file '%s' (%Rrc)"),
10143 path.c_str(),
10144 vrc);
10145 RTFileClose(f);
10146 }
10147
10148 return rc;
10149}
10150
10151/**
10152 * Saves and commits machine data, user data and hardware data.
10153 *
10154 * Note that on failure, the data remains uncommitted.
10155 *
10156 * @a aFlags may combine the following flags:
10157 *
10158 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10159 * Used when saving settings after an operation that makes them 100%
10160 * correspond to the settings from the current snapshot.
10161 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
10162 * #isReallyModified() returns false. This is necessary for cases when we
10163 * change machine data directly, not through the backup()/commit() mechanism.
10164 * - SaveS_Force: settings will be saved without doing a deep compare of the
10165 * settings structures. This is used when this is called because snapshots
10166 * have changed to avoid the overhead of the deep compare.
10167 *
10168 * @note Must be called from under this object's write lock. Locks children for
10169 * writing.
10170 *
10171 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10172 * initialized to false and that will be set to true by this function if
10173 * the caller must invoke VirtualBox::saveSettings() because the global
10174 * settings have changed. This will happen if a machine rename has been
10175 * saved and the global machine and media registries will therefore need
10176 * updating.
10177 */
10178HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
10179 int aFlags /*= 0*/)
10180{
10181 LogFlowThisFuncEnter();
10182
10183 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10184
10185 /* make sure child objects are unable to modify the settings while we are
10186 * saving them */
10187 ensureNoStateDependencies();
10188
10189 AssertReturn(!isSnapshotMachine(),
10190 E_FAIL);
10191
10192 HRESULT rc = S_OK;
10193 bool fNeedsWrite = false;
10194
10195 /* First, prepare to save settings. It will care about renaming the
10196 * settings directory and file if the machine name was changed and about
10197 * creating a new settings file if this is a new machine. */
10198 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
10199 if (FAILED(rc)) return rc;
10200
10201 // keep a pointer to the current settings structures
10202 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10203 settings::MachineConfigFile *pNewConfig = NULL;
10204
10205 try
10206 {
10207 // make a fresh one to have everyone write stuff into
10208 pNewConfig = new settings::MachineConfigFile(NULL);
10209 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10210
10211 // now go and copy all the settings data from COM to the settings structures
10212 // (this calles saveSettings() on all the COM objects in the machine)
10213 copyMachineDataToSettings(*pNewConfig);
10214
10215 if (aFlags & SaveS_ResetCurStateModified)
10216 {
10217 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10218 mData->mCurrentStateModified = FALSE;
10219 fNeedsWrite = true; // always, no need to compare
10220 }
10221 else if (aFlags & SaveS_Force)
10222 {
10223 fNeedsWrite = true; // always, no need to compare
10224 }
10225 else
10226 {
10227 if (!mData->mCurrentStateModified)
10228 {
10229 // do a deep compare of the settings that we just saved with the settings
10230 // previously stored in the config file; this invokes MachineConfigFile::operator==
10231 // which does a deep compare of all the settings, which is expensive but less expensive
10232 // than writing out XML in vain
10233 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10234
10235 // could still be modified if any settings changed
10236 mData->mCurrentStateModified = fAnySettingsChanged;
10237
10238 fNeedsWrite = fAnySettingsChanged;
10239 }
10240 else
10241 fNeedsWrite = true;
10242 }
10243
10244 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10245
10246 if (fNeedsWrite)
10247 // now spit it all out!
10248 pNewConfig->write(mData->m_strConfigFileFull);
10249
10250 mData->pMachineConfigFile = pNewConfig;
10251 delete pOldConfig;
10252 commit();
10253
10254 // after saving settings, we are no longer different from the XML on disk
10255 mData->flModifications = 0;
10256 }
10257 catch (HRESULT err)
10258 {
10259 // we assume that error info is set by the thrower
10260 rc = err;
10261
10262 // restore old config
10263 delete pNewConfig;
10264 mData->pMachineConfigFile = pOldConfig;
10265 }
10266 catch (...)
10267 {
10268 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10269 }
10270
10271 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10272 {
10273 /* Fire the data change event, even on failure (since we've already
10274 * committed all data). This is done only for SessionMachines because
10275 * mutable Machine instances are always not registered (i.e. private
10276 * to the client process that creates them) and thus don't need to
10277 * inform callbacks. */
10278 if (isSessionMachine())
10279 mParent->onMachineDataChange(mData->mUuid);
10280 }
10281
10282 LogFlowThisFunc(("rc=%08X\n", rc));
10283 LogFlowThisFuncLeave();
10284 return rc;
10285}
10286
10287/**
10288 * Implementation for saving the machine settings into the given
10289 * settings::MachineConfigFile instance. This copies machine extradata
10290 * from the previous machine config file in the instance data, if any.
10291 *
10292 * This gets called from two locations:
10293 *
10294 * -- Machine::saveSettings(), during the regular XML writing;
10295 *
10296 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10297 * exported to OVF and we write the VirtualBox proprietary XML
10298 * into a <vbox:Machine> tag.
10299 *
10300 * This routine fills all the fields in there, including snapshots, *except*
10301 * for the following:
10302 *
10303 * -- fCurrentStateModified. There is some special logic associated with that.
10304 *
10305 * The caller can then call MachineConfigFile::write() or do something else
10306 * with it.
10307 *
10308 * Caller must hold the machine lock!
10309 *
10310 * This throws XML errors and HRESULT, so the caller must have a catch block!
10311 */
10312void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10313{
10314 // deep copy extradata
10315 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10316
10317 config.uuid = mData->mUuid;
10318
10319 // copy name, description, OS type, teleport, UTC etc.
10320 config.machineUserData = mUserData->s;
10321
10322 // Encode the Icon Override data from Machine and store on config userdata.
10323 com::SafeArray<BYTE> iconByte;
10324 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10325 ssize_t cbData = iconByte.size();
10326 if (cbData > 0)
10327 {
10328 ssize_t cchOut = RTBase64EncodedLength(cbData);
10329 Utf8Str strIconData;
10330 strIconData.reserve(cchOut+1);
10331 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10332 strIconData.mutableRaw(), strIconData.capacity(),
10333 NULL);
10334 if (RT_FAILURE(vrc))
10335 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10336 strIconData.jolt();
10337 config.machineUserData.ovIcon = strIconData;
10338 }
10339 else
10340 config.machineUserData.ovIcon.setNull();
10341
10342 if ( mData->mMachineState == MachineState_Saved
10343 || mData->mMachineState == MachineState_Restoring
10344 // when deleting a snapshot we may or may not have a saved state in the current state,
10345 // so let's not assert here please
10346 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10347 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10348 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10349 && (!mSSData->strStateFilePath.isEmpty())
10350 )
10351 )
10352 {
10353 Assert(!mSSData->strStateFilePath.isEmpty());
10354 /* try to make the file name relative to the settings file dir */
10355 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10356 }
10357 else
10358 {
10359 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10360 config.strStateFile.setNull();
10361 }
10362
10363 if (mData->mCurrentSnapshot)
10364 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10365 else
10366 config.uuidCurrentSnapshot.clear();
10367
10368 config.timeLastStateChange = mData->mLastStateChange;
10369 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10370 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10371
10372 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10373 if (FAILED(rc)) throw rc;
10374
10375 rc = saveStorageControllers(config.storageMachine);
10376 if (FAILED(rc)) throw rc;
10377
10378 // save machine's media registry if this is VirtualBox 4.0 or later
10379 if (config.canHaveOwnMediaRegistry())
10380 {
10381 // determine machine folder
10382 Utf8Str strMachineFolder = getSettingsFileFull();
10383 strMachineFolder.stripFilename();
10384 mParent->saveMediaRegistry(config.mediaRegistry,
10385 getId(), // only media with registry ID == machine UUID
10386 strMachineFolder);
10387 // this throws HRESULT
10388 }
10389
10390 // save snapshots
10391 rc = saveAllSnapshots(config);
10392 if (FAILED(rc)) throw rc;
10393}
10394
10395/**
10396 * Saves all snapshots of the machine into the given machine config file. Called
10397 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10398 * @param config
10399 * @return
10400 */
10401HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10402{
10403 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10404
10405 HRESULT rc = S_OK;
10406
10407 try
10408 {
10409 config.llFirstSnapshot.clear();
10410
10411 if (mData->mFirstSnapshot)
10412 {
10413 settings::Snapshot snapNew;
10414 config.llFirstSnapshot.push_back(snapNew);
10415
10416 // get reference to the fresh copy of the snapshot on the list and
10417 // work on that copy directly to avoid excessive copying later
10418 settings::Snapshot &snap = config.llFirstSnapshot.front();
10419
10420 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10421 if (FAILED(rc)) throw rc;
10422 }
10423
10424// if (mType == IsSessionMachine)
10425// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10426
10427 }
10428 catch (HRESULT err)
10429 {
10430 /* we assume that error info is set by the thrower */
10431 rc = err;
10432 }
10433 catch (...)
10434 {
10435 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10436 }
10437
10438 return rc;
10439}
10440
10441/**
10442 * Saves the VM hardware configuration. It is assumed that the
10443 * given node is empty.
10444 *
10445 * @param data Reference to the settings object for the hardware config.
10446 * @param pDbg Pointer to the settings object for the debugging config
10447 * which happens to live in mHWData.
10448 * @param pAutostart Pointer to the settings object for the autostart config
10449 * which happens to live in mHWData.
10450 */
10451HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10452 settings::Autostart *pAutostart)
10453{
10454 HRESULT rc = S_OK;
10455
10456 try
10457 {
10458 /* The hardware version attribute (optional).
10459 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10460 if ( mHWData->mHWVersion == "1"
10461 && mSSData->strStateFilePath.isEmpty()
10462 )
10463 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. */
10464
10465 data.strVersion = mHWData->mHWVersion;
10466 data.uuid = mHWData->mHardwareUUID;
10467
10468 // CPU
10469 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10470 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10471 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10472 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10473 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10474 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10475 data.fPAE = !!mHWData->mPAEEnabled;
10476 data.enmLongMode = mHWData->mLongMode;
10477 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10478
10479 /* Standard and Extended CPUID leafs. */
10480 data.llCpuIdLeafs.clear();
10481 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10482 {
10483 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10484 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10485 }
10486 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10487 {
10488 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10489 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10490 }
10491
10492 data.cCPUs = mHWData->mCPUCount;
10493 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10494 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10495
10496 data.llCpus.clear();
10497 if (data.fCpuHotPlug)
10498 {
10499 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10500 {
10501 if (mHWData->mCPUAttached[idx])
10502 {
10503 settings::Cpu cpu;
10504 cpu.ulId = idx;
10505 data.llCpus.push_back(cpu);
10506 }
10507 }
10508 }
10509
10510 // memory
10511 data.ulMemorySizeMB = mHWData->mMemorySize;
10512 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10513
10514 // firmware
10515 data.firmwareType = mHWData->mFirmwareType;
10516
10517 // HID
10518 data.pointingHIDType = mHWData->mPointingHIDType;
10519 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10520
10521 // chipset
10522 data.chipsetType = mHWData->mChipsetType;
10523
10524 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10525 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10526
10527 // HPET
10528 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10529
10530 // boot order
10531 data.mapBootOrder.clear();
10532 for (size_t i = 0;
10533 i < RT_ELEMENTS(mHWData->mBootOrder);
10534 ++i)
10535 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10536
10537 // display
10538 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10539 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10540 data.cMonitors = mHWData->mMonitorCount;
10541 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10542 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10543 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10544 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10545 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10546 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10547 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10548 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10549 {
10550 if (mHWData->maVideoCaptureScreens[i])
10551 ASMBitSet(&data.u64VideoCaptureScreens, i);
10552 else
10553 ASMBitClear(&data.u64VideoCaptureScreens, i);
10554 }
10555 /* store relative video capture file if possible */
10556 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10557
10558 /* VRDEServer settings (optional) */
10559 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10560 if (FAILED(rc)) throw rc;
10561
10562 /* BIOS (required) */
10563 rc = mBIOSSettings->saveSettings(data.biosSettings);
10564 if (FAILED(rc)) throw rc;
10565
10566 /* USB Controller (required) */
10567 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10568 it != mUSBControllers->end();
10569 ++it)
10570 {
10571 ComObjPtr<USBController> ctrl = *it;
10572 settings::USBController settingsCtrl;
10573
10574 settingsCtrl.strName = ctrl->getName();
10575 settingsCtrl.enmType = ctrl->getControllerType();
10576
10577 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10578 }
10579
10580 /* USB device filters (required) */
10581 rc = mUSBDeviceFilters->saveSettings(data.usbSettings);
10582 if (FAILED(rc)) throw rc;
10583
10584 /* Network adapters (required) */
10585 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10586 data.llNetworkAdapters.clear();
10587 /* Write out only the nominal number of network adapters for this
10588 * chipset type. Since Machine::commit() hasn't been called there
10589 * may be extra NIC settings in the vector. */
10590 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10591 {
10592 settings::NetworkAdapter nic;
10593 nic.ulSlot = slot;
10594 /* paranoia check... must not be NULL, but must not crash either. */
10595 if (mNetworkAdapters[slot])
10596 {
10597 rc = mNetworkAdapters[slot]->saveSettings(nic);
10598 if (FAILED(rc)) throw rc;
10599
10600 data.llNetworkAdapters.push_back(nic);
10601 }
10602 }
10603
10604 /* Serial ports */
10605 data.llSerialPorts.clear();
10606 for (ULONG slot = 0;
10607 slot < RT_ELEMENTS(mSerialPorts);
10608 ++slot)
10609 {
10610 settings::SerialPort s;
10611 s.ulSlot = slot;
10612 rc = mSerialPorts[slot]->saveSettings(s);
10613 if (FAILED(rc)) return rc;
10614
10615 data.llSerialPorts.push_back(s);
10616 }
10617
10618 /* Parallel ports */
10619 data.llParallelPorts.clear();
10620 for (ULONG slot = 0;
10621 slot < RT_ELEMENTS(mParallelPorts);
10622 ++slot)
10623 {
10624 settings::ParallelPort p;
10625 p.ulSlot = slot;
10626 rc = mParallelPorts[slot]->saveSettings(p);
10627 if (FAILED(rc)) return rc;
10628
10629 data.llParallelPorts.push_back(p);
10630 }
10631
10632 /* Audio adapter */
10633 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10634 if (FAILED(rc)) return rc;
10635
10636 /* Shared folders */
10637 data.llSharedFolders.clear();
10638 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10639 it != mHWData->mSharedFolders.end();
10640 ++it)
10641 {
10642 SharedFolder *pSF = *it;
10643 AutoCaller sfCaller(pSF);
10644 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10645 settings::SharedFolder sf;
10646 sf.strName = pSF->getName();
10647 sf.strHostPath = pSF->getHostPath();
10648 sf.fWritable = !!pSF->isWritable();
10649 sf.fAutoMount = !!pSF->isAutoMounted();
10650
10651 data.llSharedFolders.push_back(sf);
10652 }
10653
10654 // clipboard
10655 data.clipboardMode = mHWData->mClipboardMode;
10656
10657 // drag'n'drop
10658 data.dragAndDropMode = mHWData->mDragAndDropMode;
10659
10660 /* Guest */
10661 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10662
10663 // IO settings
10664 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10665 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10666
10667 /* BandwidthControl (required) */
10668 rc = mBandwidthControl->saveSettings(data.ioSettings);
10669 if (FAILED(rc)) throw rc;
10670
10671 /* Host PCI devices */
10672 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10673 it != mHWData->mPCIDeviceAssignments.end();
10674 ++it)
10675 {
10676 ComObjPtr<PCIDeviceAttachment> pda = *it;
10677 settings::HostPCIDeviceAttachment hpda;
10678
10679 rc = pda->saveSettings(hpda);
10680 if (FAILED(rc)) throw rc;
10681
10682 data.pciAttachments.push_back(hpda);
10683 }
10684
10685
10686 // guest properties
10687 data.llGuestProperties.clear();
10688#ifdef VBOX_WITH_GUEST_PROPS
10689 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10690 it != mHWData->mGuestProperties.end();
10691 ++it)
10692 {
10693 HWData::GuestProperty property = it->second;
10694
10695 /* Remove transient guest properties at shutdown unless we
10696 * are saving state */
10697 if ( ( mData->mMachineState == MachineState_PoweredOff
10698 || mData->mMachineState == MachineState_Aborted
10699 || mData->mMachineState == MachineState_Teleported)
10700 && ( property.mFlags & guestProp::TRANSIENT
10701 || property.mFlags & guestProp::TRANSRESET))
10702 continue;
10703 settings::GuestProperty prop;
10704 prop.strName = it->first;
10705 prop.strValue = property.strValue;
10706 prop.timestamp = property.mTimestamp;
10707 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10708 guestProp::writeFlags(property.mFlags, szFlags);
10709 prop.strFlags = szFlags;
10710
10711 data.llGuestProperties.push_back(prop);
10712 }
10713
10714 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10715 /* I presume this doesn't require a backup(). */
10716 mData->mGuestPropertiesModified = FALSE;
10717#endif /* VBOX_WITH_GUEST_PROPS defined */
10718
10719 *pDbg = mHWData->mDebugging;
10720 *pAutostart = mHWData->mAutostart;
10721
10722 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10723 }
10724 catch(std::bad_alloc &)
10725 {
10726 return E_OUTOFMEMORY;
10727 }
10728
10729 AssertComRC(rc);
10730 return rc;
10731}
10732
10733/**
10734 * Saves the storage controller configuration.
10735 *
10736 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10737 */
10738HRESULT Machine::saveStorageControllers(settings::Storage &data)
10739{
10740 data.llStorageControllers.clear();
10741
10742 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10743 it != mStorageControllers->end();
10744 ++it)
10745 {
10746 HRESULT rc;
10747 ComObjPtr<StorageController> pCtl = *it;
10748
10749 settings::StorageController ctl;
10750 ctl.strName = pCtl->getName();
10751 ctl.controllerType = pCtl->getControllerType();
10752 ctl.storageBus = pCtl->getStorageBus();
10753 ctl.ulInstance = pCtl->getInstance();
10754 ctl.fBootable = pCtl->getBootable();
10755
10756 /* Save the port count. */
10757 ULONG portCount;
10758 rc = pCtl->COMGETTER(PortCount)(&portCount);
10759 ComAssertComRCRet(rc, rc);
10760 ctl.ulPortCount = portCount;
10761
10762 /* Save fUseHostIOCache */
10763 BOOL fUseHostIOCache;
10764 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10765 ComAssertComRCRet(rc, rc);
10766 ctl.fUseHostIOCache = !!fUseHostIOCache;
10767
10768 /* Save IDE emulation settings. */
10769 if (ctl.controllerType == StorageControllerType_IntelAhci)
10770 {
10771 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10772 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10773 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10774 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10775 )
10776 ComAssertComRCRet(rc, rc);
10777 }
10778
10779 /* save the devices now. */
10780 rc = saveStorageDevices(pCtl, ctl);
10781 ComAssertComRCRet(rc, rc);
10782
10783 data.llStorageControllers.push_back(ctl);
10784 }
10785
10786 return S_OK;
10787}
10788
10789/**
10790 * Saves the hard disk configuration.
10791 */
10792HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10793 settings::StorageController &data)
10794{
10795 MediaData::AttachmentList atts;
10796
10797 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10798 if (FAILED(rc)) return rc;
10799
10800 data.llAttachedDevices.clear();
10801 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10802 it != atts.end();
10803 ++it)
10804 {
10805 settings::AttachedDevice dev;
10806
10807 MediumAttachment *pAttach = *it;
10808 Medium *pMedium = pAttach->getMedium();
10809
10810 dev.deviceType = pAttach->getType();
10811 dev.lPort = pAttach->getPort();
10812 dev.lDevice = pAttach->getDevice();
10813 if (pMedium)
10814 {
10815 if (pMedium->isHostDrive())
10816 dev.strHostDriveSrc = pMedium->getLocationFull();
10817 else
10818 dev.uuid = pMedium->getId();
10819 dev.fPassThrough = pAttach->getPassthrough();
10820 dev.fTempEject = pAttach->getTempEject();
10821 dev.fNonRotational = pAttach->getNonRotational();
10822 dev.fDiscard = pAttach->getDiscard();
10823 }
10824
10825 dev.strBwGroup = pAttach->getBandwidthGroup();
10826
10827 data.llAttachedDevices.push_back(dev);
10828 }
10829
10830 return S_OK;
10831}
10832
10833/**
10834 * Saves machine state settings as defined by aFlags
10835 * (SaveSTS_* values).
10836 *
10837 * @param aFlags Combination of SaveSTS_* flags.
10838 *
10839 * @note Locks objects for writing.
10840 */
10841HRESULT Machine::saveStateSettings(int aFlags)
10842{
10843 if (aFlags == 0)
10844 return S_OK;
10845
10846 AutoCaller autoCaller(this);
10847 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10848
10849 /* This object's write lock is also necessary to serialize file access
10850 * (prevent concurrent reads and writes) */
10851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10852
10853 HRESULT rc = S_OK;
10854
10855 Assert(mData->pMachineConfigFile);
10856
10857 try
10858 {
10859 if (aFlags & SaveSTS_CurStateModified)
10860 mData->pMachineConfigFile->fCurrentStateModified = true;
10861
10862 if (aFlags & SaveSTS_StateFilePath)
10863 {
10864 if (!mSSData->strStateFilePath.isEmpty())
10865 /* try to make the file name relative to the settings file dir */
10866 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10867 else
10868 mData->pMachineConfigFile->strStateFile.setNull();
10869 }
10870
10871 if (aFlags & SaveSTS_StateTimeStamp)
10872 {
10873 Assert( mData->mMachineState != MachineState_Aborted
10874 || mSSData->strStateFilePath.isEmpty());
10875
10876 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10877
10878 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10879//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10880 }
10881
10882 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10883 }
10884 catch (...)
10885 {
10886 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10887 }
10888
10889 return rc;
10890}
10891
10892/**
10893 * Ensures that the given medium is added to a media registry. If this machine
10894 * was created with 4.0 or later, then the machine registry is used. Otherwise
10895 * the global VirtualBox media registry is used.
10896 *
10897 * Caller must NOT hold machine lock, media tree or any medium locks!
10898 *
10899 * @param pMedium
10900 */
10901void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10902{
10903 /* Paranoia checks: do not hold machine or media tree locks. */
10904 AssertReturnVoid(!isWriteLockOnCurrentThread());
10905 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10906
10907 ComObjPtr<Medium> pBase;
10908 {
10909 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10910 pBase = pMedium->getBase();
10911 }
10912
10913 /* Paranoia checks: do not hold medium locks. */
10914 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10915 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10916
10917 // decide which medium registry to use now that the medium is attached:
10918 Guid uuid;
10919 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10920 // machine XML is VirtualBox 4.0 or higher:
10921 uuid = getId(); // machine UUID
10922 else
10923 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10924
10925 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10926 mParent->markRegistryModified(uuid);
10927
10928 /* For more complex hard disk structures it can happen that the base
10929 * medium isn't yet associated with any medium registry. Do that now. */
10930 if (pMedium != pBase)
10931 {
10932 if (pBase->addRegistry(uuid, true /* fRecurse */))
10933 mParent->markRegistryModified(uuid);
10934 }
10935}
10936
10937/**
10938 * Creates differencing hard disks for all normal hard disks attached to this
10939 * machine and a new set of attachments to refer to created disks.
10940 *
10941 * Used when taking a snapshot or when deleting the current state. Gets called
10942 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10943 *
10944 * This method assumes that mMediaData contains the original hard disk attachments
10945 * it needs to create diffs for. On success, these attachments will be replaced
10946 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10947 * called to delete created diffs which will also rollback mMediaData and restore
10948 * whatever was backed up before calling this method.
10949 *
10950 * Attachments with non-normal hard disks are left as is.
10951 *
10952 * If @a aOnline is @c false then the original hard disks that require implicit
10953 * diffs will be locked for reading. Otherwise it is assumed that they are
10954 * already locked for writing (when the VM was started). Note that in the latter
10955 * case it is responsibility of the caller to lock the newly created diffs for
10956 * writing if this method succeeds.
10957 *
10958 * @param aProgress Progress object to run (must contain at least as
10959 * many operations left as the number of hard disks
10960 * attached).
10961 * @param aOnline Whether the VM was online prior to this operation.
10962 *
10963 * @note The progress object is not marked as completed, neither on success nor
10964 * on failure. This is a responsibility of the caller.
10965 *
10966 * @note Locks this object and the media tree for writing.
10967 */
10968HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10969 ULONG aWeight,
10970 bool aOnline)
10971{
10972 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10973
10974 AutoCaller autoCaller(this);
10975 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10976
10977 AutoMultiWriteLock2 alock(this->lockHandle(),
10978 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10979
10980 /* must be in a protective state because we release the lock below */
10981 AssertReturn( mData->mMachineState == MachineState_Saving
10982 || mData->mMachineState == MachineState_LiveSnapshotting
10983 || mData->mMachineState == MachineState_RestoringSnapshot
10984 || mData->mMachineState == MachineState_DeletingSnapshot
10985 , E_FAIL);
10986
10987 HRESULT rc = S_OK;
10988
10989 // use appropriate locked media map (online or offline)
10990 MediumLockListMap lockedMediaOffline;
10991 MediumLockListMap *lockedMediaMap;
10992 if (aOnline)
10993 lockedMediaMap = &mData->mSession.mLockedMedia;
10994 else
10995 lockedMediaMap = &lockedMediaOffline;
10996
10997 try
10998 {
10999 if (!aOnline)
11000 {
11001 /* lock all attached hard disks early to detect "in use"
11002 * situations before creating actual diffs */
11003 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11004 it != mMediaData->mAttachments.end();
11005 ++it)
11006 {
11007 MediumAttachment* pAtt = *it;
11008 if (pAtt->getType() == DeviceType_HardDisk)
11009 {
11010 Medium* pMedium = pAtt->getMedium();
11011 Assert(pMedium);
11012
11013 MediumLockList *pMediumLockList(new MediumLockList());
11014 alock.release();
11015 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11016 false /* fMediumLockWrite */,
11017 NULL,
11018 *pMediumLockList);
11019 alock.acquire();
11020 if (FAILED(rc))
11021 {
11022 delete pMediumLockList;
11023 throw rc;
11024 }
11025 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11026 if (FAILED(rc))
11027 {
11028 throw setError(rc,
11029 tr("Collecting locking information for all attached media failed"));
11030 }
11031 }
11032 }
11033
11034 /* Now lock all media. If this fails, nothing is locked. */
11035 alock.release();
11036 rc = lockedMediaMap->Lock();
11037 alock.acquire();
11038 if (FAILED(rc))
11039 {
11040 throw setError(rc,
11041 tr("Locking of attached media failed"));
11042 }
11043 }
11044
11045 /* remember the current list (note that we don't use backup() since
11046 * mMediaData may be already backed up) */
11047 MediaData::AttachmentList atts = mMediaData->mAttachments;
11048
11049 /* start from scratch */
11050 mMediaData->mAttachments.clear();
11051
11052 /* go through remembered attachments and create diffs for normal hard
11053 * disks and attach them */
11054 for (MediaData::AttachmentList::const_iterator it = atts.begin();
11055 it != atts.end();
11056 ++it)
11057 {
11058 MediumAttachment* pAtt = *it;
11059
11060 DeviceType_T devType = pAtt->getType();
11061 Medium* pMedium = pAtt->getMedium();
11062
11063 if ( devType != DeviceType_HardDisk
11064 || pMedium == NULL
11065 || pMedium->getType() != MediumType_Normal)
11066 {
11067 /* copy the attachment as is */
11068
11069 /** @todo the progress object created in Console::TakeSnaphot
11070 * only expects operations for hard disks. Later other
11071 * device types need to show up in the progress as well. */
11072 if (devType == DeviceType_HardDisk)
11073 {
11074 if (pMedium == NULL)
11075 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11076 aWeight); // weight
11077 else
11078 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11079 pMedium->getBase()->getName().c_str()).raw(),
11080 aWeight); // weight
11081 }
11082
11083 mMediaData->mAttachments.push_back(pAtt);
11084 continue;
11085 }
11086
11087 /* need a diff */
11088 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11089 pMedium->getBase()->getName().c_str()).raw(),
11090 aWeight); // weight
11091
11092 Utf8Str strFullSnapshotFolder;
11093 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11094
11095 ComObjPtr<Medium> diff;
11096 diff.createObject();
11097 // store the diff in the same registry as the parent
11098 // (this cannot fail here because we can't create implicit diffs for
11099 // unregistered images)
11100 Guid uuidRegistryParent;
11101 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
11102 Assert(fInRegistry); NOREF(fInRegistry);
11103 rc = diff->init(mParent,
11104 pMedium->getPreferredDiffFormat(),
11105 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11106 uuidRegistryParent);
11107 if (FAILED(rc)) throw rc;
11108
11109 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11110 * the push_back? Looks like we're going to release medium with the
11111 * wrong kind of lock (general issue with if we fail anywhere at all)
11112 * and an orphaned VDI in the snapshots folder. */
11113
11114 /* update the appropriate lock list */
11115 MediumLockList *pMediumLockList;
11116 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11117 AssertComRCThrowRC(rc);
11118 if (aOnline)
11119 {
11120 alock.release();
11121 /* The currently attached medium will be read-only, change
11122 * the lock type to read. */
11123 rc = pMediumLockList->Update(pMedium, false);
11124 alock.acquire();
11125 AssertComRCThrowRC(rc);
11126 }
11127
11128 /* release the locks before the potentially lengthy operation */
11129 alock.release();
11130 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
11131 pMediumLockList,
11132 NULL /* aProgress */,
11133 true /* aWait */);
11134 alock.acquire();
11135 if (FAILED(rc)) throw rc;
11136
11137 /* actual lock list update is done in Medium::commitMedia */
11138
11139 rc = diff->addBackReference(mData->mUuid);
11140 AssertComRCThrowRC(rc);
11141
11142 /* add a new attachment */
11143 ComObjPtr<MediumAttachment> attachment;
11144 attachment.createObject();
11145 rc = attachment->init(this,
11146 diff,
11147 pAtt->getControllerName(),
11148 pAtt->getPort(),
11149 pAtt->getDevice(),
11150 DeviceType_HardDisk,
11151 true /* aImplicit */,
11152 false /* aPassthrough */,
11153 false /* aTempEject */,
11154 pAtt->getNonRotational(),
11155 pAtt->getDiscard(),
11156 pAtt->getBandwidthGroup());
11157 if (FAILED(rc)) throw rc;
11158
11159 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11160 AssertComRCThrowRC(rc);
11161 mMediaData->mAttachments.push_back(attachment);
11162 }
11163 }
11164 catch (HRESULT aRC) { rc = aRC; }
11165
11166 /* unlock all hard disks we locked when there is no VM */
11167 if (!aOnline)
11168 {
11169 ErrorInfoKeeper eik;
11170
11171 HRESULT rc1 = lockedMediaMap->Clear();
11172 AssertComRC(rc1);
11173 }
11174
11175 return rc;
11176}
11177
11178/**
11179 * Deletes implicit differencing hard disks created either by
11180 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
11181 *
11182 * Note that to delete hard disks created by #AttachDevice() this method is
11183 * called from #fixupMedia() when the changes are rolled back.
11184 *
11185 * @note Locks this object and the media tree for writing.
11186 */
11187HRESULT Machine::deleteImplicitDiffs(bool aOnline)
11188{
11189 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11190
11191 AutoCaller autoCaller(this);
11192 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11193
11194 AutoMultiWriteLock2 alock(this->lockHandle(),
11195 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11196
11197 /* We absolutely must have backed up state. */
11198 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
11199
11200 /* Check if there are any implicitly created diff images. */
11201 bool fImplicitDiffs = false;
11202 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11203 it != mMediaData->mAttachments.end();
11204 ++it)
11205 {
11206 const ComObjPtr<MediumAttachment> &pAtt = *it;
11207 if (pAtt->isImplicit())
11208 {
11209 fImplicitDiffs = true;
11210 break;
11211 }
11212 }
11213 /* If there is nothing to do, leave early. This saves lots of image locking
11214 * effort. It also avoids a MachineStateChanged event without real reason.
11215 * This is important e.g. when loading a VM config, because there should be
11216 * no events. Otherwise API clients can become thoroughly confused for
11217 * inaccessible VMs (the code for loading VM configs uses this method for
11218 * cleanup if the config makes no sense), as they take such events as an
11219 * indication that the VM is alive, and they would force the VM config to
11220 * be reread, leading to an endless loop. */
11221 if (!fImplicitDiffs)
11222 return S_OK;
11223
11224 HRESULT rc = S_OK;
11225 MachineState_T oldState = mData->mMachineState;
11226
11227 /* will release the lock before the potentially lengthy operation,
11228 * so protect with the special state (unless already protected) */
11229 if ( oldState != MachineState_Saving
11230 && oldState != MachineState_LiveSnapshotting
11231 && oldState != MachineState_RestoringSnapshot
11232 && oldState != MachineState_DeletingSnapshot
11233 && oldState != MachineState_DeletingSnapshotOnline
11234 && oldState != MachineState_DeletingSnapshotPaused
11235 )
11236 setMachineState(MachineState_SettingUp);
11237
11238 // use appropriate locked media map (online or offline)
11239 MediumLockListMap lockedMediaOffline;
11240 MediumLockListMap *lockedMediaMap;
11241 if (aOnline)
11242 lockedMediaMap = &mData->mSession.mLockedMedia;
11243 else
11244 lockedMediaMap = &lockedMediaOffline;
11245
11246 try
11247 {
11248 if (!aOnline)
11249 {
11250 /* lock all attached hard disks early to detect "in use"
11251 * situations before deleting actual diffs */
11252 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11253 it != mMediaData->mAttachments.end();
11254 ++it)
11255 {
11256 MediumAttachment* pAtt = *it;
11257 if (pAtt->getType() == DeviceType_HardDisk)
11258 {
11259 Medium* pMedium = pAtt->getMedium();
11260 Assert(pMedium);
11261
11262 MediumLockList *pMediumLockList(new MediumLockList());
11263 alock.release();
11264 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11265 false /* fMediumLockWrite */,
11266 NULL,
11267 *pMediumLockList);
11268 alock.acquire();
11269
11270 if (FAILED(rc))
11271 {
11272 delete pMediumLockList;
11273 throw rc;
11274 }
11275
11276 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11277 if (FAILED(rc))
11278 throw rc;
11279 }
11280 }
11281
11282 if (FAILED(rc))
11283 throw rc;
11284 } // end of offline
11285
11286 /* Lock lists are now up to date and include implicitly created media */
11287
11288 /* Go through remembered attachments and delete all implicitly created
11289 * diffs and fix up the attachment information */
11290 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11291 MediaData::AttachmentList implicitAtts;
11292 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11293 it != mMediaData->mAttachments.end();
11294 ++it)
11295 {
11296 ComObjPtr<MediumAttachment> pAtt = *it;
11297 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11298 if (pMedium.isNull())
11299 continue;
11300
11301 // Implicit attachments go on the list for deletion and back references are removed.
11302 if (pAtt->isImplicit())
11303 {
11304 /* Deassociate and mark for deletion */
11305 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
11306 rc = pMedium->removeBackReference(mData->mUuid);
11307 if (FAILED(rc))
11308 throw rc;
11309 implicitAtts.push_back(pAtt);
11310 continue;
11311 }
11312
11313 /* Was this medium attached before? */
11314 if (!findAttachment(oldAtts, pMedium))
11315 {
11316 /* no: de-associate */
11317 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
11318 rc = pMedium->removeBackReference(mData->mUuid);
11319 if (FAILED(rc))
11320 throw rc;
11321 continue;
11322 }
11323 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
11324 }
11325
11326 /* If there are implicit attachments to delete, throw away the lock
11327 * map contents (which will unlock all media) since the medium
11328 * attachments will be rolled back. Below we need to completely
11329 * recreate the lock map anyway since it is infinitely complex to
11330 * do this incrementally (would need reconstructing each attachment
11331 * change, which would be extremely hairy). */
11332 if (implicitAtts.size() != 0)
11333 {
11334 ErrorInfoKeeper eik;
11335
11336 HRESULT rc1 = lockedMediaMap->Clear();
11337 AssertComRC(rc1);
11338 }
11339
11340 /* rollback hard disk changes */
11341 mMediaData.rollback();
11342
11343 MultiResult mrc(S_OK);
11344
11345 // Delete unused implicit diffs.
11346 if (implicitAtts.size() != 0)
11347 {
11348 alock.release();
11349
11350 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11351 it != implicitAtts.end();
11352 ++it)
11353 {
11354 // Remove medium associated with this attachment.
11355 ComObjPtr<MediumAttachment> pAtt = *it;
11356 Assert(pAtt);
11357 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11358 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11359 Assert(pMedium);
11360
11361 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11362 // continue on delete failure, just collect error messages
11363 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11364 mrc = rc;
11365 }
11366
11367 alock.acquire();
11368
11369 /* if there is a VM recreate media lock map as mentioned above,
11370 * otherwise it is a waste of time and we leave things unlocked */
11371 if (aOnline)
11372 {
11373 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11374 /* must never be NULL, but better safe than sorry */
11375 if (!pMachine.isNull())
11376 {
11377 alock.release();
11378 rc = mData->mSession.mMachine->lockMedia();
11379 alock.acquire();
11380 if (FAILED(rc))
11381 throw rc;
11382 }
11383 }
11384 }
11385 }
11386 catch (HRESULT aRC) {rc = aRC;}
11387
11388 if (mData->mMachineState == MachineState_SettingUp)
11389 setMachineState(oldState);
11390
11391 /* unlock all hard disks we locked when there is no VM */
11392 if (!aOnline)
11393 {
11394 ErrorInfoKeeper eik;
11395
11396 HRESULT rc1 = lockedMediaMap->Clear();
11397 AssertComRC(rc1);
11398 }
11399
11400 return rc;
11401}
11402
11403
11404/**
11405 * Looks through the given list of media attachments for one with the given parameters
11406 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11407 * can be searched as well if needed.
11408 *
11409 * @param list
11410 * @param aControllerName
11411 * @param aControllerPort
11412 * @param aDevice
11413 * @return
11414 */
11415MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11416 IN_BSTR aControllerName,
11417 LONG aControllerPort,
11418 LONG aDevice)
11419{
11420 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11421 it != ll.end();
11422 ++it)
11423 {
11424 MediumAttachment *pAttach = *it;
11425 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11426 return pAttach;
11427 }
11428
11429 return NULL;
11430}
11431
11432/**
11433 * Looks through the given list of media attachments for one with the given parameters
11434 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11435 * can be searched as well if needed.
11436 *
11437 * @param list
11438 * @param aControllerName
11439 * @param aControllerPort
11440 * @param aDevice
11441 * @return
11442 */
11443MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11444 ComObjPtr<Medium> pMedium)
11445{
11446 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11447 it != ll.end();
11448 ++it)
11449 {
11450 MediumAttachment *pAttach = *it;
11451 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11452 if (pMediumThis == pMedium)
11453 return pAttach;
11454 }
11455
11456 return NULL;
11457}
11458
11459/**
11460 * Looks through the given list of media attachments for one with the given parameters
11461 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11462 * can be searched as well if needed.
11463 *
11464 * @param list
11465 * @param aControllerName
11466 * @param aControllerPort
11467 * @param aDevice
11468 * @return
11469 */
11470MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11471 Guid &id)
11472{
11473 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11474 it != ll.end();
11475 ++it)
11476 {
11477 MediumAttachment *pAttach = *it;
11478 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11479 if (pMediumThis->getId() == id)
11480 return pAttach;
11481 }
11482
11483 return NULL;
11484}
11485
11486/**
11487 * Main implementation for Machine::DetachDevice. This also gets called
11488 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11489 *
11490 * @param pAttach Medium attachment to detach.
11491 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11492 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11493 * @return
11494 */
11495HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11496 AutoWriteLock &writeLock,
11497 Snapshot *pSnapshot)
11498{
11499 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11500 DeviceType_T mediumType = pAttach->getType();
11501
11502 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11503
11504 if (pAttach->isImplicit())
11505 {
11506 /* attempt to implicitly delete the implicitly created diff */
11507
11508 /// @todo move the implicit flag from MediumAttachment to Medium
11509 /// and forbid any hard disk operation when it is implicit. Or maybe
11510 /// a special media state for it to make it even more simple.
11511
11512 Assert(mMediaData.isBackedUp());
11513
11514 /* will release the lock before the potentially lengthy operation, so
11515 * protect with the special state */
11516 MachineState_T oldState = mData->mMachineState;
11517 setMachineState(MachineState_SettingUp);
11518
11519 writeLock.release();
11520
11521 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11522 true /*aWait*/);
11523
11524 writeLock.acquire();
11525
11526 setMachineState(oldState);
11527
11528 if (FAILED(rc)) return rc;
11529 }
11530
11531 setModified(IsModified_Storage);
11532 mMediaData.backup();
11533 mMediaData->mAttachments.remove(pAttach);
11534
11535 if (!oldmedium.isNull())
11536 {
11537 // if this is from a snapshot, do not defer detachment to commitMedia()
11538 if (pSnapshot)
11539 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11540 // else if non-hard disk media, do not defer detachment to commitMedia() either
11541 else if (mediumType != DeviceType_HardDisk)
11542 oldmedium->removeBackReference(mData->mUuid);
11543 }
11544
11545 return S_OK;
11546}
11547
11548/**
11549 * Goes thru all media of the given list and
11550 *
11551 * 1) calls detachDevice() on each of them for this machine and
11552 * 2) adds all Medium objects found in the process to the given list,
11553 * depending on cleanupMode.
11554 *
11555 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11556 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11557 * media to the list.
11558 *
11559 * This gets called from Machine::Unregister, both for the actual Machine and
11560 * the SnapshotMachine objects that might be found in the snapshots.
11561 *
11562 * Requires caller and locking. The machine lock must be passed in because it
11563 * will be passed on to detachDevice which needs it for temporary unlocking.
11564 *
11565 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11566 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11567 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11568 * otherwise no media get added.
11569 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11570 * @return
11571 */
11572HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11573 Snapshot *pSnapshot,
11574 CleanupMode_T cleanupMode,
11575 MediaList &llMedia)
11576{
11577 Assert(isWriteLockOnCurrentThread());
11578
11579 HRESULT rc;
11580
11581 // make a temporary list because detachDevice invalidates iterators into
11582 // mMediaData->mAttachments
11583 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11584
11585 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11586 it != llAttachments2.end();
11587 ++it)
11588 {
11589 ComObjPtr<MediumAttachment> &pAttach = *it;
11590 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11591
11592 if (!pMedium.isNull())
11593 {
11594 AutoCaller mac(pMedium);
11595 if (FAILED(mac.rc())) return mac.rc();
11596 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11597 DeviceType_T devType = pMedium->getDeviceType();
11598 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11599 && devType == DeviceType_HardDisk)
11600 || (cleanupMode == CleanupMode_Full)
11601 )
11602 {
11603 llMedia.push_back(pMedium);
11604 ComObjPtr<Medium> pParent = pMedium->getParent();
11605 /*
11606 * Search for medias which are not attached to any machine, but
11607 * in the chain to an attached disk. Mediums are only consided
11608 * if they are:
11609 * - have only one child
11610 * - no references to any machines
11611 * - are of normal medium type
11612 */
11613 while (!pParent.isNull())
11614 {
11615 AutoCaller mac1(pParent);
11616 if (FAILED(mac1.rc())) return mac1.rc();
11617 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11618 if (pParent->getChildren().size() == 1)
11619 {
11620 if ( pParent->getMachineBackRefCount() == 0
11621 && pParent->getType() == MediumType_Normal
11622 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11623 llMedia.push_back(pParent);
11624 }
11625 else
11626 break;
11627 pParent = pParent->getParent();
11628 }
11629 }
11630 }
11631
11632 // real machine: then we need to use the proper method
11633 rc = detachDevice(pAttach, writeLock, pSnapshot);
11634
11635 if (FAILED(rc))
11636 return rc;
11637 }
11638
11639 return S_OK;
11640}
11641
11642/**
11643 * Perform deferred hard disk detachments.
11644 *
11645 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11646 * backed up).
11647 *
11648 * If @a aOnline is @c true then this method will also unlock the old hard disks
11649 * for which the new implicit diffs were created and will lock these new diffs for
11650 * writing.
11651 *
11652 * @param aOnline Whether the VM was online prior to this operation.
11653 *
11654 * @note Locks this object for writing!
11655 */
11656void Machine::commitMedia(bool aOnline /*= false*/)
11657{
11658 AutoCaller autoCaller(this);
11659 AssertComRCReturnVoid(autoCaller.rc());
11660
11661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11662
11663 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11664
11665 HRESULT rc = S_OK;
11666
11667 /* no attach/detach operations -- nothing to do */
11668 if (!mMediaData.isBackedUp())
11669 return;
11670
11671 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11672 bool fMediaNeedsLocking = false;
11673
11674 /* enumerate new attachments */
11675 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11676 it != mMediaData->mAttachments.end();
11677 ++it)
11678 {
11679 MediumAttachment *pAttach = *it;
11680
11681 pAttach->commit();
11682
11683 Medium* pMedium = pAttach->getMedium();
11684 bool fImplicit = pAttach->isImplicit();
11685
11686 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11687 (pMedium) ? pMedium->getName().c_str() : "NULL",
11688 fImplicit));
11689
11690 /** @todo convert all this Machine-based voodoo to MediumAttachment
11691 * based commit logic. */
11692 if (fImplicit)
11693 {
11694 /* convert implicit attachment to normal */
11695 pAttach->setImplicit(false);
11696
11697 if ( aOnline
11698 && pMedium
11699 && pAttach->getType() == DeviceType_HardDisk
11700 )
11701 {
11702 ComObjPtr<Medium> parent = pMedium->getParent();
11703 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11704
11705 /* update the appropriate lock list */
11706 MediumLockList *pMediumLockList;
11707 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11708 AssertComRC(rc);
11709 if (pMediumLockList)
11710 {
11711 /* unlock if there's a need to change the locking */
11712 if (!fMediaNeedsLocking)
11713 {
11714 rc = mData->mSession.mLockedMedia.Unlock();
11715 AssertComRC(rc);
11716 fMediaNeedsLocking = true;
11717 }
11718 rc = pMediumLockList->Update(parent, false);
11719 AssertComRC(rc);
11720 rc = pMediumLockList->Append(pMedium, true);
11721 AssertComRC(rc);
11722 }
11723 }
11724
11725 continue;
11726 }
11727
11728 if (pMedium)
11729 {
11730 /* was this medium attached before? */
11731 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11732 oldIt != oldAtts.end();
11733 ++oldIt)
11734 {
11735 MediumAttachment *pOldAttach = *oldIt;
11736 if (pOldAttach->getMedium() == pMedium)
11737 {
11738 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11739
11740 /* yes: remove from old to avoid de-association */
11741 oldAtts.erase(oldIt);
11742 break;
11743 }
11744 }
11745 }
11746 }
11747
11748 /* enumerate remaining old attachments and de-associate from the
11749 * current machine state */
11750 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11751 it != oldAtts.end();
11752 ++it)
11753 {
11754 MediumAttachment *pAttach = *it;
11755 Medium* pMedium = pAttach->getMedium();
11756
11757 /* Detach only hard disks, since DVD/floppy media is detached
11758 * instantly in MountMedium. */
11759 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11760 {
11761 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11762
11763 /* now de-associate from the current machine state */
11764 rc = pMedium->removeBackReference(mData->mUuid);
11765 AssertComRC(rc);
11766
11767 if (aOnline)
11768 {
11769 /* unlock since medium is not used anymore */
11770 MediumLockList *pMediumLockList;
11771 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11772 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11773 {
11774 /* this happens for online snapshots, there the attachment
11775 * is changing, but only to a diff image created under
11776 * the old one, so there is no separate lock list */
11777 Assert(!pMediumLockList);
11778 }
11779 else
11780 {
11781 AssertComRC(rc);
11782 if (pMediumLockList)
11783 {
11784 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11785 AssertComRC(rc);
11786 }
11787 }
11788 }
11789 }
11790 }
11791
11792 /* take media locks again so that the locking state is consistent */
11793 if (fMediaNeedsLocking)
11794 {
11795 Assert(aOnline);
11796 rc = mData->mSession.mLockedMedia.Lock();
11797 AssertComRC(rc);
11798 }
11799
11800 /* commit the hard disk changes */
11801 mMediaData.commit();
11802
11803 if (isSessionMachine())
11804 {
11805 /*
11806 * Update the parent machine to point to the new owner.
11807 * This is necessary because the stored parent will point to the
11808 * session machine otherwise and cause crashes or errors later
11809 * when the session machine gets invalid.
11810 */
11811 /** @todo Change the MediumAttachment class to behave like any other
11812 * class in this regard by creating peer MediumAttachment
11813 * objects for session machines and share the data with the peer
11814 * machine.
11815 */
11816 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11817 it != mMediaData->mAttachments.end();
11818 ++it)
11819 {
11820 (*it)->updateParentMachine(mPeer);
11821 }
11822
11823 /* attach new data to the primary machine and reshare it */
11824 mPeer->mMediaData.attach(mMediaData);
11825 }
11826
11827 return;
11828}
11829
11830/**
11831 * Perform deferred deletion of implicitly created diffs.
11832 *
11833 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11834 * backed up).
11835 *
11836 * @note Locks this object for writing!
11837 */
11838void Machine::rollbackMedia()
11839{
11840 AutoCaller autoCaller(this);
11841 AssertComRCReturnVoid(autoCaller.rc());
11842
11843 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11844 LogFlowThisFunc(("Entering rollbackMedia\n"));
11845
11846 HRESULT rc = S_OK;
11847
11848 /* no attach/detach operations -- nothing to do */
11849 if (!mMediaData.isBackedUp())
11850 return;
11851
11852 /* enumerate new attachments */
11853 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11854 it != mMediaData->mAttachments.end();
11855 ++it)
11856 {
11857 MediumAttachment *pAttach = *it;
11858 /* Fix up the backrefs for DVD/floppy media. */
11859 if (pAttach->getType() != DeviceType_HardDisk)
11860 {
11861 Medium* pMedium = pAttach->getMedium();
11862 if (pMedium)
11863 {
11864 rc = pMedium->removeBackReference(mData->mUuid);
11865 AssertComRC(rc);
11866 }
11867 }
11868
11869 (*it)->rollback();
11870
11871 pAttach = *it;
11872 /* Fix up the backrefs for DVD/floppy media. */
11873 if (pAttach->getType() != DeviceType_HardDisk)
11874 {
11875 Medium* pMedium = pAttach->getMedium();
11876 if (pMedium)
11877 {
11878 rc = pMedium->addBackReference(mData->mUuid);
11879 AssertComRC(rc);
11880 }
11881 }
11882 }
11883
11884 /** @todo convert all this Machine-based voodoo to MediumAttachment
11885 * based rollback logic. */
11886 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11887
11888 return;
11889}
11890
11891/**
11892 * Returns true if the settings file is located in the directory named exactly
11893 * as the machine; this means, among other things, that the machine directory
11894 * should be auto-renamed.
11895 *
11896 * @param aSettingsDir if not NULL, the full machine settings file directory
11897 * name will be assigned there.
11898 *
11899 * @note Doesn't lock anything.
11900 * @note Not thread safe (must be called from this object's lock).
11901 */
11902bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11903{
11904 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11905 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11906 if (aSettingsDir)
11907 *aSettingsDir = strMachineDirName;
11908 strMachineDirName.stripPath(); // vmname
11909 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11910 strConfigFileOnly.stripPath() // vmname.vbox
11911 .stripExt(); // vmname
11912 /** @todo hack, make somehow use of ComposeMachineFilename */
11913 if (mUserData->s.fDirectoryIncludesUUID)
11914 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11915
11916 AssertReturn(!strMachineDirName.isEmpty(), false);
11917 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11918
11919 return strMachineDirName == strConfigFileOnly;
11920}
11921
11922/**
11923 * Discards all changes to machine settings.
11924 *
11925 * @param aNotify Whether to notify the direct session about changes or not.
11926 *
11927 * @note Locks objects for writing!
11928 */
11929void Machine::rollback(bool aNotify)
11930{
11931 AutoCaller autoCaller(this);
11932 AssertComRCReturn(autoCaller.rc(), (void)0);
11933
11934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11935
11936 if (!mStorageControllers.isNull())
11937 {
11938 if (mStorageControllers.isBackedUp())
11939 {
11940 /* unitialize all new devices (absent in the backed up list). */
11941 StorageControllerList::const_iterator it = mStorageControllers->begin();
11942 StorageControllerList *backedList = mStorageControllers.backedUpData();
11943 while (it != mStorageControllers->end())
11944 {
11945 if ( std::find(backedList->begin(), backedList->end(), *it)
11946 == backedList->end()
11947 )
11948 {
11949 (*it)->uninit();
11950 }
11951 ++it;
11952 }
11953
11954 /* restore the list */
11955 mStorageControllers.rollback();
11956 }
11957
11958 /* rollback any changes to devices after restoring the list */
11959 if (mData->flModifications & IsModified_Storage)
11960 {
11961 StorageControllerList::const_iterator it = mStorageControllers->begin();
11962 while (it != mStorageControllers->end())
11963 {
11964 (*it)->rollback();
11965 ++it;
11966 }
11967 }
11968 }
11969
11970 if (!mUSBControllers.isNull())
11971 {
11972 if (mUSBControllers.isBackedUp())
11973 {
11974 /* unitialize all new devices (absent in the backed up list). */
11975 USBControllerList::const_iterator it = mUSBControllers->begin();
11976 USBControllerList *backedList = mUSBControllers.backedUpData();
11977 while (it != mUSBControllers->end())
11978 {
11979 if ( std::find(backedList->begin(), backedList->end(), *it)
11980 == backedList->end()
11981 )
11982 {
11983 (*it)->uninit();
11984 }
11985 ++it;
11986 }
11987
11988 /* restore the list */
11989 mUSBControllers.rollback();
11990 }
11991
11992 /* rollback any changes to devices after restoring the list */
11993 if (mData->flModifications & IsModified_USB)
11994 {
11995 USBControllerList::const_iterator it = mUSBControllers->begin();
11996 while (it != mUSBControllers->end())
11997 {
11998 (*it)->rollback();
11999 ++it;
12000 }
12001 }
12002 }
12003
12004 mUserData.rollback();
12005
12006 mHWData.rollback();
12007
12008 if (mData->flModifications & IsModified_Storage)
12009 rollbackMedia();
12010
12011 if (mBIOSSettings)
12012 mBIOSSettings->rollback();
12013
12014 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12015 mVRDEServer->rollback();
12016
12017 if (mAudioAdapter)
12018 mAudioAdapter->rollback();
12019
12020 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12021 mUSBDeviceFilters->rollback();
12022
12023 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12024 mBandwidthControl->rollback();
12025
12026 if (!mHWData.isNull())
12027 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12028 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12029 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12030 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12031
12032 if (mData->flModifications & IsModified_NetworkAdapters)
12033 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12034 if ( mNetworkAdapters[slot]
12035 && mNetworkAdapters[slot]->isModified())
12036 {
12037 mNetworkAdapters[slot]->rollback();
12038 networkAdapters[slot] = mNetworkAdapters[slot];
12039 }
12040
12041 if (mData->flModifications & IsModified_SerialPorts)
12042 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12043 if ( mSerialPorts[slot]
12044 && mSerialPorts[slot]->isModified())
12045 {
12046 mSerialPorts[slot]->rollback();
12047 serialPorts[slot] = mSerialPorts[slot];
12048 }
12049
12050 if (mData->flModifications & IsModified_ParallelPorts)
12051 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12052 if ( mParallelPorts[slot]
12053 && mParallelPorts[slot]->isModified())
12054 {
12055 mParallelPorts[slot]->rollback();
12056 parallelPorts[slot] = mParallelPorts[slot];
12057 }
12058
12059 if (aNotify)
12060 {
12061 /* inform the direct session about changes */
12062
12063 ComObjPtr<Machine> that = this;
12064 uint32_t flModifications = mData->flModifications;
12065 alock.release();
12066
12067 if (flModifications & IsModified_SharedFolders)
12068 that->onSharedFolderChange();
12069
12070 if (flModifications & IsModified_VRDEServer)
12071 that->onVRDEServerChange(/* aRestart */ TRUE);
12072 if (flModifications & IsModified_USB)
12073 that->onUSBControllerChange();
12074
12075 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
12076 if (networkAdapters[slot])
12077 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
12078 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
12079 if (serialPorts[slot])
12080 that->onSerialPortChange(serialPorts[slot]);
12081 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
12082 if (parallelPorts[slot])
12083 that->onParallelPortChange(parallelPorts[slot]);
12084
12085 if (flModifications & IsModified_Storage)
12086 that->onStorageControllerChange();
12087
12088#if 0
12089 if (flModifications & IsModified_BandwidthControl)
12090 that->onBandwidthControlChange();
12091#endif
12092 }
12093}
12094
12095/**
12096 * Commits all the changes to machine settings.
12097 *
12098 * Note that this operation is supposed to never fail.
12099 *
12100 * @note Locks this object and children for writing.
12101 */
12102void Machine::commit()
12103{
12104 AutoCaller autoCaller(this);
12105 AssertComRCReturnVoid(autoCaller.rc());
12106
12107 AutoCaller peerCaller(mPeer);
12108 AssertComRCReturnVoid(peerCaller.rc());
12109
12110 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12111
12112 /*
12113 * use safe commit to ensure Snapshot machines (that share mUserData)
12114 * will still refer to a valid memory location
12115 */
12116 mUserData.commitCopy();
12117
12118 mHWData.commit();
12119
12120 if (mMediaData.isBackedUp())
12121 commitMedia(Global::IsOnline(mData->mMachineState));
12122
12123 mBIOSSettings->commit();
12124 mVRDEServer->commit();
12125 mAudioAdapter->commit();
12126 mUSBDeviceFilters->commit();
12127 mBandwidthControl->commit();
12128
12129 /* Since mNetworkAdapters is a list which might have been changed (resized)
12130 * without using the Backupable<> template we need to handle the copying
12131 * of the list entries manually, including the creation of peers for the
12132 * new objects. */
12133 bool commitNetworkAdapters = false;
12134 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12135 if (mPeer)
12136 {
12137 /* commit everything, even the ones which will go away */
12138 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12139 mNetworkAdapters[slot]->commit();
12140 /* copy over the new entries, creating a peer and uninit the original */
12141 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12142 for (size_t slot = 0; slot < newSize; slot++)
12143 {
12144 /* look if this adapter has a peer device */
12145 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
12146 if (!peer)
12147 {
12148 /* no peer means the adapter is a newly created one;
12149 * create a peer owning data this data share it with */
12150 peer.createObject();
12151 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12152 }
12153 mPeer->mNetworkAdapters[slot] = peer;
12154 }
12155 /* uninit any no longer needed network adapters */
12156 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
12157 mNetworkAdapters[slot]->uninit();
12158 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
12159 {
12160 if (mPeer->mNetworkAdapters[slot])
12161 mPeer->mNetworkAdapters[slot]->uninit();
12162 }
12163 /* Keep the original network adapter count until this point, so that
12164 * discarding a chipset type change will not lose settings. */
12165 mNetworkAdapters.resize(newSize);
12166 mPeer->mNetworkAdapters.resize(newSize);
12167 }
12168 else
12169 {
12170 /* we have no peer (our parent is the newly created machine);
12171 * just commit changes to the network adapters */
12172 commitNetworkAdapters = true;
12173 }
12174 if (commitNetworkAdapters)
12175 {
12176 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12177 mNetworkAdapters[slot]->commit();
12178 }
12179
12180 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12181 mSerialPorts[slot]->commit();
12182 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12183 mParallelPorts[slot]->commit();
12184
12185 bool commitStorageControllers = false;
12186
12187 if (mStorageControllers.isBackedUp())
12188 {
12189 mStorageControllers.commit();
12190
12191 if (mPeer)
12192 {
12193 /* Commit all changes to new controllers (this will reshare data with
12194 * peers for those who have peers) */
12195 StorageControllerList *newList = new StorageControllerList();
12196 StorageControllerList::const_iterator it = mStorageControllers->begin();
12197 while (it != mStorageControllers->end())
12198 {
12199 (*it)->commit();
12200
12201 /* look if this controller has a peer device */
12202 ComObjPtr<StorageController> peer = (*it)->getPeer();
12203 if (!peer)
12204 {
12205 /* no peer means the device is a newly created one;
12206 * create a peer owning data this device share it with */
12207 peer.createObject();
12208 peer->init(mPeer, *it, true /* aReshare */);
12209 }
12210 else
12211 {
12212 /* remove peer from the old list */
12213 mPeer->mStorageControllers->remove(peer);
12214 }
12215 /* and add it to the new list */
12216 newList->push_back(peer);
12217
12218 ++it;
12219 }
12220
12221 /* uninit old peer's controllers that are left */
12222 it = mPeer->mStorageControllers->begin();
12223 while (it != mPeer->mStorageControllers->end())
12224 {
12225 (*it)->uninit();
12226 ++it;
12227 }
12228
12229 /* attach new list of controllers to our peer */
12230 mPeer->mStorageControllers.attach(newList);
12231 }
12232 else
12233 {
12234 /* we have no peer (our parent is the newly created machine);
12235 * just commit changes to devices */
12236 commitStorageControllers = true;
12237 }
12238 }
12239 else
12240 {
12241 /* the list of controllers itself is not changed,
12242 * just commit changes to controllers themselves */
12243 commitStorageControllers = true;
12244 }
12245
12246 if (commitStorageControllers)
12247 {
12248 StorageControllerList::const_iterator it = mStorageControllers->begin();
12249 while (it != mStorageControllers->end())
12250 {
12251 (*it)->commit();
12252 ++it;
12253 }
12254 }
12255
12256 bool commitUSBControllers = false;
12257
12258 if (mUSBControllers.isBackedUp())
12259 {
12260 mUSBControllers.commit();
12261
12262 if (mPeer)
12263 {
12264 /* Commit all changes to new controllers (this will reshare data with
12265 * peers for those who have peers) */
12266 USBControllerList *newList = new USBControllerList();
12267 USBControllerList::const_iterator it = mUSBControllers->begin();
12268 while (it != mUSBControllers->end())
12269 {
12270 (*it)->commit();
12271
12272 /* look if this controller has a peer device */
12273 ComObjPtr<USBController> peer = (*it)->getPeer();
12274 if (!peer)
12275 {
12276 /* no peer means the device is a newly created one;
12277 * create a peer owning data this device share it with */
12278 peer.createObject();
12279 peer->init(mPeer, *it, true /* aReshare */);
12280 }
12281 else
12282 {
12283 /* remove peer from the old list */
12284 mPeer->mUSBControllers->remove(peer);
12285 }
12286 /* and add it to the new list */
12287 newList->push_back(peer);
12288
12289 ++it;
12290 }
12291
12292 /* uninit old peer's controllers that are left */
12293 it = mPeer->mUSBControllers->begin();
12294 while (it != mPeer->mUSBControllers->end())
12295 {
12296 (*it)->uninit();
12297 ++it;
12298 }
12299
12300 /* attach new list of controllers to our peer */
12301 mPeer->mUSBControllers.attach(newList);
12302 }
12303 else
12304 {
12305 /* we have no peer (our parent is the newly created machine);
12306 * just commit changes to devices */
12307 commitUSBControllers = true;
12308 }
12309 }
12310 else
12311 {
12312 /* the list of controllers itself is not changed,
12313 * just commit changes to controllers themselves */
12314 commitUSBControllers = true;
12315 }
12316
12317 if (commitUSBControllers)
12318 {
12319 USBControllerList::const_iterator it = mUSBControllers->begin();
12320 while (it != mUSBControllers->end())
12321 {
12322 (*it)->commit();
12323 ++it;
12324 }
12325 }
12326
12327 if (isSessionMachine())
12328 {
12329 /* attach new data to the primary machine and reshare it */
12330 mPeer->mUserData.attach(mUserData);
12331 mPeer->mHWData.attach(mHWData);
12332 /* mMediaData is reshared by fixupMedia */
12333 // mPeer->mMediaData.attach(mMediaData);
12334 Assert(mPeer->mMediaData.data() == mMediaData.data());
12335 }
12336}
12337
12338/**
12339 * Copies all the hardware data from the given machine.
12340 *
12341 * Currently, only called when the VM is being restored from a snapshot. In
12342 * particular, this implies that the VM is not running during this method's
12343 * call.
12344 *
12345 * @note This method must be called from under this object's lock.
12346 *
12347 * @note This method doesn't call #commit(), so all data remains backed up and
12348 * unsaved.
12349 */
12350void Machine::copyFrom(Machine *aThat)
12351{
12352 AssertReturnVoid(!isSnapshotMachine());
12353 AssertReturnVoid(aThat->isSnapshotMachine());
12354
12355 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12356
12357 mHWData.assignCopy(aThat->mHWData);
12358
12359 // create copies of all shared folders (mHWData after attaching a copy
12360 // contains just references to original objects)
12361 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12362 it != mHWData->mSharedFolders.end();
12363 ++it)
12364 {
12365 ComObjPtr<SharedFolder> folder;
12366 folder.createObject();
12367 HRESULT rc = folder->initCopy(getMachine(), *it);
12368 AssertComRC(rc);
12369 *it = folder;
12370 }
12371
12372 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
12373 mVRDEServer->copyFrom(aThat->mVRDEServer);
12374 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
12375 mUSBDeviceFilters->copyFrom(aThat->mUSBDeviceFilters);
12376 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
12377
12378 /* create private copies of all controllers */
12379 mStorageControllers.backup();
12380 mStorageControllers->clear();
12381 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12382 it != aThat->mStorageControllers->end();
12383 ++it)
12384 {
12385 ComObjPtr<StorageController> ctrl;
12386 ctrl.createObject();
12387 ctrl->initCopy(this, *it);
12388 mStorageControllers->push_back(ctrl);
12389 }
12390
12391 /* create private copies of all USB controllers */
12392 mUSBControllers.backup();
12393 mUSBControllers->clear();
12394 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12395 it != aThat->mUSBControllers->end();
12396 ++it)
12397 {
12398 ComObjPtr<USBController> ctrl;
12399 ctrl.createObject();
12400 ctrl->initCopy(this, *it);
12401 mUSBControllers->push_back(ctrl);
12402 }
12403
12404 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12405 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12406 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
12407 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12408 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
12409 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12410 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
12411}
12412
12413/**
12414 * Returns whether the given storage controller is hotplug capable.
12415 *
12416 * @returns true if the controller supports hotplugging
12417 * false otherwise.
12418 * @param enmCtrlType The controller type to check for.
12419 */
12420bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12421{
12422 switch (enmCtrlType)
12423 {
12424 case StorageControllerType_IntelAhci:
12425 return true;
12426 case StorageControllerType_LsiLogic:
12427 case StorageControllerType_LsiLogicSas:
12428 case StorageControllerType_BusLogic:
12429 case StorageControllerType_PIIX3:
12430 case StorageControllerType_PIIX4:
12431 case StorageControllerType_ICH6:
12432 case StorageControllerType_I82078:
12433 default:
12434 return false;
12435 }
12436}
12437
12438#ifdef VBOX_WITH_RESOURCE_USAGE_API
12439
12440void Machine::getDiskList(MediaList &list)
12441{
12442 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12443 it != mMediaData->mAttachments.end();
12444 ++it)
12445 {
12446 MediumAttachment* pAttach = *it;
12447 /* just in case */
12448 AssertStmt(pAttach, continue);
12449
12450 AutoCaller localAutoCallerA(pAttach);
12451 if (FAILED(localAutoCallerA.rc())) continue;
12452
12453 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12454
12455 if (pAttach->getType() == DeviceType_HardDisk)
12456 list.push_back(pAttach->getMedium());
12457 }
12458}
12459
12460void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12461{
12462 AssertReturnVoid(isWriteLockOnCurrentThread());
12463 AssertPtrReturnVoid(aCollector);
12464
12465 pm::CollectorHAL *hal = aCollector->getHAL();
12466 /* Create sub metrics */
12467 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12468 "Percentage of processor time spent in user mode by the VM process.");
12469 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12470 "Percentage of processor time spent in kernel mode by the VM process.");
12471 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12472 "Size of resident portion of VM process in memory.");
12473 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12474 "Actual size of all VM disks combined.");
12475 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12476 "Network receive rate.");
12477 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12478 "Network transmit rate.");
12479 /* Create and register base metrics */
12480 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12481 cpuLoadUser, cpuLoadKernel);
12482 aCollector->registerBaseMetric(cpuLoad);
12483 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12484 ramUsageUsed);
12485 aCollector->registerBaseMetric(ramUsage);
12486 MediaList disks;
12487 getDiskList(disks);
12488 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12489 diskUsageUsed);
12490 aCollector->registerBaseMetric(diskUsage);
12491
12492 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12493 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12494 new pm::AggregateAvg()));
12495 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12496 new pm::AggregateMin()));
12497 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12498 new pm::AggregateMax()));
12499 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12500 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12501 new pm::AggregateAvg()));
12502 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12503 new pm::AggregateMin()));
12504 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12505 new pm::AggregateMax()));
12506
12507 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12508 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12509 new pm::AggregateAvg()));
12510 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12511 new pm::AggregateMin()));
12512 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12513 new pm::AggregateMax()));
12514
12515 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12516 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12517 new pm::AggregateAvg()));
12518 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12519 new pm::AggregateMin()));
12520 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12521 new pm::AggregateMax()));
12522
12523
12524 /* Guest metrics collector */
12525 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12526 aCollector->registerGuest(mCollectorGuest);
12527 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12528 this, __PRETTY_FUNCTION__, mCollectorGuest));
12529
12530 /* Create sub metrics */
12531 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12532 "Percentage of processor time spent in user mode as seen by the guest.");
12533 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12534 "Percentage of processor time spent in kernel mode as seen by the guest.");
12535 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12536 "Percentage of processor time spent idling as seen by the guest.");
12537
12538 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12539 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12540 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12541 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12542 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12543 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12544
12545 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12546
12547 /* Create and register base metrics */
12548 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12549 machineNetRx, machineNetTx);
12550 aCollector->registerBaseMetric(machineNetRate);
12551
12552 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12553 guestLoadUser, guestLoadKernel, guestLoadIdle);
12554 aCollector->registerBaseMetric(guestCpuLoad);
12555
12556 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12557 guestMemTotal, guestMemFree,
12558 guestMemBalloon, guestMemShared,
12559 guestMemCache, guestPagedTotal);
12560 aCollector->registerBaseMetric(guestCpuMem);
12561
12562 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12563 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12564 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12565 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12566
12567 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12568 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12569 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12570 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12571
12572 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12573 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12574 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12575 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12576
12577 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12578 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12579 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12580 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12581
12582 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12583 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12584 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12585 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12586
12587 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12588 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12589 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12590 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12591
12592 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12593 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12594 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12595 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12596
12597 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12598 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12599 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12600 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12601
12602 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12603 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12604 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12605 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12606
12607 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12608 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12609 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12610 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12611
12612 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12613 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12614 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12615 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12616}
12617
12618void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12619{
12620 AssertReturnVoid(isWriteLockOnCurrentThread());
12621
12622 if (aCollector)
12623 {
12624 aCollector->unregisterMetricsFor(aMachine);
12625 aCollector->unregisterBaseMetricsFor(aMachine);
12626 }
12627}
12628
12629#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12630
12631
12632////////////////////////////////////////////////////////////////////////////////
12633
12634DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12635
12636HRESULT SessionMachine::FinalConstruct()
12637{
12638 LogFlowThisFunc(("\n"));
12639
12640 mClientToken = NULL;
12641
12642 return BaseFinalConstruct();
12643}
12644
12645void SessionMachine::FinalRelease()
12646{
12647 LogFlowThisFunc(("\n"));
12648
12649 Assert(!mClientToken);
12650 /* paranoia, should not hang around any more */
12651 if (mClientToken)
12652 {
12653 delete mClientToken;
12654 mClientToken = NULL;
12655 }
12656
12657 uninit(Uninit::Unexpected);
12658
12659 BaseFinalRelease();
12660}
12661
12662/**
12663 * @note Must be called only by Machine::LockMachine() from its own write lock.
12664 */
12665HRESULT SessionMachine::init(Machine *aMachine)
12666{
12667 LogFlowThisFuncEnter();
12668 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12669
12670 AssertReturn(aMachine, E_INVALIDARG);
12671
12672 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12673
12674 /* Enclose the state transition NotReady->InInit->Ready */
12675 AutoInitSpan autoInitSpan(this);
12676 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12677
12678 HRESULT rc = S_OK;
12679
12680 /* create the machine client token */
12681 try
12682 {
12683 mClientToken = new ClientToken(aMachine);
12684 if (!mClientToken->isReady())
12685 {
12686 delete mClientToken;
12687 mClientToken = NULL;
12688 rc = E_FAIL;
12689 }
12690 }
12691 catch (std::bad_alloc &)
12692 {
12693 rc = E_OUTOFMEMORY;
12694 }
12695 if (FAILED(rc))
12696 return rc;
12697
12698 /* memorize the peer Machine */
12699 unconst(mPeer) = aMachine;
12700 /* share the parent pointer */
12701 unconst(mParent) = aMachine->mParent;
12702
12703 /* take the pointers to data to share */
12704 mData.share(aMachine->mData);
12705 mSSData.share(aMachine->mSSData);
12706
12707 mUserData.share(aMachine->mUserData);
12708 mHWData.share(aMachine->mHWData);
12709 mMediaData.share(aMachine->mMediaData);
12710
12711 mStorageControllers.allocate();
12712 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12713 it != aMachine->mStorageControllers->end();
12714 ++it)
12715 {
12716 ComObjPtr<StorageController> ctl;
12717 ctl.createObject();
12718 ctl->init(this, *it);
12719 mStorageControllers->push_back(ctl);
12720 }
12721
12722 mUSBControllers.allocate();
12723 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12724 it != aMachine->mUSBControllers->end();
12725 ++it)
12726 {
12727 ComObjPtr<USBController> ctl;
12728 ctl.createObject();
12729 ctl->init(this, *it);
12730 mUSBControllers->push_back(ctl);
12731 }
12732
12733 unconst(mBIOSSettings).createObject();
12734 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12735 /* create another VRDEServer object that will be mutable */
12736 unconst(mVRDEServer).createObject();
12737 mVRDEServer->init(this, aMachine->mVRDEServer);
12738 /* create another audio adapter object that will be mutable */
12739 unconst(mAudioAdapter).createObject();
12740 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12741 /* create a list of serial ports that will be mutable */
12742 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12743 {
12744 unconst(mSerialPorts[slot]).createObject();
12745 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12746 }
12747 /* create a list of parallel ports that will be mutable */
12748 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12749 {
12750 unconst(mParallelPorts[slot]).createObject();
12751 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12752 }
12753
12754 /* create another USB device filters object that will be mutable */
12755 unconst(mUSBDeviceFilters).createObject();
12756 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12757
12758 /* create a list of network adapters that will be mutable */
12759 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12760 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12761 {
12762 unconst(mNetworkAdapters[slot]).createObject();
12763 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12764 }
12765
12766 /* create another bandwidth control object that will be mutable */
12767 unconst(mBandwidthControl).createObject();
12768 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12769
12770 /* default is to delete saved state on Saved -> PoweredOff transition */
12771 mRemoveSavedState = true;
12772
12773 /* Confirm a successful initialization when it's the case */
12774 autoInitSpan.setSucceeded();
12775
12776 LogFlowThisFuncLeave();
12777 return rc;
12778}
12779
12780/**
12781 * Uninitializes this session object. If the reason is other than
12782 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12783 *
12784 * @param aReason uninitialization reason
12785 *
12786 * @note Locks mParent + this object for writing.
12787 */
12788void SessionMachine::uninit(Uninit::Reason aReason)
12789{
12790 LogFlowThisFuncEnter();
12791 LogFlowThisFunc(("reason=%d\n", aReason));
12792
12793 /*
12794 * Strongly reference ourselves to prevent this object deletion after
12795 * mData->mSession.mMachine.setNull() below (which can release the last
12796 * reference and call the destructor). Important: this must be done before
12797 * accessing any members (and before AutoUninitSpan that does it as well).
12798 * This self reference will be released as the very last step on return.
12799 */
12800 ComObjPtr<SessionMachine> selfRef = this;
12801
12802 /* Enclose the state transition Ready->InUninit->NotReady */
12803 AutoUninitSpan autoUninitSpan(this);
12804 if (autoUninitSpan.uninitDone())
12805 {
12806 LogFlowThisFunc(("Already uninitialized\n"));
12807 LogFlowThisFuncLeave();
12808 return;
12809 }
12810
12811 if (autoUninitSpan.initFailed())
12812 {
12813 /* We've been called by init() because it's failed. It's not really
12814 * necessary (nor it's safe) to perform the regular uninit sequence
12815 * below, the following is enough.
12816 */
12817 LogFlowThisFunc(("Initialization failed.\n"));
12818 /* destroy the machine client token */
12819 if (mClientToken)
12820 {
12821 delete mClientToken;
12822 mClientToken = NULL;
12823 }
12824 uninitDataAndChildObjects();
12825 mData.free();
12826 unconst(mParent) = NULL;
12827 unconst(mPeer) = NULL;
12828 LogFlowThisFuncLeave();
12829 return;
12830 }
12831
12832 MachineState_T lastState;
12833 {
12834 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12835 lastState = mData->mMachineState;
12836 }
12837 NOREF(lastState);
12838
12839#ifdef VBOX_WITH_USB
12840 // release all captured USB devices, but do this before requesting the locks below
12841 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12842 {
12843 /* Console::captureUSBDevices() is called in the VM process only after
12844 * setting the machine state to Starting or Restoring.
12845 * Console::detachAllUSBDevices() will be called upon successful
12846 * termination. So, we need to release USB devices only if there was
12847 * an abnormal termination of a running VM.
12848 *
12849 * This is identical to SessionMachine::DetachAllUSBDevices except
12850 * for the aAbnormal argument. */
12851 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
12852 AssertComRC(rc);
12853 NOREF(rc);
12854
12855 USBProxyService *service = mParent->host()->usbProxyService();
12856 if (service)
12857 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12858 }
12859#endif /* VBOX_WITH_USB */
12860
12861 // we need to lock this object in uninit() because the lock is shared
12862 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12863 // and others need mParent lock, and USB needs host lock.
12864 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12865
12866#ifdef VBOX_WITH_RESOURCE_USAGE_API
12867 /*
12868 * It is safe to call Machine::unregisterMetrics() here because
12869 * PerformanceCollector::samplerCallback no longer accesses guest methods
12870 * holding the lock.
12871 */
12872 unregisterMetrics(mParent->performanceCollector(), mPeer);
12873 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12874 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12875 this, __PRETTY_FUNCTION__, mCollectorGuest));
12876 if (mCollectorGuest)
12877 {
12878 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12879 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12880 mCollectorGuest = NULL;
12881 }
12882#endif
12883
12884 if (aReason == Uninit::Abnormal)
12885 {
12886 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12887 Global::IsOnlineOrTransient(lastState)));
12888
12889 /* reset the state to Aborted */
12890 if (mData->mMachineState != MachineState_Aborted)
12891 setMachineState(MachineState_Aborted);
12892 }
12893
12894 // any machine settings modified?
12895 if (mData->flModifications)
12896 {
12897 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12898 rollback(false /* aNotify */);
12899 }
12900
12901 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12902 || !mConsoleTaskData.mSnapshot);
12903 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12904 {
12905 LogWarningThisFunc(("canceling failed save state request!\n"));
12906 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12907 }
12908 else if (!mConsoleTaskData.mSnapshot.isNull())
12909 {
12910 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12911
12912 /* delete all differencing hard disks created (this will also attach
12913 * their parents back by rolling back mMediaData) */
12914 rollbackMedia();
12915
12916 // delete the saved state file (it might have been already created)
12917 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12918 // think it's still in use
12919 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12920 mConsoleTaskData.mSnapshot->uninit();
12921 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12922 }
12923
12924 if (!mData->mSession.mType.isEmpty())
12925 {
12926 /* mType is not null when this machine's process has been started by
12927 * Machine::LaunchVMProcess(), therefore it is our child. We
12928 * need to queue the PID to reap the process (and avoid zombies on
12929 * Linux). */
12930 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12931 mParent->addProcessToReap(mData->mSession.mPID);
12932 }
12933
12934 mData->mSession.mPID = NIL_RTPROCESS;
12935
12936 if (aReason == Uninit::Unexpected)
12937 {
12938 /* Uninitialization didn't come from #checkForDeath(), so tell the
12939 * client watcher thread to update the set of machines that have open
12940 * sessions. */
12941 mParent->updateClientWatcher();
12942 }
12943
12944 /* uninitialize all remote controls */
12945 if (mData->mSession.mRemoteControls.size())
12946 {
12947 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12948 mData->mSession.mRemoteControls.size()));
12949
12950 Data::Session::RemoteControlList::iterator it =
12951 mData->mSession.mRemoteControls.begin();
12952 while (it != mData->mSession.mRemoteControls.end())
12953 {
12954 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12955 HRESULT rc = (*it)->Uninitialize();
12956 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12957 if (FAILED(rc))
12958 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12959 ++it;
12960 }
12961 mData->mSession.mRemoteControls.clear();
12962 }
12963
12964 /*
12965 * An expected uninitialization can come only from #checkForDeath().
12966 * Otherwise it means that something's gone really wrong (for example,
12967 * the Session implementation has released the VirtualBox reference
12968 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12969 * etc). However, it's also possible, that the client releases the IPC
12970 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12971 * but the VirtualBox release event comes first to the server process.
12972 * This case is practically possible, so we should not assert on an
12973 * unexpected uninit, just log a warning.
12974 */
12975
12976 if ((aReason == Uninit::Unexpected))
12977 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12978
12979 if (aReason != Uninit::Normal)
12980 {
12981 mData->mSession.mDirectControl.setNull();
12982 }
12983 else
12984 {
12985 /* this must be null here (see #OnSessionEnd()) */
12986 Assert(mData->mSession.mDirectControl.isNull());
12987 Assert(mData->mSession.mState == SessionState_Unlocking);
12988 Assert(!mData->mSession.mProgress.isNull());
12989 }
12990 if (mData->mSession.mProgress)
12991 {
12992 if (aReason == Uninit::Normal)
12993 mData->mSession.mProgress->notifyComplete(S_OK);
12994 else
12995 mData->mSession.mProgress->notifyComplete(E_FAIL,
12996 COM_IIDOF(ISession),
12997 getComponentName(),
12998 tr("The VM session was aborted"));
12999 mData->mSession.mProgress.setNull();
13000 }
13001
13002 /* remove the association between the peer machine and this session machine */
13003 Assert( (SessionMachine*)mData->mSession.mMachine == this
13004 || aReason == Uninit::Unexpected);
13005
13006 /* reset the rest of session data */
13007 mData->mSession.mMachine.setNull();
13008 mData->mSession.mState = SessionState_Unlocked;
13009 mData->mSession.mType.setNull();
13010
13011 /* destroy the machine client token before leaving the exclusive lock */
13012 if (mClientToken)
13013 {
13014 delete mClientToken;
13015 mClientToken = NULL;
13016 }
13017
13018 /* fire an event */
13019 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13020
13021 uninitDataAndChildObjects();
13022
13023 /* free the essential data structure last */
13024 mData.free();
13025
13026 /* release the exclusive lock before setting the below two to NULL */
13027 multilock.release();
13028
13029 RTThreadSleep(500);
13030 mParent->AddRef();
13031 LONG c = mParent->Release();
13032 LogFlowThisFunc(("vbox ref=%d\n", c)); NOREF(c);
13033 unconst(mParent) = NULL;
13034 unconst(mPeer) = NULL;
13035
13036 LogFlowThisFuncLeave();
13037}
13038
13039// util::Lockable interface
13040////////////////////////////////////////////////////////////////////////////////
13041
13042/**
13043 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13044 * with the primary Machine instance (mPeer).
13045 */
13046RWLockHandle *SessionMachine::lockHandle() const
13047{
13048 AssertReturn(mPeer != NULL, NULL);
13049 return mPeer->lockHandle();
13050}
13051
13052// IInternalMachineControl methods
13053////////////////////////////////////////////////////////////////////////////////
13054
13055/**
13056 * Passes collected guest statistics to performance collector object
13057 */
13058STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13059 ULONG aCpuKernel, ULONG aCpuIdle,
13060 ULONG aMemTotal, ULONG aMemFree,
13061 ULONG aMemBalloon, ULONG aMemShared,
13062 ULONG aMemCache, ULONG aPageTotal,
13063 ULONG aAllocVMM, ULONG aFreeVMM,
13064 ULONG aBalloonedVMM, ULONG aSharedVMM,
13065 ULONG aVmNetRx, ULONG aVmNetTx)
13066{
13067#ifdef VBOX_WITH_RESOURCE_USAGE_API
13068 if (mCollectorGuest)
13069 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13070 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13071 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13072 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13073
13074 return S_OK;
13075#else
13076 NOREF(aValidStats);
13077 NOREF(aCpuUser);
13078 NOREF(aCpuKernel);
13079 NOREF(aCpuIdle);
13080 NOREF(aMemTotal);
13081 NOREF(aMemFree);
13082 NOREF(aMemBalloon);
13083 NOREF(aMemShared);
13084 NOREF(aMemCache);
13085 NOREF(aPageTotal);
13086 NOREF(aAllocVMM);
13087 NOREF(aFreeVMM);
13088 NOREF(aBalloonedVMM);
13089 NOREF(aSharedVMM);
13090 NOREF(aVmNetRx);
13091 NOREF(aVmNetTx);
13092 return E_NOTIMPL;
13093#endif
13094}
13095
13096/**
13097 * @note Locks this object for writing.
13098 */
13099STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
13100{
13101 AutoCaller autoCaller(this);
13102 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13103
13104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13105
13106 mRemoveSavedState = aRemove;
13107
13108 return S_OK;
13109}
13110
13111/**
13112 * @note Locks the same as #setMachineState() does.
13113 */
13114STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
13115{
13116 return setMachineState(aMachineState);
13117}
13118
13119/**
13120 * @note Locks this object for writing.
13121 */
13122STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
13123{
13124 LogFlowThisFunc(("aProgress=%p\n", aProgress));
13125 AutoCaller autoCaller(this);
13126 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13127
13128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13129
13130 if (mData->mSession.mState != SessionState_Locked)
13131 return VBOX_E_INVALID_OBJECT_STATE;
13132
13133 if (!mData->mSession.mProgress.isNull())
13134 mData->mSession.mProgress->setOtherProgressObject(aProgress);
13135
13136 LogFlowThisFunc(("returns S_OK.\n"));
13137 return S_OK;
13138}
13139
13140/**
13141 * @note Locks this object for writing.
13142 */
13143STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
13144{
13145 AutoCaller autoCaller(this);
13146 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13147
13148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13149
13150 if (mData->mSession.mState != SessionState_Locked)
13151 return VBOX_E_INVALID_OBJECT_STATE;
13152
13153 /* Finalize the LaunchVMProcess progress object. */
13154 if (mData->mSession.mProgress)
13155 {
13156 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
13157 mData->mSession.mProgress.setNull();
13158 }
13159
13160 if (SUCCEEDED((HRESULT)iResult))
13161 {
13162#ifdef VBOX_WITH_RESOURCE_USAGE_API
13163 /* The VM has been powered up successfully, so it makes sense
13164 * now to offer the performance metrics for a running machine
13165 * object. Doing it earlier wouldn't be safe. */
13166 registerMetrics(mParent->performanceCollector(), mPeer,
13167 mData->mSession.mPID);
13168#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13169 }
13170
13171 return S_OK;
13172}
13173
13174/**
13175 * @note Locks this object for writing.
13176 */
13177STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
13178{
13179 LogFlowThisFuncEnter();
13180
13181 CheckComArgOutPointerValid(aProgress);
13182
13183 AutoCaller autoCaller(this);
13184 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13185
13186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13187
13188 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13189 E_FAIL);
13190
13191 /* create a progress object to track operation completion */
13192 ComObjPtr<Progress> pProgress;
13193 pProgress.createObject();
13194 pProgress->init(getVirtualBox(),
13195 static_cast<IMachine *>(this) /* aInitiator */,
13196 Bstr(tr("Stopping the virtual machine")).raw(),
13197 FALSE /* aCancelable */);
13198
13199 /* fill in the console task data */
13200 mConsoleTaskData.mLastState = mData->mMachineState;
13201 mConsoleTaskData.mProgress = pProgress;
13202
13203 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13204 setMachineState(MachineState_Stopping);
13205
13206 pProgress.queryInterfaceTo(aProgress);
13207
13208 return S_OK;
13209}
13210
13211/**
13212 * @note Locks this object for writing.
13213 */
13214STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
13215{
13216 LogFlowThisFuncEnter();
13217
13218 AutoCaller autoCaller(this);
13219 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13220
13221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13222
13223 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
13224 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
13225 && mConsoleTaskData.mLastState != MachineState_Null,
13226 E_FAIL);
13227
13228 /*
13229 * On failure, set the state to the state we had when BeginPoweringDown()
13230 * was called (this is expected by Console::PowerDown() and the associated
13231 * task). On success the VM process already changed the state to
13232 * MachineState_PoweredOff, so no need to do anything.
13233 */
13234 if (FAILED(iResult))
13235 setMachineState(mConsoleTaskData.mLastState);
13236
13237 /* notify the progress object about operation completion */
13238 Assert(mConsoleTaskData.mProgress);
13239 if (SUCCEEDED(iResult))
13240 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13241 else
13242 {
13243 Utf8Str strErrMsg(aErrMsg);
13244 if (strErrMsg.length())
13245 mConsoleTaskData.mProgress->notifyComplete(iResult,
13246 COM_IIDOF(ISession),
13247 getComponentName(),
13248 strErrMsg.c_str());
13249 else
13250 mConsoleTaskData.mProgress->notifyComplete(iResult);
13251 }
13252
13253 /* clear out the temporary saved state data */
13254 mConsoleTaskData.mLastState = MachineState_Null;
13255 mConsoleTaskData.mProgress.setNull();
13256
13257 LogFlowThisFuncLeave();
13258 return S_OK;
13259}
13260
13261
13262/**
13263 * Goes through the USB filters of the given machine to see if the given
13264 * device matches any filter or not.
13265 *
13266 * @note Locks the same as USBController::hasMatchingFilter() does.
13267 */
13268STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13269 BOOL *aMatched,
13270 ULONG *aMaskedIfs)
13271{
13272 LogFlowThisFunc(("\n"));
13273
13274 CheckComArgNotNull(aUSBDevice);
13275 CheckComArgOutPointerValid(aMatched);
13276
13277 AutoCaller autoCaller(this);
13278 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13279
13280#ifdef VBOX_WITH_USB
13281 *aMatched = mUSBDeviceFilters->hasMatchingFilter(aUSBDevice, aMaskedIfs);
13282#else
13283 NOREF(aUSBDevice);
13284 NOREF(aMaskedIfs);
13285 *aMatched = FALSE;
13286#endif
13287
13288 return S_OK;
13289}
13290
13291/**
13292 * @note Locks the same as Host::captureUSBDevice() does.
13293 */
13294STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13295{
13296 LogFlowThisFunc(("\n"));
13297
13298 AutoCaller autoCaller(this);
13299 AssertComRCReturnRC(autoCaller.rc());
13300
13301#ifdef VBOX_WITH_USB
13302 /* if captureDeviceForVM() fails, it must have set extended error info */
13303 clearError();
13304 MultiResult rc = mParent->host()->checkUSBProxyService();
13305 if (FAILED(rc)) return rc;
13306
13307 USBProxyService *service = mParent->host()->usbProxyService();
13308 AssertReturn(service, E_FAIL);
13309 return service->captureDeviceForVM(this, Guid(aId).ref());
13310#else
13311 NOREF(aId);
13312 return E_NOTIMPL;
13313#endif
13314}
13315
13316/**
13317 * @note Locks the same as Host::detachUSBDevice() does.
13318 */
13319STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13320{
13321 LogFlowThisFunc(("\n"));
13322
13323 AutoCaller autoCaller(this);
13324 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13325
13326#ifdef VBOX_WITH_USB
13327 USBProxyService *service = mParent->host()->usbProxyService();
13328 AssertReturn(service, E_FAIL);
13329 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13330#else
13331 NOREF(aId);
13332 NOREF(aDone);
13333 return E_NOTIMPL;
13334#endif
13335}
13336
13337/**
13338 * Inserts all machine filters to the USB proxy service and then calls
13339 * Host::autoCaptureUSBDevices().
13340 *
13341 * Called by Console from the VM process upon VM startup.
13342 *
13343 * @note Locks what called methods lock.
13344 */
13345STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13346{
13347 LogFlowThisFunc(("\n"));
13348
13349 AutoCaller autoCaller(this);
13350 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13351
13352#ifdef VBOX_WITH_USB
13353 HRESULT rc = mUSBDeviceFilters->notifyProxy(true /* aInsertFilters */);
13354 AssertComRC(rc);
13355 NOREF(rc);
13356
13357 USBProxyService *service = mParent->host()->usbProxyService();
13358 AssertReturn(service, E_FAIL);
13359 return service->autoCaptureDevicesForVM(this);
13360#else
13361 return S_OK;
13362#endif
13363}
13364
13365/**
13366 * Removes all machine filters from the USB proxy service and then calls
13367 * Host::detachAllUSBDevices().
13368 *
13369 * Called by Console from the VM process upon normal VM termination or by
13370 * SessionMachine::uninit() upon abnormal VM termination (from under the
13371 * Machine/SessionMachine lock).
13372 *
13373 * @note Locks what called methods lock.
13374 */
13375STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13376{
13377 LogFlowThisFunc(("\n"));
13378
13379 AutoCaller autoCaller(this);
13380 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13381
13382#ifdef VBOX_WITH_USB
13383 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
13384 AssertComRC(rc);
13385 NOREF(rc);
13386
13387 USBProxyService *service = mParent->host()->usbProxyService();
13388 AssertReturn(service, E_FAIL);
13389 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13390#else
13391 NOREF(aDone);
13392 return S_OK;
13393#endif
13394}
13395
13396/**
13397 * @note Locks this object for writing.
13398 */
13399STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13400 IProgress **aProgress)
13401{
13402 LogFlowThisFuncEnter();
13403
13404 AssertReturn(aSession, E_INVALIDARG);
13405 AssertReturn(aProgress, E_INVALIDARG);
13406
13407 AutoCaller autoCaller(this);
13408
13409 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13410 /*
13411 * We don't assert below because it might happen that a non-direct session
13412 * informs us it is closed right after we've been uninitialized -- it's ok.
13413 */
13414 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13415
13416 /* get IInternalSessionControl interface */
13417 ComPtr<IInternalSessionControl> control(aSession);
13418
13419 ComAssertRet(!control.isNull(), E_INVALIDARG);
13420
13421 /* Creating a Progress object requires the VirtualBox lock, and
13422 * thus locking it here is required by the lock order rules. */
13423 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13424
13425 if (control == mData->mSession.mDirectControl)
13426 {
13427 ComAssertRet(aProgress, E_POINTER);
13428
13429 /* The direct session is being normally closed by the client process
13430 * ----------------------------------------------------------------- */
13431
13432 /* go to the closing state (essential for all open*Session() calls and
13433 * for #checkForDeath()) */
13434 Assert(mData->mSession.mState == SessionState_Locked);
13435 mData->mSession.mState = SessionState_Unlocking;
13436
13437 /* set direct control to NULL to release the remote instance */
13438 mData->mSession.mDirectControl.setNull();
13439 LogFlowThisFunc(("Direct control is set to NULL\n"));
13440
13441 if (mData->mSession.mProgress)
13442 {
13443 /* finalize the progress, someone might wait if a frontend
13444 * closes the session before powering on the VM. */
13445 mData->mSession.mProgress->notifyComplete(E_FAIL,
13446 COM_IIDOF(ISession),
13447 getComponentName(),
13448 tr("The VM session was closed before any attempt to power it on"));
13449 mData->mSession.mProgress.setNull();
13450 }
13451
13452 /* Create the progress object the client will use to wait until
13453 * #checkForDeath() is called to uninitialize this session object after
13454 * it releases the IPC semaphore.
13455 * Note! Because we're "reusing" mProgress here, this must be a proxy
13456 * object just like for LaunchVMProcess. */
13457 Assert(mData->mSession.mProgress.isNull());
13458 ComObjPtr<ProgressProxy> progress;
13459 progress.createObject();
13460 ComPtr<IUnknown> pPeer(mPeer);
13461 progress->init(mParent, pPeer,
13462 Bstr(tr("Closing session")).raw(),
13463 FALSE /* aCancelable */);
13464 progress.queryInterfaceTo(aProgress);
13465 mData->mSession.mProgress = progress;
13466 }
13467 else
13468 {
13469 /* the remote session is being normally closed */
13470 Data::Session::RemoteControlList::iterator it =
13471 mData->mSession.mRemoteControls.begin();
13472 while (it != mData->mSession.mRemoteControls.end())
13473 {
13474 if (control == *it)
13475 break;
13476 ++it;
13477 }
13478 BOOL found = it != mData->mSession.mRemoteControls.end();
13479 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13480 E_INVALIDARG);
13481 // This MUST be erase(it), not remove(*it) as the latter triggers a
13482 // very nasty use after free due to the place where the value "lives".
13483 mData->mSession.mRemoteControls.erase(it);
13484 }
13485
13486 /* signal the client watcher thread, because the client is going away */
13487 mParent->updateClientWatcher();
13488
13489 LogFlowThisFuncLeave();
13490 return S_OK;
13491}
13492
13493/**
13494 * @note Locks this object for writing.
13495 */
13496STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13497{
13498 LogFlowThisFuncEnter();
13499
13500 CheckComArgOutPointerValid(aProgress);
13501 CheckComArgOutPointerValid(aStateFilePath);
13502
13503 AutoCaller autoCaller(this);
13504 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13505
13506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13507
13508 AssertReturn( mData->mMachineState == MachineState_Paused
13509 && mConsoleTaskData.mLastState == MachineState_Null
13510 && mConsoleTaskData.strStateFilePath.isEmpty(),
13511 E_FAIL);
13512
13513 /* create a progress object to track operation completion */
13514 ComObjPtr<Progress> pProgress;
13515 pProgress.createObject();
13516 pProgress->init(getVirtualBox(),
13517 static_cast<IMachine *>(this) /* aInitiator */,
13518 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13519 FALSE /* aCancelable */);
13520
13521 Utf8Str strStateFilePath;
13522 /* stateFilePath is null when the machine is not running */
13523 if (mData->mMachineState == MachineState_Paused)
13524 composeSavedStateFilename(strStateFilePath);
13525
13526 /* fill in the console task data */
13527 mConsoleTaskData.mLastState = mData->mMachineState;
13528 mConsoleTaskData.strStateFilePath = strStateFilePath;
13529 mConsoleTaskData.mProgress = pProgress;
13530
13531 /* set the state to Saving (this is expected by Console::SaveState()) */
13532 setMachineState(MachineState_Saving);
13533
13534 strStateFilePath.cloneTo(aStateFilePath);
13535 pProgress.queryInterfaceTo(aProgress);
13536
13537 return S_OK;
13538}
13539
13540/**
13541 * @note Locks mParent + this object for writing.
13542 */
13543STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13544{
13545 LogFlowThisFunc(("\n"));
13546
13547 AutoCaller autoCaller(this);
13548 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13549
13550 /* endSavingState() need mParent lock */
13551 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13552
13553 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13554 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13555 && mConsoleTaskData.mLastState != MachineState_Null
13556 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13557 E_FAIL);
13558
13559 /*
13560 * On failure, set the state to the state we had when BeginSavingState()
13561 * was called (this is expected by Console::SaveState() and the associated
13562 * task). On success the VM process already changed the state to
13563 * MachineState_Saved, so no need to do anything.
13564 */
13565 if (FAILED(iResult))
13566 setMachineState(mConsoleTaskData.mLastState);
13567
13568 return endSavingState(iResult, aErrMsg);
13569}
13570
13571/**
13572 * @note Locks this object for writing.
13573 */
13574STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13575{
13576 LogFlowThisFunc(("\n"));
13577
13578 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13579
13580 AutoCaller autoCaller(this);
13581 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13582
13583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13584
13585 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13586 || mData->mMachineState == MachineState_Teleported
13587 || mData->mMachineState == MachineState_Aborted
13588 , E_FAIL); /** @todo setError. */
13589
13590 Utf8Str stateFilePathFull = aSavedStateFile;
13591 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13592 if (RT_FAILURE(vrc))
13593 return setError(VBOX_E_FILE_ERROR,
13594 tr("Invalid saved state file path '%ls' (%Rrc)"),
13595 aSavedStateFile,
13596 vrc);
13597
13598 mSSData->strStateFilePath = stateFilePathFull;
13599
13600 /* The below setMachineState() will detect the state transition and will
13601 * update the settings file */
13602
13603 return setMachineState(MachineState_Saved);
13604}
13605
13606STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13607 ComSafeArrayOut(BSTR, aValues),
13608 ComSafeArrayOut(LONG64, aTimestamps),
13609 ComSafeArrayOut(BSTR, aFlags))
13610{
13611 LogFlowThisFunc(("\n"));
13612
13613#ifdef VBOX_WITH_GUEST_PROPS
13614 using namespace guestProp;
13615
13616 AutoCaller autoCaller(this);
13617 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13618
13619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13620
13621 CheckComArgOutSafeArrayPointerValid(aNames);
13622 CheckComArgOutSafeArrayPointerValid(aValues);
13623 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13624 CheckComArgOutSafeArrayPointerValid(aFlags);
13625
13626 size_t cEntries = mHWData->mGuestProperties.size();
13627 com::SafeArray<BSTR> names(cEntries);
13628 com::SafeArray<BSTR> values(cEntries);
13629 com::SafeArray<LONG64> timestamps(cEntries);
13630 com::SafeArray<BSTR> flags(cEntries);
13631 unsigned i = 0;
13632 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13633 it != mHWData->mGuestProperties.end();
13634 ++it)
13635 {
13636 char szFlags[MAX_FLAGS_LEN + 1];
13637 it->first.cloneTo(&names[i]);
13638 it->second.strValue.cloneTo(&values[i]);
13639 timestamps[i] = it->second.mTimestamp;
13640 /* If it is NULL, keep it NULL. */
13641 if (it->second.mFlags)
13642 {
13643 writeFlags(it->second.mFlags, szFlags);
13644 Bstr(szFlags).cloneTo(&flags[i]);
13645 }
13646 else
13647 flags[i] = NULL;
13648 ++i;
13649 }
13650 names.detachTo(ComSafeArrayOutArg(aNames));
13651 values.detachTo(ComSafeArrayOutArg(aValues));
13652 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13653 flags.detachTo(ComSafeArrayOutArg(aFlags));
13654 return S_OK;
13655#else
13656 ReturnComNotImplemented();
13657#endif
13658}
13659
13660STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13661 IN_BSTR aValue,
13662 LONG64 aTimestamp,
13663 IN_BSTR aFlags)
13664{
13665 LogFlowThisFunc(("\n"));
13666
13667#ifdef VBOX_WITH_GUEST_PROPS
13668 using namespace guestProp;
13669
13670 CheckComArgStrNotEmptyOrNull(aName);
13671 CheckComArgNotNull(aValue);
13672 CheckComArgNotNull(aFlags);
13673
13674 try
13675 {
13676 /*
13677 * Convert input up front.
13678 */
13679 Utf8Str utf8Name(aName);
13680 uint32_t fFlags = NILFLAG;
13681 if (aFlags)
13682 {
13683 Utf8Str utf8Flags(aFlags);
13684 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13685 AssertRCReturn(vrc, E_INVALIDARG);
13686 }
13687
13688 /*
13689 * Now grab the object lock, validate the state and do the update.
13690 */
13691 AutoCaller autoCaller(this);
13692 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13693
13694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13695
13696 switch (mData->mMachineState)
13697 {
13698 case MachineState_Paused:
13699 case MachineState_Running:
13700 case MachineState_Teleporting:
13701 case MachineState_TeleportingPausedVM:
13702 case MachineState_LiveSnapshotting:
13703 case MachineState_DeletingSnapshotOnline:
13704 case MachineState_DeletingSnapshotPaused:
13705 case MachineState_Saving:
13706 case MachineState_Stopping:
13707 break;
13708
13709 default:
13710 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13711 VBOX_E_INVALID_VM_STATE);
13712 }
13713
13714 setModified(IsModified_MachineData);
13715 mHWData.backup();
13716
13717 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13718 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13719 if (it != mHWData->mGuestProperties.end())
13720 {
13721 if (!fDelete)
13722 {
13723 it->second.strValue = aValue;
13724 it->second.mTimestamp = aTimestamp;
13725 it->second.mFlags = fFlags;
13726 }
13727 else
13728 mHWData->mGuestProperties.erase(it);
13729
13730 mData->mGuestPropertiesModified = TRUE;
13731 }
13732 else if (!fDelete)
13733 {
13734 HWData::GuestProperty prop;
13735 prop.strValue = aValue;
13736 prop.mTimestamp = aTimestamp;
13737 prop.mFlags = fFlags;
13738
13739 mHWData->mGuestProperties[utf8Name] = prop;
13740 mData->mGuestPropertiesModified = TRUE;
13741 }
13742
13743 /*
13744 * Send a callback notification if appropriate
13745 */
13746 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13747 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13748 RTSTR_MAX,
13749 utf8Name.c_str(),
13750 RTSTR_MAX, NULL)
13751 )
13752 {
13753 alock.release();
13754
13755 mParent->onGuestPropertyChange(mData->mUuid,
13756 aName,
13757 aValue,
13758 aFlags);
13759 }
13760 }
13761 catch (...)
13762 {
13763 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13764 }
13765 return S_OK;
13766#else
13767 ReturnComNotImplemented();
13768#endif
13769}
13770
13771STDMETHODIMP SessionMachine::LockMedia()
13772{
13773 AutoCaller autoCaller(this);
13774 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13775
13776 AutoMultiWriteLock2 alock(this->lockHandle(),
13777 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13778
13779 AssertReturn( mData->mMachineState == MachineState_Starting
13780 || mData->mMachineState == MachineState_Restoring
13781 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13782
13783 clearError();
13784 alock.release();
13785 return lockMedia();
13786}
13787
13788STDMETHODIMP SessionMachine::UnlockMedia()
13789{
13790 unlockMedia();
13791 return S_OK;
13792}
13793
13794STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13795 IMediumAttachment **aNewAttachment)
13796{
13797 CheckComArgNotNull(aAttachment);
13798 CheckComArgOutPointerValid(aNewAttachment);
13799
13800 AutoCaller autoCaller(this);
13801 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13802
13803 // request the host lock first, since might be calling Host methods for getting host drives;
13804 // next, protect the media tree all the while we're in here, as well as our member variables
13805 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13806 this->lockHandle(),
13807 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13808
13809 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13810
13811 Bstr ctrlName;
13812 LONG lPort;
13813 LONG lDevice;
13814 bool fTempEject;
13815 {
13816 AutoCaller autoAttachCaller(this);
13817 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13818
13819 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13820
13821 /* Need to query the details first, as the IMediumAttachment reference
13822 * might be to the original settings, which we are going to change. */
13823 ctrlName = pAttach->getControllerName();
13824 lPort = pAttach->getPort();
13825 lDevice = pAttach->getDevice();
13826 fTempEject = pAttach->getTempEject();
13827 }
13828
13829 if (!fTempEject)
13830 {
13831 /* Remember previously mounted medium. The medium before taking the
13832 * backup is not necessarily the same thing. */
13833 ComObjPtr<Medium> oldmedium;
13834 oldmedium = pAttach->getMedium();
13835
13836 setModified(IsModified_Storage);
13837 mMediaData.backup();
13838
13839 // The backup operation makes the pAttach reference point to the
13840 // old settings. Re-get the correct reference.
13841 pAttach = findAttachment(mMediaData->mAttachments,
13842 ctrlName.raw(),
13843 lPort,
13844 lDevice);
13845
13846 {
13847 AutoCaller autoAttachCaller(this);
13848 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13849
13850 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13851 if (!oldmedium.isNull())
13852 oldmedium->removeBackReference(mData->mUuid);
13853
13854 pAttach->updateMedium(NULL);
13855 pAttach->updateEjected();
13856 }
13857
13858 setModified(IsModified_Storage);
13859 }
13860 else
13861 {
13862 {
13863 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13864 pAttach->updateEjected();
13865 }
13866 }
13867
13868 pAttach.queryInterfaceTo(aNewAttachment);
13869
13870 return S_OK;
13871}
13872
13873// public methods only for internal purposes
13874/////////////////////////////////////////////////////////////////////////////
13875
13876/**
13877 * Called from the client watcher thread to check for expected or unexpected
13878 * death of the client process that has a direct session to this machine.
13879 *
13880 * On Win32 and on OS/2, this method is called only when we've got the
13881 * mutex (i.e. the client has either died or terminated normally) so it always
13882 * returns @c true (the client is terminated, the session machine is
13883 * uninitialized).
13884 *
13885 * On other platforms, the method returns @c true if the client process has
13886 * terminated normally or abnormally and the session machine was uninitialized,
13887 * and @c false if the client process is still alive.
13888 *
13889 * @note Locks this object for writing.
13890 */
13891bool SessionMachine::checkForDeath()
13892{
13893 Uninit::Reason reason;
13894 bool terminated = false;
13895
13896 /* Enclose autoCaller with a block because calling uninit() from under it
13897 * will deadlock. */
13898 {
13899 AutoCaller autoCaller(this);
13900 if (!autoCaller.isOk())
13901 {
13902 /* return true if not ready, to cause the client watcher to exclude
13903 * the corresponding session from watching */
13904 LogFlowThisFunc(("Already uninitialized!\n"));
13905 return true;
13906 }
13907
13908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13909
13910 /* Determine the reason of death: if the session state is Closing here,
13911 * everything is fine. Otherwise it means that the client did not call
13912 * OnSessionEnd() before it released the IPC semaphore. This may happen
13913 * either because the client process has abnormally terminated, or
13914 * because it simply forgot to call ISession::Close() before exiting. We
13915 * threat the latter also as an abnormal termination (see
13916 * Session::uninit() for details). */
13917 reason = mData->mSession.mState == SessionState_Unlocking ?
13918 Uninit::Normal :
13919 Uninit::Abnormal;
13920
13921 if (mClientToken)
13922 terminated = mClientToken->release();
13923 } /* AutoCaller block */
13924
13925 if (terminated)
13926 uninit(reason);
13927
13928 return terminated;
13929}
13930
13931void SessionMachine::getTokenId(Utf8Str &strTokenId)
13932{
13933 LogFlowThisFunc(("\n"));
13934
13935 strTokenId.setNull();
13936
13937 AutoCaller autoCaller(this);
13938 AssertComRCReturnVoid(autoCaller.rc());
13939
13940 Assert(mClientToken);
13941 if (mClientToken)
13942 mClientToken->getId(strTokenId);
13943}
13944
13945Machine::ClientToken *SessionMachine::getClientToken()
13946{
13947 LogFlowThisFunc(("\n"));
13948
13949 AutoCaller autoCaller(this);
13950 AssertComRCReturn(autoCaller.rc(), NULL);
13951
13952 return mClientToken;
13953}
13954
13955
13956/**
13957 * @note Locks this object for reading.
13958 */
13959HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13960{
13961 LogFlowThisFunc(("\n"));
13962
13963 AutoCaller autoCaller(this);
13964 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13965
13966 ComPtr<IInternalSessionControl> directControl;
13967 {
13968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13969 directControl = mData->mSession.mDirectControl;
13970 }
13971
13972 /* ignore notifications sent after #OnSessionEnd() is called */
13973 if (!directControl)
13974 return S_OK;
13975
13976 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13977}
13978
13979/**
13980 * @note Locks this object for reading.
13981 */
13982HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13983 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13984{
13985 LogFlowThisFunc(("\n"));
13986
13987 AutoCaller autoCaller(this);
13988 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13989
13990 ComPtr<IInternalSessionControl> directControl;
13991 {
13992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13993 directControl = mData->mSession.mDirectControl;
13994 }
13995
13996 /* ignore notifications sent after #OnSessionEnd() is called */
13997 if (!directControl)
13998 return S_OK;
13999 /*
14000 * instead acting like callback we ask IVirtualBox deliver corresponding event
14001 */
14002
14003 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14004 return S_OK;
14005}
14006
14007/**
14008 * @note Locks this object for reading.
14009 */
14010HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
14011{
14012 LogFlowThisFunc(("\n"));
14013
14014 AutoCaller autoCaller(this);
14015 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14016
14017 ComPtr<IInternalSessionControl> directControl;
14018 {
14019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14020 directControl = mData->mSession.mDirectControl;
14021 }
14022
14023 /* ignore notifications sent after #OnSessionEnd() is called */
14024 if (!directControl)
14025 return S_OK;
14026
14027 return directControl->OnSerialPortChange(serialPort);
14028}
14029
14030/**
14031 * @note Locks this object for reading.
14032 */
14033HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
14034{
14035 LogFlowThisFunc(("\n"));
14036
14037 AutoCaller autoCaller(this);
14038 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14039
14040 ComPtr<IInternalSessionControl> directControl;
14041 {
14042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14043 directControl = mData->mSession.mDirectControl;
14044 }
14045
14046 /* ignore notifications sent after #OnSessionEnd() is called */
14047 if (!directControl)
14048 return S_OK;
14049
14050 return directControl->OnParallelPortChange(parallelPort);
14051}
14052
14053/**
14054 * @note Locks this object for reading.
14055 */
14056HRESULT SessionMachine::onStorageControllerChange()
14057{
14058 LogFlowThisFunc(("\n"));
14059
14060 AutoCaller autoCaller(this);
14061 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14062
14063 ComPtr<IInternalSessionControl> directControl;
14064 {
14065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14066 directControl = mData->mSession.mDirectControl;
14067 }
14068
14069 /* ignore notifications sent after #OnSessionEnd() is called */
14070 if (!directControl)
14071 return S_OK;
14072
14073 return directControl->OnStorageControllerChange();
14074}
14075
14076/**
14077 * @note Locks this object for reading.
14078 */
14079HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14080{
14081 LogFlowThisFunc(("\n"));
14082
14083 AutoCaller autoCaller(this);
14084 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14085
14086 ComPtr<IInternalSessionControl> directControl;
14087 {
14088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14089 directControl = mData->mSession.mDirectControl;
14090 }
14091
14092 /* ignore notifications sent after #OnSessionEnd() is called */
14093 if (!directControl)
14094 return S_OK;
14095
14096 return directControl->OnMediumChange(aAttachment, aForce);
14097}
14098
14099/**
14100 * @note Locks this object for reading.
14101 */
14102HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
14103{
14104 LogFlowThisFunc(("\n"));
14105
14106 AutoCaller autoCaller(this);
14107 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14108
14109 ComPtr<IInternalSessionControl> directControl;
14110 {
14111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14112 directControl = mData->mSession.mDirectControl;
14113 }
14114
14115 /* ignore notifications sent after #OnSessionEnd() is called */
14116 if (!directControl)
14117 return S_OK;
14118
14119 return directControl->OnCPUChange(aCPU, aRemove);
14120}
14121
14122HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
14123{
14124 LogFlowThisFunc(("\n"));
14125
14126 AutoCaller autoCaller(this);
14127 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14128
14129 ComPtr<IInternalSessionControl> directControl;
14130 {
14131 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14132 directControl = mData->mSession.mDirectControl;
14133 }
14134
14135 /* ignore notifications sent after #OnSessionEnd() is called */
14136 if (!directControl)
14137 return S_OK;
14138
14139 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14140}
14141
14142/**
14143 * @note Locks this object for reading.
14144 */
14145HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
14146{
14147 LogFlowThisFunc(("\n"));
14148
14149 AutoCaller autoCaller(this);
14150 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14151
14152 ComPtr<IInternalSessionControl> directControl;
14153 {
14154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14155 directControl = mData->mSession.mDirectControl;
14156 }
14157
14158 /* ignore notifications sent after #OnSessionEnd() is called */
14159 if (!directControl)
14160 return S_OK;
14161
14162 return directControl->OnVRDEServerChange(aRestart);
14163}
14164
14165/**
14166 * @note Locks this object for reading.
14167 */
14168HRESULT SessionMachine::onVideoCaptureChange()
14169{
14170 LogFlowThisFunc(("\n"));
14171
14172 AutoCaller autoCaller(this);
14173 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14174
14175 ComPtr<IInternalSessionControl> directControl;
14176 {
14177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14178 directControl = mData->mSession.mDirectControl;
14179 }
14180
14181 /* ignore notifications sent after #OnSessionEnd() is called */
14182 if (!directControl)
14183 return S_OK;
14184
14185 return directControl->OnVideoCaptureChange();
14186}
14187
14188/**
14189 * @note Locks this object for reading.
14190 */
14191HRESULT SessionMachine::onUSBControllerChange()
14192{
14193 LogFlowThisFunc(("\n"));
14194
14195 AutoCaller autoCaller(this);
14196 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14197
14198 ComPtr<IInternalSessionControl> directControl;
14199 {
14200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14201 directControl = mData->mSession.mDirectControl;
14202 }
14203
14204 /* ignore notifications sent after #OnSessionEnd() is called */
14205 if (!directControl)
14206 return S_OK;
14207
14208 return directControl->OnUSBControllerChange();
14209}
14210
14211/**
14212 * @note Locks this object for reading.
14213 */
14214HRESULT SessionMachine::onSharedFolderChange()
14215{
14216 LogFlowThisFunc(("\n"));
14217
14218 AutoCaller autoCaller(this);
14219 AssertComRCReturnRC(autoCaller.rc());
14220
14221 ComPtr<IInternalSessionControl> directControl;
14222 {
14223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14224 directControl = mData->mSession.mDirectControl;
14225 }
14226
14227 /* ignore notifications sent after #OnSessionEnd() is called */
14228 if (!directControl)
14229 return S_OK;
14230
14231 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14232}
14233
14234/**
14235 * @note Locks this object for reading.
14236 */
14237HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14238{
14239 LogFlowThisFunc(("\n"));
14240
14241 AutoCaller autoCaller(this);
14242 AssertComRCReturnRC(autoCaller.rc());
14243
14244 ComPtr<IInternalSessionControl> directControl;
14245 {
14246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14247 directControl = mData->mSession.mDirectControl;
14248 }
14249
14250 /* ignore notifications sent after #OnSessionEnd() is called */
14251 if (!directControl)
14252 return S_OK;
14253
14254 return directControl->OnClipboardModeChange(aClipboardMode);
14255}
14256
14257/**
14258 * @note Locks this object for reading.
14259 */
14260HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14261{
14262 LogFlowThisFunc(("\n"));
14263
14264 AutoCaller autoCaller(this);
14265 AssertComRCReturnRC(autoCaller.rc());
14266
14267 ComPtr<IInternalSessionControl> directControl;
14268 {
14269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14270 directControl = mData->mSession.mDirectControl;
14271 }
14272
14273 /* ignore notifications sent after #OnSessionEnd() is called */
14274 if (!directControl)
14275 return S_OK;
14276
14277 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14278}
14279
14280/**
14281 * @note Locks this object for reading.
14282 */
14283HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14284{
14285 LogFlowThisFunc(("\n"));
14286
14287 AutoCaller autoCaller(this);
14288 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14289
14290 ComPtr<IInternalSessionControl> directControl;
14291 {
14292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14293 directControl = mData->mSession.mDirectControl;
14294 }
14295
14296 /* ignore notifications sent after #OnSessionEnd() is called */
14297 if (!directControl)
14298 return S_OK;
14299
14300 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14301}
14302
14303/**
14304 * @note Locks this object for reading.
14305 */
14306HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14307{
14308 LogFlowThisFunc(("\n"));
14309
14310 AutoCaller autoCaller(this);
14311 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14312
14313 ComPtr<IInternalSessionControl> directControl;
14314 {
14315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14316 directControl = mData->mSession.mDirectControl;
14317 }
14318
14319 /* ignore notifications sent after #OnSessionEnd() is called */
14320 if (!directControl)
14321 return S_OK;
14322
14323 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14324}
14325
14326/**
14327 * Returns @c true if this machine's USB controller reports it has a matching
14328 * filter for the given USB device and @c false otherwise.
14329 *
14330 * @note locks this object for reading.
14331 */
14332bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14333{
14334 AutoCaller autoCaller(this);
14335 /* silently return if not ready -- this method may be called after the
14336 * direct machine session has been called */
14337 if (!autoCaller.isOk())
14338 return false;
14339
14340#ifdef VBOX_WITH_USB
14341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14342
14343 switch (mData->mMachineState)
14344 {
14345 case MachineState_Starting:
14346 case MachineState_Restoring:
14347 case MachineState_TeleportingIn:
14348 case MachineState_Paused:
14349 case MachineState_Running:
14350 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14351 * elsewhere... */
14352 alock.release();
14353 return mUSBDeviceFilters->hasMatchingFilter(aDevice, aMaskedIfs);
14354 default: break;
14355 }
14356#else
14357 NOREF(aDevice);
14358 NOREF(aMaskedIfs);
14359#endif
14360 return false;
14361}
14362
14363/**
14364 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14365 */
14366HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14367 IVirtualBoxErrorInfo *aError,
14368 ULONG aMaskedIfs)
14369{
14370 LogFlowThisFunc(("\n"));
14371
14372 AutoCaller autoCaller(this);
14373
14374 /* This notification may happen after the machine object has been
14375 * uninitialized (the session was closed), so don't assert. */
14376 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14377
14378 ComPtr<IInternalSessionControl> directControl;
14379 {
14380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14381 directControl = mData->mSession.mDirectControl;
14382 }
14383
14384 /* fail on notifications sent after #OnSessionEnd() is called, it is
14385 * expected by the caller */
14386 if (!directControl)
14387 return E_FAIL;
14388
14389 /* No locks should be held at this point. */
14390 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14391 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14392
14393 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14394}
14395
14396/**
14397 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14398 */
14399HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14400 IVirtualBoxErrorInfo *aError)
14401{
14402 LogFlowThisFunc(("\n"));
14403
14404 AutoCaller autoCaller(this);
14405
14406 /* This notification may happen after the machine object has been
14407 * uninitialized (the session was closed), so don't assert. */
14408 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14409
14410 ComPtr<IInternalSessionControl> directControl;
14411 {
14412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14413 directControl = mData->mSession.mDirectControl;
14414 }
14415
14416 /* fail on notifications sent after #OnSessionEnd() is called, it is
14417 * expected by the caller */
14418 if (!directControl)
14419 return E_FAIL;
14420
14421 /* No locks should be held at this point. */
14422 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14423 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14424
14425 return directControl->OnUSBDeviceDetach(aId, aError);
14426}
14427
14428// protected methods
14429/////////////////////////////////////////////////////////////////////////////
14430
14431/**
14432 * Helper method to finalize saving the state.
14433 *
14434 * @note Must be called from under this object's lock.
14435 *
14436 * @param aRc S_OK if the snapshot has been taken successfully
14437 * @param aErrMsg human readable error message for failure
14438 *
14439 * @note Locks mParent + this objects for writing.
14440 */
14441HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14442{
14443 LogFlowThisFuncEnter();
14444
14445 AutoCaller autoCaller(this);
14446 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14447
14448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14449
14450 HRESULT rc = S_OK;
14451
14452 if (SUCCEEDED(aRc))
14453 {
14454 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14455
14456 /* save all VM settings */
14457 rc = saveSettings(NULL);
14458 // no need to check whether VirtualBox.xml needs saving also since
14459 // we can't have a name change pending at this point
14460 }
14461 else
14462 {
14463 // delete the saved state file (it might have been already created);
14464 // we need not check whether this is shared with a snapshot here because
14465 // we certainly created this saved state file here anew
14466 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14467 }
14468
14469 /* notify the progress object about operation completion */
14470 Assert(mConsoleTaskData.mProgress);
14471 if (SUCCEEDED(aRc))
14472 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14473 else
14474 {
14475 if (aErrMsg.length())
14476 mConsoleTaskData.mProgress->notifyComplete(aRc,
14477 COM_IIDOF(ISession),
14478 getComponentName(),
14479 aErrMsg.c_str());
14480 else
14481 mConsoleTaskData.mProgress->notifyComplete(aRc);
14482 }
14483
14484 /* clear out the temporary saved state data */
14485 mConsoleTaskData.mLastState = MachineState_Null;
14486 mConsoleTaskData.strStateFilePath.setNull();
14487 mConsoleTaskData.mProgress.setNull();
14488
14489 LogFlowThisFuncLeave();
14490 return rc;
14491}
14492
14493/**
14494 * Deletes the given file if it is no longer in use by either the current machine state
14495 * (if the machine is "saved") or any of the machine's snapshots.
14496 *
14497 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14498 * but is different for each SnapshotMachine. When calling this, the order of calling this
14499 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14500 * is therefore critical. I know, it's all rather messy.
14501 *
14502 * @param strStateFile
14503 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14504 */
14505void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14506 Snapshot *pSnapshotToIgnore)
14507{
14508 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14509 if ( (strStateFile.isNotEmpty())
14510 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14511 )
14512 // ... and it must also not be shared with other snapshots
14513 if ( !mData->mFirstSnapshot
14514 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14515 // this checks the SnapshotMachine's state file paths
14516 )
14517 RTFileDelete(strStateFile.c_str());
14518}
14519
14520/**
14521 * Locks the attached media.
14522 *
14523 * All attached hard disks are locked for writing and DVD/floppy are locked for
14524 * reading. Parents of attached hard disks (if any) are locked for reading.
14525 *
14526 * This method also performs accessibility check of all media it locks: if some
14527 * media is inaccessible, the method will return a failure and a bunch of
14528 * extended error info objects per each inaccessible medium.
14529 *
14530 * Note that this method is atomic: if it returns a success, all media are
14531 * locked as described above; on failure no media is locked at all (all
14532 * succeeded individual locks will be undone).
14533 *
14534 * The caller is responsible for doing the necessary state sanity checks.
14535 *
14536 * The locks made by this method must be undone by calling #unlockMedia() when
14537 * no more needed.
14538 */
14539HRESULT SessionMachine::lockMedia()
14540{
14541 AutoCaller autoCaller(this);
14542 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14543
14544 AutoMultiWriteLock2 alock(this->lockHandle(),
14545 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14546
14547 /* bail out if trying to lock things with already set up locking */
14548 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14549
14550 MultiResult mrc(S_OK);
14551
14552 /* Collect locking information for all medium objects attached to the VM. */
14553 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14554 it != mMediaData->mAttachments.end();
14555 ++it)
14556 {
14557 MediumAttachment* pAtt = *it;
14558 DeviceType_T devType = pAtt->getType();
14559 Medium *pMedium = pAtt->getMedium();
14560
14561 MediumLockList *pMediumLockList(new MediumLockList());
14562 // There can be attachments without a medium (floppy/dvd), and thus
14563 // it's impossible to create a medium lock list. It still makes sense
14564 // to have the empty medium lock list in the map in case a medium is
14565 // attached later.
14566 if (pMedium != NULL)
14567 {
14568 MediumType_T mediumType = pMedium->getType();
14569 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14570 || mediumType == MediumType_Shareable;
14571 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14572
14573 alock.release();
14574 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14575 !fIsReadOnlyLock /* fMediumLockWrite */,
14576 NULL,
14577 *pMediumLockList);
14578 alock.acquire();
14579 if (FAILED(mrc))
14580 {
14581 delete pMediumLockList;
14582 mData->mSession.mLockedMedia.Clear();
14583 break;
14584 }
14585 }
14586
14587 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14588 if (FAILED(rc))
14589 {
14590 mData->mSession.mLockedMedia.Clear();
14591 mrc = setError(rc,
14592 tr("Collecting locking information for all attached media failed"));
14593 break;
14594 }
14595 }
14596
14597 if (SUCCEEDED(mrc))
14598 {
14599 /* Now lock all media. If this fails, nothing is locked. */
14600 alock.release();
14601 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14602 alock.acquire();
14603 if (FAILED(rc))
14604 {
14605 mrc = setError(rc,
14606 tr("Locking of attached media failed"));
14607 }
14608 }
14609
14610 return mrc;
14611}
14612
14613/**
14614 * Undoes the locks made by by #lockMedia().
14615 */
14616void SessionMachine::unlockMedia()
14617{
14618 AutoCaller autoCaller(this);
14619 AssertComRCReturnVoid(autoCaller.rc());
14620
14621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14622
14623 /* we may be holding important error info on the current thread;
14624 * preserve it */
14625 ErrorInfoKeeper eik;
14626
14627 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14628 AssertComRC(rc);
14629}
14630
14631/**
14632 * Helper to change the machine state (reimplementation).
14633 *
14634 * @note Locks this object for writing.
14635 * @note This method must not call saveSettings or SaveSettings, otherwise
14636 * it can cause crashes in random places due to unexpectedly committing
14637 * the current settings. The caller is responsible for that. The call
14638 * to saveStateSettings is fine, because this method does not commit.
14639 */
14640HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14641{
14642 LogFlowThisFuncEnter();
14643 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14644
14645 AutoCaller autoCaller(this);
14646 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14647
14648 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14649
14650 MachineState_T oldMachineState = mData->mMachineState;
14651
14652 AssertMsgReturn(oldMachineState != aMachineState,
14653 ("oldMachineState=%s, aMachineState=%s\n",
14654 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14655 E_FAIL);
14656
14657 HRESULT rc = S_OK;
14658
14659 int stsFlags = 0;
14660 bool deleteSavedState = false;
14661
14662 /* detect some state transitions */
14663
14664 if ( ( oldMachineState == MachineState_Saved
14665 && aMachineState == MachineState_Restoring)
14666 || ( ( oldMachineState == MachineState_PoweredOff
14667 || oldMachineState == MachineState_Teleported
14668 || oldMachineState == MachineState_Aborted
14669 )
14670 && ( aMachineState == MachineState_TeleportingIn
14671 || aMachineState == MachineState_Starting
14672 )
14673 )
14674 )
14675 {
14676 /* The EMT thread is about to start */
14677
14678 /* Nothing to do here for now... */
14679
14680 /// @todo NEWMEDIA don't let mDVDDrive and other children
14681 /// change anything when in the Starting/Restoring state
14682 }
14683 else if ( ( oldMachineState == MachineState_Running
14684 || oldMachineState == MachineState_Paused
14685 || oldMachineState == MachineState_Teleporting
14686 || oldMachineState == MachineState_LiveSnapshotting
14687 || oldMachineState == MachineState_Stuck
14688 || oldMachineState == MachineState_Starting
14689 || oldMachineState == MachineState_Stopping
14690 || oldMachineState == MachineState_Saving
14691 || oldMachineState == MachineState_Restoring
14692 || oldMachineState == MachineState_TeleportingPausedVM
14693 || oldMachineState == MachineState_TeleportingIn
14694 )
14695 && ( aMachineState == MachineState_PoweredOff
14696 || aMachineState == MachineState_Saved
14697 || aMachineState == MachineState_Teleported
14698 || aMachineState == MachineState_Aborted
14699 )
14700 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14701 * snapshot */
14702 && ( mConsoleTaskData.mSnapshot.isNull()
14703 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14704 )
14705 )
14706 {
14707 /* The EMT thread has just stopped, unlock attached media. Note that as
14708 * opposed to locking that is done from Console, we do unlocking here
14709 * because the VM process may have aborted before having a chance to
14710 * properly unlock all media it locked. */
14711
14712 unlockMedia();
14713 }
14714
14715 if (oldMachineState == MachineState_Restoring)
14716 {
14717 if (aMachineState != MachineState_Saved)
14718 {
14719 /*
14720 * delete the saved state file once the machine has finished
14721 * restoring from it (note that Console sets the state from
14722 * Restoring to Saved if the VM couldn't restore successfully,
14723 * to give the user an ability to fix an error and retry --
14724 * we keep the saved state file in this case)
14725 */
14726 deleteSavedState = true;
14727 }
14728 }
14729 else if ( oldMachineState == MachineState_Saved
14730 && ( aMachineState == MachineState_PoweredOff
14731 || aMachineState == MachineState_Aborted
14732 || aMachineState == MachineState_Teleported
14733 )
14734 )
14735 {
14736 /*
14737 * delete the saved state after Console::ForgetSavedState() is called
14738 * or if the VM process (owning a direct VM session) crashed while the
14739 * VM was Saved
14740 */
14741
14742 /// @todo (dmik)
14743 // Not sure that deleting the saved state file just because of the
14744 // client death before it attempted to restore the VM is a good
14745 // thing. But when it crashes we need to go to the Aborted state
14746 // which cannot have the saved state file associated... The only
14747 // way to fix this is to make the Aborted condition not a VM state
14748 // but a bool flag: i.e., when a crash occurs, set it to true and
14749 // change the state to PoweredOff or Saved depending on the
14750 // saved state presence.
14751
14752 deleteSavedState = true;
14753 mData->mCurrentStateModified = TRUE;
14754 stsFlags |= SaveSTS_CurStateModified;
14755 }
14756
14757 if ( aMachineState == MachineState_Starting
14758 || aMachineState == MachineState_Restoring
14759 || aMachineState == MachineState_TeleportingIn
14760 )
14761 {
14762 /* set the current state modified flag to indicate that the current
14763 * state is no more identical to the state in the
14764 * current snapshot */
14765 if (!mData->mCurrentSnapshot.isNull())
14766 {
14767 mData->mCurrentStateModified = TRUE;
14768 stsFlags |= SaveSTS_CurStateModified;
14769 }
14770 }
14771
14772 if (deleteSavedState)
14773 {
14774 if (mRemoveSavedState)
14775 {
14776 Assert(!mSSData->strStateFilePath.isEmpty());
14777
14778 // it is safe to delete the saved state file if ...
14779 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14780 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14781 // ... none of the snapshots share the saved state file
14782 )
14783 RTFileDelete(mSSData->strStateFilePath.c_str());
14784 }
14785
14786 mSSData->strStateFilePath.setNull();
14787 stsFlags |= SaveSTS_StateFilePath;
14788 }
14789
14790 /* redirect to the underlying peer machine */
14791 mPeer->setMachineState(aMachineState);
14792
14793 if ( aMachineState == MachineState_PoweredOff
14794 || aMachineState == MachineState_Teleported
14795 || aMachineState == MachineState_Aborted
14796 || aMachineState == MachineState_Saved)
14797 {
14798 /* the machine has stopped execution
14799 * (or the saved state file was adopted) */
14800 stsFlags |= SaveSTS_StateTimeStamp;
14801 }
14802
14803 if ( ( oldMachineState == MachineState_PoweredOff
14804 || oldMachineState == MachineState_Aborted
14805 || oldMachineState == MachineState_Teleported
14806 )
14807 && aMachineState == MachineState_Saved)
14808 {
14809 /* the saved state file was adopted */
14810 Assert(!mSSData->strStateFilePath.isEmpty());
14811 stsFlags |= SaveSTS_StateFilePath;
14812 }
14813
14814#ifdef VBOX_WITH_GUEST_PROPS
14815 if ( aMachineState == MachineState_PoweredOff
14816 || aMachineState == MachineState_Aborted
14817 || aMachineState == MachineState_Teleported)
14818 {
14819 /* Make sure any transient guest properties get removed from the
14820 * property store on shutdown. */
14821
14822 HWData::GuestPropertyMap::const_iterator it;
14823 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14824 if (!fNeedsSaving)
14825 for (it = mHWData->mGuestProperties.begin();
14826 it != mHWData->mGuestProperties.end(); ++it)
14827 if ( (it->second.mFlags & guestProp::TRANSIENT)
14828 || (it->second.mFlags & guestProp::TRANSRESET))
14829 {
14830 fNeedsSaving = true;
14831 break;
14832 }
14833 if (fNeedsSaving)
14834 {
14835 mData->mCurrentStateModified = TRUE;
14836 stsFlags |= SaveSTS_CurStateModified;
14837 }
14838 }
14839#endif
14840
14841 rc = saveStateSettings(stsFlags);
14842
14843 if ( ( oldMachineState != MachineState_PoweredOff
14844 && oldMachineState != MachineState_Aborted
14845 && oldMachineState != MachineState_Teleported
14846 )
14847 && ( aMachineState == MachineState_PoweredOff
14848 || aMachineState == MachineState_Aborted
14849 || aMachineState == MachineState_Teleported
14850 )
14851 )
14852 {
14853 /* we've been shut down for any reason */
14854 /* no special action so far */
14855 }
14856
14857 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14858 LogFlowThisFuncLeave();
14859 return rc;
14860}
14861
14862/**
14863 * Sends the current machine state value to the VM process.
14864 *
14865 * @note Locks this object for reading, then calls a client process.
14866 */
14867HRESULT SessionMachine::updateMachineStateOnClient()
14868{
14869 AutoCaller autoCaller(this);
14870 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14871
14872 ComPtr<IInternalSessionControl> directControl;
14873 {
14874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14875 AssertReturn(!!mData, E_FAIL);
14876 directControl = mData->mSession.mDirectControl;
14877
14878 /* directControl may be already set to NULL here in #OnSessionEnd()
14879 * called too early by the direct session process while there is still
14880 * some operation (like deleting the snapshot) in progress. The client
14881 * process in this case is waiting inside Session::close() for the
14882 * "end session" process object to complete, while #uninit() called by
14883 * #checkForDeath() on the Watcher thread is waiting for the pending
14884 * operation to complete. For now, we accept this inconsistent behavior
14885 * and simply do nothing here. */
14886
14887 if (mData->mSession.mState == SessionState_Unlocking)
14888 return S_OK;
14889
14890 AssertReturn(!directControl.isNull(), E_FAIL);
14891 }
14892
14893 return directControl->UpdateMachineState(mData->mMachineState);
14894}
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