VirtualBox

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

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

Main, Fe/Qt4: Added method to get effective paravirt. provider in MachineImpl; adjusted GUI and ConsoleImpl2 to use the new method as required.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 496.2 KB
Line 
1/* $Id: MachineImpl.cpp 52090 2014-07-18 06:37:32Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2014 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 mTripleFaultReset = false;
191 mHPETEnabled = false;
192
193 /* default boot order: floppy - DVD - HDD */
194 mBootOrder[0] = DeviceType_Floppy;
195 mBootOrder[1] = DeviceType_DVD;
196 mBootOrder[2] = DeviceType_HardDisk;
197 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
198 mBootOrder[i] = DeviceType_Null;
199
200 mClipboardMode = ClipboardMode_Disabled;
201 mDnDMode = DnDMode_Disabled;
202 mGuestPropertyNotificationPatterns = "";
203
204 mFirmwareType = FirmwareType_BIOS;
205 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
206 mPointingHIDType = PointingHIDType_PS2Mouse;
207 mChipsetType = ChipsetType_PIIX3;
208 mParavirtProvider = ParavirtProvider_Default;
209 mEmulatedUSBCardReaderEnabled = FALSE;
210
211 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
212 mCPUAttached[i] = false;
213
214 mIOCacheEnabled = true;
215 mIOCacheSize = 5; /* 5MB */
216
217 /* Maximum CPU execution cap by default. */
218 mCpuExecutionCap = 100;
219}
220
221Machine::HWData::~HWData()
222{
223}
224
225/////////////////////////////////////////////////////////////////////////////
226// Machine::HDData structure
227/////////////////////////////////////////////////////////////////////////////
228
229Machine::MediaData::MediaData()
230{
231}
232
233Machine::MediaData::~MediaData()
234{
235}
236
237/////////////////////////////////////////////////////////////////////////////
238// Machine class
239/////////////////////////////////////////////////////////////////////////////
240
241// constructor / destructor
242/////////////////////////////////////////////////////////////////////////////
243
244Machine::Machine() :
245#ifdef VBOX_WITH_RESOURCE_USAGE_API
246 mCollectorGuest(NULL),
247#endif
248 mPeer(NULL),
249 mParent(NULL),
250 mSerialPorts(),
251 mParallelPorts(),
252 uRegistryNeedsSaving(0)
253{}
254
255Machine::~Machine()
256{}
257
258HRESULT Machine::FinalConstruct()
259{
260 LogFlowThisFunc(("\n"));
261 return BaseFinalConstruct();
262}
263
264void Machine::FinalRelease()
265{
266 LogFlowThisFunc(("\n"));
267 uninit();
268 BaseFinalRelease();
269}
270
271/**
272 * Initializes a new machine instance; this init() variant creates a new, empty machine.
273 * This gets called from VirtualBox::CreateMachine().
274 *
275 * @param aParent Associated parent object
276 * @param strConfigFile Local file system path to the VM settings file (can
277 * be relative to the VirtualBox config directory).
278 * @param strName name for the machine
279 * @param llGroups list of groups for the machine
280 * @param aOsType OS Type of this machine or NULL.
281 * @param aId UUID for the new machine.
282 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
283 *
284 * @return Success indicator. if not S_OK, the machine object is invalid
285 */
286HRESULT Machine::init(VirtualBox *aParent,
287 const Utf8Str &strConfigFile,
288 const Utf8Str &strName,
289 const StringsList &llGroups,
290 GuestOSType *aOsType,
291 const Guid &aId,
292 bool fForceOverwrite,
293 bool fDirectoryIncludesUUID)
294{
295 LogFlowThisFuncEnter();
296 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
297
298 /* Enclose the state transition NotReady->InInit->Ready */
299 AutoInitSpan autoInitSpan(this);
300 AssertReturn(autoInitSpan.isOk(), E_FAIL);
301
302 HRESULT rc = initImpl(aParent, strConfigFile);
303 if (FAILED(rc)) return rc;
304
305 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
306 if (FAILED(rc)) return rc;
307
308 if (SUCCEEDED(rc))
309 {
310 // create an empty machine config
311 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
312
313 rc = initDataAndChildObjects();
314 }
315
316 if (SUCCEEDED(rc))
317 {
318 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
319 mData->mAccessible = TRUE;
320
321 unconst(mData->mUuid) = aId;
322
323 mUserData->s.strName = strName;
324
325 mUserData->s.llGroups = llGroups;
326
327 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
328 // the "name sync" flag determines whether the machine directory gets renamed along
329 // with the machine file; say so if the settings file name is the same as the
330 // settings file parent directory (machine directory)
331 mUserData->s.fNameSync = i_isInOwnDir();
332
333 // initialize the default snapshots folder
334 rc = COMSETTER(SnapshotFolder)(NULL);
335 AssertComRC(rc);
336
337 if (aOsType)
338 {
339 /* Store OS type */
340 mUserData->s.strOsType = aOsType->i_id();
341
342 /* Apply BIOS defaults */
343 mBIOSSettings->i_applyDefaults(aOsType);
344
345 /* Apply network adapters defaults */
346 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
347 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
348
349 /* Apply serial port defaults */
350 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
351 mSerialPorts[slot]->i_applyDefaults(aOsType);
352
353 /* Let the OS type select 64-bit ness. */
354 mHWData->mLongMode = aOsType->i_is64Bit()
355 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
356 }
357
358 /* At this point the changing of the current state modification
359 * flag is allowed. */
360 i_allowStateModification();
361
362 /* commit all changes made during the initialization */
363 i_commit();
364 }
365
366 /* Confirm a successful initialization when it's the case */
367 if (SUCCEEDED(rc))
368 {
369 if (mData->mAccessible)
370 autoInitSpan.setSucceeded();
371 else
372 autoInitSpan.setLimited();
373 }
374
375 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
376 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
377 mData->mRegistered,
378 mData->mAccessible,
379 rc));
380
381 LogFlowThisFuncLeave();
382
383 return rc;
384}
385
386/**
387 * Initializes a new instance with data from machine XML (formerly Init_Registered).
388 * Gets called in two modes:
389 *
390 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
391 * UUID is specified and we mark the machine as "registered";
392 *
393 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
394 * and the machine remains unregistered until RegisterMachine() is called.
395 *
396 * @param aParent Associated parent object
397 * @param aConfigFile Local file system path to the VM settings file (can
398 * be relative to the VirtualBox config directory).
399 * @param aId UUID of the machine or NULL (see above).
400 *
401 * @return Success indicator. if not S_OK, the machine object is invalid
402 */
403HRESULT Machine::initFromSettings(VirtualBox *aParent,
404 const Utf8Str &strConfigFile,
405 const Guid *aId)
406{
407 LogFlowThisFuncEnter();
408 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
409
410 /* Enclose the state transition NotReady->InInit->Ready */
411 AutoInitSpan autoInitSpan(this);
412 AssertReturn(autoInitSpan.isOk(), E_FAIL);
413
414 HRESULT rc = initImpl(aParent, strConfigFile);
415 if (FAILED(rc)) return rc;
416
417 if (aId)
418 {
419 // loading a registered VM:
420 unconst(mData->mUuid) = *aId;
421 mData->mRegistered = TRUE;
422 // now load the settings from XML:
423 rc = i_registeredInit();
424 // this calls initDataAndChildObjects() and loadSettings()
425 }
426 else
427 {
428 // opening an unregistered VM (VirtualBox::OpenMachine()):
429 rc = initDataAndChildObjects();
430
431 if (SUCCEEDED(rc))
432 {
433 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
434 mData->mAccessible = TRUE;
435
436 try
437 {
438 // load and parse machine XML; this will throw on XML or logic errors
439 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
440
441 // reject VM UUID duplicates, they can happen if someone
442 // tries to register an already known VM config again
443 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
444 true /* fPermitInaccessible */,
445 false /* aDoSetError */,
446 NULL) != VBOX_E_OBJECT_NOT_FOUND)
447 {
448 throw setError(E_FAIL,
449 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
450 mData->m_strConfigFile.c_str());
451 }
452
453 // use UUID from machine config
454 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
455
456 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
457 NULL /* puuidRegistry */);
458 if (FAILED(rc)) throw rc;
459
460 /* At this point the changing of the current state modification
461 * flag is allowed. */
462 i_allowStateModification();
463
464 i_commit();
465 }
466 catch (HRESULT err)
467 {
468 /* we assume that error info is set by the thrower */
469 rc = err;
470 }
471 catch (...)
472 {
473 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
474 }
475 }
476 }
477
478 /* Confirm a successful initialization when it's the case */
479 if (SUCCEEDED(rc))
480 {
481 if (mData->mAccessible)
482 autoInitSpan.setSucceeded();
483 else
484 {
485 autoInitSpan.setLimited();
486
487 // uninit media from this machine's media registry, or else
488 // reloading the settings will fail
489 mParent->i_unregisterMachineMedia(i_getId());
490 }
491 }
492
493 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
494 "rc=%08X\n",
495 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
496 mData->mRegistered, mData->mAccessible, rc));
497
498 LogFlowThisFuncLeave();
499
500 return rc;
501}
502
503/**
504 * Initializes a new instance from a machine config that is already in memory
505 * (import OVF case). Since we are importing, the UUID in the machine
506 * config is ignored and we always generate a fresh one.
507 *
508 * @param strName Name for the new machine; this overrides what is specified in config and is used
509 * for the settings file as well.
510 * @param config Machine configuration loaded and parsed from XML.
511 *
512 * @return Success indicator. if not S_OK, the machine object is invalid
513 */
514HRESULT Machine::init(VirtualBox *aParent,
515 const Utf8Str &strName,
516 const settings::MachineConfigFile &config)
517{
518 LogFlowThisFuncEnter();
519
520 /* Enclose the state transition NotReady->InInit->Ready */
521 AutoInitSpan autoInitSpan(this);
522 AssertReturn(autoInitSpan.isOk(), E_FAIL);
523
524 Utf8Str strConfigFile;
525 aParent->i_getDefaultMachineFolder(strConfigFile);
526 strConfigFile.append(RTPATH_DELIMITER);
527 strConfigFile.append(strName);
528 strConfigFile.append(RTPATH_DELIMITER);
529 strConfigFile.append(strName);
530 strConfigFile.append(".vbox");
531
532 HRESULT rc = initImpl(aParent, strConfigFile);
533 if (FAILED(rc)) return rc;
534
535 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
536 if (FAILED(rc)) return rc;
537
538 rc = initDataAndChildObjects();
539
540 if (SUCCEEDED(rc))
541 {
542 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
543 mData->mAccessible = TRUE;
544
545 // create empty machine config for instance data
546 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
547
548 // generate fresh UUID, ignore machine config
549 unconst(mData->mUuid).create();
550
551 rc = i_loadMachineDataFromSettings(config,
552 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
553
554 // override VM name as well, it may be different
555 mUserData->s.strName = strName;
556
557 if (SUCCEEDED(rc))
558 {
559 /* At this point the changing of the current state modification
560 * flag is allowed. */
561 i_allowStateModification();
562
563 /* commit all changes made during the initialization */
564 i_commit();
565 }
566 }
567
568 /* Confirm a successful initialization when it's the case */
569 if (SUCCEEDED(rc))
570 {
571 if (mData->mAccessible)
572 autoInitSpan.setSucceeded();
573 else
574 {
575 /* Ignore all errors from unregistering, they would destroy
576- * the more interesting error information we already have,
577- * pinpointing the issue with the VM config. */
578 ErrorInfoKeeper eik;
579
580 autoInitSpan.setLimited();
581
582 // uninit media from this machine's media registry, or else
583 // reloading the settings will fail
584 mParent->i_unregisterMachineMedia(i_getId());
585 }
586 }
587
588 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
589 "rc=%08X\n",
590 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
591 mData->mRegistered, mData->mAccessible, rc));
592
593 LogFlowThisFuncLeave();
594
595 return rc;
596}
597
598/**
599 * Shared code between the various init() implementations.
600 * @param aParent
601 * @return
602 */
603HRESULT Machine::initImpl(VirtualBox *aParent,
604 const Utf8Str &strConfigFile)
605{
606 LogFlowThisFuncEnter();
607
608 AssertReturn(aParent, E_INVALIDARG);
609 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
610
611 HRESULT rc = S_OK;
612
613 /* share the parent weakly */
614 unconst(mParent) = aParent;
615
616 /* allocate the essential machine data structure (the rest will be
617 * allocated later by initDataAndChildObjects() */
618 mData.allocate();
619
620 /* memorize the config file name (as provided) */
621 mData->m_strConfigFile = strConfigFile;
622
623 /* get the full file name */
624 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
625 if (RT_FAILURE(vrc1))
626 return setError(VBOX_E_FILE_ERROR,
627 tr("Invalid machine settings file name '%s' (%Rrc)"),
628 strConfigFile.c_str(),
629 vrc1);
630
631 LogFlowThisFuncLeave();
632
633 return rc;
634}
635
636/**
637 * Tries to create a machine settings file in the path stored in the machine
638 * instance data. Used when a new machine is created to fail gracefully if
639 * the settings file could not be written (e.g. because machine dir is read-only).
640 * @return
641 */
642HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
643{
644 HRESULT rc = S_OK;
645
646 // when we create a new machine, we must be able to create the settings file
647 RTFILE f = NIL_RTFILE;
648 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
649 if ( RT_SUCCESS(vrc)
650 || vrc == VERR_SHARING_VIOLATION
651 )
652 {
653 if (RT_SUCCESS(vrc))
654 RTFileClose(f);
655 if (!fForceOverwrite)
656 rc = setError(VBOX_E_FILE_ERROR,
657 tr("Machine settings file '%s' already exists"),
658 mData->m_strConfigFileFull.c_str());
659 else
660 {
661 /* try to delete the config file, as otherwise the creation
662 * of a new settings file will fail. */
663 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
664 if (RT_FAILURE(vrc2))
665 rc = setError(VBOX_E_FILE_ERROR,
666 tr("Could not delete the existing settings file '%s' (%Rrc)"),
667 mData->m_strConfigFileFull.c_str(), vrc2);
668 }
669 }
670 else if ( vrc != VERR_FILE_NOT_FOUND
671 && vrc != VERR_PATH_NOT_FOUND
672 )
673 rc = setError(VBOX_E_FILE_ERROR,
674 tr("Invalid machine settings file name '%s' (%Rrc)"),
675 mData->m_strConfigFileFull.c_str(),
676 vrc);
677 return rc;
678}
679
680/**
681 * Initializes the registered machine by loading the settings file.
682 * This method is separated from #init() in order to make it possible to
683 * retry the operation after VirtualBox startup instead of refusing to
684 * startup the whole VirtualBox server in case if the settings file of some
685 * registered VM is invalid or inaccessible.
686 *
687 * @note Must be always called from this object's write lock
688 * (unless called from #init() that doesn't need any locking).
689 * @note Locks the mUSBController method for writing.
690 * @note Subclasses must not call this method.
691 */
692HRESULT Machine::i_registeredInit()
693{
694 AssertReturn(!i_isSessionMachine(), E_FAIL);
695 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
696 AssertReturn(mData->mUuid.isValid(), E_FAIL);
697 AssertReturn(!mData->mAccessible, E_FAIL);
698
699 HRESULT rc = initDataAndChildObjects();
700
701 if (SUCCEEDED(rc))
702 {
703 /* Temporarily reset the registered flag in order to let setters
704 * potentially called from loadSettings() succeed (isMutable() used in
705 * all setters will return FALSE for a Machine instance if mRegistered
706 * is TRUE). */
707 mData->mRegistered = FALSE;
708
709 try
710 {
711 // load and parse machine XML; this will throw on XML or logic errors
712 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
713
714 if (mData->mUuid != mData->pMachineConfigFile->uuid)
715 throw setError(E_FAIL,
716 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
717 mData->pMachineConfigFile->uuid.raw(),
718 mData->m_strConfigFileFull.c_str(),
719 mData->mUuid.toString().c_str(),
720 mParent->i_settingsFilePath().c_str());
721
722 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
723 NULL /* const Guid *puuidRegistry */);
724 if (FAILED(rc)) throw rc;
725 }
726 catch (HRESULT err)
727 {
728 /* we assume that error info is set by the thrower */
729 rc = err;
730 }
731 catch (...)
732 {
733 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
734 }
735
736 /* Restore the registered flag (even on failure) */
737 mData->mRegistered = TRUE;
738 }
739
740 if (SUCCEEDED(rc))
741 {
742 /* Set mAccessible to TRUE only if we successfully locked and loaded
743 * the settings file */
744 mData->mAccessible = TRUE;
745
746 /* commit all changes made during loading the settings file */
747 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
748 /// @todo r=klaus for some reason the settings loading logic backs up
749 // the settings, and therefore a commit is needed. Should probably be changed.
750 }
751 else
752 {
753 /* If the machine is registered, then, instead of returning a
754 * failure, we mark it as inaccessible and set the result to
755 * success to give it a try later */
756
757 /* fetch the current error info */
758 mData->mAccessError = com::ErrorInfo();
759 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
760 mData->mUuid.raw(),
761 mData->mAccessError.getText().raw()));
762
763 /* rollback all changes */
764 i_rollback(false /* aNotify */);
765
766 // uninit media from this machine's media registry, or else
767 // reloading the settings will fail
768 mParent->i_unregisterMachineMedia(i_getId());
769
770 /* uninitialize the common part to make sure all data is reset to
771 * default (null) values */
772 uninitDataAndChildObjects();
773
774 rc = S_OK;
775 }
776
777 return rc;
778}
779
780/**
781 * Uninitializes the instance.
782 * Called either from FinalRelease() or by the parent when it gets destroyed.
783 *
784 * @note The caller of this method must make sure that this object
785 * a) doesn't have active callers on the current thread and b) is not locked
786 * by the current thread; otherwise uninit() will hang either a) due to
787 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
788 * a dead-lock caused by this thread waiting for all callers on the other
789 * threads are done but preventing them from doing so by holding a lock.
790 */
791void Machine::uninit()
792{
793 LogFlowThisFuncEnter();
794
795 Assert(!isWriteLockOnCurrentThread());
796
797 Assert(!uRegistryNeedsSaving);
798 if (uRegistryNeedsSaving)
799 {
800 AutoCaller autoCaller(this);
801 if (SUCCEEDED(autoCaller.rc()))
802 {
803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
804 i_saveSettings(NULL, Machine::SaveS_Force);
805 }
806 }
807
808 /* Enclose the state transition Ready->InUninit->NotReady */
809 AutoUninitSpan autoUninitSpan(this);
810 if (autoUninitSpan.uninitDone())
811 return;
812
813 Assert(!i_isSnapshotMachine());
814 Assert(!i_isSessionMachine());
815 Assert(!!mData);
816
817 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
818 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
819
820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
821
822 if (!mData->mSession.mMachine.isNull())
823 {
824 /* Theoretically, this can only happen if the VirtualBox server has been
825 * terminated while there were clients running that owned open direct
826 * sessions. Since in this case we are definitely called by
827 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
828 * won't happen on the client watcher thread (because it does
829 * VirtualBox::addCaller() for the duration of the
830 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
831 * cannot happen until the VirtualBox caller is released). This is
832 * important, because SessionMachine::uninit() cannot correctly operate
833 * after we return from this method (it expects the Machine instance is
834 * still valid). We'll call it ourselves below.
835 */
836 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
837 (SessionMachine*)mData->mSession.mMachine));
838
839 if (Global::IsOnlineOrTransient(mData->mMachineState))
840 {
841 LogWarningThisFunc(("Setting state to Aborted!\n"));
842 /* set machine state using SessionMachine reimplementation */
843 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
844 }
845
846 /*
847 * Uninitialize SessionMachine using public uninit() to indicate
848 * an unexpected uninitialization.
849 */
850 mData->mSession.mMachine->uninit();
851 /* SessionMachine::uninit() must set mSession.mMachine to null */
852 Assert(mData->mSession.mMachine.isNull());
853 }
854
855 // uninit media from this machine's media registry, if they're still there
856 Guid uuidMachine(i_getId());
857
858 /* the lock is no more necessary (SessionMachine is uninitialized) */
859 alock.release();
860
861 /* XXX This will fail with
862 * "cannot be closed because it is still attached to 1 virtual machines"
863 * because at this point we did not call uninitDataAndChildObjects() yet
864 * and therefore also removeBackReference() for all these mediums was not called! */
865
866 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
867 mParent->i_unregisterMachineMedia(uuidMachine);
868
869 // has machine been modified?
870 if (mData->flModifications)
871 {
872 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
873 i_rollback(false /* aNotify */);
874 }
875
876 if (mData->mAccessible)
877 uninitDataAndChildObjects();
878
879 /* free the essential data structure last */
880 mData.free();
881
882 LogFlowThisFuncLeave();
883}
884
885// Wrapped IMachine properties
886/////////////////////////////////////////////////////////////////////////////
887HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
888{
889 /* mParent is constant during life time, no need to lock */
890 ComObjPtr<VirtualBox> pVirtualBox(mParent);
891 pVirtualBox.queryInterfaceTo(aParent.asOutParam());
892
893 return S_OK;
894}
895
896
897HRESULT Machine::getAccessible(BOOL *aAccessible)
898{
899 /* In some cases (medium registry related), it is necessary to be able to
900 * go through the list of all machines. Happens when an inaccessible VM
901 * has a sensible medium registry. */
902 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
904
905 HRESULT rc = S_OK;
906
907 if (!mData->mAccessible)
908 {
909 /* try to initialize the VM once more if not accessible */
910
911 AutoReinitSpan autoReinitSpan(this);
912 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
913
914#ifdef DEBUG
915 LogFlowThisFunc(("Dumping media backreferences\n"));
916 mParent->i_dumpAllBackRefs();
917#endif
918
919 if (mData->pMachineConfigFile)
920 {
921 // reset the XML file to force loadSettings() (called from registeredInit())
922 // to parse it again; the file might have changed
923 delete mData->pMachineConfigFile;
924 mData->pMachineConfigFile = NULL;
925 }
926
927 rc = i_registeredInit();
928
929 if (SUCCEEDED(rc) && mData->mAccessible)
930 {
931 autoReinitSpan.setSucceeded();
932
933 /* make sure interesting parties will notice the accessibility
934 * state change */
935 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
936 mParent->i_onMachineDataChange(mData->mUuid);
937 }
938 }
939
940 if (SUCCEEDED(rc))
941 *aAccessible = mData->mAccessible;
942
943 LogFlowThisFuncLeave();
944
945 return rc;
946}
947
948HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
949{
950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
951
952 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
953 {
954 /* return shortly */
955 aAccessError = NULL;
956 return S_OK;
957 }
958
959 HRESULT rc = S_OK;
960
961 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
962 rc = errorInfo.createObject();
963 if (SUCCEEDED(rc))
964 {
965 errorInfo->init(mData->mAccessError.getResultCode(),
966 mData->mAccessError.getInterfaceID().ref(),
967 Utf8Str(mData->mAccessError.getComponent()).c_str(),
968 Utf8Str(mData->mAccessError.getText()));
969 rc = errorInfo.queryInterfaceTo(aAccessError.asOutParam());
970 }
971
972 return rc;
973}
974
975HRESULT Machine::getName(com::Utf8Str &aName)
976{
977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
978
979 aName = mUserData->s.strName;
980
981 return S_OK;
982}
983
984HRESULT Machine::setName(const com::Utf8Str &aName)
985{
986 // prohibit setting a UUID only as the machine name, or else it can
987 // never be found by findMachine()
988 Guid test(aName);
989
990 if (test.isValid())
991 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
992
993 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
994
995 HRESULT rc = i_checkStateDependency(MutableStateDep);
996 if (FAILED(rc)) return rc;
997
998 i_setModified(IsModified_MachineData);
999 mUserData.backup();
1000 mUserData->s.strName = aName;
1001
1002 return S_OK;
1003}
1004
1005HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1006{
1007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1008
1009 aDescription = mUserData->s.strDescription;
1010
1011 return S_OK;
1012}
1013
1014HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1015{
1016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1017
1018 // this can be done in principle in any state as it doesn't affect the VM
1019 // significantly, but play safe by not messing around while complex
1020 // activities are going on
1021 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1022 if (FAILED(rc)) return rc;
1023
1024 i_setModified(IsModified_MachineData);
1025 mUserData.backup();
1026 mUserData->s.strDescription = aDescription;
1027
1028 return S_OK;
1029}
1030
1031HRESULT Machine::getId(com::Guid &aId)
1032{
1033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1034
1035 aId = mData->mUuid;
1036
1037 return S_OK;
1038}
1039
1040HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1041{
1042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1043 aGroups.resize(mUserData->s.llGroups.size());
1044 size_t i = 0;
1045 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1046 it != mUserData->s.llGroups.end(); ++it, ++i)
1047 aGroups[i] = (*it);
1048
1049 return S_OK;
1050}
1051
1052HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1053{
1054 StringsList llGroups;
1055 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1056 if (FAILED(rc))
1057 return rc;
1058
1059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1060
1061 // changing machine groups is possible while the VM is offline
1062 rc = i_checkStateDependency(OfflineStateDep);
1063 if (FAILED(rc)) return rc;
1064
1065 i_setModified(IsModified_MachineData);
1066 mUserData.backup();
1067 mUserData->s.llGroups = llGroups;
1068
1069 return S_OK;
1070}
1071
1072HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1073{
1074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1075
1076 aOSTypeId = mUserData->s.strOsType;
1077
1078 return S_OK;
1079}
1080
1081HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1082{
1083 /* look up the object by Id to check it is valid */
1084 ComPtr<IGuestOSType> guestOSType;
1085 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1086 if (FAILED(rc)) return rc;
1087
1088 /* when setting, always use the "etalon" value for consistency -- lookup
1089 * by ID is case-insensitive and the input value may have different case */
1090 Bstr osTypeId;
1091 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1092 if (FAILED(rc)) return rc;
1093
1094 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1095
1096 rc = i_checkStateDependency(MutableStateDep);
1097 if (FAILED(rc)) return rc;
1098
1099 i_setModified(IsModified_MachineData);
1100 mUserData.backup();
1101 mUserData->s.strOsType = osTypeId;
1102
1103 return S_OK;
1104}
1105
1106HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1107{
1108 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1109
1110 *aFirmwareType = mHWData->mFirmwareType;
1111
1112 return S_OK;
1113}
1114
1115HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1116{
1117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1118
1119 HRESULT rc = i_checkStateDependency(MutableStateDep);
1120 if (FAILED(rc)) return rc;
1121
1122 i_setModified(IsModified_MachineData);
1123 mHWData.backup();
1124 mHWData->mFirmwareType = aFirmwareType;
1125
1126 return S_OK;
1127}
1128
1129HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1130{
1131 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1132
1133 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1134
1135 return S_OK;
1136}
1137
1138HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1139{
1140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1141
1142 HRESULT rc = i_checkStateDependency(MutableStateDep);
1143 if (FAILED(rc)) return rc;
1144
1145 i_setModified(IsModified_MachineData);
1146 mHWData.backup();
1147 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1148
1149 return S_OK;
1150}
1151
1152HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1153{
1154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1155
1156 *aPointingHIDType = mHWData->mPointingHIDType;
1157
1158 return S_OK;
1159}
1160
1161HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1162{
1163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1164
1165 HRESULT rc = i_checkStateDependency(MutableStateDep);
1166 if (FAILED(rc)) return rc;
1167
1168 i_setModified(IsModified_MachineData);
1169 mHWData.backup();
1170 mHWData->mPointingHIDType = aPointingHIDType;
1171
1172 return S_OK;
1173}
1174
1175HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1176{
1177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1178
1179 *aChipsetType = mHWData->mChipsetType;
1180
1181 return S_OK;
1182}
1183
1184HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1185{
1186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1187
1188 HRESULT rc = i_checkStateDependency(MutableStateDep);
1189 if (FAILED(rc)) return rc;
1190
1191 if (aChipsetType != mHWData->mChipsetType)
1192 {
1193 i_setModified(IsModified_MachineData);
1194 mHWData.backup();
1195 mHWData->mChipsetType = aChipsetType;
1196
1197 // Resize network adapter array, to be finalized on commit/rollback.
1198 // We must not throw away entries yet, otherwise settings are lost
1199 // without a way to roll back.
1200 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1201 size_t oldCount = mNetworkAdapters.size();
1202 if (newCount > oldCount)
1203 {
1204 mNetworkAdapters.resize(newCount);
1205 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1206 {
1207 unconst(mNetworkAdapters[slot]).createObject();
1208 mNetworkAdapters[slot]->init(this, slot);
1209 }
1210 }
1211 }
1212
1213 return S_OK;
1214}
1215
1216HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1217{
1218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1219
1220 *aParavirtProvider = mHWData->mParavirtProvider;
1221
1222 return S_OK;
1223}
1224
1225HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1226{
1227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1228
1229 HRESULT rc = i_checkStateDependency(MutableStateDep);
1230 if (FAILED(rc)) return rc;
1231
1232 if (aParavirtProvider != mHWData->mParavirtProvider)
1233 {
1234 i_setModified(IsModified_MachineData);
1235 mHWData.backup();
1236 mHWData->mParavirtProvider = aParavirtProvider;
1237 }
1238
1239 return S_OK;
1240}
1241
1242HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1243{
1244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1245
1246 *aParavirtProvider = mHWData->mParavirtProvider;
1247 switch (mHWData->mParavirtProvider)
1248 {
1249 case ParavirtProvider_None:
1250 case ParavirtProvider_HyperV:
1251 case ParavirtProvider_Minimal:
1252 break;
1253
1254 /* Resolve dynamic provider types to the effective types. */
1255 default:
1256 {
1257 ComPtr<IGuestOSType> ptrGuestOSType;
1258 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1259 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1260
1261 Bstr guestTypeFamilyId;
1262 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1263 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1264 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1265
1266 switch (mHWData->mParavirtProvider)
1267 {
1268 case ParavirtProvider_Legacy:
1269 {
1270 if (fOsXGuest)
1271 *aParavirtProvider = ParavirtProvider_Minimal;
1272 else
1273 *aParavirtProvider = ParavirtProvider_None;
1274 break;
1275 }
1276
1277 case ParavirtProvider_Default:
1278 {
1279 if (fOsXGuest)
1280 *aParavirtProvider = ParavirtProvider_Minimal;
1281#if 0 /* Activate this soon. */
1282 else if ( mUserData->s.strOsType == "Windows81"
1283 || mUserData->s.strOsType == "Windows81_64"
1284 || mUserData->s.strOsType == "Windows8"
1285 || mUserData->s.strOsType == "Windows8_64"
1286 || mUserData->s.strOsType == "Windows7"
1287 || mUserData->s.strOsType == "Windows7_64"
1288 || mUserData->s.strOsType == "WindowsVista"
1289 || mUserData->s.strOsType == "WindowsVista_64"
1290 || mUserData->s.strOsType == "Windows2012"
1291 || mUserData->s.strOsType == "Windows2012_64"
1292 || mUserData->s.strOsType == "Windows2008"
1293 || mUserData->s.strOsType == "Windows2008_64")
1294 {
1295 *aParavirtProvider = ParavirtProvider_HyperV;
1296 }
1297#endif
1298 else
1299 *aParavirtProvider = ParavirtProvider_None;
1300 break;
1301 }
1302 }
1303 break;
1304 }
1305 }
1306
1307 Assert( *aParavirtProvider == ParavirtProvider_None
1308 || *aParavirtProvider == ParavirtProvider_Minimal
1309 || *aParavirtProvider == ParavirtProvider_HyperV);
1310 return S_OK;
1311}
1312
1313HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1314{
1315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1316
1317 aHardwareVersion = mHWData->mHWVersion;
1318
1319 return S_OK;
1320}
1321
1322HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1323{
1324 /* check known version */
1325 Utf8Str hwVersion = aHardwareVersion;
1326 if ( hwVersion.compare("1") != 0
1327 && hwVersion.compare("2") != 0)
1328 return setError(E_INVALIDARG,
1329 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1330
1331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1332
1333 HRESULT rc = i_checkStateDependency(MutableStateDep);
1334 if (FAILED(rc)) return rc;
1335
1336 i_setModified(IsModified_MachineData);
1337 mHWData.backup();
1338 mHWData->mHWVersion = aHardwareVersion;
1339
1340 return S_OK;
1341}
1342
1343HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1344{
1345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1346
1347 if (!mHWData->mHardwareUUID.isZero())
1348 aHardwareUUID = mHWData->mHardwareUUID;
1349 else
1350 aHardwareUUID = mData->mUuid;
1351
1352 return S_OK;
1353}
1354
1355HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1356{
1357 if (!aHardwareUUID.isValid())
1358 return E_INVALIDARG;
1359
1360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1361
1362 HRESULT rc = i_checkStateDependency(MutableStateDep);
1363 if (FAILED(rc)) return rc;
1364
1365 i_setModified(IsModified_MachineData);
1366 mHWData.backup();
1367 if (aHardwareUUID == mData->mUuid)
1368 mHWData->mHardwareUUID.clear();
1369 else
1370 mHWData->mHardwareUUID = aHardwareUUID;
1371
1372 return S_OK;
1373}
1374
1375HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1376{
1377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1378
1379 *aMemorySize = mHWData->mMemorySize;
1380
1381 return S_OK;
1382}
1383
1384HRESULT Machine::setMemorySize(ULONG aMemorySize)
1385{
1386 /* check RAM limits */
1387 if ( aMemorySize < MM_RAM_MIN_IN_MB
1388 || aMemorySize > MM_RAM_MAX_IN_MB
1389 )
1390 return setError(E_INVALIDARG,
1391 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1392 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1393
1394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 HRESULT rc = i_checkStateDependency(MutableStateDep);
1397 if (FAILED(rc)) return rc;
1398
1399 i_setModified(IsModified_MachineData);
1400 mHWData.backup();
1401 mHWData->mMemorySize = aMemorySize;
1402
1403 return S_OK;
1404}
1405
1406HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1407{
1408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 *aCPUCount = mHWData->mCPUCount;
1411
1412 return S_OK;
1413}
1414
1415HRESULT Machine::setCPUCount(ULONG aCPUCount)
1416{
1417 /* check CPU limits */
1418 if ( aCPUCount < SchemaDefs::MinCPUCount
1419 || aCPUCount > SchemaDefs::MaxCPUCount
1420 )
1421 return setError(E_INVALIDARG,
1422 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1423 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1424
1425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1426
1427 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1428 if (mHWData->mCPUHotPlugEnabled)
1429 {
1430 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1431 {
1432 if (mHWData->mCPUAttached[idx])
1433 return setError(E_INVALIDARG,
1434 tr("There is still a CPU attached to socket %lu."
1435 "Detach the CPU before removing the socket"),
1436 aCPUCount, idx+1);
1437 }
1438 }
1439
1440 HRESULT rc = i_checkStateDependency(MutableStateDep);
1441 if (FAILED(rc)) return rc;
1442
1443 i_setModified(IsModified_MachineData);
1444 mHWData.backup();
1445 mHWData->mCPUCount = aCPUCount;
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1451{
1452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1455
1456 return S_OK;
1457}
1458
1459HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1460{
1461 HRESULT rc = S_OK;
1462
1463 /* check throttle limits */
1464 if ( aCPUExecutionCap < 1
1465 || aCPUExecutionCap > 100
1466 )
1467 return setError(E_INVALIDARG,
1468 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1469 aCPUExecutionCap, 1, 100);
1470
1471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1472
1473 alock.release();
1474 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1475 alock.acquire();
1476 if (FAILED(rc)) return rc;
1477
1478 i_setModified(IsModified_MachineData);
1479 mHWData.backup();
1480 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1481
1482 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1483 if (Global::IsOnline(mData->mMachineState))
1484 i_saveSettings(NULL);
1485
1486 return S_OK;
1487}
1488
1489HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1490{
1491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1492
1493 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1494
1495 return S_OK;
1496}
1497
1498HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1499{
1500 HRESULT rc = S_OK;
1501
1502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1503
1504 rc = i_checkStateDependency(MutableStateDep);
1505 if (FAILED(rc)) return rc;
1506
1507 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1508 {
1509 if (aCPUHotPlugEnabled)
1510 {
1511 i_setModified(IsModified_MachineData);
1512 mHWData.backup();
1513
1514 /* Add the amount of CPUs currently attached */
1515 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1516 mHWData->mCPUAttached[i] = true;
1517 }
1518 else
1519 {
1520 /*
1521 * We can disable hotplug only if the amount of maximum CPUs is equal
1522 * to the amount of attached CPUs
1523 */
1524 unsigned cCpusAttached = 0;
1525 unsigned iHighestId = 0;
1526
1527 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1528 {
1529 if (mHWData->mCPUAttached[i])
1530 {
1531 cCpusAttached++;
1532 iHighestId = i;
1533 }
1534 }
1535
1536 if ( (cCpusAttached != mHWData->mCPUCount)
1537 || (iHighestId >= mHWData->mCPUCount))
1538 return setError(E_INVALIDARG,
1539 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1540
1541 i_setModified(IsModified_MachineData);
1542 mHWData.backup();
1543 }
1544 }
1545
1546 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1547
1548 return rc;
1549}
1550
1551HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1552{
1553#ifdef VBOX_WITH_USB_CARDREADER
1554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1557
1558 return S_OK;
1559#else
1560 NOREF(aEmulatedUSBCardReaderEnabled);
1561 return E_NOTIMPL;
1562#endif
1563}
1564
1565HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1566{
1567#ifdef VBOX_WITH_USB_CARDREADER
1568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1569
1570 HRESULT rc = i_checkStateDependency(MutableStateDep);
1571 if (FAILED(rc)) return rc;
1572
1573 i_setModified(IsModified_MachineData);
1574 mHWData.backup();
1575 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1576
1577 return S_OK;
1578#else
1579 NOREF(aEmulatedUSBCardReaderEnabled);
1580 return E_NOTIMPL;
1581#endif
1582}
1583
1584HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1585{
1586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1587
1588 *aHPETEnabled = mHWData->mHPETEnabled;
1589
1590 return S_OK;
1591}
1592
1593HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1594{
1595 HRESULT rc = S_OK;
1596
1597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1598
1599 rc = i_checkStateDependency(MutableStateDep);
1600 if (FAILED(rc)) return rc;
1601
1602 i_setModified(IsModified_MachineData);
1603 mHWData.backup();
1604
1605 mHWData->mHPETEnabled = aHPETEnabled;
1606
1607 return rc;
1608}
1609
1610HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1611{
1612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1613
1614 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1615 return S_OK;
1616}
1617
1618HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1619{
1620 HRESULT rc = S_OK;
1621
1622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 i_setModified(IsModified_MachineData);
1625 mHWData.backup();
1626 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1627
1628 alock.release();
1629 rc = i_onVideoCaptureChange();
1630 alock.acquire();
1631 if (FAILED(rc))
1632 {
1633 /*
1634 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1635 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1636 * determine if it should start or stop capturing. Therefore we need to manually
1637 * undo change.
1638 */
1639 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1640 return rc;
1641 }
1642
1643 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1644 if (Global::IsOnline(mData->mMachineState))
1645 i_saveSettings(NULL);
1646
1647 return rc;
1648}
1649
1650HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1651{
1652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1653 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1654 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1655 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1656 return S_OK;
1657}
1658
1659HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1660{
1661 SafeArray<BOOL> screens(aVideoCaptureScreens);
1662 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1663 bool fChanged = false;
1664
1665 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1666
1667 for (unsigned i = 0; i < screens.size(); ++i)
1668 {
1669 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1670 {
1671 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1672 fChanged = true;
1673 }
1674 }
1675 if (fChanged)
1676 {
1677 alock.release();
1678 HRESULT rc = i_onVideoCaptureChange();
1679 alock.acquire();
1680 if (FAILED(rc)) return rc;
1681 i_setModified(IsModified_MachineData);
1682
1683 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1684 if (Global::IsOnline(mData->mMachineState))
1685 i_saveSettings(NULL);
1686 }
1687
1688 return S_OK;
1689}
1690
1691HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1692{
1693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1694 if (mHWData->mVideoCaptureFile.isEmpty())
1695 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1696 else
1697 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1698 return S_OK;
1699}
1700
1701HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1702{
1703 Utf8Str strFile(aVideoCaptureFile);
1704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1705
1706 if ( Global::IsOnline(mData->mMachineState)
1707 && mHWData->mVideoCaptureEnabled)
1708 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1709
1710 if (!RTPathStartsWithRoot(strFile.c_str()))
1711 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1712
1713 if (!strFile.isEmpty())
1714 {
1715 Utf8Str defaultFile;
1716 i_getDefaultVideoCaptureFile(defaultFile);
1717 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1718 strFile.setNull();
1719 }
1720
1721 i_setModified(IsModified_MachineData);
1722 mHWData.backup();
1723 mHWData->mVideoCaptureFile = strFile;
1724
1725 return S_OK;
1726}
1727
1728HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1729{
1730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1731 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1732 return S_OK;
1733}
1734
1735HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1736{
1737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1738
1739 if ( Global::IsOnline(mData->mMachineState)
1740 && mHWData->mVideoCaptureEnabled)
1741 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1742
1743 i_setModified(IsModified_MachineData);
1744 mHWData.backup();
1745 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1746
1747 return S_OK;
1748}
1749
1750HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1751{
1752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1753 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1754 return S_OK;
1755}
1756
1757HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1758{
1759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1760
1761 if ( Global::IsOnline(mData->mMachineState)
1762 && mHWData->mVideoCaptureEnabled)
1763 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1764
1765 i_setModified(IsModified_MachineData);
1766 mHWData.backup();
1767 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1768
1769 return S_OK;
1770}
1771
1772HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1773{
1774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1775 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1776 return S_OK;
1777}
1778
1779HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1780{
1781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1782
1783 if ( Global::IsOnline(mData->mMachineState)
1784 && mHWData->mVideoCaptureEnabled)
1785 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1786
1787 i_setModified(IsModified_MachineData);
1788 mHWData.backup();
1789 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1790
1791 return S_OK;
1792}
1793
1794HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1795{
1796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1797 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1798 return S_OK;
1799}
1800
1801HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1802{
1803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1804
1805 if ( Global::IsOnline(mData->mMachineState)
1806 && mHWData->mVideoCaptureEnabled)
1807 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1808
1809 i_setModified(IsModified_MachineData);
1810 mHWData.backup();
1811 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1812
1813 return S_OK;
1814}
1815
1816HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1817{
1818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1819
1820 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1821
1822 return S_OK;
1823}
1824
1825HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1826{
1827 switch (aGraphicsControllerType)
1828 {
1829 case GraphicsControllerType_Null:
1830 case GraphicsControllerType_VBoxVGA:
1831#ifdef VBOX_WITH_VMSVGA
1832 case GraphicsControllerType_VMSVGA:
1833#endif
1834 break;
1835 default:
1836 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1837 }
1838
1839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1840
1841 HRESULT rc = i_checkStateDependency(MutableStateDep);
1842 if (FAILED(rc)) return rc;
1843
1844 i_setModified(IsModified_MachineData);
1845 mHWData.backup();
1846 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1847
1848 return S_OK;
1849}
1850
1851HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1852{
1853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1854
1855 *aVRAMSize = mHWData->mVRAMSize;
1856
1857 return S_OK;
1858}
1859
1860HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1861{
1862 /* check VRAM limits */
1863 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1864 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1865 return setError(E_INVALIDARG,
1866 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1867 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1868
1869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1870
1871 HRESULT rc = i_checkStateDependency(MutableStateDep);
1872 if (FAILED(rc)) return rc;
1873
1874 i_setModified(IsModified_MachineData);
1875 mHWData.backup();
1876 mHWData->mVRAMSize = aVRAMSize;
1877
1878 return S_OK;
1879}
1880
1881/** @todo this method should not be public */
1882HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1883{
1884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1885
1886 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1887
1888 return S_OK;
1889}
1890
1891/**
1892 * Set the memory balloon size.
1893 *
1894 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1895 * we have to make sure that we never call IGuest from here.
1896 */
1897HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1898{
1899 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1900#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1901 /* check limits */
1902 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1903 return setError(E_INVALIDARG,
1904 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1905 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1906
1907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1908
1909 i_setModified(IsModified_MachineData);
1910 mHWData.backup();
1911 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1912
1913 return S_OK;
1914#else
1915 NOREF(aMemoryBalloonSize);
1916 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1917#endif
1918}
1919
1920HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1921{
1922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1923
1924 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1925 return S_OK;
1926}
1927
1928HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1929{
1930#ifdef VBOX_WITH_PAGE_SHARING
1931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1932
1933 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1934 i_setModified(IsModified_MachineData);
1935 mHWData.backup();
1936 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1937 return S_OK;
1938#else
1939 NOREF(aPageFusionEnabled);
1940 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1941#endif
1942}
1943
1944HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1945{
1946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1947
1948 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1949
1950 return S_OK;
1951}
1952
1953HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1954{
1955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1956
1957 HRESULT rc = i_checkStateDependency(MutableStateDep);
1958 if (FAILED(rc)) return rc;
1959
1960 /** @todo check validity! */
1961
1962 i_setModified(IsModified_MachineData);
1963 mHWData.backup();
1964 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1965
1966 return S_OK;
1967}
1968
1969
1970HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1971{
1972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1973
1974 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1975
1976 return S_OK;
1977}
1978
1979HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1980{
1981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1982
1983 HRESULT rc = i_checkStateDependency(MutableStateDep);
1984 if (FAILED(rc)) return rc;
1985
1986 /** @todo check validity! */
1987 i_setModified(IsModified_MachineData);
1988 mHWData.backup();
1989 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1990
1991 return S_OK;
1992}
1993
1994HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1995{
1996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1997
1998 *aMonitorCount = mHWData->mMonitorCount;
1999
2000 return S_OK;
2001}
2002
2003HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2004{
2005 /* make sure monitor count is a sensible number */
2006 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2007 return setError(E_INVALIDARG,
2008 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2009 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2010
2011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2012
2013 HRESULT rc = i_checkStateDependency(MutableStateDep);
2014 if (FAILED(rc)) return rc;
2015
2016 i_setModified(IsModified_MachineData);
2017 mHWData.backup();
2018 mHWData->mMonitorCount = aMonitorCount;
2019
2020 return S_OK;
2021}
2022
2023HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2024{
2025 /* mBIOSSettings is constant during life time, no need to lock */
2026 mBIOSSettings.queryInterfaceTo(aBIOSSettings.asOutParam());
2027
2028 return S_OK;
2029}
2030
2031HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2032{
2033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2034
2035 switch (aProperty)
2036 {
2037 case CPUPropertyType_PAE:
2038 *aValue = mHWData->mPAEEnabled;
2039 break;
2040
2041 case CPUPropertyType_Synthetic:
2042 *aValue = mHWData->mSyntheticCpu;
2043 break;
2044
2045 case CPUPropertyType_LongMode:
2046 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2047 *aValue = TRUE;
2048 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2049 *aValue = FALSE;
2050#if HC_ARCH_BITS == 64
2051 else
2052 *aValue = TRUE;
2053#else
2054 else
2055 {
2056 *aValue = FALSE;
2057
2058 ComPtr<IGuestOSType> ptrGuestOSType;
2059 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2060 if (SUCCEEDED(hrc2))
2061 {
2062 BOOL fIs64Bit = FALSE;
2063 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2064 if (SUCCEEDED(hrc2) && fIs64Bit)
2065 {
2066 ComObjPtr<Host> ptrHost = mParent->i_host();
2067 alock.release();
2068
2069 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2070 if (FAILED(hrc2))
2071 *aValue = FALSE;
2072 }
2073 }
2074 }
2075#endif
2076 break;
2077
2078 case CPUPropertyType_TripleFaultReset:
2079 *aValue = mHWData->mTripleFaultReset;
2080 break;
2081
2082 default:
2083 return E_INVALIDARG;
2084 }
2085 return S_OK;
2086}
2087
2088HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2089{
2090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2091
2092 HRESULT rc = i_checkStateDependency(MutableStateDep);
2093 if (FAILED(rc)) return rc;
2094
2095 switch (aProperty)
2096 {
2097 case CPUPropertyType_PAE:
2098 i_setModified(IsModified_MachineData);
2099 mHWData.backup();
2100 mHWData->mPAEEnabled = !!aValue;
2101 break;
2102
2103 case CPUPropertyType_Synthetic:
2104 i_setModified(IsModified_MachineData);
2105 mHWData.backup();
2106 mHWData->mSyntheticCpu = !!aValue;
2107 break;
2108
2109 case CPUPropertyType_LongMode:
2110 i_setModified(IsModified_MachineData);
2111 mHWData.backup();
2112 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2113 break;
2114
2115 case CPUPropertyType_TripleFaultReset:
2116 i_setModified(IsModified_MachineData);
2117 mHWData.backup();
2118 mHWData->mTripleFaultReset = !!aValue;
2119 break;
2120
2121 default:
2122 return E_INVALIDARG;
2123 }
2124 return S_OK;
2125}
2126
2127HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2128{
2129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2130
2131 switch(aId)
2132 {
2133 case 0x0:
2134 case 0x1:
2135 case 0x2:
2136 case 0x3:
2137 case 0x4:
2138 case 0x5:
2139 case 0x6:
2140 case 0x7:
2141 case 0x8:
2142 case 0x9:
2143 case 0xA:
2144 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2145 return E_INVALIDARG;
2146
2147 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2148 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2149 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2150 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2151 break;
2152
2153 case 0x80000000:
2154 case 0x80000001:
2155 case 0x80000002:
2156 case 0x80000003:
2157 case 0x80000004:
2158 case 0x80000005:
2159 case 0x80000006:
2160 case 0x80000007:
2161 case 0x80000008:
2162 case 0x80000009:
2163 case 0x8000000A:
2164 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2165 return E_INVALIDARG;
2166
2167 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2168 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2169 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2170 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2171 break;
2172
2173 default:
2174 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2175 }
2176 return S_OK;
2177}
2178
2179
2180HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2181{
2182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2183
2184 HRESULT rc = i_checkStateDependency(MutableStateDep);
2185 if (FAILED(rc)) return rc;
2186
2187 switch(aId)
2188 {
2189 case 0x0:
2190 case 0x1:
2191 case 0x2:
2192 case 0x3:
2193 case 0x4:
2194 case 0x5:
2195 case 0x6:
2196 case 0x7:
2197 case 0x8:
2198 case 0x9:
2199 case 0xA:
2200 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2201 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2202 i_setModified(IsModified_MachineData);
2203 mHWData.backup();
2204 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2205 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2206 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2207 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2208 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2209 break;
2210
2211 case 0x80000000:
2212 case 0x80000001:
2213 case 0x80000002:
2214 case 0x80000003:
2215 case 0x80000004:
2216 case 0x80000005:
2217 case 0x80000006:
2218 case 0x80000007:
2219 case 0x80000008:
2220 case 0x80000009:
2221 case 0x8000000A:
2222 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2223 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2224 i_setModified(IsModified_MachineData);
2225 mHWData.backup();
2226 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2227 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2228 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2229 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2230 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2231 break;
2232
2233 default:
2234 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2235 }
2236 return S_OK;
2237}
2238
2239HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2240{
2241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2242
2243 HRESULT rc = i_checkStateDependency(MutableStateDep);
2244 if (FAILED(rc)) return rc;
2245
2246 switch(aId)
2247 {
2248 case 0x0:
2249 case 0x1:
2250 case 0x2:
2251 case 0x3:
2252 case 0x4:
2253 case 0x5:
2254 case 0x6:
2255 case 0x7:
2256 case 0x8:
2257 case 0x9:
2258 case 0xA:
2259 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2260 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2261 i_setModified(IsModified_MachineData);
2262 mHWData.backup();
2263 /* Invalidate leaf. */
2264 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2265 break;
2266
2267 case 0x80000000:
2268 case 0x80000001:
2269 case 0x80000002:
2270 case 0x80000003:
2271 case 0x80000004:
2272 case 0x80000005:
2273 case 0x80000006:
2274 case 0x80000007:
2275 case 0x80000008:
2276 case 0x80000009:
2277 case 0x8000000A:
2278 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2279 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2280 i_setModified(IsModified_MachineData);
2281 mHWData.backup();
2282 /* Invalidate leaf. */
2283 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2284 break;
2285
2286 default:
2287 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2288 }
2289 return S_OK;
2290}
2291
2292HRESULT Machine::removeAllCPUIDLeaves()
2293{
2294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2295
2296 HRESULT rc = i_checkStateDependency(MutableStateDep);
2297 if (FAILED(rc)) return rc;
2298
2299 i_setModified(IsModified_MachineData);
2300 mHWData.backup();
2301
2302 /* Invalidate all standard leafs. */
2303 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2304 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2305
2306 /* Invalidate all extended leafs. */
2307 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2308 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2309
2310 return S_OK;
2311}
2312HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2313{
2314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2315
2316 switch(aProperty)
2317 {
2318 case HWVirtExPropertyType_Enabled:
2319 *aValue = mHWData->mHWVirtExEnabled;
2320 break;
2321
2322 case HWVirtExPropertyType_VPID:
2323 *aValue = mHWData->mHWVirtExVPIDEnabled;
2324 break;
2325
2326 case HWVirtExPropertyType_NestedPaging:
2327 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2328 break;
2329
2330 case HWVirtExPropertyType_UnrestrictedExecution:
2331 *aValue = mHWData->mHWVirtExUXEnabled;
2332 break;
2333
2334 case HWVirtExPropertyType_LargePages:
2335 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2336#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2337 *aValue = FALSE;
2338#endif
2339 break;
2340
2341 case HWVirtExPropertyType_Force:
2342 *aValue = mHWData->mHWVirtExForceEnabled;
2343 break;
2344
2345 default:
2346 return E_INVALIDARG;
2347 }
2348 return S_OK;
2349}
2350
2351HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2352{
2353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2354
2355 HRESULT rc = i_checkStateDependency(MutableStateDep);
2356 if (FAILED(rc)) return rc;
2357
2358 switch(aProperty)
2359 {
2360 case HWVirtExPropertyType_Enabled:
2361 i_setModified(IsModified_MachineData);
2362 mHWData.backup();
2363 mHWData->mHWVirtExEnabled = !!aValue;
2364 break;
2365
2366 case HWVirtExPropertyType_VPID:
2367 i_setModified(IsModified_MachineData);
2368 mHWData.backup();
2369 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2370 break;
2371
2372 case HWVirtExPropertyType_NestedPaging:
2373 i_setModified(IsModified_MachineData);
2374 mHWData.backup();
2375 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2376 break;
2377
2378 case HWVirtExPropertyType_UnrestrictedExecution:
2379 i_setModified(IsModified_MachineData);
2380 mHWData.backup();
2381 mHWData->mHWVirtExUXEnabled = !!aValue;
2382 break;
2383
2384 case HWVirtExPropertyType_LargePages:
2385 i_setModified(IsModified_MachineData);
2386 mHWData.backup();
2387 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2388 break;
2389
2390 case HWVirtExPropertyType_Force:
2391 i_setModified(IsModified_MachineData);
2392 mHWData.backup();
2393 mHWData->mHWVirtExForceEnabled = !!aValue;
2394 break;
2395
2396 default:
2397 return E_INVALIDARG;
2398 }
2399
2400 return S_OK;
2401}
2402
2403HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2404{
2405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2406
2407 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2408
2409 return S_OK;
2410}
2411
2412HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2413{
2414 /* @todo (r=dmik):
2415 * 1. Allow to change the name of the snapshot folder containing snapshots
2416 * 2. Rename the folder on disk instead of just changing the property
2417 * value (to be smart and not to leave garbage). Note that it cannot be
2418 * done here because the change may be rolled back. Thus, the right
2419 * place is #saveSettings().
2420 */
2421
2422 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2423
2424 HRESULT rc = i_checkStateDependency(MutableStateDep);
2425 if (FAILED(rc)) return rc;
2426
2427 if (!mData->mCurrentSnapshot.isNull())
2428 return setError(E_FAIL,
2429 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2430
2431 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2432
2433 if (strSnapshotFolder.isEmpty())
2434 strSnapshotFolder = "Snapshots";
2435 int vrc = i_calculateFullPath(strSnapshotFolder,
2436 strSnapshotFolder);
2437 if (RT_FAILURE(vrc))
2438 return setError(E_FAIL,
2439 tr("Invalid snapshot folder '%s' (%Rrc)"),
2440 strSnapshotFolder.c_str(), vrc);
2441
2442 i_setModified(IsModified_MachineData);
2443 mUserData.backup();
2444
2445 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2446
2447 return S_OK;
2448}
2449
2450HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2451{
2452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2453
2454 aMediumAttachments.resize(mMediaData->mAttachments.size());
2455 size_t i = 0;
2456 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2457 it != mMediaData->mAttachments.end(); ++it, ++i)
2458 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
2459
2460 return S_OK;
2461}
2462
2463HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2464{
2465 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2466
2467 Assert(!!mVRDEServer);
2468
2469 mVRDEServer.queryInterfaceTo(aVRDEServer.asOutParam());
2470
2471 return S_OK;
2472}
2473
2474HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2475{
2476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2477
2478 mAudioAdapter.queryInterfaceTo(aAudioAdapter.asOutParam());
2479
2480 return S_OK;
2481}
2482
2483HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2484{
2485#ifdef VBOX_WITH_VUSB
2486 clearError();
2487 MultiResult rc(S_OK);
2488
2489# ifdef VBOX_WITH_USB
2490 rc = mParent->i_host()->i_checkUSBProxyService();
2491 if (FAILED(rc)) return rc;
2492# endif
2493
2494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2495
2496 USBControllerList data = *mUSBControllers.data();
2497 aUSBControllers.resize(data.size());
2498 size_t i = 0;
2499 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2500 (*it).queryInterfaceTo(aUSBControllers[i].asOutParam());
2501
2502 return S_OK;
2503#else
2504 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2505 * extended error info to indicate that USB is simply not available
2506 * (w/o treating it as a failure), for example, as in OSE */
2507 NOREF(aUSBControllers);
2508 ReturnComNotImplemented();
2509#endif /* VBOX_WITH_VUSB */
2510}
2511
2512HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2513{
2514#ifdef VBOX_WITH_VUSB
2515 clearError();
2516 MultiResult rc(S_OK);
2517
2518# ifdef VBOX_WITH_USB
2519 rc = mParent->i_host()->i_checkUSBProxyService();
2520 if (FAILED(rc)) return rc;
2521# endif
2522
2523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2524
2525 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters.asOutParam());
2526#else
2527 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2528 * extended error info to indicate that USB is simply not available
2529 * (w/o treating it as a failure), for example, as in OSE */
2530 NOREF(aUSBDeviceFilters);
2531 ReturnComNotImplemented();
2532#endif /* VBOX_WITH_VUSB */
2533}
2534
2535HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2536{
2537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2538
2539 aSettingsFilePath = mData->m_strConfigFileFull;
2540
2541 return S_OK;
2542}
2543
2544HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2545{
2546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2547
2548 HRESULT rc = i_checkStateDependency(MutableStateDep);
2549 if (FAILED(rc)) return rc;
2550
2551 if (!mData->pMachineConfigFile->fileExists())
2552 // this is a new machine, and no config file exists yet:
2553 *aSettingsModified = TRUE;
2554 else
2555 *aSettingsModified = (mData->flModifications != 0);
2556
2557 return S_OK;
2558}
2559
2560HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2561{
2562
2563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2564
2565 *aSessionState = mData->mSession.mState;
2566
2567 return S_OK;
2568}
2569
2570HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2571{
2572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2573
2574 aSessionType = mData->mSession.mType;
2575
2576 return S_OK;
2577}
2578
2579HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2580{
2581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2582
2583 *aSessionPID = mData->mSession.mPID;
2584
2585 return S_OK;
2586}
2587
2588HRESULT Machine::getState(MachineState_T *aState)
2589{
2590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2591
2592 *aState = mData->mMachineState;
2593
2594 return S_OK;
2595}
2596
2597HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2598{
2599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2600
2601 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2602
2603 return S_OK;
2604}
2605
2606HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2607{
2608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2609
2610 aStateFilePath = mSSData->strStateFilePath;
2611
2612 return S_OK;
2613}
2614
2615HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2616{
2617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2618
2619 i_getLogFolder(aLogFolder);
2620
2621 return S_OK;
2622}
2623
2624HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2625{
2626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2627
2628 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot.asOutParam());
2629
2630 return S_OK;
2631}
2632
2633HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2634{
2635 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2636
2637 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2638 ? 0
2639 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2640
2641 return S_OK;
2642}
2643
2644HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2645{
2646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2647
2648 /* Note: for machines with no snapshots, we always return FALSE
2649 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2650 * reasons :) */
2651
2652 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2653 ? FALSE
2654 : mData->mCurrentStateModified;
2655
2656 return S_OK;
2657}
2658
2659HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2660{
2661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2662
2663 aSharedFolders.resize(mHWData->mSharedFolders.size());
2664 size_t i = 0;
2665 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2666 it != mHWData->mSharedFolders.end(); ++i, ++it)
2667 (*it).queryInterfaceTo(aSharedFolders[i].asOutParam());
2668
2669 return S_OK;
2670}
2671
2672HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2673{
2674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2675
2676 *aClipboardMode = mHWData->mClipboardMode;
2677
2678 return S_OK;
2679}
2680
2681HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2682{
2683 HRESULT rc = S_OK;
2684
2685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2686
2687 alock.release();
2688 rc = i_onClipboardModeChange(aClipboardMode);
2689 alock.acquire();
2690 if (FAILED(rc)) return rc;
2691
2692 i_setModified(IsModified_MachineData);
2693 mHWData.backup();
2694 mHWData->mClipboardMode = aClipboardMode;
2695
2696 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2697 if (Global::IsOnline(mData->mMachineState))
2698 i_saveSettings(NULL);
2699
2700 return S_OK;
2701}
2702
2703HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2704{
2705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2706
2707 *aDnDMode = mHWData->mDnDMode;
2708
2709 return S_OK;
2710}
2711
2712HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2713{
2714 HRESULT rc = S_OK;
2715
2716 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2717
2718 alock.release();
2719 rc = i_onDnDModeChange(aDnDMode);
2720
2721 alock.acquire();
2722 if (FAILED(rc)) return rc;
2723
2724 i_setModified(IsModified_MachineData);
2725 mHWData.backup();
2726 mHWData->mDnDMode = aDnDMode;
2727
2728 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2729 if (Global::IsOnline(mData->mMachineState))
2730 i_saveSettings(NULL);
2731
2732 return S_OK;
2733}
2734
2735HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2736{
2737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2738
2739 try
2740 {
2741 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2742 }
2743 catch (...)
2744 {
2745 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2746 }
2747
2748 return S_OK;
2749}
2750
2751HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2752{
2753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2754
2755 HRESULT rc = i_checkStateDependency(MutableStateDep);
2756 if (FAILED(rc)) return rc;
2757
2758 i_setModified(IsModified_MachineData);
2759 mHWData.backup();
2760 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2761 return rc;
2762}
2763
2764HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2765{
2766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2767 StorageControllerList data = *mStorageControllers.data();
2768 size_t i = 0;
2769 aStorageControllers.resize(data.size());
2770 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2771 (*it).queryInterfaceTo(aStorageControllers[i].asOutParam());
2772 return S_OK;
2773}
2774
2775HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2776{
2777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2778
2779 *aEnabled = mUserData->s.fTeleporterEnabled;
2780
2781 return S_OK;
2782}
2783
2784HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2785{
2786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2787
2788 /* Only allow it to be set to true when PoweredOff or Aborted.
2789 (Clearing it is always permitted.) */
2790 if ( aTeleporterEnabled
2791 && mData->mRegistered
2792 && ( !i_isSessionMachine()
2793 || ( mData->mMachineState != MachineState_PoweredOff
2794 && mData->mMachineState != MachineState_Teleported
2795 && mData->mMachineState != MachineState_Aborted
2796 )
2797 )
2798 )
2799 return setError(VBOX_E_INVALID_VM_STATE,
2800 tr("The machine is not powered off (state is %s)"),
2801 Global::stringifyMachineState(mData->mMachineState));
2802
2803 i_setModified(IsModified_MachineData);
2804 mUserData.backup();
2805 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2806
2807 return S_OK;
2808}
2809
2810HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2811{
2812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2813
2814 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2815
2816 return S_OK;
2817}
2818
2819HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2820{
2821 if (aTeleporterPort >= _64K)
2822 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2823
2824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2825
2826 HRESULT rc = i_checkStateDependency(MutableStateDep);
2827 if (FAILED(rc)) return rc;
2828
2829 i_setModified(IsModified_MachineData);
2830 mUserData.backup();
2831 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2832
2833 return S_OK;
2834}
2835
2836HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2837{
2838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2839
2840 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2841
2842 return S_OK;
2843}
2844
2845HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2846{
2847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2848
2849 HRESULT rc = i_checkStateDependency(MutableStateDep);
2850 if (FAILED(rc)) return rc;
2851
2852 i_setModified(IsModified_MachineData);
2853 mUserData.backup();
2854 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2855
2856 return S_OK;
2857}
2858
2859HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2860{
2861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2862 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2863
2864 return S_OK;
2865}
2866
2867HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2868{
2869 /*
2870 * Hash the password first.
2871 */
2872 com::Utf8Str aT = aTeleporterPassword;
2873
2874 if (!aT.isEmpty())
2875 {
2876 if (VBoxIsPasswordHashed(&aT))
2877 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2878 VBoxHashPassword(&aT);
2879 }
2880
2881 /*
2882 * Do the update.
2883 */
2884 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2885 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2886 if (SUCCEEDED(hrc))
2887 {
2888 i_setModified(IsModified_MachineData);
2889 mUserData.backup();
2890 mUserData->s.strTeleporterPassword = aT;
2891 }
2892
2893 return hrc;
2894}
2895
2896HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2897{
2898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2899
2900 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2901 return S_OK;
2902}
2903
2904HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2905{
2906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 /* @todo deal with running state change. */
2909 HRESULT rc = i_checkStateDependency(MutableStateDep);
2910 if (FAILED(rc)) return rc;
2911
2912 i_setModified(IsModified_MachineData);
2913 mUserData.backup();
2914 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2915 return S_OK;
2916}
2917
2918HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2919{
2920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2921
2922 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2923 return S_OK;
2924}
2925
2926HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2927{
2928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2929
2930 /* @todo deal with running state change. */
2931 HRESULT rc = i_checkStateDependency(MutableStateDep);
2932 if (FAILED(rc)) return rc;
2933
2934 i_setModified(IsModified_MachineData);
2935 mUserData.backup();
2936 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
2937 return S_OK;
2938}
2939
2940HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
2941{
2942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2943
2944 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
2945 return S_OK;
2946}
2947
2948HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
2949{
2950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 /* @todo deal with running state change. */
2953 HRESULT rc = i_checkStateDependency(MutableStateDep);
2954 if (FAILED(rc)) return rc;
2955
2956 i_setModified(IsModified_MachineData);
2957 mUserData.backup();
2958 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
2959 return S_OK;
2960}
2961
2962HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
2963{
2964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2965
2966 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
2967
2968 return S_OK;
2969}
2970
2971HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
2972{
2973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 /* @todo deal with running state change. */
2976 HRESULT rc = i_checkStateDependency(MutableStateDep);
2977 if (FAILED(rc)) return rc;
2978
2979 i_setModified(IsModified_MachineData);
2980 mUserData.backup();
2981 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
2982
2983 return S_OK;
2984}
2985
2986HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
2987{
2988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2989
2990 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
2991 return S_OK;
2992}
2993
2994HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
2995{
2996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2997
2998 /* @todo deal with running state change. */
2999 HRESULT rc = i_checkStateDependency(MutableStateDep);
3000 if (FAILED(rc)) return rc;
3001
3002 i_setModified(IsModified_MachineData);
3003 mUserData.backup();
3004 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3005 return S_OK;
3006}
3007
3008HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3009{
3010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3011
3012 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3013
3014 return S_OK;
3015}
3016
3017HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3018{
3019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3020
3021 /* Only allow it to be set to true when PoweredOff or Aborted.
3022 (Clearing it is always permitted.) */
3023 if ( aRTCUseUTC
3024 && mData->mRegistered
3025 && ( !i_isSessionMachine()
3026 || ( mData->mMachineState != MachineState_PoweredOff
3027 && mData->mMachineState != MachineState_Teleported
3028 && mData->mMachineState != MachineState_Aborted
3029 )
3030 )
3031 )
3032 return setError(VBOX_E_INVALID_VM_STATE,
3033 tr("The machine is not powered off (state is %s)"),
3034 Global::stringifyMachineState(mData->mMachineState));
3035
3036 i_setModified(IsModified_MachineData);
3037 mUserData.backup();
3038 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3039
3040 return S_OK;
3041}
3042
3043HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3044{
3045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3046
3047 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3048
3049 return S_OK;
3050}
3051
3052HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3053{
3054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3055
3056 HRESULT rc = i_checkStateDependency(MutableStateDep);
3057 if (FAILED(rc)) return rc;
3058
3059 i_setModified(IsModified_MachineData);
3060 mHWData.backup();
3061 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3062
3063 return S_OK;
3064}
3065
3066HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3067{
3068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3069
3070 *aIOCacheSize = mHWData->mIOCacheSize;
3071
3072 return S_OK;
3073}
3074
3075HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3076{
3077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3078
3079 HRESULT rc = i_checkStateDependency(MutableStateDep);
3080 if (FAILED(rc)) return rc;
3081
3082 i_setModified(IsModified_MachineData);
3083 mHWData.backup();
3084 mHWData->mIOCacheSize = aIOCacheSize;
3085
3086 return S_OK;
3087}
3088
3089
3090/**
3091 * @note Locks objects!
3092 */
3093HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3094 LockType_T aLockType)
3095
3096{
3097 /* check the session state */
3098 SessionState_T state;
3099 HRESULT rc = aSession->COMGETTER(State)(&state);
3100 if (FAILED(rc)) return rc;
3101
3102 if (state != SessionState_Unlocked)
3103 return setError(VBOX_E_INVALID_OBJECT_STATE,
3104 tr("The given session is busy"));
3105
3106 // get the client's IInternalSessionControl interface
3107 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3108 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3109 E_INVALIDARG);
3110
3111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3112
3113 if (!mData->mRegistered)
3114 return setError(E_UNEXPECTED,
3115 tr("The machine '%s' is not registered"),
3116 mUserData->s.strName.c_str());
3117
3118 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3119
3120 SessionState_T oldState = mData->mSession.mState;
3121 /* Hack: in case the session is closing and there is a progress object
3122 * which allows waiting for the session to be closed, take the opportunity
3123 * and do a limited wait (max. 1 second). This helps a lot when the system
3124 * is busy and thus session closing can take a little while. */
3125 if ( mData->mSession.mState == SessionState_Unlocking
3126 && mData->mSession.mProgress)
3127 {
3128 alock.release();
3129 mData->mSession.mProgress->WaitForCompletion(1000);
3130 alock.acquire();
3131 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3132 }
3133
3134 // try again now
3135 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3136 // (i.e. session machine exists)
3137 && (aLockType == LockType_Shared) // caller wants a shared link to the
3138 // existing session that holds the write lock:
3139 )
3140 {
3141 // OK, share the session... we are now dealing with three processes:
3142 // 1) VBoxSVC (where this code runs);
3143 // 2) process C: the caller's client process (who wants a shared session);
3144 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3145
3146 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3147 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3148 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3149 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3150 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3151
3152 /*
3153 * Release the lock before calling the client process. It's safe here
3154 * since the only thing to do after we get the lock again is to add
3155 * the remote control to the list (which doesn't directly influence
3156 * anything).
3157 */
3158 alock.release();
3159
3160 // get the console of the session holding the write lock (this is a remote call)
3161 ComPtr<IConsole> pConsoleW;
3162 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3163 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3164 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3165 if (FAILED(rc))
3166 // the failure may occur w/o any error info (from RPC), so provide one
3167 return setError(VBOX_E_VM_ERROR,
3168 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3169
3170 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3171
3172 // share the session machine and W's console with the caller's session
3173 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3174 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3175 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3176
3177 if (FAILED(rc))
3178 // the failure may occur w/o any error info (from RPC), so provide one
3179 return setError(VBOX_E_VM_ERROR,
3180 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3181 alock.acquire();
3182
3183 // need to revalidate the state after acquiring the lock again
3184 if (mData->mSession.mState != SessionState_Locked)
3185 {
3186 pSessionControl->Uninitialize();
3187 return setError(VBOX_E_INVALID_SESSION_STATE,
3188 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3189 mUserData->s.strName.c_str());
3190 }
3191
3192 // add the caller's session to the list
3193 mData->mSession.mRemoteControls.push_back(pSessionControl);
3194 }
3195 else if ( mData->mSession.mState == SessionState_Locked
3196 || mData->mSession.mState == SessionState_Unlocking
3197 )
3198 {
3199 // sharing not permitted, or machine still unlocking:
3200 return setError(VBOX_E_INVALID_OBJECT_STATE,
3201 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3202 mUserData->s.strName.c_str());
3203 }
3204 else
3205 {
3206 // machine is not locked: then write-lock the machine (create the session machine)
3207
3208 // must not be busy
3209 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3210
3211 // get the caller's session PID
3212 RTPROCESS pid = NIL_RTPROCESS;
3213 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3214 pSessionControl->GetPID((ULONG*)&pid);
3215 Assert(pid != NIL_RTPROCESS);
3216
3217 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3218
3219 if (fLaunchingVMProcess)
3220 {
3221 if (mData->mSession.mPID == NIL_RTPROCESS)
3222 {
3223 // two or more clients racing for a lock, the one which set the
3224 // session state to Spawning will win, the others will get an
3225 // error as we can't decide here if waiting a little would help
3226 // (only for shared locks this would avoid an error)
3227 return setError(VBOX_E_INVALID_OBJECT_STATE,
3228 tr("The machine '%s' already has a lock request pending"),
3229 mUserData->s.strName.c_str());
3230 }
3231
3232 // this machine is awaiting for a spawning session to be opened:
3233 // then the calling process must be the one that got started by
3234 // LaunchVMProcess()
3235
3236 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3237 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3238
3239#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3240 /* Hardened windows builds have spawns two processes when a VM is
3241 launched, the 2nd one is the one that will end up here. */
3242 RTPROCESS ppid;
3243 int rc = RTProcQueryParent(pid, &ppid);
3244 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3245 || rc == VERR_ACCESS_DENIED)
3246 {
3247 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3248 mData->mSession.mPID = pid;
3249 }
3250#endif
3251
3252 if (mData->mSession.mPID != pid)
3253 return setError(E_ACCESSDENIED,
3254 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3255 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3256 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3257 }
3258
3259 // create the mutable SessionMachine from the current machine
3260 ComObjPtr<SessionMachine> sessionMachine;
3261 sessionMachine.createObject();
3262 rc = sessionMachine->init(this);
3263 AssertComRC(rc);
3264
3265 /* NOTE: doing return from this function after this point but
3266 * before the end is forbidden since it may call SessionMachine::uninit()
3267 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3268 * lock while still holding the Machine lock in alock so that a deadlock
3269 * is possible due to the wrong lock order. */
3270
3271 if (SUCCEEDED(rc))
3272 {
3273 /*
3274 * Set the session state to Spawning to protect against subsequent
3275 * attempts to open a session and to unregister the machine after
3276 * we release the lock.
3277 */
3278 SessionState_T origState = mData->mSession.mState;
3279 mData->mSession.mState = SessionState_Spawning;
3280
3281#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3282 /* Get the client token ID to be passed to the client process */
3283 Utf8Str strTokenId;
3284 sessionMachine->i_getTokenId(strTokenId);
3285 Assert(!strTokenId.isEmpty());
3286#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3287 /* Get the client token to be passed to the client process */
3288 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3289 /* The token is now "owned" by pToken, fix refcount */
3290 if (!pToken.isNull())
3291 pToken->Release();
3292#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3293
3294 /*
3295 * Release the lock before calling the client process -- it will call
3296 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3297 * because the state is Spawning, so that LaunchVMProcess() and
3298 * LockMachine() calls will fail. This method, called before we
3299 * acquire the lock again, will fail because of the wrong PID.
3300 *
3301 * Note that mData->mSession.mRemoteControls accessed outside
3302 * the lock may not be modified when state is Spawning, so it's safe.
3303 */
3304 alock.release();
3305
3306 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3307#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3308 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3309#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3310 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3311 /* Now the token is owned by the client process. */
3312 pToken.setNull();
3313#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3314 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3315
3316 /* The failure may occur w/o any error info (from RPC), so provide one */
3317 if (FAILED(rc))
3318 setError(VBOX_E_VM_ERROR,
3319 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3320
3321 if ( SUCCEEDED(rc)
3322 && fLaunchingVMProcess
3323 )
3324 {
3325 /* complete the remote session initialization */
3326
3327 /* get the console from the direct session */
3328 ComPtr<IConsole> console;
3329 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3330 ComAssertComRC(rc);
3331
3332 if (SUCCEEDED(rc) && !console)
3333 {
3334 ComAssert(!!console);
3335 rc = E_FAIL;
3336 }
3337
3338 /* assign machine & console to the remote session */
3339 if (SUCCEEDED(rc))
3340 {
3341 /*
3342 * after LaunchVMProcess(), the first and the only
3343 * entry in remoteControls is that remote session
3344 */
3345 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3346 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3347 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3348
3349 /* The failure may occur w/o any error info (from RPC), so provide one */
3350 if (FAILED(rc))
3351 setError(VBOX_E_VM_ERROR,
3352 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3353 }
3354
3355 if (FAILED(rc))
3356 pSessionControl->Uninitialize();
3357 }
3358
3359 /* acquire the lock again */
3360 alock.acquire();
3361
3362 /* Restore the session state */
3363 mData->mSession.mState = origState;
3364 }
3365
3366 // finalize spawning anyway (this is why we don't return on errors above)
3367 if (fLaunchingVMProcess)
3368 {
3369 /* Note that the progress object is finalized later */
3370 /** @todo Consider checking mData->mSession.mProgress for cancellation
3371 * around here. */
3372
3373 /* We don't reset mSession.mPID here because it is necessary for
3374 * SessionMachine::uninit() to reap the child process later. */
3375
3376 if (FAILED(rc))
3377 {
3378 /* Close the remote session, remove the remote control from the list
3379 * and reset session state to Closed (@note keep the code in sync
3380 * with the relevant part in checkForSpawnFailure()). */
3381
3382 Assert(mData->mSession.mRemoteControls.size() == 1);
3383 if (mData->mSession.mRemoteControls.size() == 1)
3384 {
3385 ErrorInfoKeeper eik;
3386 mData->mSession.mRemoteControls.front()->Uninitialize();
3387 }
3388
3389 mData->mSession.mRemoteControls.clear();
3390 mData->mSession.mState = SessionState_Unlocked;
3391 }
3392 }
3393 else
3394 {
3395 /* memorize PID of the directly opened session */
3396 if (SUCCEEDED(rc))
3397 mData->mSession.mPID = pid;
3398 }
3399
3400 if (SUCCEEDED(rc))
3401 {
3402 /* memorize the direct session control and cache IUnknown for it */
3403 mData->mSession.mDirectControl = pSessionControl;
3404 mData->mSession.mState = SessionState_Locked;
3405 /* associate the SessionMachine with this Machine */
3406 mData->mSession.mMachine = sessionMachine;
3407
3408 /* request an IUnknown pointer early from the remote party for later
3409 * identity checks (it will be internally cached within mDirectControl
3410 * at least on XPCOM) */
3411 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3412 NOREF(unk);
3413 }
3414
3415 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3416 * would break the lock order */
3417 alock.release();
3418
3419 /* uninitialize the created session machine on failure */
3420 if (FAILED(rc))
3421 sessionMachine->uninit();
3422
3423 }
3424
3425 if (SUCCEEDED(rc))
3426 {
3427 /*
3428 * tell the client watcher thread to update the set of
3429 * machines that have open sessions
3430 */
3431 mParent->i_updateClientWatcher();
3432
3433 if (oldState != SessionState_Locked)
3434 /* fire an event */
3435 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3436 }
3437
3438 return rc;
3439}
3440
3441/**
3442 * @note Locks objects!
3443 */
3444HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3445 const com::Utf8Str &aType,
3446 const com::Utf8Str &aEnvironment,
3447 ComPtr<IProgress> &aProgress)
3448{
3449 Utf8Str strFrontend(aType);
3450 Utf8Str strEnvironment(aEnvironment);
3451 /* "emergencystop" doesn't need the session, so skip the checks/interface
3452 * retrieval. This code doesn't quite fit in here, but introducing a
3453 * special API method would be even more effort, and would require explicit
3454 * support by every API client. It's better to hide the feature a bit. */
3455 if (strFrontend != "emergencystop")
3456 CheckComArgNotNull(aSession);
3457
3458 HRESULT rc = S_OK;
3459 if (strFrontend.isEmpty())
3460 {
3461 Bstr bstrFrontend;
3462 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3463 if (FAILED(rc))
3464 return rc;
3465 strFrontend = bstrFrontend;
3466 if (strFrontend.isEmpty())
3467 {
3468 ComPtr<ISystemProperties> systemProperties;
3469 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3470 if (FAILED(rc))
3471 return rc;
3472 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3473 if (FAILED(rc))
3474 return rc;
3475 strFrontend = bstrFrontend;
3476 }
3477 /* paranoia - emergencystop is not a valid default */
3478 if (strFrontend == "emergencystop")
3479 strFrontend = Utf8Str::Empty;
3480 }
3481 /* default frontend: Qt GUI */
3482 if (strFrontend.isEmpty())
3483 strFrontend = "GUI/Qt";
3484
3485 if (strFrontend != "emergencystop")
3486 {
3487 /* check the session state */
3488 SessionState_T state;
3489 rc = aSession->COMGETTER(State)(&state);
3490 if (FAILED(rc))
3491 return rc;
3492
3493 if (state != SessionState_Unlocked)
3494 return setError(VBOX_E_INVALID_OBJECT_STATE,
3495 tr("The given session is busy"));
3496
3497 /* get the IInternalSessionControl interface */
3498 ComPtr<IInternalSessionControl> control(aSession);
3499 ComAssertMsgRet(!control.isNull(),
3500 ("No IInternalSessionControl interface"),
3501 E_INVALIDARG);
3502
3503 /* get the teleporter enable state for the progress object init. */
3504 BOOL fTeleporterEnabled;
3505 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3506 if (FAILED(rc))
3507 return rc;
3508
3509 /* create a progress object */
3510 ComObjPtr<ProgressProxy> progress;
3511 progress.createObject();
3512 rc = progress->init(mParent,
3513 static_cast<IMachine*>(this),
3514 Bstr(tr("Starting VM")).raw(),
3515 TRUE /* aCancelable */,
3516 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3517 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3518 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3519 2 /* uFirstOperationWeight */,
3520 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3521
3522 if (SUCCEEDED(rc))
3523 {
3524 rc = i_launchVMProcess(control, strFrontend, strEnvironment, progress);
3525 if (SUCCEEDED(rc))
3526 {
3527 progress.queryInterfaceTo(aProgress.asOutParam());
3528
3529 /* signal the client watcher thread */
3530 mParent->i_updateClientWatcher();
3531
3532 /* fire an event */
3533 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3534 }
3535 }
3536 }
3537 else
3538 {
3539 /* no progress object - either instant success or failure */
3540 aProgress = NULL;
3541
3542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3543
3544 if (mData->mSession.mState != SessionState_Locked)
3545 return setError(VBOX_E_INVALID_OBJECT_STATE,
3546 tr("The machine '%s' is not locked by a session"),
3547 mUserData->s.strName.c_str());
3548
3549 /* must have a VM process associated - do not kill normal API clients
3550 * with an open session */
3551 if (!Global::IsOnline(mData->mMachineState))
3552 return setError(VBOX_E_INVALID_OBJECT_STATE,
3553 tr("The machine '%s' does not have a VM process"),
3554 mUserData->s.strName.c_str());
3555
3556 /* forcibly terminate the VM process */
3557 if (mData->mSession.mPID != NIL_RTPROCESS)
3558 RTProcTerminate(mData->mSession.mPID);
3559
3560 /* signal the client watcher thread, as most likely the client has
3561 * been terminated */
3562 mParent->i_updateClientWatcher();
3563 }
3564
3565 return rc;
3566}
3567
3568HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3569{
3570 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3571 return setError(E_INVALIDARG,
3572 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3573 aPosition, SchemaDefs::MaxBootPosition);
3574
3575 if (aDevice == DeviceType_USB)
3576 return setError(E_NOTIMPL,
3577 tr("Booting from USB device is currently not supported"));
3578
3579 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3580
3581 HRESULT rc = i_checkStateDependency(MutableStateDep);
3582 if (FAILED(rc)) return rc;
3583
3584 i_setModified(IsModified_MachineData);
3585 mHWData.backup();
3586 mHWData->mBootOrder[aPosition - 1] = aDevice;
3587
3588 return S_OK;
3589}
3590
3591HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3592{
3593 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3594 return setError(E_INVALIDARG,
3595 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3596 aPosition, SchemaDefs::MaxBootPosition);
3597
3598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3599
3600 *aDevice = mHWData->mBootOrder[aPosition - 1];
3601
3602 return S_OK;
3603}
3604
3605HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3606 LONG aControllerPort,
3607 LONG aDevice,
3608 DeviceType_T aType,
3609 const ComPtr<IMedium> &aMedium)
3610{
3611 IMedium *aM = aMedium;
3612 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3613 aName.c_str(), aControllerPort, aDevice, aType, aM));
3614
3615 // request the host lock first, since might be calling Host methods for getting host drives;
3616 // next, protect the media tree all the while we're in here, as well as our member variables
3617 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3618 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3619
3620 HRESULT rc = i_checkStateDependency(MutableStateDep);
3621 if (FAILED(rc)) return rc;
3622
3623 /// @todo NEWMEDIA implicit machine registration
3624 if (!mData->mRegistered)
3625 return setError(VBOX_E_INVALID_OBJECT_STATE,
3626 tr("Cannot attach storage devices to an unregistered machine"));
3627
3628 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3629
3630 /* Check for an existing controller. */
3631 ComObjPtr<StorageController> ctl;
3632 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3633 if (FAILED(rc)) return rc;
3634
3635 StorageControllerType_T ctrlType;
3636 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3637 if (FAILED(rc))
3638 return setError(E_FAIL,
3639 tr("Could not get type of controller '%s'"),
3640 aName.c_str());
3641
3642 bool fSilent = false;
3643 Utf8Str strReconfig;
3644
3645 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3646 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3647 if ( mData->mMachineState == MachineState_Paused
3648 && strReconfig == "1")
3649 fSilent = true;
3650
3651 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3652 bool fHotplug = false;
3653 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3654 fHotplug = true;
3655
3656 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3657 return setError(VBOX_E_INVALID_VM_STATE,
3658 tr("Controller '%s' does not support hotplugging"),
3659 aName.c_str());
3660
3661 // check that the port and device are not out of range
3662 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3663 if (FAILED(rc)) return rc;
3664
3665 /* check if the device slot is already busy */
3666 MediumAttachment *pAttachTemp;
3667 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3668 Bstr(aName).raw(),
3669 aControllerPort,
3670 aDevice)))
3671 {
3672 Medium *pMedium = pAttachTemp->i_getMedium();
3673 if (pMedium)
3674 {
3675 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3676 return setError(VBOX_E_OBJECT_IN_USE,
3677 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3678 pMedium->i_getLocationFull().c_str(),
3679 aControllerPort,
3680 aDevice,
3681 aName.c_str());
3682 }
3683 else
3684 return setError(VBOX_E_OBJECT_IN_USE,
3685 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3686 aControllerPort, aDevice, aName.c_str());
3687 }
3688
3689 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3690 if (aMedium && medium.isNull())
3691 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3692
3693 AutoCaller mediumCaller(medium);
3694 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3695
3696 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3697
3698 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3699 && !medium.isNull()
3700 )
3701 return setError(VBOX_E_OBJECT_IN_USE,
3702 tr("Medium '%s' is already attached to this virtual machine"),
3703 medium->i_getLocationFull().c_str());
3704
3705 if (!medium.isNull())
3706 {
3707 MediumType_T mtype = medium->i_getType();
3708 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3709 // For DVDs it's not written to the config file, so needs no global config
3710 // version bump. For floppies it's a new attribute "type", which is ignored
3711 // by older VirtualBox version, so needs no global config version bump either.
3712 // For hard disks this type is not accepted.
3713 if (mtype == MediumType_MultiAttach)
3714 {
3715 // This type is new with VirtualBox 4.0 and therefore requires settings
3716 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3717 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3718 // two reasons: The medium type is a property of the media registry tree, which
3719 // can reside in the global config file (for pre-4.0 media); we would therefore
3720 // possibly need to bump the global config version. We don't want to do that though
3721 // because that might make downgrading to pre-4.0 impossible.
3722 // As a result, we can only use these two new types if the medium is NOT in the
3723 // global registry:
3724 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3725 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3726 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3727 )
3728 return setError(VBOX_E_INVALID_OBJECT_STATE,
3729 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3730 "to machines that were created with VirtualBox 4.0 or later"),
3731 medium->i_getLocationFull().c_str());
3732 }
3733 }
3734
3735 bool fIndirect = false;
3736 if (!medium.isNull())
3737 fIndirect = medium->i_isReadOnly();
3738 bool associate = true;
3739
3740 do
3741 {
3742 if ( aType == DeviceType_HardDisk
3743 && mMediaData.isBackedUp())
3744 {
3745 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3746
3747 /* check if the medium was attached to the VM before we started
3748 * changing attachments in which case the attachment just needs to
3749 * be restored */
3750 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3751 {
3752 AssertReturn(!fIndirect, E_FAIL);
3753
3754 /* see if it's the same bus/channel/device */
3755 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3756 {
3757 /* the simplest case: restore the whole attachment
3758 * and return, nothing else to do */
3759 mMediaData->mAttachments.push_back(pAttachTemp);
3760
3761 /* Reattach the medium to the VM. */
3762 if (fHotplug || fSilent)
3763 {
3764 mediumLock.release();
3765 treeLock.release();
3766 alock.release();
3767
3768 MediumLockList *pMediumLockList(new MediumLockList());
3769
3770 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3771 true /* fMediumLockWrite */,
3772 NULL,
3773 *pMediumLockList);
3774 alock.acquire();
3775 if (FAILED(rc))
3776 delete pMediumLockList;
3777 else
3778 {
3779 mData->mSession.mLockedMedia.Unlock();
3780 alock.release();
3781 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3782 mData->mSession.mLockedMedia.Lock();
3783 alock.acquire();
3784 }
3785 alock.release();
3786
3787 if (SUCCEEDED(rc))
3788 {
3789 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3790 /* Remove lock list in case of error. */
3791 if (FAILED(rc))
3792 {
3793 mData->mSession.mLockedMedia.Unlock();
3794 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3795 mData->mSession.mLockedMedia.Lock();
3796 }
3797 }
3798 }
3799
3800 return S_OK;
3801 }
3802
3803 /* bus/channel/device differ; we need a new attachment object,
3804 * but don't try to associate it again */
3805 associate = false;
3806 break;
3807 }
3808 }
3809
3810 /* go further only if the attachment is to be indirect */
3811 if (!fIndirect)
3812 break;
3813
3814 /* perform the so called smart attachment logic for indirect
3815 * attachments. Note that smart attachment is only applicable to base
3816 * hard disks. */
3817
3818 if (medium->i_getParent().isNull())
3819 {
3820 /* first, investigate the backup copy of the current hard disk
3821 * attachments to make it possible to re-attach existing diffs to
3822 * another device slot w/o losing their contents */
3823 if (mMediaData.isBackedUp())
3824 {
3825 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3826
3827 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3828 uint32_t foundLevel = 0;
3829
3830 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3831 {
3832 uint32_t level = 0;
3833 MediumAttachment *pAttach = *it;
3834 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3835 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3836 if (pMedium.isNull())
3837 continue;
3838
3839 if (pMedium->i_getBase(&level) == medium)
3840 {
3841 /* skip the hard disk if its currently attached (we
3842 * cannot attach the same hard disk twice) */
3843 if (i_findAttachment(mMediaData->mAttachments,
3844 pMedium))
3845 continue;
3846
3847 /* matched device, channel and bus (i.e. attached to the
3848 * same place) will win and immediately stop the search;
3849 * otherwise the attachment that has the youngest
3850 * descendant of medium will be used
3851 */
3852 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3853 {
3854 /* the simplest case: restore the whole attachment
3855 * and return, nothing else to do */
3856 mMediaData->mAttachments.push_back(*it);
3857
3858 /* Reattach the medium to the VM. */
3859 if (fHotplug || fSilent)
3860 {
3861 mediumLock.release();
3862 treeLock.release();
3863 alock.release();
3864
3865 MediumLockList *pMediumLockList(new MediumLockList());
3866
3867 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3868 true /* fMediumLockWrite */,
3869 NULL,
3870 *pMediumLockList);
3871 alock.acquire();
3872 if (FAILED(rc))
3873 delete pMediumLockList;
3874 else
3875 {
3876 mData->mSession.mLockedMedia.Unlock();
3877 alock.release();
3878 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3879 mData->mSession.mLockedMedia.Lock();
3880 alock.acquire();
3881 }
3882 alock.release();
3883
3884 if (SUCCEEDED(rc))
3885 {
3886 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3887 /* Remove lock list in case of error. */
3888 if (FAILED(rc))
3889 {
3890 mData->mSession.mLockedMedia.Unlock();
3891 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3892 mData->mSession.mLockedMedia.Lock();
3893 }
3894 }
3895 }
3896
3897 return S_OK;
3898 }
3899 else if ( foundIt == oldAtts.end()
3900 || level > foundLevel /* prefer younger */
3901 )
3902 {
3903 foundIt = it;
3904 foundLevel = level;
3905 }
3906 }
3907 }
3908
3909 if (foundIt != oldAtts.end())
3910 {
3911 /* use the previously attached hard disk */
3912 medium = (*foundIt)->i_getMedium();
3913 mediumCaller.attach(medium);
3914 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3915 mediumLock.attach(medium);
3916 /* not implicit, doesn't require association with this VM */
3917 fIndirect = false;
3918 associate = false;
3919 /* go right to the MediumAttachment creation */
3920 break;
3921 }
3922 }
3923
3924 /* must give up the medium lock and medium tree lock as below we
3925 * go over snapshots, which needs a lock with higher lock order. */
3926 mediumLock.release();
3927 treeLock.release();
3928
3929 /* then, search through snapshots for the best diff in the given
3930 * hard disk's chain to base the new diff on */
3931
3932 ComObjPtr<Medium> base;
3933 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3934 while (snap)
3935 {
3936 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3937
3938 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
3939
3940 MediumAttachment *pAttachFound = NULL;
3941 uint32_t foundLevel = 0;
3942
3943 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
3944 {
3945 MediumAttachment *pAttach = *it;
3946 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3947 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3948 if (pMedium.isNull())
3949 continue;
3950
3951 uint32_t level = 0;
3952 if (pMedium->i_getBase(&level) == medium)
3953 {
3954 /* matched device, channel and bus (i.e. attached to the
3955 * same place) will win and immediately stop the search;
3956 * otherwise the attachment that has the youngest
3957 * descendant of medium will be used
3958 */
3959 if ( pAttach->i_getDevice() == aDevice
3960 && pAttach->i_getPort() == aControllerPort
3961 && pAttach->i_getControllerName() == aName
3962 )
3963 {
3964 pAttachFound = pAttach;
3965 break;
3966 }
3967 else if ( !pAttachFound
3968 || level > foundLevel /* prefer younger */
3969 )
3970 {
3971 pAttachFound = pAttach;
3972 foundLevel = level;
3973 }
3974 }
3975 }
3976
3977 if (pAttachFound)
3978 {
3979 base = pAttachFound->i_getMedium();
3980 break;
3981 }
3982
3983 snap = snap->i_getParent();
3984 }
3985
3986 /* re-lock medium tree and the medium, as we need it below */
3987 treeLock.acquire();
3988 mediumLock.acquire();
3989
3990 /* found a suitable diff, use it as a base */
3991 if (!base.isNull())
3992 {
3993 medium = base;
3994 mediumCaller.attach(medium);
3995 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3996 mediumLock.attach(medium);
3997 }
3998 }
3999
4000 Utf8Str strFullSnapshotFolder;
4001 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4002
4003 ComObjPtr<Medium> diff;
4004 diff.createObject();
4005 // store this diff in the same registry as the parent
4006 Guid uuidRegistryParent;
4007 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4008 {
4009 // parent image has no registry: this can happen if we're attaching a new immutable
4010 // image that has not yet been attached (medium then points to the base and we're
4011 // creating the diff image for the immutable, and the parent is not yet registered);
4012 // put the parent in the machine registry then
4013 mediumLock.release();
4014 treeLock.release();
4015 alock.release();
4016 i_addMediumToRegistry(medium);
4017 alock.acquire();
4018 treeLock.acquire();
4019 mediumLock.acquire();
4020 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4021 }
4022 rc = diff->init(mParent,
4023 medium->i_getPreferredDiffFormat(),
4024 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4025 uuidRegistryParent);
4026 if (FAILED(rc)) return rc;
4027
4028 /* Apply the normal locking logic to the entire chain. */
4029 MediumLockList *pMediumLockList(new MediumLockList());
4030 mediumLock.release();
4031 treeLock.release();
4032 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4033 true /* fMediumLockWrite */,
4034 medium,
4035 *pMediumLockList);
4036 treeLock.acquire();
4037 mediumLock.acquire();
4038 if (SUCCEEDED(rc))
4039 {
4040 mediumLock.release();
4041 treeLock.release();
4042 rc = pMediumLockList->Lock();
4043 treeLock.acquire();
4044 mediumLock.acquire();
4045 if (FAILED(rc))
4046 setError(rc,
4047 tr("Could not lock medium when creating diff '%s'"),
4048 diff->i_getLocationFull().c_str());
4049 else
4050 {
4051 /* will release the lock before the potentially lengthy
4052 * operation, so protect with the special state */
4053 MachineState_T oldState = mData->mMachineState;
4054 i_setMachineState(MachineState_SettingUp);
4055
4056 mediumLock.release();
4057 treeLock.release();
4058 alock.release();
4059
4060 rc = medium->i_createDiffStorage(diff,
4061 MediumVariant_Standard,
4062 pMediumLockList,
4063 NULL /* aProgress */,
4064 true /* aWait */);
4065
4066 alock.acquire();
4067 treeLock.acquire();
4068 mediumLock.acquire();
4069
4070 i_setMachineState(oldState);
4071 }
4072 }
4073
4074 /* Unlock the media and free the associated memory. */
4075 delete pMediumLockList;
4076
4077 if (FAILED(rc)) return rc;
4078
4079 /* use the created diff for the actual attachment */
4080 medium = diff;
4081 mediumCaller.attach(medium);
4082 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4083 mediumLock.attach(medium);
4084 }
4085 while (0);
4086
4087 ComObjPtr<MediumAttachment> attachment;
4088 attachment.createObject();
4089 rc = attachment->init(this,
4090 medium,
4091 aName,
4092 aControllerPort,
4093 aDevice,
4094 aType,
4095 fIndirect,
4096 false /* fPassthrough */,
4097 false /* fTempEject */,
4098 false /* fNonRotational */,
4099 false /* fDiscard */,
4100 fHotplug /* fHotPluggable */,
4101 Utf8Str::Empty);
4102 if (FAILED(rc)) return rc;
4103
4104 if (associate && !medium.isNull())
4105 {
4106 // as the last step, associate the medium to the VM
4107 rc = medium->i_addBackReference(mData->mUuid);
4108 // here we can fail because of Deleting, or being in process of creating a Diff
4109 if (FAILED(rc)) return rc;
4110
4111 mediumLock.release();
4112 treeLock.release();
4113 alock.release();
4114 i_addMediumToRegistry(medium);
4115 alock.acquire();
4116 treeLock.acquire();
4117 mediumLock.acquire();
4118 }
4119
4120 /* success: finally remember the attachment */
4121 i_setModified(IsModified_Storage);
4122 mMediaData.backup();
4123 mMediaData->mAttachments.push_back(attachment);
4124
4125 mediumLock.release();
4126 treeLock.release();
4127 alock.release();
4128
4129 if (fHotplug || fSilent)
4130 {
4131 if (!medium.isNull())
4132 {
4133 MediumLockList *pMediumLockList(new MediumLockList());
4134
4135 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4136 true /* fMediumLockWrite */,
4137 NULL,
4138 *pMediumLockList);
4139 alock.acquire();
4140 if (FAILED(rc))
4141 delete pMediumLockList;
4142 else
4143 {
4144 mData->mSession.mLockedMedia.Unlock();
4145 alock.release();
4146 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4147 mData->mSession.mLockedMedia.Lock();
4148 alock.acquire();
4149 }
4150 alock.release();
4151 }
4152
4153 if (SUCCEEDED(rc))
4154 {
4155 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4156 /* Remove lock list in case of error. */
4157 if (FAILED(rc))
4158 {
4159 mData->mSession.mLockedMedia.Unlock();
4160 mData->mSession.mLockedMedia.Remove(attachment);
4161 mData->mSession.mLockedMedia.Lock();
4162 }
4163 }
4164 }
4165
4166 mParent->i_saveModifiedRegistries();
4167
4168 return rc;
4169}
4170
4171HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4172 LONG aDevice)
4173{
4174 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4175 aName.c_str(), aControllerPort, aDevice));
4176
4177 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4178
4179 HRESULT rc = i_checkStateDependency(MutableStateDep);
4180 if (FAILED(rc)) return rc;
4181
4182 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4183
4184 /* Check for an existing controller. */
4185 ComObjPtr<StorageController> ctl;
4186 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4187 if (FAILED(rc)) return rc;
4188
4189 StorageControllerType_T ctrlType;
4190 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4191 if (FAILED(rc))
4192 return setError(E_FAIL,
4193 tr("Could not get type of controller '%s'"),
4194 aName.c_str());
4195
4196 bool fSilent = false;
4197 Utf8Str strReconfig;
4198
4199 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4200 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4201 if ( mData->mMachineState == MachineState_Paused
4202 && strReconfig == "1")
4203 fSilent = true;
4204
4205 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4206 bool fHotplug = false;
4207 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4208 fHotplug = true;
4209
4210 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4211 return setError(VBOX_E_INVALID_VM_STATE,
4212 tr("Controller '%s' does not support hotplugging"),
4213 aName.c_str());
4214
4215 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4216 Bstr(aName).raw(),
4217 aControllerPort,
4218 aDevice);
4219 if (!pAttach)
4220 return setError(VBOX_E_OBJECT_NOT_FOUND,
4221 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4222 aDevice, aControllerPort, aName.c_str());
4223
4224 if (fHotplug && !pAttach->i_getHotPluggable())
4225 return setError(VBOX_E_NOT_SUPPORTED,
4226 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4227 aDevice, aControllerPort, aName.c_str());
4228
4229 /*
4230 * The VM has to detach the device before we delete any implicit diffs.
4231 * If this fails we can roll back without loosing data.
4232 */
4233 if (fHotplug || fSilent)
4234 {
4235 alock.release();
4236 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4237 alock.acquire();
4238 }
4239 if (FAILED(rc)) return rc;
4240
4241 /* If we are here everything went well and we can delete the implicit now. */
4242 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4243
4244 alock.release();
4245
4246 mParent->i_saveModifiedRegistries();
4247
4248 return rc;
4249}
4250
4251HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4252 LONG aDevice, BOOL aPassthrough)
4253{
4254 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4255 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4256
4257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4258
4259 HRESULT rc = i_checkStateDependency(MutableStateDep);
4260 if (FAILED(rc)) return rc;
4261
4262 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4263
4264 if (Global::IsOnlineOrTransient(mData->mMachineState))
4265 return setError(VBOX_E_INVALID_VM_STATE,
4266 tr("Invalid machine state: %s"),
4267 Global::stringifyMachineState(mData->mMachineState));
4268
4269 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4270 Bstr(aName).raw(),
4271 aControllerPort,
4272 aDevice);
4273 if (!pAttach)
4274 return setError(VBOX_E_OBJECT_NOT_FOUND,
4275 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4276 aDevice, aControllerPort, aName.c_str());
4277
4278
4279 i_setModified(IsModified_Storage);
4280 mMediaData.backup();
4281
4282 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4283
4284 if (pAttach->i_getType() != DeviceType_DVD)
4285 return setError(E_INVALIDARG,
4286 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4287 aDevice, aControllerPort, aName.c_str());
4288 pAttach->i_updatePassthrough(!!aPassthrough);
4289
4290 return S_OK;
4291}
4292
4293HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4294 LONG aDevice, BOOL aTemporaryEject)
4295{
4296
4297 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4298 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4299
4300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4301
4302 HRESULT rc = i_checkStateDependency(MutableStateDep);
4303 if (FAILED(rc)) return rc;
4304
4305 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4306 Bstr(aName).raw(),
4307 aControllerPort,
4308 aDevice);
4309 if (!pAttach)
4310 return setError(VBOX_E_OBJECT_NOT_FOUND,
4311 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4312 aDevice, aControllerPort, aName.c_str());
4313
4314
4315 i_setModified(IsModified_Storage);
4316 mMediaData.backup();
4317
4318 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4319
4320 if (pAttach->i_getType() != DeviceType_DVD)
4321 return setError(E_INVALIDARG,
4322 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4323 aDevice, aControllerPort, aName.c_str());
4324 pAttach->i_updateTempEject(!!aTemporaryEject);
4325
4326 return S_OK;
4327}
4328
4329HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4330 LONG aDevice, BOOL aNonRotational)
4331{
4332
4333 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4334 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4335
4336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4337
4338 HRESULT rc = i_checkStateDependency(MutableStateDep);
4339 if (FAILED(rc)) return rc;
4340
4341 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4342
4343 if (Global::IsOnlineOrTransient(mData->mMachineState))
4344 return setError(VBOX_E_INVALID_VM_STATE,
4345 tr("Invalid machine state: %s"),
4346 Global::stringifyMachineState(mData->mMachineState));
4347
4348 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4349 Bstr(aName).raw(),
4350 aControllerPort,
4351 aDevice);
4352 if (!pAttach)
4353 return setError(VBOX_E_OBJECT_NOT_FOUND,
4354 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4355 aDevice, aControllerPort, aName.c_str());
4356
4357
4358 i_setModified(IsModified_Storage);
4359 mMediaData.backup();
4360
4361 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4362
4363 if (pAttach->i_getType() != DeviceType_HardDisk)
4364 return setError(E_INVALIDARG,
4365 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4366 aDevice, aControllerPort, aName.c_str());
4367 pAttach->i_updateNonRotational(!!aNonRotational);
4368
4369 return S_OK;
4370}
4371
4372HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4373 LONG aDevice, BOOL aDiscard)
4374{
4375
4376 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4377 aName.c_str(), aControllerPort, aDevice, aDiscard));
4378
4379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4380
4381 HRESULT rc = i_checkStateDependency(MutableStateDep);
4382 if (FAILED(rc)) return rc;
4383
4384 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4385
4386 if (Global::IsOnlineOrTransient(mData->mMachineState))
4387 return setError(VBOX_E_INVALID_VM_STATE,
4388 tr("Invalid machine state: %s"),
4389 Global::stringifyMachineState(mData->mMachineState));
4390
4391 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4392 Bstr(aName).raw(),
4393 aControllerPort,
4394 aDevice);
4395 if (!pAttach)
4396 return setError(VBOX_E_OBJECT_NOT_FOUND,
4397 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4398 aDevice, aControllerPort, aName.c_str());
4399
4400
4401 i_setModified(IsModified_Storage);
4402 mMediaData.backup();
4403
4404 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4405
4406 if (pAttach->i_getType() != DeviceType_HardDisk)
4407 return setError(E_INVALIDARG,
4408 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4409 aDevice, aControllerPort, aName.c_str());
4410 pAttach->i_updateDiscard(!!aDiscard);
4411
4412 return S_OK;
4413}
4414
4415HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4416 LONG aDevice, BOOL aHotPluggable)
4417{
4418 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4419 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4420
4421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4422
4423 HRESULT rc = i_checkStateDependency(MutableStateDep);
4424 if (FAILED(rc)) return rc;
4425
4426 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4427
4428 if (Global::IsOnlineOrTransient(mData->mMachineState))
4429 return setError(VBOX_E_INVALID_VM_STATE,
4430 tr("Invalid machine state: %s"),
4431 Global::stringifyMachineState(mData->mMachineState));
4432
4433 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4434 Bstr(aName).raw(),
4435 aControllerPort,
4436 aDevice);
4437 if (!pAttach)
4438 return setError(VBOX_E_OBJECT_NOT_FOUND,
4439 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4440 aDevice, aControllerPort, aName.c_str());
4441
4442 /* Check for an existing controller. */
4443 ComObjPtr<StorageController> ctl;
4444 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4445 if (FAILED(rc)) return rc;
4446
4447 StorageControllerType_T ctrlType;
4448 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4449 if (FAILED(rc))
4450 return setError(E_FAIL,
4451 tr("Could not get type of controller '%s'"),
4452 aName.c_str());
4453
4454 if (!i_isControllerHotplugCapable(ctrlType))
4455 return setError(VBOX_E_NOT_SUPPORTED,
4456 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4457 aName.c_str());
4458
4459 i_setModified(IsModified_Storage);
4460 mMediaData.backup();
4461
4462 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4463
4464 if (pAttach->i_getType() == DeviceType_Floppy)
4465 return setError(E_INVALIDARG,
4466 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4467 aDevice, aControllerPort, aName.c_str());
4468 pAttach->i_updateHotPluggable(!!aHotPluggable);
4469
4470 return S_OK;
4471}
4472
4473HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4474 LONG aDevice)
4475{
4476 int rc = S_OK;
4477 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4478 aName.c_str(), aControllerPort, aDevice));
4479
4480 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4481
4482 return rc;
4483}
4484
4485HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4486 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4487{
4488 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4489 aName.c_str(), aControllerPort, aDevice));
4490
4491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4492
4493 HRESULT rc = i_checkStateDependency(MutableStateDep);
4494 if (FAILED(rc)) return rc;
4495
4496 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4497
4498 if (Global::IsOnlineOrTransient(mData->mMachineState))
4499 return setError(VBOX_E_INVALID_VM_STATE,
4500 tr("Invalid machine state: %s"),
4501 Global::stringifyMachineState(mData->mMachineState));
4502
4503 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4504 Bstr(aName).raw(),
4505 aControllerPort,
4506 aDevice);
4507 if (!pAttach)
4508 return setError(VBOX_E_OBJECT_NOT_FOUND,
4509 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4510 aDevice, aControllerPort, aName.c_str());
4511
4512
4513 i_setModified(IsModified_Storage);
4514 mMediaData.backup();
4515
4516 IBandwidthGroup *iB = aBandwidthGroup;
4517 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4518 if (aBandwidthGroup && group.isNull())
4519 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4520
4521 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4522
4523 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4524 if (strBandwidthGroupOld.isNotEmpty())
4525 {
4526 /* Get the bandwidth group object and release it - this must not fail. */
4527 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4528 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4529 Assert(SUCCEEDED(rc));
4530
4531 pBandwidthGroupOld->i_release();
4532 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4533 }
4534
4535 if (!group.isNull())
4536 {
4537 group->i_reference();
4538 pAttach->i_updateBandwidthGroup(group->i_getName());
4539 }
4540
4541 return S_OK;
4542}
4543
4544HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4545 LONG aControllerPort,
4546 LONG aDevice,
4547 DeviceType_T aType)
4548{
4549 HRESULT rc = S_OK;
4550
4551 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4552 aName.c_str(), aControllerPort, aDevice, aType));
4553
4554 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4555
4556 return rc;
4557}
4558
4559
4560HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4561 LONG aControllerPort,
4562 LONG aDevice,
4563 BOOL aForce)
4564{
4565 int rc = S_OK;
4566 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4567 aName.c_str(), aControllerPort, aForce));
4568
4569 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4570
4571 return rc;
4572}
4573
4574HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4575 LONG aControllerPort,
4576 LONG aDevice,
4577 const ComPtr<IMedium> &aMedium,
4578 BOOL aForce)
4579{
4580 int rc = S_OK;
4581 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4582 aName.c_str(), aControllerPort, aDevice, aForce));
4583
4584 // request the host lock first, since might be calling Host methods for getting host drives;
4585 // next, protect the media tree all the while we're in here, as well as our member variables
4586 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4587 this->lockHandle(),
4588 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4589
4590 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4591 Bstr(aName).raw(),
4592 aControllerPort,
4593 aDevice);
4594 if (pAttach.isNull())
4595 return setError(VBOX_E_OBJECT_NOT_FOUND,
4596 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4597 aDevice, aControllerPort, aName.c_str());
4598
4599 /* Remember previously mounted medium. The medium before taking the
4600 * backup is not necessarily the same thing. */
4601 ComObjPtr<Medium> oldmedium;
4602 oldmedium = pAttach->i_getMedium();
4603
4604 IMedium *iM = aMedium;
4605 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4606 if (aMedium && pMedium.isNull())
4607 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4608
4609 AutoCaller mediumCaller(pMedium);
4610 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4611
4612 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4613 if (pMedium)
4614 {
4615 DeviceType_T mediumType = pAttach->i_getType();
4616 switch (mediumType)
4617 {
4618 case DeviceType_DVD:
4619 case DeviceType_Floppy:
4620 break;
4621
4622 default:
4623 return setError(VBOX_E_INVALID_OBJECT_STATE,
4624 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4625 aControllerPort,
4626 aDevice,
4627 aName.c_str());
4628 }
4629 }
4630
4631 i_setModified(IsModified_Storage);
4632 mMediaData.backup();
4633
4634 {
4635 // The backup operation makes the pAttach reference point to the
4636 // old settings. Re-get the correct reference.
4637 pAttach = i_findAttachment(mMediaData->mAttachments,
4638 Bstr(aName).raw(),
4639 aControllerPort,
4640 aDevice);
4641 if (!oldmedium.isNull())
4642 oldmedium->i_removeBackReference(mData->mUuid);
4643 if (!pMedium.isNull())
4644 {
4645 pMedium->i_addBackReference(mData->mUuid);
4646
4647 mediumLock.release();
4648 multiLock.release();
4649 i_addMediumToRegistry(pMedium);
4650 multiLock.acquire();
4651 mediumLock.acquire();
4652 }
4653
4654 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4655 pAttach->i_updateMedium(pMedium);
4656 }
4657
4658 i_setModified(IsModified_Storage);
4659
4660 mediumLock.release();
4661 multiLock.release();
4662 rc = i_onMediumChange(pAttach, aForce);
4663 multiLock.acquire();
4664 mediumLock.acquire();
4665
4666 /* On error roll back this change only. */
4667 if (FAILED(rc))
4668 {
4669 if (!pMedium.isNull())
4670 pMedium->i_removeBackReference(mData->mUuid);
4671 pAttach = i_findAttachment(mMediaData->mAttachments,
4672 Bstr(aName).raw(),
4673 aControllerPort,
4674 aDevice);
4675 /* If the attachment is gone in the meantime, bail out. */
4676 if (pAttach.isNull())
4677 return rc;
4678 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4679 if (!oldmedium.isNull())
4680 oldmedium->i_addBackReference(mData->mUuid);
4681 pAttach->i_updateMedium(oldmedium);
4682 }
4683
4684 mediumLock.release();
4685 multiLock.release();
4686
4687 mParent->i_saveModifiedRegistries();
4688
4689 return rc;
4690}
4691HRESULT Machine::getMedium(const com::Utf8Str &aName,
4692 LONG aControllerPort,
4693 LONG aDevice,
4694 ComPtr<IMedium> &aMedium)
4695{
4696 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4697 aName.c_str(), aControllerPort, aDevice));
4698
4699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4700
4701 aMedium = NULL;
4702
4703 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4704 Bstr(aName).raw(),
4705 aControllerPort,
4706 aDevice);
4707 if (pAttach.isNull())
4708 return setError(VBOX_E_OBJECT_NOT_FOUND,
4709 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4710 aDevice, aControllerPort, aName.c_str());
4711
4712 pAttach->i_getMedium().queryInterfaceTo(aMedium.asOutParam());
4713
4714 return S_OK;
4715}
4716
4717HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4718{
4719
4720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4721
4722 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4723
4724 return S_OK;
4725}
4726
4727HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4728{
4729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4730
4731 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4732
4733 return S_OK;
4734}
4735
4736HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4737{
4738 /* Do not assert if slot is out of range, just return the advertised
4739 status. testdriver/vbox.py triggers this in logVmInfo. */
4740 if (aSlot >= mNetworkAdapters.size())
4741 return setError(E_INVALIDARG,
4742 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4743 aSlot, mNetworkAdapters.size());
4744
4745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4746
4747 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4748
4749 return S_OK;
4750}
4751
4752HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4753{
4754 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4755
4756 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4757 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4758 size_t i = 0;
4759 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4760 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4761 ++it, ++i)
4762 aKeys[i] = it->first;
4763
4764 return S_OK;
4765}
4766
4767 /**
4768 * @note Locks this object for reading.
4769 */
4770HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4771 com::Utf8Str &aValue)
4772{
4773 /* start with nothing found */
4774 aValue = "";
4775
4776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4777
4778 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4779 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4780 // found:
4781 aValue = it->second; // source is a Utf8Str
4782
4783 /* return the result to caller (may be empty) */
4784 return S_OK;
4785}
4786
4787 /**
4788 * @note Locks mParent for writing + this object for writing.
4789 */
4790HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4791{
4792 Utf8Str strOldValue; // empty
4793
4794 // locking note: we only hold the read lock briefly to look up the old value,
4795 // then release it and call the onExtraCanChange callbacks. There is a small
4796 // chance of a race insofar as the callback might be called twice if two callers
4797 // change the same key at the same time, but that's a much better solution
4798 // than the deadlock we had here before. The actual changing of the extradata
4799 // is then performed under the write lock and race-free.
4800
4801 // look up the old value first; if nothing has changed then we need not do anything
4802 {
4803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4804 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4805 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4806 strOldValue = it->second;
4807 }
4808
4809 bool fChanged;
4810 if ((fChanged = (strOldValue != aValue)))
4811 {
4812 // ask for permission from all listeners outside the locks;
4813 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4814 // lock to copy the list of callbacks to invoke
4815 Bstr error;
4816 Bstr bstrValue(aValue);
4817
4818 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4819 {
4820 const char *sep = error.isEmpty() ? "" : ": ";
4821 CBSTR err = error.raw();
4822 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4823 sep, err));
4824 return setError(E_ACCESSDENIED,
4825 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4826 aKey.c_str(),
4827 aValue.c_str(),
4828 sep,
4829 err);
4830 }
4831
4832 // data is changing and change not vetoed: then write it out under the lock
4833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4834
4835 if (i_isSnapshotMachine())
4836 {
4837 HRESULT rc = i_checkStateDependency(MutableStateDep);
4838 if (FAILED(rc)) return rc;
4839 }
4840
4841 if (aValue.isEmpty())
4842 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4843 else
4844 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4845 // creates a new key if needed
4846
4847 bool fNeedsGlobalSaveSettings = false;
4848 i_saveSettings(&fNeedsGlobalSaveSettings);
4849
4850 if (fNeedsGlobalSaveSettings)
4851 {
4852 // save the global settings; for that we should hold only the VirtualBox lock
4853 alock.release();
4854 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4855 mParent->i_saveSettings();
4856 }
4857 }
4858
4859 // fire notification outside the lock
4860 if (fChanged)
4861 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4862
4863 return S_OK;
4864}
4865
4866HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4867{
4868 aProgress = NULL;
4869 NOREF(aSettingsFilePath);
4870 ReturnComNotImplemented();
4871}
4872
4873HRESULT Machine::saveSettings()
4874{
4875 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4876
4877 /* when there was auto-conversion, we want to save the file even if
4878 * the VM is saved */
4879 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4880 if (FAILED(rc)) return rc;
4881
4882 /* the settings file path may never be null */
4883 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4884
4885 /* save all VM data excluding snapshots */
4886 bool fNeedsGlobalSaveSettings = false;
4887 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4888 mlock.release();
4889
4890 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4891 {
4892 // save the global settings; for that we should hold only the VirtualBox lock
4893 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4894 rc = mParent->i_saveSettings();
4895 }
4896
4897 return rc;
4898}
4899
4900
4901HRESULT Machine::discardSettings()
4902{
4903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4904
4905 HRESULT rc = i_checkStateDependency(MutableStateDep);
4906 if (FAILED(rc)) return rc;
4907
4908 /*
4909 * during this rollback, the session will be notified if data has
4910 * been actually changed
4911 */
4912 i_rollback(true /* aNotify */);
4913
4914 return S_OK;
4915}
4916
4917/** @note Locks objects! */
4918HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
4919 std::vector<ComPtr<IMedium> > &aMedia)
4920{
4921 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4922 AutoLimitedCaller autoCaller(this);
4923 AssertComRCReturnRC(autoCaller.rc());
4924
4925 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4926
4927 Guid id(i_getId());
4928
4929 if (mData->mSession.mState != SessionState_Unlocked)
4930 return setError(VBOX_E_INVALID_OBJECT_STATE,
4931 tr("Cannot unregister the machine '%s' while it is locked"),
4932 mUserData->s.strName.c_str());
4933
4934 // wait for state dependents to drop to zero
4935 i_ensureNoStateDependencies();
4936
4937 if (!mData->mAccessible)
4938 {
4939 // inaccessible maschines can only be unregistered; uninitialize ourselves
4940 // here because currently there may be no unregistered that are inaccessible
4941 // (this state combination is not supported). Note releasing the caller and
4942 // leaving the lock before calling uninit()
4943 alock.release();
4944 autoCaller.release();
4945
4946 uninit();
4947
4948 mParent->i_unregisterMachine(this, id);
4949 // calls VirtualBox::i_saveSettings()
4950
4951 return S_OK;
4952 }
4953
4954 HRESULT rc = S_OK;
4955
4956 // discard saved state
4957 if (mData->mMachineState == MachineState_Saved)
4958 {
4959 // add the saved state file to the list of files the caller should delete
4960 Assert(!mSSData->strStateFilePath.isEmpty());
4961 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4962
4963 mSSData->strStateFilePath.setNull();
4964
4965 // unconditionally set the machine state to powered off, we now
4966 // know no session has locked the machine
4967 mData->mMachineState = MachineState_PoweredOff;
4968 }
4969
4970 size_t cSnapshots = 0;
4971 if (mData->mFirstSnapshot)
4972 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
4973 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
4974 // fail now before we start detaching media
4975 return setError(VBOX_E_INVALID_OBJECT_STATE,
4976 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4977 mUserData->s.strName.c_str(), cSnapshots);
4978
4979 // This list collects the medium objects from all medium attachments
4980 // which we will detach from the machine and its snapshots, in a specific
4981 // order which allows for closing all media without getting "media in use"
4982 // errors, simply by going through the list from the front to the back:
4983 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4984 // and must be closed before the parent media from the snapshots, or closing the parents
4985 // will fail because they still have children);
4986 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4987 // the root ("first") snapshot of the machine.
4988 MediaList llMedia;
4989
4990 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4991 && mMediaData->mAttachments.size()
4992 )
4993 {
4994 // we have media attachments: detach them all and add the Medium objects to our list
4995 if (aCleanupMode != CleanupMode_UnregisterOnly)
4996 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4997 else
4998 return setError(VBOX_E_INVALID_OBJECT_STATE,
4999 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5000 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5001 }
5002
5003 if (cSnapshots)
5004 {
5005 // autoCleanup must be true here, or we would have failed above
5006
5007 // add the media from the medium attachments of the snapshots to llMedia
5008 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5009 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5010 // into the children first
5011
5012 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5013 MachineState_T oldState = mData->mMachineState;
5014 mData->mMachineState = MachineState_DeletingSnapshot;
5015
5016 // make a copy of the first snapshot so the refcount does not drop to 0
5017 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5018 // because of the AutoCaller voodoo)
5019 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5020
5021 // GO!
5022 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5023
5024 mData->mMachineState = oldState;
5025 }
5026
5027 if (FAILED(rc))
5028 {
5029 i_rollbackMedia();
5030 return rc;
5031 }
5032
5033 // commit all the media changes made above
5034 i_commitMedia();
5035
5036 mData->mRegistered = false;
5037
5038 // machine lock no longer needed
5039 alock.release();
5040
5041 // return media to caller
5042 size_t i = 0;
5043 aMedia.resize(llMedia.size());
5044 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5045 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5046
5047 mParent->i_unregisterMachine(this, id);
5048 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5049
5050 return S_OK;
5051}
5052
5053struct Machine::DeleteTask
5054{
5055 ComObjPtr<Machine> pMachine;
5056 RTCList<ComPtr<IMedium> > llMediums;
5057 StringsList llFilesToDelete;
5058 ComObjPtr<Progress> pProgress;
5059};
5060
5061HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5062{
5063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5064
5065 HRESULT rc = i_checkStateDependency(MutableStateDep);
5066 if (FAILED(rc)) return rc;
5067
5068 if (mData->mRegistered)
5069 return setError(VBOX_E_INVALID_VM_STATE,
5070 tr("Cannot delete settings of a registered machine"));
5071
5072 DeleteTask *pTask = new DeleteTask;
5073 pTask->pMachine = this;
5074
5075 // collect files to delete
5076 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5077
5078 for (size_t i = 0; i < aMedia.size(); ++i)
5079 {
5080 IMedium *pIMedium(aMedia[i]);
5081 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5082 if (pMedium.isNull())
5083 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5084 SafeArray<BSTR> ids;
5085 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5086 if (FAILED(rc)) return rc;
5087 /* At this point the medium should not have any back references
5088 * anymore. If it has it is attached to another VM and *must* not
5089 * deleted. */
5090 if (ids.size() < 1)
5091 pTask->llMediums.append(pMedium);
5092 }
5093 if (mData->pMachineConfigFile->fileExists())
5094 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5095
5096 pTask->pProgress.createObject();
5097 pTask->pProgress->init(i_getVirtualBox(),
5098 static_cast<IMachine*>(this) /* aInitiator */,
5099 Bstr(tr("Deleting files")).raw(),
5100 true /* fCancellable */,
5101 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5102 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5103
5104 int vrc = RTThreadCreate(NULL,
5105 Machine::deleteThread,
5106 (void*)pTask,
5107 0,
5108 RTTHREADTYPE_MAIN_WORKER,
5109 0,
5110 "MachineDelete");
5111
5112 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5113
5114 if (RT_FAILURE(vrc))
5115 {
5116 delete pTask;
5117 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5118 }
5119
5120 LogFlowFuncLeave();
5121
5122 return S_OK;
5123}
5124
5125/**
5126 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5127 * calls Machine::deleteTaskWorker() on the actual machine object.
5128 * @param Thread
5129 * @param pvUser
5130 * @return
5131 */
5132/*static*/
5133DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5134{
5135 LogFlowFuncEnter();
5136
5137 DeleteTask *pTask = (DeleteTask*)pvUser;
5138 Assert(pTask);
5139 Assert(pTask->pMachine);
5140 Assert(pTask->pProgress);
5141
5142 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5143 pTask->pProgress->i_notifyComplete(rc);
5144
5145 delete pTask;
5146
5147 LogFlowFuncLeave();
5148
5149 NOREF(Thread);
5150
5151 return VINF_SUCCESS;
5152}
5153
5154/**
5155 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5156 * @param task
5157 * @return
5158 */
5159HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5160{
5161 AutoCaller autoCaller(this);
5162 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5163
5164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5165
5166 HRESULT rc = S_OK;
5167
5168 try
5169 {
5170 ULONG uLogHistoryCount = 3;
5171 ComPtr<ISystemProperties> systemProperties;
5172 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5173 if (FAILED(rc)) throw rc;
5174
5175 if (!systemProperties.isNull())
5176 {
5177 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5178 if (FAILED(rc)) throw rc;
5179 }
5180
5181 MachineState_T oldState = mData->mMachineState;
5182 i_setMachineState(MachineState_SettingUp);
5183 alock.release();
5184 for (size_t i = 0; i < task.llMediums.size(); ++i)
5185 {
5186 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5187 {
5188 AutoCaller mac(pMedium);
5189 if (FAILED(mac.rc())) throw mac.rc();
5190 Utf8Str strLocation = pMedium->i_getLocationFull();
5191 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5192 if (FAILED(rc)) throw rc;
5193 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5194 }
5195 ComPtr<IProgress> pProgress2;
5196 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5197 if (FAILED(rc)) throw rc;
5198 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5199 if (FAILED(rc)) throw rc;
5200 /* Check the result of the asynchronous process. */
5201 LONG iRc;
5202 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5203 if (FAILED(rc)) throw rc;
5204 /* If the thread of the progress object has an error, then
5205 * retrieve the error info from there, or it'll be lost. */
5206 if (FAILED(iRc))
5207 throw setError(ProgressErrorInfo(pProgress2));
5208
5209 /* Close the medium, deliberately without checking the return
5210- * code, and without leaving any trace in the error info, as
5211- * a failure here is a very minor issue, which shouldn't happen
5212- * as above we even managed to delete the medium. */
5213 {
5214 ErrorInfoKeeper eik;
5215 pMedium->Close();
5216 }
5217 }
5218 i_setMachineState(oldState);
5219 alock.acquire();
5220
5221 // delete the files pushed on the task list by Machine::Delete()
5222 // (this includes saved states of the machine and snapshots and
5223 // medium storage files from the IMedium list passed in, and the
5224 // machine XML file)
5225 StringsList::const_iterator it = task.llFilesToDelete.begin();
5226 while (it != task.llFilesToDelete.end())
5227 {
5228 const Utf8Str &strFile = *it;
5229 LogFunc(("Deleting file %s\n", strFile.c_str()));
5230 int vrc = RTFileDelete(strFile.c_str());
5231 if (RT_FAILURE(vrc))
5232 throw setError(VBOX_E_IPRT_ERROR,
5233 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5234
5235 ++it;
5236 if (it == task.llFilesToDelete.end())
5237 {
5238 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5239 if (FAILED(rc)) throw rc;
5240 break;
5241 }
5242
5243 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5244 if (FAILED(rc)) throw rc;
5245 }
5246
5247 /* delete the settings only when the file actually exists */
5248 if (mData->pMachineConfigFile->fileExists())
5249 {
5250 /* Delete any backup or uncommitted XML files. Ignore failures.
5251 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5252 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5253 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5254 RTFileDelete(otherXml.c_str());
5255 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5256 RTFileDelete(otherXml.c_str());
5257
5258 /* delete the Logs folder, nothing important should be left
5259 * there (we don't check for errors because the user might have
5260 * some private files there that we don't want to delete) */
5261 Utf8Str logFolder;
5262 getLogFolder(logFolder);
5263 Assert(logFolder.length());
5264 if (RTDirExists(logFolder.c_str()))
5265 {
5266 /* Delete all VBox.log[.N] files from the Logs folder
5267 * (this must be in sync with the rotation logic in
5268 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5269 * files that may have been created by the GUI. */
5270 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5271 logFolder.c_str(), RTPATH_DELIMITER);
5272 RTFileDelete(log.c_str());
5273 log = Utf8StrFmt("%s%cVBox.png",
5274 logFolder.c_str(), RTPATH_DELIMITER);
5275 RTFileDelete(log.c_str());
5276 for (int i = uLogHistoryCount; i > 0; i--)
5277 {
5278 log = Utf8StrFmt("%s%cVBox.log.%d",
5279 logFolder.c_str(), RTPATH_DELIMITER, i);
5280 RTFileDelete(log.c_str());
5281 log = Utf8StrFmt("%s%cVBox.png.%d",
5282 logFolder.c_str(), RTPATH_DELIMITER, i);
5283 RTFileDelete(log.c_str());
5284 }
5285
5286 RTDirRemove(logFolder.c_str());
5287 }
5288
5289 /* delete the Snapshots folder, nothing important should be left
5290 * there (we don't check for errors because the user might have
5291 * some private files there that we don't want to delete) */
5292 Utf8Str strFullSnapshotFolder;
5293 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5294 Assert(!strFullSnapshotFolder.isEmpty());
5295 if (RTDirExists(strFullSnapshotFolder.c_str()))
5296 RTDirRemove(strFullSnapshotFolder.c_str());
5297
5298 // delete the directory that contains the settings file, but only
5299 // if it matches the VM name
5300 Utf8Str settingsDir;
5301 if (i_isInOwnDir(&settingsDir))
5302 RTDirRemove(settingsDir.c_str());
5303 }
5304
5305 alock.release();
5306
5307 mParent->i_saveModifiedRegistries();
5308 }
5309 catch (HRESULT aRC) { rc = aRC; }
5310
5311 return rc;
5312}
5313
5314HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5315{
5316 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5317
5318 ComObjPtr<Snapshot> pSnapshot;
5319 HRESULT rc;
5320
5321 if (aNameOrId.isEmpty())
5322 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5323 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5324 else
5325 {
5326 Guid uuid(aNameOrId);
5327 if (uuid.isValid())
5328 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5329 else
5330 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5331 }
5332 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5333
5334 return rc;
5335}
5336
5337HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5338{
5339 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5340
5341 HRESULT rc = i_checkStateDependency(MutableStateDep);
5342 if (FAILED(rc)) return rc;
5343
5344 ComObjPtr<SharedFolder> sharedFolder;
5345 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5346 if (SUCCEEDED(rc))
5347 return setError(VBOX_E_OBJECT_IN_USE,
5348 tr("Shared folder named '%s' already exists"),
5349 aName.c_str());
5350
5351 sharedFolder.createObject();
5352 rc = sharedFolder->init(i_getMachine(),
5353 aName,
5354 aHostPath,
5355 !!aWritable,
5356 !!aAutomount,
5357 true /* fFailOnError */);
5358 if (FAILED(rc)) return rc;
5359
5360 i_setModified(IsModified_SharedFolders);
5361 mHWData.backup();
5362 mHWData->mSharedFolders.push_back(sharedFolder);
5363
5364 /* inform the direct session if any */
5365 alock.release();
5366 i_onSharedFolderChange();
5367
5368 return S_OK;
5369}
5370
5371HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5372{
5373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5374
5375 HRESULT rc = i_checkStateDependency(MutableStateDep);
5376 if (FAILED(rc)) return rc;
5377
5378 ComObjPtr<SharedFolder> sharedFolder;
5379 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5380 if (FAILED(rc)) return rc;
5381
5382 i_setModified(IsModified_SharedFolders);
5383 mHWData.backup();
5384 mHWData->mSharedFolders.remove(sharedFolder);
5385
5386 /* inform the direct session if any */
5387 alock.release();
5388 i_onSharedFolderChange();
5389
5390 return S_OK;
5391}
5392
5393HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5394{
5395 /* start with No */
5396 *aCanShow = FALSE;
5397
5398 ComPtr<IInternalSessionControl> directControl;
5399 {
5400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5401
5402 if (mData->mSession.mState != SessionState_Locked)
5403 return setError(VBOX_E_INVALID_VM_STATE,
5404 tr("Machine is not locked for session (session state: %s)"),
5405 Global::stringifySessionState(mData->mSession.mState));
5406
5407 directControl = mData->mSession.mDirectControl;
5408 }
5409
5410 /* ignore calls made after #OnSessionEnd() is called */
5411 if (!directControl)
5412 return S_OK;
5413
5414 LONG64 dummy;
5415 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5416}
5417
5418HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5419{
5420 ComPtr<IInternalSessionControl> directControl;
5421 {
5422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5423
5424 if (mData->mSession.mState != SessionState_Locked)
5425 return setError(E_FAIL,
5426 tr("Machine is not locked for session (session state: %s)"),
5427 Global::stringifySessionState(mData->mSession.mState));
5428
5429 directControl = mData->mSession.mDirectControl;
5430 }
5431
5432 /* ignore calls made after #OnSessionEnd() is called */
5433 if (!directControl)
5434 return S_OK;
5435
5436 BOOL dummy;
5437 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5438}
5439
5440#ifdef VBOX_WITH_GUEST_PROPS
5441/**
5442 * Look up a guest property in VBoxSVC's internal structures.
5443 */
5444HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5445 com::Utf8Str &aValue,
5446 LONG64 *aTimestamp,
5447 com::Utf8Str &aFlags) const
5448{
5449 using namespace guestProp;
5450
5451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5452 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5453
5454 if (it != mHWData->mGuestProperties.end())
5455 {
5456 char szFlags[MAX_FLAGS_LEN + 1];
5457 aValue = it->second.strValue;
5458 *aTimestamp = it->second.mTimestamp;
5459 writeFlags(it->second.mFlags, szFlags);
5460 aFlags = Utf8Str(szFlags);
5461 }
5462
5463 return S_OK;
5464}
5465
5466/**
5467 * Query the VM that a guest property belongs to for the property.
5468 * @returns E_ACCESSDENIED if the VM process is not available or not
5469 * currently handling queries and the lookup should then be done in
5470 * VBoxSVC.
5471 */
5472HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5473 com::Utf8Str &aValue,
5474 LONG64 *aTimestamp,
5475 com::Utf8Str &aFlags) const
5476{
5477 HRESULT rc = S_OK;
5478 BSTR bValue = NULL;
5479 BSTR bFlags = NULL;
5480
5481 ComPtr<IInternalSessionControl> directControl;
5482 directControl = mData->mSession.mDirectControl;
5483
5484 /* fail if we were called after #OnSessionEnd() is called. This is a
5485 * silly race condition. */
5486
5487 /** @todo This code is bothering API clients (like python script clients) with
5488 * the AccessGuestProperty call, creating unncessary IPC. Need to
5489 * have a way of figuring out which kind of direct session it is... */
5490 if (!directControl)
5491 rc = E_ACCESSDENIED;
5492 else
5493 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), NULL, NULL,
5494 false /* isSetter */,
5495 &bValue, aTimestamp, &bFlags);
5496
5497 aValue = bValue;
5498 aFlags = bFlags;
5499
5500 return rc;
5501}
5502#endif // VBOX_WITH_GUEST_PROPS
5503
5504HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5505 com::Utf8Str &aValue,
5506 LONG64 *aTimestamp,
5507 com::Utf8Str &aFlags)
5508{
5509#ifndef VBOX_WITH_GUEST_PROPS
5510 ReturnComNotImplemented();
5511#else // VBOX_WITH_GUEST_PROPS
5512
5513 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5514
5515 if (rc == E_ACCESSDENIED)
5516 /* The VM is not running or the service is not (yet) accessible */
5517 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5518 return rc;
5519#endif // VBOX_WITH_GUEST_PROPS
5520}
5521
5522HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5523{
5524 LONG64 dummyTimestamp;
5525 com::Utf8Str dummyFlags;
5526 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5527 return rc;
5528
5529}
5530HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5531{
5532 com::Utf8Str dummyFlags;
5533 com::Utf8Str dummyValue;
5534 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5535 return rc;
5536}
5537
5538#ifdef VBOX_WITH_GUEST_PROPS
5539/**
5540 * Set a guest property in VBoxSVC's internal structures.
5541 */
5542HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5543 const com::Utf8Str &aFlags)
5544{
5545 using namespace guestProp;
5546
5547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5548 HRESULT rc = S_OK;
5549
5550 rc = i_checkStateDependency(MutableStateDep);
5551 if (FAILED(rc)) return rc;
5552
5553 try
5554 {
5555 uint32_t fFlags = NILFLAG;
5556 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5557 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5558
5559 bool fDelete = aValue.isEmpty();
5560 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5561 if (it == mHWData->mGuestProperties.end())
5562 {
5563 if (!fDelete)
5564 {
5565 i_setModified(IsModified_MachineData);
5566 mHWData.backupEx();
5567
5568 RTTIMESPEC time;
5569 HWData::GuestProperty prop;
5570 prop.strValue = Bstr(aValue).raw();
5571 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5572 prop.mFlags = fFlags;
5573 mHWData->mGuestProperties[aName] = prop;
5574 }
5575 }
5576 else
5577 {
5578 if (it->second.mFlags & (RDONLYHOST))
5579 {
5580 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5581 }
5582 else
5583 {
5584 i_setModified(IsModified_MachineData);
5585 mHWData.backupEx();
5586
5587 /* The backupEx() operation invalidates our iterator,
5588 * so get a new one. */
5589 it = mHWData->mGuestProperties.find(aName);
5590 Assert(it != mHWData->mGuestProperties.end());
5591
5592 if (!fDelete)
5593 {
5594 RTTIMESPEC time;
5595 it->second.strValue = aValue;
5596 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5597 it->second.mFlags = fFlags;
5598 }
5599 else
5600 mHWData->mGuestProperties.erase(it);
5601 }
5602 }
5603
5604 if ( SUCCEEDED(rc)
5605 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5606 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5607 RTSTR_MAX,
5608 aName.c_str(),
5609 RTSTR_MAX,
5610 NULL)
5611 )
5612 )
5613 {
5614 alock.release();
5615
5616 mParent->i_onGuestPropertyChange(mData->mUuid,
5617 Bstr(aName).raw(),
5618 Bstr(aValue).raw(),
5619 Bstr(aFlags).raw());
5620 }
5621 }
5622 catch (std::bad_alloc &)
5623 {
5624 rc = E_OUTOFMEMORY;
5625 }
5626
5627 return rc;
5628}
5629
5630/**
5631 * Set a property on the VM that that property belongs to.
5632 * @returns E_ACCESSDENIED if the VM process is not available or not
5633 * currently handling queries and the setting should then be done in
5634 * VBoxSVC.
5635 */
5636HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5637 const com::Utf8Str &aFlags)
5638{
5639 HRESULT rc;
5640
5641 try
5642 {
5643 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5644
5645 BSTR dummy = NULL; /* will not be changed (setter) */
5646 LONG64 dummy64;
5647 if (!directControl)
5648 rc = E_ACCESSDENIED;
5649 else
5650 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5651 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5652 true /* isSetter */,
5653 &dummy, &dummy64, &dummy);
5654 }
5655 catch (std::bad_alloc &)
5656 {
5657 rc = E_OUTOFMEMORY;
5658 }
5659
5660 return rc;
5661}
5662#endif // VBOX_WITH_GUEST_PROPS
5663
5664HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5665 const com::Utf8Str &aFlags)
5666{
5667#ifndef VBOX_WITH_GUEST_PROPS
5668 ReturnComNotImplemented();
5669#else // VBOX_WITH_GUEST_PROPS
5670 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags);
5671 if (rc == E_ACCESSDENIED)
5672 /* The VM is not running or the service is not (yet) accessible */
5673 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags);
5674 return rc;
5675#endif // VBOX_WITH_GUEST_PROPS
5676}
5677
5678HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5679{
5680 return setGuestProperty(aProperty, aValue, "");
5681}
5682
5683HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5684{
5685 return setGuestProperty(aName, "", "");
5686}
5687
5688#ifdef VBOX_WITH_GUEST_PROPS
5689/**
5690 * Enumerate the guest properties in VBoxSVC's internal structures.
5691 */
5692HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5693 std::vector<com::Utf8Str> &aNames,
5694 std::vector<com::Utf8Str> &aValues,
5695 std::vector<LONG64> &aTimestamps,
5696 std::vector<com::Utf8Str> &aFlags)
5697{
5698 using namespace guestProp;
5699
5700 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5701 Utf8Str strPatterns(aPatterns);
5702
5703 HWData::GuestPropertyMap propMap;
5704
5705 /*
5706 * Look for matching patterns and build up a list.
5707 */
5708 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5709 while (it != mHWData->mGuestProperties.end())
5710 {
5711 if ( strPatterns.isEmpty()
5712 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5713 RTSTR_MAX,
5714 it->first.c_str(),
5715 RTSTR_MAX,
5716 NULL)
5717 )
5718 propMap.insert(*it);
5719 it++;
5720 }
5721
5722 alock.release();
5723
5724 /*
5725 * And build up the arrays for returning the property information.
5726 */
5727 size_t cEntries = propMap.size();
5728
5729 aNames.resize(cEntries);
5730 aValues.resize(cEntries);
5731 aTimestamps.resize(cEntries);
5732 aFlags.resize(cEntries);
5733
5734 char szFlags[MAX_FLAGS_LEN + 1];
5735 size_t i= 0;
5736 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5737 {
5738 aNames[i] = it->first;
5739 aValues[i] = it->second.strValue;
5740 aTimestamps[i] = it->second.mTimestamp;
5741 writeFlags(it->second.mFlags, szFlags);
5742 aFlags[i] = Utf8Str(szFlags);
5743 }
5744
5745 return S_OK;
5746}
5747
5748/**
5749 * Enumerate the properties managed by a VM.
5750 * @returns E_ACCESSDENIED if the VM process is not available or not
5751 * currently handling queries and the setting should then be done in
5752 * VBoxSVC.
5753 */
5754HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5755 std::vector<com::Utf8Str> &aNames,
5756 std::vector<com::Utf8Str> &aValues,
5757 std::vector<LONG64> &aTimestamps,
5758 std::vector<com::Utf8Str> &aFlags)
5759{
5760 HRESULT rc;
5761 ComPtr<IInternalSessionControl> directControl;
5762 directControl = mData->mSession.mDirectControl;
5763
5764
5765 com::SafeArray<BSTR> bNames;
5766 com::SafeArray<BSTR> bValues;
5767 com::SafeArray<LONG64> bTimestamps;
5768 com::SafeArray<BSTR> bFlags;
5769
5770 if (!directControl)
5771 rc = E_ACCESSDENIED;
5772 else
5773 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5774 ComSafeArrayAsOutParam(bNames),
5775 ComSafeArrayAsOutParam(bValues),
5776 ComSafeArrayAsOutParam(bTimestamps),
5777 ComSafeArrayAsOutParam(bFlags));
5778 size_t i;
5779 aNames.resize(bNames.size());
5780 for (i = 0; i < bNames.size(); ++i)
5781 aNames[i] = Utf8Str(bNames[i]);
5782 aValues.resize(bValues.size());
5783 for (i = 0; i < bValues.size(); ++i)
5784 aValues[i] = Utf8Str(bValues[i]);
5785 aTimestamps.resize(bTimestamps.size());
5786 for (i = 0; i < bTimestamps.size(); ++i)
5787 aTimestamps[i] = bTimestamps[i];
5788 aFlags.resize(bFlags.size());
5789 for (i = 0; i < bFlags.size(); ++i)
5790 aFlags[i] = Utf8Str(bFlags[i]);
5791
5792 return rc;
5793}
5794#endif // VBOX_WITH_GUEST_PROPS
5795HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5796 std::vector<com::Utf8Str> &aNames,
5797 std::vector<com::Utf8Str> &aValues,
5798 std::vector<LONG64> &aTimestamps,
5799 std::vector<com::Utf8Str> &aFlags)
5800{
5801#ifndef VBOX_WITH_GUEST_PROPS
5802 ReturnComNotImplemented();
5803#else // VBOX_WITH_GUEST_PROPS
5804
5805 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5806
5807 if (rc == E_ACCESSDENIED)
5808 /* The VM is not running or the service is not (yet) accessible */
5809 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5810 return rc;
5811#endif // VBOX_WITH_GUEST_PROPS
5812}
5813
5814HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5815 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5816{
5817 MediaData::AttachmentList atts;
5818
5819 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5820 if (FAILED(rc)) return rc;
5821
5822 size_t i = 0;
5823 aMediumAttachments.resize(atts.size());
5824 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5825 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5826
5827 return S_OK;
5828}
5829
5830HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5831 LONG aControllerPort,
5832 LONG aDevice,
5833 ComPtr<IMediumAttachment> &aAttachment)
5834{
5835 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5836 aName.c_str(), aControllerPort, aDevice));
5837
5838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5839
5840 aAttachment = NULL;
5841
5842 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5843 Bstr(aName).raw(),
5844 aControllerPort,
5845 aDevice);
5846 if (pAttach.isNull())
5847 return setError(VBOX_E_OBJECT_NOT_FOUND,
5848 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5849 aDevice, aControllerPort, aName.c_str());
5850
5851 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5852
5853 return S_OK;
5854}
5855
5856
5857HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5858 StorageBus_T aConnectionType,
5859 ComPtr<IStorageController> &aController)
5860{
5861 if ( (aConnectionType <= StorageBus_Null)
5862 || (aConnectionType > StorageBus_USB))
5863 return setError(E_INVALIDARG,
5864 tr("Invalid connection type: %d"),
5865 aConnectionType);
5866
5867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5868
5869 HRESULT rc = i_checkStateDependency(MutableStateDep);
5870 if (FAILED(rc)) return rc;
5871
5872 /* try to find one with the name first. */
5873 ComObjPtr<StorageController> ctrl;
5874
5875 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5876 if (SUCCEEDED(rc))
5877 return setError(VBOX_E_OBJECT_IN_USE,
5878 tr("Storage controller named '%s' already exists"),
5879 aName.c_str());
5880
5881 ctrl.createObject();
5882
5883 /* get a new instance number for the storage controller */
5884 ULONG ulInstance = 0;
5885 bool fBootable = true;
5886 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5887 it != mStorageControllers->end();
5888 ++it)
5889 {
5890 if ((*it)->i_getStorageBus() == aConnectionType)
5891 {
5892 ULONG ulCurInst = (*it)->i_getInstance();
5893
5894 if (ulCurInst >= ulInstance)
5895 ulInstance = ulCurInst + 1;
5896
5897 /* Only one controller of each type can be marked as bootable. */
5898 if ((*it)->i_getBootable())
5899 fBootable = false;
5900 }
5901 }
5902
5903 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5904 if (FAILED(rc)) return rc;
5905
5906 i_setModified(IsModified_Storage);
5907 mStorageControllers.backup();
5908 mStorageControllers->push_back(ctrl);
5909
5910 ctrl.queryInterfaceTo(aController.asOutParam());
5911
5912 /* inform the direct session if any */
5913 alock.release();
5914 i_onStorageControllerChange();
5915
5916 return S_OK;
5917}
5918
5919HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5920 ComPtr<IStorageController> &aStorageController)
5921{
5922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5923
5924 ComObjPtr<StorageController> ctrl;
5925
5926 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5927 if (SUCCEEDED(rc))
5928 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5929
5930 return rc;
5931}
5932
5933HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
5934 ComPtr<IStorageController> &aStorageController)
5935{
5936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5937
5938 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5939 it != mStorageControllers->end();
5940 ++it)
5941 {
5942 if ((*it)->i_getInstance() == aInstance)
5943 {
5944 (*it).queryInterfaceTo(aStorageController.asOutParam());
5945 return S_OK;
5946 }
5947 }
5948
5949 return setError(VBOX_E_OBJECT_NOT_FOUND,
5950 tr("Could not find a storage controller with instance number '%lu'"),
5951 aInstance);
5952}
5953
5954HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5955{
5956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5957
5958 HRESULT rc = i_checkStateDependency(MutableStateDep);
5959 if (FAILED(rc)) return rc;
5960
5961 ComObjPtr<StorageController> ctrl;
5962
5963 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5964 if (SUCCEEDED(rc))
5965 {
5966 /* Ensure that only one controller of each type is marked as bootable. */
5967 if (aBootable == TRUE)
5968 {
5969 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5970 it != mStorageControllers->end();
5971 ++it)
5972 {
5973 ComObjPtr<StorageController> aCtrl = (*it);
5974
5975 if ( (aCtrl->i_getName() != aName)
5976 && aCtrl->i_getBootable() == TRUE
5977 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5978 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5979 {
5980 aCtrl->i_setBootable(FALSE);
5981 break;
5982 }
5983 }
5984 }
5985
5986 if (SUCCEEDED(rc))
5987 {
5988 ctrl->i_setBootable(aBootable);
5989 i_setModified(IsModified_Storage);
5990 }
5991 }
5992
5993 if (SUCCEEDED(rc))
5994 {
5995 /* inform the direct session if any */
5996 alock.release();
5997 i_onStorageControllerChange();
5998 }
5999
6000 return rc;
6001}
6002
6003HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6004{
6005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6006
6007 HRESULT rc = i_checkStateDependency(MutableStateDep);
6008 if (FAILED(rc)) return rc;
6009
6010 ComObjPtr<StorageController> ctrl;
6011 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6012 if (FAILED(rc)) return rc;
6013
6014 {
6015 /* find all attached devices to the appropriate storage controller and detach them all */
6016 // make a temporary list because detachDevice invalidates iterators into
6017 // mMediaData->mAttachments
6018 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6019
6020 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6021 it != llAttachments2.end();
6022 ++it)
6023 {
6024 MediumAttachment *pAttachTemp = *it;
6025
6026 AutoCaller localAutoCaller(pAttachTemp);
6027 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6028
6029 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6030
6031 if (pAttachTemp->i_getControllerName() == aName)
6032 {
6033 rc = i_detachDevice(pAttachTemp, alock, NULL);
6034 if (FAILED(rc)) return rc;
6035 }
6036 }
6037 }
6038
6039 /* We can remove it now. */
6040 i_setModified(IsModified_Storage);
6041 mStorageControllers.backup();
6042
6043 ctrl->i_unshare();
6044
6045 mStorageControllers->remove(ctrl);
6046
6047 /* inform the direct session if any */
6048 alock.release();
6049 i_onStorageControllerChange();
6050
6051 return S_OK;
6052}
6053
6054HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6055 ComPtr<IUSBController> &aController)
6056{
6057 if ( (aType <= USBControllerType_Null)
6058 || (aType >= USBControllerType_Last))
6059 return setError(E_INVALIDARG,
6060 tr("Invalid USB controller type: %d"),
6061 aType);
6062
6063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6064
6065 HRESULT rc = i_checkStateDependency(MutableStateDep);
6066 if (FAILED(rc)) return rc;
6067
6068 /* try to find one with the same type first. */
6069 ComObjPtr<USBController> ctrl;
6070
6071 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6072 if (SUCCEEDED(rc))
6073 return setError(VBOX_E_OBJECT_IN_USE,
6074 tr("USB controller named '%s' already exists"),
6075 aName.c_str());
6076
6077 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6078 ULONG maxInstances;
6079 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6080 if (FAILED(rc))
6081 return rc;
6082
6083 ULONG cInstances = i_getUSBControllerCountByType(aType);
6084 if (cInstances >= maxInstances)
6085 return setError(E_INVALIDARG,
6086 tr("Too many USB controllers of this type"));
6087
6088 ctrl.createObject();
6089
6090 rc = ctrl->init(this, aName, aType);
6091 if (FAILED(rc)) return rc;
6092
6093 i_setModified(IsModified_USB);
6094 mUSBControllers.backup();
6095 mUSBControllers->push_back(ctrl);
6096
6097 ctrl.queryInterfaceTo(aController.asOutParam());
6098
6099 /* inform the direct session if any */
6100 alock.release();
6101 i_onUSBControllerChange();
6102
6103 return S_OK;
6104}
6105
6106HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6107{
6108 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6109
6110 ComObjPtr<USBController> ctrl;
6111
6112 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6113 if (SUCCEEDED(rc))
6114 ctrl.queryInterfaceTo(aController.asOutParam());
6115
6116 return rc;
6117}
6118
6119HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6120 ULONG *aControllers)
6121{
6122 if ( (aType <= USBControllerType_Null)
6123 || (aType >= USBControllerType_Last))
6124 return setError(E_INVALIDARG,
6125 tr("Invalid USB controller type: %d"),
6126 aType);
6127
6128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6129
6130 ComObjPtr<USBController> ctrl;
6131
6132 *aControllers = i_getUSBControllerCountByType(aType);
6133
6134 return S_OK;
6135}
6136
6137HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6138{
6139
6140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6141
6142 HRESULT rc = i_checkStateDependency(MutableStateDep);
6143 if (FAILED(rc)) return rc;
6144
6145 ComObjPtr<USBController> ctrl;
6146 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6147 if (FAILED(rc)) return rc;
6148
6149 i_setModified(IsModified_USB);
6150 mUSBControllers.backup();
6151
6152 ctrl->i_unshare();
6153
6154 mUSBControllers->remove(ctrl);
6155
6156 /* inform the direct session if any */
6157 alock.release();
6158 i_onUSBControllerChange();
6159
6160 return S_OK;
6161}
6162
6163HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6164 ULONG *aOriginX,
6165 ULONG *aOriginY,
6166 ULONG *aWidth,
6167 ULONG *aHeight,
6168 BOOL *aEnabled)
6169{
6170 uint32_t u32OriginX= 0;
6171 uint32_t u32OriginY= 0;
6172 uint32_t u32Width = 0;
6173 uint32_t u32Height = 0;
6174 uint16_t u16Flags = 0;
6175
6176 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6177 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6178 if (RT_FAILURE(vrc))
6179 {
6180#ifdef RT_OS_WINDOWS
6181 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6182 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6183 * So just assign fEnable to TRUE again.
6184 * The right fix would be to change GUI API wrappers to make sure that parameters
6185 * are changed only if API succeeds.
6186 */
6187 *aEnabled = TRUE;
6188#endif
6189 return setError(VBOX_E_IPRT_ERROR,
6190 tr("Saved guest size is not available (%Rrc)"),
6191 vrc);
6192 }
6193
6194 *aOriginX = u32OriginX;
6195 *aOriginY = u32OriginY;
6196 *aWidth = u32Width;
6197 *aHeight = u32Height;
6198 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6199
6200 return S_OK;
6201}
6202
6203HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6204{
6205 if (aScreenId != 0)
6206 return E_NOTIMPL;
6207
6208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6209
6210 uint8_t *pu8Data = NULL;
6211 uint32_t cbData = 0;
6212 uint32_t u32Width = 0;
6213 uint32_t u32Height = 0;
6214
6215 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6216
6217 if (RT_FAILURE(vrc))
6218 return setError(VBOX_E_IPRT_ERROR,
6219 tr("Saved screenshot data is not available (%Rrc)"),
6220 vrc);
6221
6222 *aSize = cbData;
6223 *aWidth = u32Width;
6224 *aHeight = u32Height;
6225
6226 freeSavedDisplayScreenshot(pu8Data);
6227
6228 return S_OK;
6229}
6230
6231
6232HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6233{
6234 if (aScreenId != 0)
6235 return E_NOTIMPL;
6236
6237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6238
6239 uint8_t *pu8Data = NULL;
6240 uint32_t cbData = 0;
6241 uint32_t u32Width = 0;
6242 uint32_t u32Height = 0;
6243
6244 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6245
6246 if (RT_FAILURE(vrc))
6247 return setError(VBOX_E_IPRT_ERROR,
6248 tr("Saved screenshot data is not available (%Rrc)"),
6249 vrc);
6250
6251 *aWidth = u32Width;
6252 *aHeight = u32Height;
6253
6254 com::SafeArray<BYTE> bitmap(cbData);
6255 /* Convert pixels to format expected by the API caller. */
6256 if (aBGR)
6257 {
6258 /* [0] B, [1] G, [2] R, [3] A. */
6259 for (unsigned i = 0; i < cbData; i += 4)
6260 {
6261 bitmap[i] = pu8Data[i];
6262 bitmap[i + 1] = pu8Data[i + 1];
6263 bitmap[i + 2] = pu8Data[i + 2];
6264 bitmap[i + 3] = 0xff;
6265 }
6266 }
6267 else
6268 {
6269 /* [0] R, [1] G, [2] B, [3] A. */
6270 for (unsigned i = 0; i < cbData; i += 4)
6271 {
6272 bitmap[i] = pu8Data[i + 2];
6273 bitmap[i + 1] = pu8Data[i + 1];
6274 bitmap[i + 2] = pu8Data[i];
6275 bitmap[i + 3] = 0xff;
6276 }
6277 }
6278 aData.resize(bitmap.size());
6279 for (size_t i = 0; i < bitmap.size(); ++i)
6280 aData[i] = bitmap[i];
6281
6282 freeSavedDisplayScreenshot(pu8Data);
6283
6284 return S_OK;
6285}
6286
6287HRESULT Machine::readSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6288{
6289 if (aScreenId != 0)
6290 return E_NOTIMPL;
6291
6292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6293
6294 uint8_t *pu8Data = NULL;
6295 uint32_t cbData = 0;
6296 uint32_t u32Width = 0;
6297 uint32_t u32Height = 0;
6298
6299 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6300
6301 if (RT_FAILURE(vrc))
6302 return setError(VBOX_E_IPRT_ERROR,
6303 tr("Saved screenshot data is not available (%Rrc)"),
6304 vrc);
6305
6306 *aWidth = u32Width;
6307 *aHeight = u32Height;
6308
6309 HRESULT rc = S_OK;
6310 uint8_t *pu8PNG = NULL;
6311 uint32_t cbPNG = 0;
6312 uint32_t cxPNG = 0;
6313 uint32_t cyPNG = 0;
6314
6315 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6316
6317 if (RT_SUCCESS(vrc))
6318 {
6319 com::SafeArray<BYTE> screenData(cbPNG);
6320 screenData.initFrom(pu8PNG, cbPNG);
6321 if (pu8PNG)
6322 RTMemFree(pu8PNG);
6323 aData.resize(screenData.size());
6324 for (size_t i = 0; i < screenData.size(); ++i)
6325 aData[i] = screenData[i];
6326 }
6327 else
6328 {
6329 if (pu8PNG)
6330 RTMemFree(pu8PNG);
6331 return setError(VBOX_E_IPRT_ERROR,
6332 tr("Could not convert screenshot to PNG (%Rrc)"),
6333 vrc);
6334 }
6335
6336 freeSavedDisplayScreenshot(pu8Data);
6337
6338 return rc;
6339}
6340
6341HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6342{
6343 if (aScreenId != 0)
6344 return E_NOTIMPL;
6345
6346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6347
6348 uint8_t *pu8Data = NULL;
6349 uint32_t cbData = 0;
6350 uint32_t u32Width = 0;
6351 uint32_t u32Height = 0;
6352
6353 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6354
6355 if (RT_FAILURE(vrc))
6356 return setError(VBOX_E_IPRT_ERROR,
6357 tr("Saved screenshot data is not available (%Rrc)"),
6358 vrc);
6359
6360 *aSize = cbData;
6361 *aWidth = u32Width;
6362 *aHeight = u32Height;
6363
6364 freeSavedDisplayScreenshot(pu8Data);
6365
6366 return S_OK;
6367}
6368
6369HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6370{
6371 if (aScreenId != 0)
6372 return E_NOTIMPL;
6373
6374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6375
6376 uint8_t *pu8Data = NULL;
6377 uint32_t cbData = 0;
6378 uint32_t u32Width = 0;
6379 uint32_t u32Height = 0;
6380
6381 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6382
6383 if (RT_FAILURE(vrc))
6384 return setError(VBOX_E_IPRT_ERROR,
6385 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6386 vrc);
6387
6388 *aWidth = u32Width;
6389 *aHeight = u32Height;
6390
6391 com::SafeArray<BYTE> png(cbData);
6392 png.initFrom(pu8Data, cbData);
6393 aData.resize(png.size());
6394 for (size_t i = 0; i < png.size(); ++i)
6395 aData[i] = png[i];
6396
6397 freeSavedDisplayScreenshot(pu8Data);
6398
6399 return S_OK;
6400}
6401
6402HRESULT Machine::hotPlugCPU(ULONG aCpu)
6403{
6404 HRESULT rc = S_OK;
6405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6406
6407 if (!mHWData->mCPUHotPlugEnabled)
6408 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6409
6410 if (aCpu >= mHWData->mCPUCount)
6411 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6412
6413 if (mHWData->mCPUAttached[aCpu])
6414 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6415
6416 alock.release();
6417 rc = i_onCPUChange(aCpu, false);
6418 alock.acquire();
6419 if (FAILED(rc)) return rc;
6420
6421 i_setModified(IsModified_MachineData);
6422 mHWData.backup();
6423 mHWData->mCPUAttached[aCpu] = true;
6424
6425 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6426 if (Global::IsOnline(mData->mMachineState))
6427 i_saveSettings(NULL);
6428
6429 return S_OK;
6430}
6431
6432HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6433{
6434 HRESULT rc = S_OK;
6435
6436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6437
6438 if (!mHWData->mCPUHotPlugEnabled)
6439 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6440
6441 if (aCpu >= SchemaDefs::MaxCPUCount)
6442 return setError(E_INVALIDARG,
6443 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6444 SchemaDefs::MaxCPUCount);
6445
6446 if (!mHWData->mCPUAttached[aCpu])
6447 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6448
6449 /* CPU 0 can't be detached */
6450 if (aCpu == 0)
6451 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6452
6453 alock.release();
6454 rc = i_onCPUChange(aCpu, true);
6455 alock.acquire();
6456 if (FAILED(rc)) return rc;
6457
6458 i_setModified(IsModified_MachineData);
6459 mHWData.backup();
6460 mHWData->mCPUAttached[aCpu] = false;
6461
6462 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6463 if (Global::IsOnline(mData->mMachineState))
6464 i_saveSettings(NULL);
6465
6466 return S_OK;
6467}
6468
6469HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6470{
6471 *aAttached = false;
6472
6473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6474
6475 /* If hotplug is enabled the CPU is always enabled. */
6476 if (!mHWData->mCPUHotPlugEnabled)
6477 {
6478 if (aCpu < mHWData->mCPUCount)
6479 *aAttached = true;
6480 }
6481 else
6482 {
6483 if (aCpu < SchemaDefs::MaxCPUCount)
6484 *aAttached = mHWData->mCPUAttached[aCpu];
6485 }
6486
6487 return S_OK;
6488}
6489
6490HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6491{
6492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6493
6494 Utf8Str log = i_queryLogFilename(aIdx);
6495 if (!RTFileExists(log.c_str()))
6496 log.setNull();
6497 aFilename = log;
6498
6499 return S_OK;
6500}
6501
6502HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6503{
6504 if (aSize < 0)
6505 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6506
6507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6508
6509 HRESULT rc = S_OK;
6510 Utf8Str log = i_queryLogFilename(aIdx);
6511
6512 /* do not unnecessarily hold the lock while doing something which does
6513 * not need the lock and potentially takes a long time. */
6514 alock.release();
6515
6516 /* Limit the chunk size to 32K for now, as that gives better performance
6517 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6518 * One byte expands to approx. 25 bytes of breathtaking XML. */
6519 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6520 com::SafeArray<BYTE> logData(cbData);
6521
6522 RTFILE LogFile;
6523 int vrc = RTFileOpen(&LogFile, log.c_str(),
6524 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6525 if (RT_SUCCESS(vrc))
6526 {
6527 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6528 if (RT_SUCCESS(vrc))
6529 logData.resize(cbData);
6530 else
6531 rc = setError(VBOX_E_IPRT_ERROR,
6532 tr("Could not read log file '%s' (%Rrc)"),
6533 log.c_str(), vrc);
6534 RTFileClose(LogFile);
6535 }
6536 else
6537 rc = setError(VBOX_E_IPRT_ERROR,
6538 tr("Could not open log file '%s' (%Rrc)"),
6539 log.c_str(), vrc);
6540
6541 if (FAILED(rc))
6542 logData.resize(0);
6543
6544 aData.resize(logData.size());
6545 for (size_t i = 0; i < logData.size(); ++i)
6546 aData[i] = logData[i];
6547
6548 return rc;
6549}
6550
6551
6552/**
6553 * Currently this method doesn't attach device to the running VM,
6554 * just makes sure it's plugged on next VM start.
6555 */
6556HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6557{
6558 // lock scope
6559 {
6560 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6561
6562 HRESULT rc = i_checkStateDependency(MutableStateDep);
6563 if (FAILED(rc)) return rc;
6564
6565 ChipsetType_T aChipset = ChipsetType_PIIX3;
6566 COMGETTER(ChipsetType)(&aChipset);
6567
6568 if (aChipset != ChipsetType_ICH9)
6569 {
6570 return setError(E_INVALIDARG,
6571 tr("Host PCI attachment only supported with ICH9 chipset"));
6572 }
6573
6574 // check if device with this host PCI address already attached
6575 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6576 it != mHWData->mPCIDeviceAssignments.end();
6577 ++it)
6578 {
6579 LONG iHostAddress = -1;
6580 ComPtr<PCIDeviceAttachment> pAttach;
6581 pAttach = *it;
6582 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6583 if (iHostAddress == aHostAddress)
6584 return setError(E_INVALIDARG,
6585 tr("Device with host PCI address already attached to this VM"));
6586 }
6587
6588 ComObjPtr<PCIDeviceAttachment> pda;
6589 char name[32];
6590
6591 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6592 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6593 Bstr bname(name);
6594 pda.createObject();
6595 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6596 i_setModified(IsModified_MachineData);
6597 mHWData.backup();
6598 mHWData->mPCIDeviceAssignments.push_back(pda);
6599 }
6600
6601 return S_OK;
6602}
6603
6604/**
6605 * Currently this method doesn't detach device from the running VM,
6606 * just makes sure it's not plugged on next VM start.
6607 */
6608HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6609{
6610 ComObjPtr<PCIDeviceAttachment> pAttach;
6611 bool fRemoved = false;
6612 HRESULT rc;
6613
6614 // lock scope
6615 {
6616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6617
6618 rc = i_checkStateDependency(MutableStateDep);
6619 if (FAILED(rc)) return rc;
6620
6621 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6622 it != mHWData->mPCIDeviceAssignments.end();
6623 ++it)
6624 {
6625 LONG iHostAddress = -1;
6626 pAttach = *it;
6627 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6628 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6629 {
6630 i_setModified(IsModified_MachineData);
6631 mHWData.backup();
6632 mHWData->mPCIDeviceAssignments.remove(pAttach);
6633 fRemoved = true;
6634 break;
6635 }
6636 }
6637 }
6638
6639
6640 /* Fire event outside of the lock */
6641 if (fRemoved)
6642 {
6643 Assert(!pAttach.isNull());
6644 ComPtr<IEventSource> es;
6645 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6646 Assert(SUCCEEDED(rc));
6647 Bstr mid;
6648 rc = this->COMGETTER(Id)(mid.asOutParam());
6649 Assert(SUCCEEDED(rc));
6650 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6651 }
6652
6653 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6654 tr("No host PCI device %08x attached"),
6655 aHostAddress
6656 );
6657}
6658
6659HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6660{
6661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6662
6663 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6664
6665 size_t i = 0;
6666 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6667 it != mHWData->mPCIDeviceAssignments.end();
6668 ++i, ++it)
6669 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6670
6671 return S_OK;
6672}
6673
6674HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6675{
6676 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6677
6678 return S_OK;
6679}
6680
6681HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6682{
6683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6684
6685 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6686
6687 return S_OK;
6688}
6689
6690HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6691{
6692 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6693 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6694 if (SUCCEEDED(hrc))
6695 {
6696 hrc = mHWData.backupEx();
6697 if (SUCCEEDED(hrc))
6698 {
6699 i_setModified(IsModified_MachineData);
6700 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6701 }
6702 }
6703 return hrc;
6704}
6705
6706HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6707{
6708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6709 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6710 return S_OK;
6711}
6712
6713HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6714{
6715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6716 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6717 if (SUCCEEDED(hrc))
6718 {
6719 hrc = mHWData.backupEx();
6720 if (SUCCEEDED(hrc))
6721 {
6722 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6723 if (SUCCEEDED(hrc))
6724 i_setModified(IsModified_MachineData);
6725 }
6726 }
6727 return hrc;
6728}
6729
6730HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6731{
6732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6733
6734 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6735
6736 return S_OK;
6737}
6738
6739HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6740{
6741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6742 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6743 if (SUCCEEDED(hrc))
6744 {
6745 hrc = mHWData.backupEx();
6746 if (SUCCEEDED(hrc))
6747 {
6748 i_setModified(IsModified_MachineData);
6749 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6750 }
6751 }
6752 return hrc;
6753}
6754
6755HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6756{
6757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6758
6759 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6760
6761 return S_OK;
6762}
6763
6764HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6765{
6766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6767
6768 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6769 if ( SUCCEEDED(hrc)
6770 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6771 {
6772 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6773 int vrc;
6774
6775 if (aAutostartEnabled)
6776 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6777 else
6778 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6779
6780 if (RT_SUCCESS(vrc))
6781 {
6782 hrc = mHWData.backupEx();
6783 if (SUCCEEDED(hrc))
6784 {
6785 i_setModified(IsModified_MachineData);
6786 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6787 }
6788 }
6789 else if (vrc == VERR_NOT_SUPPORTED)
6790 hrc = setError(VBOX_E_NOT_SUPPORTED,
6791 tr("The VM autostart feature is not supported on this platform"));
6792 else if (vrc == VERR_PATH_NOT_FOUND)
6793 hrc = setError(E_FAIL,
6794 tr("The path to the autostart database is not set"));
6795 else
6796 hrc = setError(E_UNEXPECTED,
6797 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6798 aAutostartEnabled ? "Adding" : "Removing",
6799 mUserData->s.strName.c_str(), vrc);
6800 }
6801 return hrc;
6802}
6803
6804HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6805{
6806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6807
6808 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6809
6810 return S_OK;
6811}
6812
6813HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6814{
6815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6816 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6817 if (SUCCEEDED(hrc))
6818 {
6819 hrc = mHWData.backupEx();
6820 if (SUCCEEDED(hrc))
6821 {
6822 i_setModified(IsModified_MachineData);
6823 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6824 }
6825 }
6826 return hrc;
6827}
6828
6829HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6830{
6831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6832
6833 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6834
6835 return S_OK;
6836}
6837
6838HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6839{
6840 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6841 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6842 if ( SUCCEEDED(hrc)
6843 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6844 {
6845 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6846 int vrc;
6847
6848 if (aAutostopType != AutostopType_Disabled)
6849 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6850 else
6851 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6852
6853 if (RT_SUCCESS(vrc))
6854 {
6855 hrc = mHWData.backupEx();
6856 if (SUCCEEDED(hrc))
6857 {
6858 i_setModified(IsModified_MachineData);
6859 mHWData->mAutostart.enmAutostopType = aAutostopType;
6860 }
6861 }
6862 else if (vrc == VERR_NOT_SUPPORTED)
6863 hrc = setError(VBOX_E_NOT_SUPPORTED,
6864 tr("The VM autostop feature is not supported on this platform"));
6865 else if (vrc == VERR_PATH_NOT_FOUND)
6866 hrc = setError(E_FAIL,
6867 tr("The path to the autostart database is not set"));
6868 else
6869 hrc = setError(E_UNEXPECTED,
6870 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6871 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6872 mUserData->s.strName.c_str(), vrc);
6873 }
6874 return hrc;
6875}
6876
6877HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6878{
6879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6880
6881 aDefaultFrontend = mHWData->mDefaultFrontend;
6882
6883 return S_OK;
6884}
6885
6886HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6887{
6888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6889 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6890 if (SUCCEEDED(hrc))
6891 {
6892 hrc = mHWData.backupEx();
6893 if (SUCCEEDED(hrc))
6894 {
6895 i_setModified(IsModified_MachineData);
6896 mHWData->mDefaultFrontend = aDefaultFrontend;
6897 }
6898 }
6899 return hrc;
6900}
6901
6902HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6903{
6904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6905 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
6906 aIcon.resize(mUserData->mIcon.size());
6907 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
6908 aIcon.resize(icon.size());
6909 for (size_t i = 0; i < icon.size(); ++i)
6910 aIcon[i] = icon[i];
6911 return S_OK;
6912}
6913
6914HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6915{
6916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6917 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6918 if (SUCCEEDED(hrc))
6919 {
6920 i_setModified(IsModified_MachineData);
6921 mUserData.backup();
6922 com::SafeArray<BYTE> icon(aIcon);
6923 mUserData->mIcon.resize(aIcon.size());
6924 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
6925 }
6926 return hrc;
6927}
6928
6929HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6930{
6931
6932#ifdef VBOX_WITH_USB
6933 *aUSBProxyAvailable = true;
6934#else
6935 *aUSBProxyAvailable = false;
6936#endif
6937 return S_OK;
6938}
6939
6940HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6941 ComPtr<IProgress> &aProgress)
6942{
6943 ComObjPtr<Progress> pP;
6944 Progress *ppP = pP;
6945 IProgress *iP = static_cast<IProgress *>(ppP);
6946 IProgress **pProgress = &iP;
6947
6948 IMachine *pTarget = aTarget;
6949
6950 /* Convert the options. */
6951 RTCList<CloneOptions_T> optList;
6952 if (aOptions.size())
6953 for (size_t i = 0; i < aOptions.size(); ++i)
6954 optList.append(aOptions[i]);
6955
6956 if (optList.contains(CloneOptions_Link))
6957 {
6958 if (!i_isSnapshotMachine())
6959 return setError(E_INVALIDARG,
6960 tr("Linked clone can only be created from a snapshot"));
6961 if (aMode != CloneMode_MachineState)
6962 return setError(E_INVALIDARG,
6963 tr("Linked clone can only be created for a single machine state"));
6964 }
6965 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6966
6967 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6968
6969 HRESULT rc = pWorker->start(pProgress);
6970
6971 pP = static_cast<Progress *>(*pProgress);
6972 pP.queryInterfaceTo(aProgress.asOutParam());
6973
6974 return rc;
6975
6976}
6977
6978// public methods for internal purposes
6979/////////////////////////////////////////////////////////////////////////////
6980
6981/**
6982 * Adds the given IsModified_* flag to the dirty flags of the machine.
6983 * This must be called either during i_loadSettings or under the machine write lock.
6984 * @param fl
6985 */
6986void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6987{
6988 mData->flModifications |= fl;
6989 if (fAllowStateModification && i_isStateModificationAllowed())
6990 mData->mCurrentStateModified = true;
6991}
6992
6993/**
6994 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6995 * care of the write locking.
6996 *
6997 * @param fModifications The flag to add.
6998 */
6999void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7000{
7001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7002 i_setModified(fModification, fAllowStateModification);
7003}
7004
7005/**
7006 * Saves the registry entry of this machine to the given configuration node.
7007 *
7008 * @param aEntryNode Node to save the registry entry to.
7009 *
7010 * @note locks this object for reading.
7011 */
7012HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7013{
7014 AutoLimitedCaller autoCaller(this);
7015 AssertComRCReturnRC(autoCaller.rc());
7016
7017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7018
7019 data.uuid = mData->mUuid;
7020 data.strSettingsFile = mData->m_strConfigFile;
7021
7022 return S_OK;
7023}
7024
7025/**
7026 * Calculates the absolute path of the given path taking the directory of the
7027 * machine settings file as the current directory.
7028 *
7029 * @param aPath Path to calculate the absolute path for.
7030 * @param aResult Where to put the result (used only on success, can be the
7031 * same Utf8Str instance as passed in @a aPath).
7032 * @return IPRT result.
7033 *
7034 * @note Locks this object for reading.
7035 */
7036int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7037{
7038 AutoCaller autoCaller(this);
7039 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7040
7041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7042
7043 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7044
7045 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7046
7047 strSettingsDir.stripFilename();
7048 char folder[RTPATH_MAX];
7049 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7050 if (RT_SUCCESS(vrc))
7051 aResult = folder;
7052
7053 return vrc;
7054}
7055
7056/**
7057 * Copies strSource to strTarget, making it relative to the machine folder
7058 * if it is a subdirectory thereof, or simply copying it otherwise.
7059 *
7060 * @param strSource Path to evaluate and copy.
7061 * @param strTarget Buffer to receive target path.
7062 *
7063 * @note Locks this object for reading.
7064 */
7065void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7066 Utf8Str &strTarget)
7067{
7068 AutoCaller autoCaller(this);
7069 AssertComRCReturn(autoCaller.rc(), (void)0);
7070
7071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7072
7073 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7074 // use strTarget as a temporary buffer to hold the machine settings dir
7075 strTarget = mData->m_strConfigFileFull;
7076 strTarget.stripFilename();
7077 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7078 {
7079 // is relative: then append what's left
7080 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7081 // for empty paths (only possible for subdirs) use "." to avoid
7082 // triggering default settings for not present config attributes.
7083 if (strTarget.isEmpty())
7084 strTarget = ".";
7085 }
7086 else
7087 // is not relative: then overwrite
7088 strTarget = strSource;
7089}
7090
7091/**
7092 * Returns the full path to the machine's log folder in the
7093 * \a aLogFolder argument.
7094 */
7095void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7096{
7097 AutoCaller autoCaller(this);
7098 AssertComRCReturnVoid(autoCaller.rc());
7099
7100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7101
7102 char szTmp[RTPATH_MAX];
7103 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7104 if (RT_SUCCESS(vrc))
7105 {
7106 if (szTmp[0] && !mUserData.isNull())
7107 {
7108 char szTmp2[RTPATH_MAX];
7109 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7110 if (RT_SUCCESS(vrc))
7111 aLogFolder = BstrFmt("%s%c%s",
7112 szTmp2,
7113 RTPATH_DELIMITER,
7114 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7115 }
7116 else
7117 vrc = VERR_PATH_IS_RELATIVE;
7118 }
7119
7120 if (RT_FAILURE(vrc))
7121 {
7122 // fallback if VBOX_USER_LOGHOME is not set or invalid
7123 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7124 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7125 aLogFolder.append(RTPATH_DELIMITER);
7126 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7127 }
7128}
7129
7130/**
7131 * Returns the full path to the machine's log file for an given index.
7132 */
7133Utf8Str Machine::i_queryLogFilename(ULONG idx)
7134{
7135 Utf8Str logFolder;
7136 getLogFolder(logFolder);
7137 Assert(logFolder.length());
7138 Utf8Str log;
7139 if (idx == 0)
7140 log = Utf8StrFmt("%s%cVBox.log",
7141 logFolder.c_str(), RTPATH_DELIMITER);
7142 else
7143 log = Utf8StrFmt("%s%cVBox.log.%d",
7144 logFolder.c_str(), RTPATH_DELIMITER, idx);
7145 return log;
7146}
7147
7148/**
7149 * Composes a unique saved state filename based on the current system time. The filename is
7150 * granular to the second so this will work so long as no more than one snapshot is taken on
7151 * a machine per second.
7152 *
7153 * Before version 4.1, we used this formula for saved state files:
7154 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7155 * which no longer works because saved state files can now be shared between the saved state of the
7156 * "saved" machine and an online snapshot, and the following would cause problems:
7157 * 1) save machine
7158 * 2) create online snapshot from that machine state --> reusing saved state file
7159 * 3) save machine again --> filename would be reused, breaking the online snapshot
7160 *
7161 * So instead we now use a timestamp.
7162 *
7163 * @param str
7164 */
7165
7166void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7167{
7168 AutoCaller autoCaller(this);
7169 AssertComRCReturnVoid(autoCaller.rc());
7170
7171 {
7172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7173 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7174 }
7175
7176 RTTIMESPEC ts;
7177 RTTimeNow(&ts);
7178 RTTIME time;
7179 RTTimeExplode(&time, &ts);
7180
7181 strStateFilePath += RTPATH_DELIMITER;
7182 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7183 time.i32Year, time.u8Month, time.u8MonthDay,
7184 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7185}
7186
7187/**
7188 * Returns the full path to the default video capture file.
7189 */
7190void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7191{
7192 AutoCaller autoCaller(this);
7193 AssertComRCReturnVoid(autoCaller.rc());
7194
7195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7196
7197 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7198 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7199 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7200}
7201
7202/**
7203 * Returns whether at least one USB controller is present for the VM.
7204 */
7205bool Machine::i_isUSBControllerPresent()
7206{
7207 AutoCaller autoCaller(this);
7208 AssertComRCReturn(autoCaller.rc(), false);
7209
7210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7211
7212 return (mUSBControllers->size() > 0);
7213}
7214
7215/**
7216 * @note Locks this object for writing, calls the client process
7217 * (inside the lock).
7218 */
7219HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7220 const Utf8Str &strFrontend,
7221 const Utf8Str &strEnvironment,
7222 ProgressProxy *aProgress)
7223{
7224 LogFlowThisFuncEnter();
7225
7226 AssertReturn(aControl, E_FAIL);
7227 AssertReturn(aProgress, E_FAIL);
7228 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7229
7230 AutoCaller autoCaller(this);
7231 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7232
7233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7234
7235 if (!mData->mRegistered)
7236 return setError(E_UNEXPECTED,
7237 tr("The machine '%s' is not registered"),
7238 mUserData->s.strName.c_str());
7239
7240 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7241
7242 if ( mData->mSession.mState == SessionState_Locked
7243 || mData->mSession.mState == SessionState_Spawning
7244 || mData->mSession.mState == SessionState_Unlocking)
7245 return setError(VBOX_E_INVALID_OBJECT_STATE,
7246 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7247 mUserData->s.strName.c_str());
7248
7249 /* may not be busy */
7250 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7251
7252 /* get the path to the executable */
7253 char szPath[RTPATH_MAX];
7254 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7255 size_t sz = strlen(szPath);
7256 szPath[sz++] = RTPATH_DELIMITER;
7257 szPath[sz] = 0;
7258 char *cmd = szPath + sz;
7259 sz = sizeof(szPath) - sz;
7260
7261 int vrc = VINF_SUCCESS;
7262 RTPROCESS pid = NIL_RTPROCESS;
7263
7264 RTENV env = RTENV_DEFAULT;
7265
7266 if (!strEnvironment.isEmpty())
7267 {
7268 char *newEnvStr = NULL;
7269
7270 do
7271 {
7272 /* clone the current environment */
7273 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7274 AssertRCBreakStmt(vrc2, vrc = vrc2);
7275
7276 newEnvStr = RTStrDup(strEnvironment.c_str());
7277 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7278
7279 /* put new variables to the environment
7280 * (ignore empty variable names here since RTEnv API
7281 * intentionally doesn't do that) */
7282 char *var = newEnvStr;
7283 for (char *p = newEnvStr; *p; ++p)
7284 {
7285 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7286 {
7287 *p = '\0';
7288 if (*var)
7289 {
7290 char *val = strchr(var, '=');
7291 if (val)
7292 {
7293 *val++ = '\0';
7294 vrc2 = RTEnvSetEx(env, var, val);
7295 }
7296 else
7297 vrc2 = RTEnvUnsetEx(env, var);
7298 if (RT_FAILURE(vrc2))
7299 break;
7300 }
7301 var = p + 1;
7302 }
7303 }
7304 if (RT_SUCCESS(vrc2) && *var)
7305 vrc2 = RTEnvPutEx(env, var);
7306
7307 AssertRCBreakStmt(vrc2, vrc = vrc2);
7308 }
7309 while (0);
7310
7311 if (newEnvStr != NULL)
7312 RTStrFree(newEnvStr);
7313 }
7314
7315#ifdef VBOX_WITH_QTGUI
7316 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7317 {
7318# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7319 /* Modify the base path so that we don't need to use ".." below. */
7320 RTPathStripTrailingSlash(szPath);
7321 RTPathStripFilename(szPath);
7322 sz = strlen(szPath);
7323 cmd = szPath + sz;
7324 sz = sizeof(szPath) - sz;
7325
7326#define OSX_APP_NAME "VirtualBoxVM"
7327#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7328
7329 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7330 if ( strAppOverride.contains(".")
7331 || strAppOverride.contains("/")
7332 || strAppOverride.contains("\\")
7333 || strAppOverride.contains(":"))
7334 strAppOverride.setNull();
7335 Utf8Str strAppPath;
7336 if (!strAppOverride.isEmpty())
7337 {
7338 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7339 Utf8Str strFullPath(szPath);
7340 strFullPath.append(strAppPath);
7341 /* there is a race, but people using this deserve the failure */
7342 if (!RTFileExists(strFullPath.c_str()))
7343 strAppOverride.setNull();
7344 }
7345 if (strAppOverride.isEmpty())
7346 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7347 const char *VirtualBox_exe = strAppPath.c_str();
7348 AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED);
7349# else
7350 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7351 Assert(sz >= sizeof(VirtualBox_exe));
7352# endif
7353 strcpy(cmd, VirtualBox_exe);
7354
7355 Utf8Str idStr = mData->mUuid.toString();
7356 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7357 "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7358 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7359 }
7360#else /* !VBOX_WITH_QTGUI */
7361 if (0)
7362 ;
7363#endif /* VBOX_WITH_QTGUI */
7364
7365 else
7366
7367#ifdef VBOX_WITH_VBOXSDL
7368 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7369 {
7370 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7371 Assert(sz >= sizeof(VBoxSDL_exe));
7372 strcpy(cmd, VBoxSDL_exe);
7373
7374 Utf8Str idStr = mData->mUuid.toString();
7375 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7376 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7377 }
7378#else /* !VBOX_WITH_VBOXSDL */
7379 if (0)
7380 ;
7381#endif /* !VBOX_WITH_VBOXSDL */
7382
7383 else
7384
7385#ifdef VBOX_WITH_HEADLESS
7386 if ( strFrontend == "headless"
7387 || strFrontend == "capture"
7388 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7389 )
7390 {
7391 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7392 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7393 * and a VM works even if the server has not been installed.
7394 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7395 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7396 * differently in 4.0 and 3.x.
7397 */
7398 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7399 Assert(sz >= sizeof(VBoxHeadless_exe));
7400 strcpy(cmd, VBoxHeadless_exe);
7401
7402 Utf8Str idStr = mData->mUuid.toString();
7403 /* Leave space for "--capture" arg. */
7404 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7405 "--startvm", idStr.c_str(),
7406 "--vrde", "config",
7407 0, /* For "--capture". */
7408 0 };
7409 if (strFrontend == "capture")
7410 {
7411 unsigned pos = RT_ELEMENTS(args) - 2;
7412 args[pos] = "--capture";
7413 }
7414 vrc = RTProcCreate(szPath, args, env,
7415#ifdef RT_OS_WINDOWS
7416 RTPROC_FLAGS_NO_WINDOW
7417#else
7418 0
7419#endif
7420 , &pid);
7421 }
7422#else /* !VBOX_WITH_HEADLESS */
7423 if (0)
7424 ;
7425#endif /* !VBOX_WITH_HEADLESS */
7426 else
7427 {
7428 RTEnvDestroy(env);
7429 return setError(E_INVALIDARG,
7430 tr("Invalid frontend name: '%s'"),
7431 strFrontend.c_str());
7432 }
7433
7434 RTEnvDestroy(env);
7435
7436 if (RT_FAILURE(vrc))
7437 return setError(VBOX_E_IPRT_ERROR,
7438 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7439 mUserData->s.strName.c_str(), vrc);
7440
7441 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7442
7443 /*
7444 * Note that we don't release the lock here before calling the client,
7445 * because it doesn't need to call us back if called with a NULL argument.
7446 * Releasing the lock here is dangerous because we didn't prepare the
7447 * launch data yet, but the client we've just started may happen to be
7448 * too fast and call LockMachine() that will fail (because of PID, etc.),
7449 * so that the Machine will never get out of the Spawning session state.
7450 */
7451
7452 /* inform the session that it will be a remote one */
7453 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7454#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7455 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7456#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7457 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7458#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7459 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7460
7461 if (FAILED(rc))
7462 {
7463 /* restore the session state */
7464 mData->mSession.mState = SessionState_Unlocked;
7465 alock.release();
7466 mParent->i_addProcessToReap(pid);
7467 /* The failure may occur w/o any error info (from RPC), so provide one */
7468 return setError(VBOX_E_VM_ERROR,
7469 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7470 }
7471
7472 /* attach launch data to the machine */
7473 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7474 mData->mSession.mRemoteControls.push_back(aControl);
7475 mData->mSession.mProgress = aProgress;
7476 mData->mSession.mPID = pid;
7477 mData->mSession.mState = SessionState_Spawning;
7478 mData->mSession.mType = strFrontend;
7479
7480 alock.release();
7481 mParent->i_addProcessToReap(pid);
7482
7483 LogFlowThisFuncLeave();
7484 return S_OK;
7485}
7486
7487/**
7488 * Returns @c true if the given session machine instance has an open direct
7489 * session (and optionally also for direct sessions which are closing) and
7490 * returns the session control machine instance if so.
7491 *
7492 * Note that when the method returns @c false, the arguments remain unchanged.
7493 *
7494 * @param aMachine Session machine object.
7495 * @param aControl Direct session control object (optional).
7496 * @param aAllowClosing If true then additionally a session which is currently
7497 * being closed will also be allowed.
7498 *
7499 * @note locks this object for reading.
7500 */
7501bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7502 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7503 bool aAllowClosing /*= false*/)
7504{
7505 AutoLimitedCaller autoCaller(this);
7506 AssertComRCReturn(autoCaller.rc(), false);
7507
7508 /* just return false for inaccessible machines */
7509 if (getObjectState().getState() != ObjectState::Ready)
7510 return false;
7511
7512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7513
7514 if ( mData->mSession.mState == SessionState_Locked
7515 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7516 )
7517 {
7518 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7519
7520 aMachine = mData->mSession.mMachine;
7521
7522 if (aControl != NULL)
7523 *aControl = mData->mSession.mDirectControl;
7524
7525 return true;
7526 }
7527
7528 return false;
7529}
7530
7531/**
7532 * Returns @c true if the given machine has an spawning direct session.
7533 *
7534 * @note locks this object for reading.
7535 */
7536bool Machine::i_isSessionSpawning()
7537{
7538 AutoLimitedCaller autoCaller(this);
7539 AssertComRCReturn(autoCaller.rc(), false);
7540
7541 /* just return false for inaccessible machines */
7542 if (getObjectState().getState() != ObjectState::Ready)
7543 return false;
7544
7545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7546
7547 if (mData->mSession.mState == SessionState_Spawning)
7548 return true;
7549
7550 return false;
7551}
7552
7553/**
7554 * Called from the client watcher thread to check for unexpected client process
7555 * death during Session_Spawning state (e.g. before it successfully opened a
7556 * direct session).
7557 *
7558 * On Win32 and on OS/2, this method is called only when we've got the
7559 * direct client's process termination notification, so it always returns @c
7560 * true.
7561 *
7562 * On other platforms, this method returns @c true if the client process is
7563 * terminated and @c false if it's still alive.
7564 *
7565 * @note Locks this object for writing.
7566 */
7567bool Machine::i_checkForSpawnFailure()
7568{
7569 AutoCaller autoCaller(this);
7570 if (!autoCaller.isOk())
7571 {
7572 /* nothing to do */
7573 LogFlowThisFunc(("Already uninitialized!\n"));
7574 return true;
7575 }
7576
7577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7578
7579 if (mData->mSession.mState != SessionState_Spawning)
7580 {
7581 /* nothing to do */
7582 LogFlowThisFunc(("Not spawning any more!\n"));
7583 return true;
7584 }
7585
7586 HRESULT rc = S_OK;
7587
7588 /* PID not yet initialized, skip check. */
7589 if (mData->mSession.mPID == NIL_RTPROCESS)
7590 return false;
7591
7592 RTPROCSTATUS status;
7593 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7594
7595 if (vrc != VERR_PROCESS_RUNNING)
7596 {
7597 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7598 rc = setError(E_FAIL,
7599 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7600 i_getName().c_str(), status.iStatus);
7601 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7602 rc = setError(E_FAIL,
7603 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7604 i_getName().c_str(), status.iStatus);
7605 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7606 rc = setError(E_FAIL,
7607 tr("The virtual machine '%s' has terminated abnormally"),
7608 i_getName().c_str(), status.iStatus);
7609 else
7610 rc = setError(E_FAIL,
7611 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7612 i_getName().c_str(), vrc);
7613 }
7614
7615 if (FAILED(rc))
7616 {
7617 /* Close the remote session, remove the remote control from the list
7618 * and reset session state to Closed (@note keep the code in sync with
7619 * the relevant part in LockMachine()). */
7620
7621 Assert(mData->mSession.mRemoteControls.size() == 1);
7622 if (mData->mSession.mRemoteControls.size() == 1)
7623 {
7624 ErrorInfoKeeper eik;
7625 mData->mSession.mRemoteControls.front()->Uninitialize();
7626 }
7627
7628 mData->mSession.mRemoteControls.clear();
7629 mData->mSession.mState = SessionState_Unlocked;
7630
7631 /* finalize the progress after setting the state */
7632 if (!mData->mSession.mProgress.isNull())
7633 {
7634 mData->mSession.mProgress->notifyComplete(rc);
7635 mData->mSession.mProgress.setNull();
7636 }
7637
7638 mData->mSession.mPID = NIL_RTPROCESS;
7639
7640 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7641 return true;
7642 }
7643
7644 return false;
7645}
7646
7647/**
7648 * Checks whether the machine can be registered. If so, commits and saves
7649 * all settings.
7650 *
7651 * @note Must be called from mParent's write lock. Locks this object and
7652 * children for writing.
7653 */
7654HRESULT Machine::i_prepareRegister()
7655{
7656 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7657
7658 AutoLimitedCaller autoCaller(this);
7659 AssertComRCReturnRC(autoCaller.rc());
7660
7661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7662
7663 /* wait for state dependents to drop to zero */
7664 i_ensureNoStateDependencies();
7665
7666 if (!mData->mAccessible)
7667 return setError(VBOX_E_INVALID_OBJECT_STATE,
7668 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7669 mUserData->s.strName.c_str(),
7670 mData->mUuid.toString().c_str());
7671
7672 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7673
7674 if (mData->mRegistered)
7675 return setError(VBOX_E_INVALID_OBJECT_STATE,
7676 tr("The machine '%s' with UUID {%s} is already registered"),
7677 mUserData->s.strName.c_str(),
7678 mData->mUuid.toString().c_str());
7679
7680 HRESULT rc = S_OK;
7681
7682 // Ensure the settings are saved. If we are going to be registered and
7683 // no config file exists yet, create it by calling i_saveSettings() too.
7684 if ( (mData->flModifications)
7685 || (!mData->pMachineConfigFile->fileExists())
7686 )
7687 {
7688 rc = i_saveSettings(NULL);
7689 // no need to check whether VirtualBox.xml needs saving too since
7690 // we can't have a machine XML file rename pending
7691 if (FAILED(rc)) return rc;
7692 }
7693
7694 /* more config checking goes here */
7695
7696 if (SUCCEEDED(rc))
7697 {
7698 /* we may have had implicit modifications we want to fix on success */
7699 i_commit();
7700
7701 mData->mRegistered = true;
7702 }
7703 else
7704 {
7705 /* we may have had implicit modifications we want to cancel on failure*/
7706 i_rollback(false /* aNotify */);
7707 }
7708
7709 return rc;
7710}
7711
7712/**
7713 * Increases the number of objects dependent on the machine state or on the
7714 * registered state. Guarantees that these two states will not change at least
7715 * until #releaseStateDependency() is called.
7716 *
7717 * Depending on the @a aDepType value, additional state checks may be made.
7718 * These checks will set extended error info on failure. See
7719 * #checkStateDependency() for more info.
7720 *
7721 * If this method returns a failure, the dependency is not added and the caller
7722 * is not allowed to rely on any particular machine state or registration state
7723 * value and may return the failed result code to the upper level.
7724 *
7725 * @param aDepType Dependency type to add.
7726 * @param aState Current machine state (NULL if not interested).
7727 * @param aRegistered Current registered state (NULL if not interested).
7728 *
7729 * @note Locks this object for writing.
7730 */
7731HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7732 MachineState_T *aState /* = NULL */,
7733 BOOL *aRegistered /* = NULL */)
7734{
7735 AutoCaller autoCaller(this);
7736 AssertComRCReturnRC(autoCaller.rc());
7737
7738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7739
7740 HRESULT rc = i_checkStateDependency(aDepType);
7741 if (FAILED(rc)) return rc;
7742
7743 {
7744 if (mData->mMachineStateChangePending != 0)
7745 {
7746 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7747 * drop to zero so don't add more. It may make sense to wait a bit
7748 * and retry before reporting an error (since the pending state
7749 * transition should be really quick) but let's just assert for
7750 * now to see if it ever happens on practice. */
7751
7752 AssertFailed();
7753
7754 return setError(E_ACCESSDENIED,
7755 tr("Machine state change is in progress. Please retry the operation later."));
7756 }
7757
7758 ++mData->mMachineStateDeps;
7759 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7760 }
7761
7762 if (aState)
7763 *aState = mData->mMachineState;
7764 if (aRegistered)
7765 *aRegistered = mData->mRegistered;
7766
7767 return S_OK;
7768}
7769
7770/**
7771 * Decreases the number of objects dependent on the machine state.
7772 * Must always complete the #addStateDependency() call after the state
7773 * dependency is no more necessary.
7774 */
7775void Machine::i_releaseStateDependency()
7776{
7777 AutoCaller autoCaller(this);
7778 AssertComRCReturnVoid(autoCaller.rc());
7779
7780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7781
7782 /* releaseStateDependency() w/o addStateDependency()? */
7783 AssertReturnVoid(mData->mMachineStateDeps != 0);
7784 -- mData->mMachineStateDeps;
7785
7786 if (mData->mMachineStateDeps == 0)
7787 {
7788 /* inform i_ensureNoStateDependencies() that there are no more deps */
7789 if (mData->mMachineStateChangePending != 0)
7790 {
7791 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7792 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7793 }
7794 }
7795}
7796
7797Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7798{
7799 /* start with nothing found */
7800 Utf8Str strResult("");
7801
7802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7803
7804 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7805 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7806 // found:
7807 strResult = it->second; // source is a Utf8Str
7808
7809 return strResult;
7810}
7811
7812// protected methods
7813/////////////////////////////////////////////////////////////////////////////
7814
7815/**
7816 * Performs machine state checks based on the @a aDepType value. If a check
7817 * fails, this method will set extended error info, otherwise it will return
7818 * S_OK. It is supposed, that on failure, the caller will immediately return
7819 * the return value of this method to the upper level.
7820 *
7821 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7822 *
7823 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7824 * current state of this machine object allows to change settings of the
7825 * machine (i.e. the machine is not registered, or registered but not running
7826 * and not saved). It is useful to call this method from Machine setters
7827 * before performing any change.
7828 *
7829 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7830 * as for MutableStateDep except that if the machine is saved, S_OK is also
7831 * returned. This is useful in setters which allow changing machine
7832 * properties when it is in the saved state.
7833 *
7834 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7835 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7836 * Aborted).
7837 *
7838 * @param aDepType Dependency type to check.
7839 *
7840 * @note Non Machine based classes should use #addStateDependency() and
7841 * #releaseStateDependency() methods or the smart AutoStateDependency
7842 * template.
7843 *
7844 * @note This method must be called from under this object's read or write
7845 * lock.
7846 */
7847HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7848{
7849 switch (aDepType)
7850 {
7851 case AnyStateDep:
7852 {
7853 break;
7854 }
7855 case MutableStateDep:
7856 {
7857 if ( mData->mRegistered
7858 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7859 Paused should actually be included here... (Live Migration) */
7860 || ( mData->mMachineState != MachineState_Paused
7861 && mData->mMachineState != MachineState_Running
7862 && mData->mMachineState != MachineState_Aborted
7863 && mData->mMachineState != MachineState_Teleported
7864 && mData->mMachineState != MachineState_PoweredOff
7865 )
7866 )
7867 )
7868 return setError(VBOX_E_INVALID_VM_STATE,
7869 tr("The machine is not mutable (state is %s)"),
7870 Global::stringifyMachineState(mData->mMachineState));
7871 break;
7872 }
7873 case MutableOrSavedStateDep:
7874 {
7875 if ( mData->mRegistered
7876 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7877 Paused should actually be included here... (Live Migration) */
7878 || ( mData->mMachineState != MachineState_Paused
7879 && mData->mMachineState != MachineState_Running
7880 && mData->mMachineState != MachineState_Aborted
7881 && mData->mMachineState != MachineState_Teleported
7882 && mData->mMachineState != MachineState_Saved
7883 && mData->mMachineState != MachineState_PoweredOff
7884 )
7885 )
7886 )
7887 return setError(VBOX_E_INVALID_VM_STATE,
7888 tr("The machine is not mutable (state is %s)"),
7889 Global::stringifyMachineState(mData->mMachineState));
7890 break;
7891 }
7892 case OfflineStateDep:
7893 {
7894 if ( mData->mRegistered
7895 && ( !i_isSessionMachine()
7896 || ( mData->mMachineState != MachineState_PoweredOff
7897 && mData->mMachineState != MachineState_Saved
7898 && mData->mMachineState != MachineState_Aborted
7899 && mData->mMachineState != MachineState_Teleported
7900 )
7901 )
7902 )
7903 return setError(VBOX_E_INVALID_VM_STATE,
7904 tr("The machine is not offline (state is %s)"),
7905 Global::stringifyMachineState(mData->mMachineState));
7906 break;
7907 }
7908 }
7909
7910 return S_OK;
7911}
7912
7913/**
7914 * Helper to initialize all associated child objects and allocate data
7915 * structures.
7916 *
7917 * This method must be called as a part of the object's initialization procedure
7918 * (usually done in the #init() method).
7919 *
7920 * @note Must be called only from #init() or from #registeredInit().
7921 */
7922HRESULT Machine::initDataAndChildObjects()
7923{
7924 AutoCaller autoCaller(this);
7925 AssertComRCReturnRC(autoCaller.rc());
7926 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
7927 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
7928
7929 AssertReturn(!mData->mAccessible, E_FAIL);
7930
7931 /* allocate data structures */
7932 mSSData.allocate();
7933 mUserData.allocate();
7934 mHWData.allocate();
7935 mMediaData.allocate();
7936 mStorageControllers.allocate();
7937 mUSBControllers.allocate();
7938
7939 /* initialize mOSTypeId */
7940 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
7941
7942 /* create associated BIOS settings object */
7943 unconst(mBIOSSettings).createObject();
7944 mBIOSSettings->init(this);
7945
7946 /* create an associated VRDE object (default is disabled) */
7947 unconst(mVRDEServer).createObject();
7948 mVRDEServer->init(this);
7949
7950 /* create associated serial port objects */
7951 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
7952 {
7953 unconst(mSerialPorts[slot]).createObject();
7954 mSerialPorts[slot]->init(this, slot);
7955 }
7956
7957 /* create associated parallel port objects */
7958 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
7959 {
7960 unconst(mParallelPorts[slot]).createObject();
7961 mParallelPorts[slot]->init(this, slot);
7962 }
7963
7964 /* create the audio adapter object (always present, default is disabled) */
7965 unconst(mAudioAdapter).createObject();
7966 mAudioAdapter->init(this);
7967
7968 /* create the USB device filters object (always present) */
7969 unconst(mUSBDeviceFilters).createObject();
7970 mUSBDeviceFilters->init(this);
7971
7972 /* create associated network adapter objects */
7973 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7974 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
7975 {
7976 unconst(mNetworkAdapters[slot]).createObject();
7977 mNetworkAdapters[slot]->init(this, slot);
7978 }
7979
7980 /* create the bandwidth control */
7981 unconst(mBandwidthControl).createObject();
7982 mBandwidthControl->init(this);
7983
7984 return S_OK;
7985}
7986
7987/**
7988 * Helper to uninitialize all associated child objects and to free all data
7989 * structures.
7990 *
7991 * This method must be called as a part of the object's uninitialization
7992 * procedure (usually done in the #uninit() method).
7993 *
7994 * @note Must be called only from #uninit() or from #registeredInit().
7995 */
7996void Machine::uninitDataAndChildObjects()
7997{
7998 AutoCaller autoCaller(this);
7999 AssertComRCReturnVoid(autoCaller.rc());
8000 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8001 || getObjectState().getState() == ObjectState::Limited);
8002
8003 /* tell all our other child objects we've been uninitialized */
8004 if (mBandwidthControl)
8005 {
8006 mBandwidthControl->uninit();
8007 unconst(mBandwidthControl).setNull();
8008 }
8009
8010 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8011 {
8012 if (mNetworkAdapters[slot])
8013 {
8014 mNetworkAdapters[slot]->uninit();
8015 unconst(mNetworkAdapters[slot]).setNull();
8016 }
8017 }
8018
8019 if (mUSBDeviceFilters)
8020 {
8021 mUSBDeviceFilters->uninit();
8022 unconst(mUSBDeviceFilters).setNull();
8023 }
8024
8025 if (mAudioAdapter)
8026 {
8027 mAudioAdapter->uninit();
8028 unconst(mAudioAdapter).setNull();
8029 }
8030
8031 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8032 {
8033 if (mParallelPorts[slot])
8034 {
8035 mParallelPorts[slot]->uninit();
8036 unconst(mParallelPorts[slot]).setNull();
8037 }
8038 }
8039
8040 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8041 {
8042 if (mSerialPorts[slot])
8043 {
8044 mSerialPorts[slot]->uninit();
8045 unconst(mSerialPorts[slot]).setNull();
8046 }
8047 }
8048
8049 if (mVRDEServer)
8050 {
8051 mVRDEServer->uninit();
8052 unconst(mVRDEServer).setNull();
8053 }
8054
8055 if (mBIOSSettings)
8056 {
8057 mBIOSSettings->uninit();
8058 unconst(mBIOSSettings).setNull();
8059 }
8060
8061 /* Deassociate media (only when a real Machine or a SnapshotMachine
8062 * instance is uninitialized; SessionMachine instances refer to real
8063 * Machine media). This is necessary for a clean re-initialization of
8064 * the VM after successfully re-checking the accessibility state. Note
8065 * that in case of normal Machine or SnapshotMachine uninitialization (as
8066 * a result of unregistering or deleting the snapshot), outdated media
8067 * attachments will already be uninitialized and deleted, so this
8068 * code will not affect them. */
8069 if ( !!mMediaData
8070 && (!i_isSessionMachine())
8071 )
8072 {
8073 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8074 it != mMediaData->mAttachments.end();
8075 ++it)
8076 {
8077 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8078 if (pMedium.isNull())
8079 continue;
8080 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8081 AssertComRC(rc);
8082 }
8083 }
8084
8085 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8086 {
8087 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8088 if (mData->mFirstSnapshot)
8089 {
8090 // snapshots tree is protected by machine write lock; strictly
8091 // this isn't necessary here since we're deleting the entire
8092 // machine, but otherwise we assert in Snapshot::uninit()
8093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8094 mData->mFirstSnapshot->uninit();
8095 mData->mFirstSnapshot.setNull();
8096 }
8097
8098 mData->mCurrentSnapshot.setNull();
8099 }
8100
8101 /* free data structures (the essential mData structure is not freed here
8102 * since it may be still in use) */
8103 mMediaData.free();
8104 mStorageControllers.free();
8105 mUSBControllers.free();
8106 mHWData.free();
8107 mUserData.free();
8108 mSSData.free();
8109}
8110
8111/**
8112 * Returns a pointer to the Machine object for this machine that acts like a
8113 * parent for complex machine data objects such as shared folders, etc.
8114 *
8115 * For primary Machine objects and for SnapshotMachine objects, returns this
8116 * object's pointer itself. For SessionMachine objects, returns the peer
8117 * (primary) machine pointer.
8118 */
8119Machine* Machine::i_getMachine()
8120{
8121 if (i_isSessionMachine())
8122 return (Machine*)mPeer;
8123 return this;
8124}
8125
8126/**
8127 * Makes sure that there are no machine state dependents. If necessary, waits
8128 * for the number of dependents to drop to zero.
8129 *
8130 * Make sure this method is called from under this object's write lock to
8131 * guarantee that no new dependents may be added when this method returns
8132 * control to the caller.
8133 *
8134 * @note Locks this object for writing. The lock will be released while waiting
8135 * (if necessary).
8136 *
8137 * @warning To be used only in methods that change the machine state!
8138 */
8139void Machine::i_ensureNoStateDependencies()
8140{
8141 AssertReturnVoid(isWriteLockOnCurrentThread());
8142
8143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8144
8145 /* Wait for all state dependents if necessary */
8146 if (mData->mMachineStateDeps != 0)
8147 {
8148 /* lazy semaphore creation */
8149 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8150 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8151
8152 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8153 mData->mMachineStateDeps));
8154
8155 ++mData->mMachineStateChangePending;
8156
8157 /* reset the semaphore before waiting, the last dependent will signal
8158 * it */
8159 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8160
8161 alock.release();
8162
8163 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8164
8165 alock.acquire();
8166
8167 -- mData->mMachineStateChangePending;
8168 }
8169}
8170
8171/**
8172 * Changes the machine state and informs callbacks.
8173 *
8174 * This method is not intended to fail so it either returns S_OK or asserts (and
8175 * returns a failure).
8176 *
8177 * @note Locks this object for writing.
8178 */
8179HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8180{
8181 LogFlowThisFuncEnter();
8182 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8183
8184 AutoCaller autoCaller(this);
8185 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8186
8187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8188
8189 /* wait for state dependents to drop to zero */
8190 i_ensureNoStateDependencies();
8191
8192 if (mData->mMachineState != aMachineState)
8193 {
8194 mData->mMachineState = aMachineState;
8195
8196 RTTimeNow(&mData->mLastStateChange);
8197
8198 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8199 }
8200
8201 LogFlowThisFuncLeave();
8202 return S_OK;
8203}
8204
8205/**
8206 * Searches for a shared folder with the given logical name
8207 * in the collection of shared folders.
8208 *
8209 * @param aName logical name of the shared folder
8210 * @param aSharedFolder where to return the found object
8211 * @param aSetError whether to set the error info if the folder is
8212 * not found
8213 * @return
8214 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8215 *
8216 * @note
8217 * must be called from under the object's lock!
8218 */
8219HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8220 ComObjPtr<SharedFolder> &aSharedFolder,
8221 bool aSetError /* = false */)
8222{
8223 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8224 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8225 it != mHWData->mSharedFolders.end();
8226 ++it)
8227 {
8228 SharedFolder *pSF = *it;
8229 AutoCaller autoCaller(pSF);
8230 if (pSF->i_getName() == aName)
8231 {
8232 aSharedFolder = pSF;
8233 rc = S_OK;
8234 break;
8235 }
8236 }
8237
8238 if (aSetError && FAILED(rc))
8239 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8240
8241 return rc;
8242}
8243
8244/**
8245 * Initializes all machine instance data from the given settings structures
8246 * from XML. The exception is the machine UUID which needs special handling
8247 * depending on the caller's use case, so the caller needs to set that herself.
8248 *
8249 * This gets called in several contexts during machine initialization:
8250 *
8251 * -- When machine XML exists on disk already and needs to be loaded into memory,
8252 * for example, from registeredInit() to load all registered machines on
8253 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8254 * attached to the machine should be part of some media registry already.
8255 *
8256 * -- During OVF import, when a machine config has been constructed from an
8257 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8258 * ensure that the media listed as attachments in the config (which have
8259 * been imported from the OVF) receive the correct registry ID.
8260 *
8261 * -- During VM cloning.
8262 *
8263 * @param config Machine settings from XML.
8264 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8265 * for each attached medium in the config.
8266 * @return
8267 */
8268HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8269 const Guid *puuidRegistry)
8270{
8271 // copy name, description, OS type, teleporter, UTC etc.
8272 mUserData->s = config.machineUserData;
8273
8274 // Decode the Icon overide data from config userdata and set onto Machine.
8275 #define DECODE_STR_MAX _1M
8276 const char* pszStr = config.machineUserData.ovIcon.c_str();
8277 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8278 if (cbOut > DECODE_STR_MAX)
8279 return setError(E_FAIL,
8280 tr("Icon Data too long.'%d' > '%d'"),
8281 cbOut,
8282 DECODE_STR_MAX);
8283 com::SafeArray<BYTE> iconByte(cbOut);
8284 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8285 if (FAILED(rc))
8286 return setError(E_FAIL,
8287 tr("Failure to Decode Icon Data. '%s' (%d)"),
8288 pszStr,
8289 rc);
8290 mUserData->mIcon.resize(iconByte.size());
8291 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
8292
8293 // look up the object by Id to check it is valid
8294 ComPtr<IGuestOSType> guestOSType;
8295 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8296 guestOSType.asOutParam());
8297 if (FAILED(rc)) return rc;
8298
8299 // stateFile (optional)
8300 if (config.strStateFile.isEmpty())
8301 mSSData->strStateFilePath.setNull();
8302 else
8303 {
8304 Utf8Str stateFilePathFull(config.strStateFile);
8305 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8306 if (RT_FAILURE(vrc))
8307 return setError(E_FAIL,
8308 tr("Invalid saved state file path '%s' (%Rrc)"),
8309 config.strStateFile.c_str(),
8310 vrc);
8311 mSSData->strStateFilePath = stateFilePathFull;
8312 }
8313
8314 // snapshot folder needs special processing so set it again
8315 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8316 if (FAILED(rc)) return rc;
8317
8318 /* Copy the extra data items (Not in any case config is already the same as
8319 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8320 * make sure the extra data map is copied). */
8321 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8322
8323 /* currentStateModified (optional, default is true) */
8324 mData->mCurrentStateModified = config.fCurrentStateModified;
8325
8326 mData->mLastStateChange = config.timeLastStateChange;
8327
8328 /*
8329 * note: all mUserData members must be assigned prior this point because
8330 * we need to commit changes in order to let mUserData be shared by all
8331 * snapshot machine instances.
8332 */
8333 mUserData.commitCopy();
8334
8335 // machine registry, if present (must be loaded before snapshots)
8336 if (config.canHaveOwnMediaRegistry())
8337 {
8338 // determine machine folder
8339 Utf8Str strMachineFolder = i_getSettingsFileFull();
8340 strMachineFolder.stripFilename();
8341 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8342 config.mediaRegistry,
8343 strMachineFolder);
8344 if (FAILED(rc)) return rc;
8345 }
8346
8347 /* Snapshot node (optional) */
8348 size_t cRootSnapshots;
8349 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8350 {
8351 // there must be only one root snapshot
8352 Assert(cRootSnapshots == 1);
8353
8354 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8355
8356 rc = i_loadSnapshot(snap,
8357 config.uuidCurrentSnapshot,
8358 NULL); // no parent == first snapshot
8359 if (FAILED(rc)) return rc;
8360 }
8361
8362 // hardware data
8363 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8364 if (FAILED(rc)) return rc;
8365
8366 // load storage controllers
8367 rc = i_loadStorageControllers(config.storageMachine,
8368 puuidRegistry,
8369 NULL /* puuidSnapshot */);
8370 if (FAILED(rc)) return rc;
8371
8372 /*
8373 * NOTE: the assignment below must be the last thing to do,
8374 * otherwise it will be not possible to change the settings
8375 * somewhere in the code above because all setters will be
8376 * blocked by i_checkStateDependency(MutableStateDep).
8377 */
8378
8379 /* set the machine state to Aborted or Saved when appropriate */
8380 if (config.fAborted)
8381 {
8382 mSSData->strStateFilePath.setNull();
8383
8384 /* no need to use i_setMachineState() during init() */
8385 mData->mMachineState = MachineState_Aborted;
8386 }
8387 else if (!mSSData->strStateFilePath.isEmpty())
8388 {
8389 /* no need to use i_setMachineState() during init() */
8390 mData->mMachineState = MachineState_Saved;
8391 }
8392
8393 // after loading settings, we are no longer different from the XML on disk
8394 mData->flModifications = 0;
8395
8396 return S_OK;
8397}
8398
8399/**
8400 * Recursively loads all snapshots starting from the given.
8401 *
8402 * @param aNode <Snapshot> node.
8403 * @param aCurSnapshotId Current snapshot ID from the settings file.
8404 * @param aParentSnapshot Parent snapshot.
8405 */
8406HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8407 const Guid &aCurSnapshotId,
8408 Snapshot *aParentSnapshot)
8409{
8410 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8411 AssertReturn(!i_isSessionMachine(), E_FAIL);
8412
8413 HRESULT rc = S_OK;
8414
8415 Utf8Str strStateFile;
8416 if (!data.strStateFile.isEmpty())
8417 {
8418 /* optional */
8419 strStateFile = data.strStateFile;
8420 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8421 if (RT_FAILURE(vrc))
8422 return setError(E_FAIL,
8423 tr("Invalid saved state file path '%s' (%Rrc)"),
8424 strStateFile.c_str(),
8425 vrc);
8426 }
8427
8428 /* create a snapshot machine object */
8429 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8430 pSnapshotMachine.createObject();
8431 rc = pSnapshotMachine->initFromSettings(this,
8432 data.hardware,
8433 &data.debugging,
8434 &data.autostart,
8435 data.storage,
8436 data.uuid.ref(),
8437 strStateFile);
8438 if (FAILED(rc)) return rc;
8439
8440 /* create a snapshot object */
8441 ComObjPtr<Snapshot> pSnapshot;
8442 pSnapshot.createObject();
8443 /* initialize the snapshot */
8444 rc = pSnapshot->init(mParent, // VirtualBox object
8445 data.uuid,
8446 data.strName,
8447 data.strDescription,
8448 data.timestamp,
8449 pSnapshotMachine,
8450 aParentSnapshot);
8451 if (FAILED(rc)) return rc;
8452
8453 /* memorize the first snapshot if necessary */
8454 if (!mData->mFirstSnapshot)
8455 mData->mFirstSnapshot = pSnapshot;
8456
8457 /* memorize the current snapshot when appropriate */
8458 if ( !mData->mCurrentSnapshot
8459 && pSnapshot->i_getId() == aCurSnapshotId
8460 )
8461 mData->mCurrentSnapshot = pSnapshot;
8462
8463 // now create the children
8464 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8465 it != data.llChildSnapshots.end();
8466 ++it)
8467 {
8468 const settings::Snapshot &childData = *it;
8469 // recurse
8470 rc = i_loadSnapshot(childData,
8471 aCurSnapshotId,
8472 pSnapshot); // parent = the one we created above
8473 if (FAILED(rc)) return rc;
8474 }
8475
8476 return rc;
8477}
8478
8479/**
8480 * Loads settings into mHWData.
8481 *
8482 * @param data Reference to the hardware settings.
8483 * @param pDbg Pointer to the debugging settings.
8484 * @param pAutostart Pointer to the autostart settings.
8485 */
8486HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8487 const settings::Autostart *pAutostart)
8488{
8489 AssertReturn(!i_isSessionMachine(), E_FAIL);
8490
8491 HRESULT rc = S_OK;
8492
8493 try
8494 {
8495 /* The hardware version attribute (optional). */
8496 mHWData->mHWVersion = data.strVersion;
8497 mHWData->mHardwareUUID = data.uuid;
8498
8499 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8500 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8501 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8502 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8503 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8504 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8505 mHWData->mPAEEnabled = data.fPAE;
8506 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8507 mHWData->mLongMode = data.enmLongMode;
8508 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8509 mHWData->mCPUCount = data.cCPUs;
8510 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8511 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8512
8513 // cpu
8514 if (mHWData->mCPUHotPlugEnabled)
8515 {
8516 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8517 it != data.llCpus.end();
8518 ++it)
8519 {
8520 const settings::Cpu &cpu = *it;
8521
8522 mHWData->mCPUAttached[cpu.ulId] = true;
8523 }
8524 }
8525
8526 // cpuid leafs
8527 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8528 it != data.llCpuIdLeafs.end();
8529 ++it)
8530 {
8531 const settings::CpuIdLeaf &leaf = *it;
8532
8533 switch (leaf.ulId)
8534 {
8535 case 0x0:
8536 case 0x1:
8537 case 0x2:
8538 case 0x3:
8539 case 0x4:
8540 case 0x5:
8541 case 0x6:
8542 case 0x7:
8543 case 0x8:
8544 case 0x9:
8545 case 0xA:
8546 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8547 break;
8548
8549 case 0x80000000:
8550 case 0x80000001:
8551 case 0x80000002:
8552 case 0x80000003:
8553 case 0x80000004:
8554 case 0x80000005:
8555 case 0x80000006:
8556 case 0x80000007:
8557 case 0x80000008:
8558 case 0x80000009:
8559 case 0x8000000A:
8560 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8561 break;
8562
8563 default:
8564 /* just ignore */
8565 break;
8566 }
8567 }
8568
8569 mHWData->mMemorySize = data.ulMemorySizeMB;
8570 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8571
8572 // boot order
8573 for (size_t i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8574 {
8575 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8576 if (it == data.mapBootOrder.end())
8577 mHWData->mBootOrder[i] = DeviceType_Null;
8578 else
8579 mHWData->mBootOrder[i] = it->second;
8580 }
8581
8582 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8583 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8584 mHWData->mMonitorCount = data.cMonitors;
8585 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8586 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8587 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8588 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8589 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8590 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8591 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8592 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8593 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8594 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8595 if (!data.strVideoCaptureFile.isEmpty())
8596 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8597 else
8598 mHWData->mVideoCaptureFile.setNull();
8599 mHWData->mFirmwareType = data.firmwareType;
8600 mHWData->mPointingHIDType = data.pointingHIDType;
8601 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8602 mHWData->mChipsetType = data.chipsetType;
8603 mHWData->mParavirtProvider = data.paravirtProvider;
8604 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8605 mHWData->mHPETEnabled = data.fHPETEnabled;
8606
8607 /* VRDEServer */
8608 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8609 if (FAILED(rc)) return rc;
8610
8611 /* BIOS */
8612 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8613 if (FAILED(rc)) return rc;
8614
8615 // Bandwidth control (must come before network adapters)
8616 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8617 if (FAILED(rc)) return rc;
8618
8619 /* Shared folders */
8620 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8621 it != data.usbSettings.llUSBControllers.end();
8622 ++it)
8623 {
8624 const settings::USBController &settingsCtrl = *it;
8625 ComObjPtr<USBController> newCtrl;
8626
8627 newCtrl.createObject();
8628 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8629 mUSBControllers->push_back(newCtrl);
8630 }
8631
8632 /* USB device filters */
8633 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8634 if (FAILED(rc)) return rc;
8635
8636 // network adapters
8637 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8638 uint32_t oldCount = mNetworkAdapters.size();
8639 if (newCount > oldCount)
8640 {
8641 mNetworkAdapters.resize(newCount);
8642 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8643 {
8644 unconst(mNetworkAdapters[slot]).createObject();
8645 mNetworkAdapters[slot]->init(this, slot);
8646 }
8647 }
8648 else if (newCount < oldCount)
8649 mNetworkAdapters.resize(newCount);
8650 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8651 it != data.llNetworkAdapters.end();
8652 ++it)
8653 {
8654 const settings::NetworkAdapter &nic = *it;
8655
8656 /* slot unicity is guaranteed by XML Schema */
8657 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8658 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8659 if (FAILED(rc)) return rc;
8660 }
8661
8662 // serial ports
8663 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8664 it != data.llSerialPorts.end();
8665 ++it)
8666 {
8667 const settings::SerialPort &s = *it;
8668
8669 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8670 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8671 if (FAILED(rc)) return rc;
8672 }
8673
8674 // parallel ports (optional)
8675 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8676 it != data.llParallelPorts.end();
8677 ++it)
8678 {
8679 const settings::ParallelPort &p = *it;
8680
8681 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8682 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8683 if (FAILED(rc)) return rc;
8684 }
8685
8686 /* AudioAdapter */
8687 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8688 if (FAILED(rc)) return rc;
8689
8690 /* Shared folders */
8691 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8692 it != data.llSharedFolders.end();
8693 ++it)
8694 {
8695 const settings::SharedFolder &sf = *it;
8696
8697 ComObjPtr<SharedFolder> sharedFolder;
8698 /* Check for double entries. Not allowed! */
8699 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8700 if (SUCCEEDED(rc))
8701 return setError(VBOX_E_OBJECT_IN_USE,
8702 tr("Shared folder named '%s' already exists"),
8703 sf.strName.c_str());
8704
8705 /* Create the new shared folder. Don't break on error. This will be
8706 * reported when the machine starts. */
8707 sharedFolder.createObject();
8708 rc = sharedFolder->init(i_getMachine(),
8709 sf.strName,
8710 sf.strHostPath,
8711 RT_BOOL(sf.fWritable),
8712 RT_BOOL(sf.fAutoMount),
8713 false /* fFailOnError */);
8714 if (FAILED(rc)) return rc;
8715 mHWData->mSharedFolders.push_back(sharedFolder);
8716 }
8717
8718 // Clipboard
8719 mHWData->mClipboardMode = data.clipboardMode;
8720
8721 // drag'n'drop
8722 mHWData->mDnDMode = data.dndMode;
8723
8724 // guest settings
8725 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8726
8727 // IO settings
8728 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8729 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8730
8731 // Host PCI devices
8732 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8733 it != data.pciAttachments.end();
8734 ++it)
8735 {
8736 const settings::HostPCIDeviceAttachment &hpda = *it;
8737 ComObjPtr<PCIDeviceAttachment> pda;
8738
8739 pda.createObject();
8740 pda->i_loadSettings(this, hpda);
8741 mHWData->mPCIDeviceAssignments.push_back(pda);
8742 }
8743
8744 /*
8745 * (The following isn't really real hardware, but it lives in HWData
8746 * for reasons of convenience.)
8747 */
8748
8749#ifdef VBOX_WITH_GUEST_PROPS
8750 /* Guest properties (optional) */
8751 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8752 it != data.llGuestProperties.end();
8753 ++it)
8754 {
8755 const settings::GuestProperty &prop = *it;
8756 uint32_t fFlags = guestProp::NILFLAG;
8757 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8758 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8759 mHWData->mGuestProperties[prop.strName] = property;
8760 }
8761
8762 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8763#endif /* VBOX_WITH_GUEST_PROPS defined */
8764
8765 rc = i_loadDebugging(pDbg);
8766 if (FAILED(rc))
8767 return rc;
8768
8769 mHWData->mAutostart = *pAutostart;
8770
8771 /* default frontend */
8772 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8773 }
8774 catch(std::bad_alloc &)
8775 {
8776 return E_OUTOFMEMORY;
8777 }
8778
8779 AssertComRC(rc);
8780 return rc;
8781}
8782
8783/**
8784 * Called from Machine::loadHardware() to load the debugging settings of the
8785 * machine.
8786 *
8787 * @param pDbg Pointer to the settings.
8788 */
8789HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8790{
8791 mHWData->mDebugging = *pDbg;
8792 /* no more processing currently required, this will probably change. */
8793 return S_OK;
8794}
8795
8796/**
8797 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8798 *
8799 * @param data
8800 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8801 * @param puuidSnapshot
8802 * @return
8803 */
8804HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8805 const Guid *puuidRegistry,
8806 const Guid *puuidSnapshot)
8807{
8808 AssertReturn(!i_isSessionMachine(), E_FAIL);
8809
8810 HRESULT rc = S_OK;
8811
8812 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8813 it != data.llStorageControllers.end();
8814 ++it)
8815 {
8816 const settings::StorageController &ctlData = *it;
8817
8818 ComObjPtr<StorageController> pCtl;
8819 /* Try to find one with the name first. */
8820 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8821 if (SUCCEEDED(rc))
8822 return setError(VBOX_E_OBJECT_IN_USE,
8823 tr("Storage controller named '%s' already exists"),
8824 ctlData.strName.c_str());
8825
8826 pCtl.createObject();
8827 rc = pCtl->init(this,
8828 ctlData.strName,
8829 ctlData.storageBus,
8830 ctlData.ulInstance,
8831 ctlData.fBootable);
8832 if (FAILED(rc)) return rc;
8833
8834 mStorageControllers->push_back(pCtl);
8835
8836 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8837 if (FAILED(rc)) return rc;
8838
8839 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8840 if (FAILED(rc)) return rc;
8841
8842 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8843 if (FAILED(rc)) return rc;
8844
8845 /* Set IDE emulation settings (only for AHCI controller). */
8846 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8847 {
8848 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8849 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8850 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8851 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8852 )
8853 return rc;
8854 }
8855
8856 /* Load the attached devices now. */
8857 rc = i_loadStorageDevices(pCtl,
8858 ctlData,
8859 puuidRegistry,
8860 puuidSnapshot);
8861 if (FAILED(rc)) return rc;
8862 }
8863
8864 return S_OK;
8865}
8866
8867/**
8868 * Called from i_loadStorageControllers for a controller's devices.
8869 *
8870 * @param aStorageController
8871 * @param data
8872 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8873 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8874 * @return
8875 */
8876HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
8877 const settings::StorageController &data,
8878 const Guid *puuidRegistry,
8879 const Guid *puuidSnapshot)
8880{
8881 HRESULT rc = S_OK;
8882
8883 /* paranoia: detect duplicate attachments */
8884 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8885 it != data.llAttachedDevices.end();
8886 ++it)
8887 {
8888 const settings::AttachedDevice &ad = *it;
8889
8890 for (settings::AttachedDevicesList::const_iterator it2 = it;
8891 it2 != data.llAttachedDevices.end();
8892 ++it2)
8893 {
8894 if (it == it2)
8895 continue;
8896
8897 const settings::AttachedDevice &ad2 = *it2;
8898
8899 if ( ad.lPort == ad2.lPort
8900 && ad.lDevice == ad2.lDevice)
8901 {
8902 return setError(E_FAIL,
8903 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8904 aStorageController->i_getName().c_str(),
8905 ad.lPort,
8906 ad.lDevice,
8907 mUserData->s.strName.c_str());
8908 }
8909 }
8910 }
8911
8912 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8913 it != data.llAttachedDevices.end();
8914 ++it)
8915 {
8916 const settings::AttachedDevice &dev = *it;
8917 ComObjPtr<Medium> medium;
8918
8919 switch (dev.deviceType)
8920 {
8921 case DeviceType_Floppy:
8922 case DeviceType_DVD:
8923 if (dev.strHostDriveSrc.isNotEmpty())
8924 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
8925 false /* fRefresh */, medium);
8926 else
8927 rc = mParent->i_findRemoveableMedium(dev.deviceType,
8928 dev.uuid,
8929 false /* fRefresh */,
8930 false /* aSetError */,
8931 medium);
8932 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8933 // This is not an error. The host drive or UUID might have vanished, so just go
8934 // ahead without this removeable medium attachment
8935 rc = S_OK;
8936 break;
8937
8938 case DeviceType_HardDisk:
8939 {
8940 /* find a hard disk by UUID */
8941 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8942 if (FAILED(rc))
8943 {
8944 if (i_isSnapshotMachine())
8945 {
8946 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8947 // so the user knows that the bad disk is in a snapshot somewhere
8948 com::ErrorInfo info;
8949 return setError(E_FAIL,
8950 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8951 puuidSnapshot->raw(),
8952 info.getText().raw());
8953 }
8954 else
8955 return rc;
8956 }
8957
8958 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8959
8960 if (medium->i_getType() == MediumType_Immutable)
8961 {
8962 if (i_isSnapshotMachine())
8963 return setError(E_FAIL,
8964 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8965 "of the virtual machine '%s' ('%s')"),
8966 medium->i_getLocationFull().c_str(),
8967 dev.uuid.raw(),
8968 puuidSnapshot->raw(),
8969 mUserData->s.strName.c_str(),
8970 mData->m_strConfigFileFull.c_str());
8971
8972 return setError(E_FAIL,
8973 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8974 medium->i_getLocationFull().c_str(),
8975 dev.uuid.raw(),
8976 mUserData->s.strName.c_str(),
8977 mData->m_strConfigFileFull.c_str());
8978 }
8979
8980 if (medium->i_getType() == MediumType_MultiAttach)
8981 {
8982 if (i_isSnapshotMachine())
8983 return setError(E_FAIL,
8984 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8985 "of the virtual machine '%s' ('%s')"),
8986 medium->i_getLocationFull().c_str(),
8987 dev.uuid.raw(),
8988 puuidSnapshot->raw(),
8989 mUserData->s.strName.c_str(),
8990 mData->m_strConfigFileFull.c_str());
8991
8992 return setError(E_FAIL,
8993 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8994 medium->i_getLocationFull().c_str(),
8995 dev.uuid.raw(),
8996 mUserData->s.strName.c_str(),
8997 mData->m_strConfigFileFull.c_str());
8998 }
8999
9000 if ( !i_isSnapshotMachine()
9001 && medium->i_getChildren().size() != 0
9002 )
9003 return setError(E_FAIL,
9004 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9005 "because it has %d differencing child hard disks"),
9006 medium->i_getLocationFull().c_str(),
9007 dev.uuid.raw(),
9008 mUserData->s.strName.c_str(),
9009 mData->m_strConfigFileFull.c_str(),
9010 medium->i_getChildren().size());
9011
9012 if (i_findAttachment(mMediaData->mAttachments,
9013 medium))
9014 return setError(E_FAIL,
9015 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9016 medium->i_getLocationFull().c_str(),
9017 dev.uuid.raw(),
9018 mUserData->s.strName.c_str(),
9019 mData->m_strConfigFileFull.c_str());
9020
9021 break;
9022 }
9023
9024 default:
9025 return setError(E_FAIL,
9026 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9027 medium->i_getLocationFull().c_str(),
9028 mUserData->s.strName.c_str(),
9029 mData->m_strConfigFileFull.c_str());
9030 }
9031
9032 if (FAILED(rc))
9033 break;
9034
9035 /* Bandwidth groups are loaded at this point. */
9036 ComObjPtr<BandwidthGroup> pBwGroup;
9037
9038 if (!dev.strBwGroup.isEmpty())
9039 {
9040 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9041 if (FAILED(rc))
9042 return setError(E_FAIL,
9043 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9044 medium->i_getLocationFull().c_str(),
9045 dev.strBwGroup.c_str(),
9046 mUserData->s.strName.c_str(),
9047 mData->m_strConfigFileFull.c_str());
9048 pBwGroup->i_reference();
9049 }
9050
9051 const Bstr controllerName = aStorageController->i_getName();
9052 ComObjPtr<MediumAttachment> pAttachment;
9053 pAttachment.createObject();
9054 rc = pAttachment->init(this,
9055 medium,
9056 controllerName,
9057 dev.lPort,
9058 dev.lDevice,
9059 dev.deviceType,
9060 false,
9061 dev.fPassThrough,
9062 dev.fTempEject,
9063 dev.fNonRotational,
9064 dev.fDiscard,
9065 dev.fHotPluggable,
9066 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9067 if (FAILED(rc)) break;
9068
9069 /* associate the medium with this machine and snapshot */
9070 if (!medium.isNull())
9071 {
9072 AutoCaller medCaller(medium);
9073 if (FAILED(medCaller.rc())) return medCaller.rc();
9074 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9075
9076 if (i_isSnapshotMachine())
9077 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9078 else
9079 rc = medium->i_addBackReference(mData->mUuid);
9080 /* If the medium->addBackReference fails it sets an appropriate
9081 * error message, so no need to do any guesswork here. */
9082
9083 if (puuidRegistry)
9084 // caller wants registry ID to be set on all attached media (OVF import case)
9085 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9086 }
9087
9088 if (FAILED(rc))
9089 break;
9090
9091 /* back up mMediaData to let registeredInit() properly rollback on failure
9092 * (= limited accessibility) */
9093 i_setModified(IsModified_Storage);
9094 mMediaData.backup();
9095 mMediaData->mAttachments.push_back(pAttachment);
9096 }
9097
9098 return rc;
9099}
9100
9101/**
9102 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9103 *
9104 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9105 * @param aSnapshot where to return the found snapshot
9106 * @param aSetError true to set extended error info on failure
9107 */
9108HRESULT Machine::i_findSnapshotById(const Guid &aId,
9109 ComObjPtr<Snapshot> &aSnapshot,
9110 bool aSetError /* = false */)
9111{
9112 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9113
9114 if (!mData->mFirstSnapshot)
9115 {
9116 if (aSetError)
9117 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9118 return E_FAIL;
9119 }
9120
9121 if (aId.isZero())
9122 aSnapshot = mData->mFirstSnapshot;
9123 else
9124 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9125
9126 if (!aSnapshot)
9127 {
9128 if (aSetError)
9129 return setError(E_FAIL,
9130 tr("Could not find a snapshot with UUID {%s}"),
9131 aId.toString().c_str());
9132 return E_FAIL;
9133 }
9134
9135 return S_OK;
9136}
9137
9138/**
9139 * Returns the snapshot with the given name or fails of no such snapshot.
9140 *
9141 * @param aName snapshot name to find
9142 * @param aSnapshot where to return the found snapshot
9143 * @param aSetError true to set extended error info on failure
9144 */
9145HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9146 ComObjPtr<Snapshot> &aSnapshot,
9147 bool aSetError /* = false */)
9148{
9149 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9150
9151 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9152
9153 if (!mData->mFirstSnapshot)
9154 {
9155 if (aSetError)
9156 return setError(VBOX_E_OBJECT_NOT_FOUND,
9157 tr("This machine does not have any snapshots"));
9158 return VBOX_E_OBJECT_NOT_FOUND;
9159 }
9160
9161 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9162
9163 if (!aSnapshot)
9164 {
9165 if (aSetError)
9166 return setError(VBOX_E_OBJECT_NOT_FOUND,
9167 tr("Could not find a snapshot named '%s'"), strName.c_str());
9168 return VBOX_E_OBJECT_NOT_FOUND;
9169 }
9170
9171 return S_OK;
9172}
9173
9174/**
9175 * Returns a storage controller object with the given name.
9176 *
9177 * @param aName storage controller name to find
9178 * @param aStorageController where to return the found storage controller
9179 * @param aSetError true to set extended error info on failure
9180 */
9181HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9182 ComObjPtr<StorageController> &aStorageController,
9183 bool aSetError /* = false */)
9184{
9185 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9186
9187 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9188 it != mStorageControllers->end();
9189 ++it)
9190 {
9191 if ((*it)->i_getName() == aName)
9192 {
9193 aStorageController = (*it);
9194 return S_OK;
9195 }
9196 }
9197
9198 if (aSetError)
9199 return setError(VBOX_E_OBJECT_NOT_FOUND,
9200 tr("Could not find a storage controller named '%s'"),
9201 aName.c_str());
9202 return VBOX_E_OBJECT_NOT_FOUND;
9203}
9204
9205/**
9206 * Returns a USB controller object with the given name.
9207 *
9208 * @param aName USB controller name to find
9209 * @param aUSBController where to return the found USB controller
9210 * @param aSetError true to set extended error info on failure
9211 */
9212HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9213 ComObjPtr<USBController> &aUSBController,
9214 bool aSetError /* = false */)
9215{
9216 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9217
9218 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9219 it != mUSBControllers->end();
9220 ++it)
9221 {
9222 if ((*it)->i_getName() == aName)
9223 {
9224 aUSBController = (*it);
9225 return S_OK;
9226 }
9227 }
9228
9229 if (aSetError)
9230 return setError(VBOX_E_OBJECT_NOT_FOUND,
9231 tr("Could not find a storage controller named '%s'"),
9232 aName.c_str());
9233 return VBOX_E_OBJECT_NOT_FOUND;
9234}
9235
9236/**
9237 * Returns the number of USB controller instance of the given type.
9238 *
9239 * @param enmType USB controller type.
9240 */
9241ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9242{
9243 ULONG cCtrls = 0;
9244
9245 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9246 it != mUSBControllers->end();
9247 ++it)
9248 {
9249 if ((*it)->i_getControllerType() == enmType)
9250 cCtrls++;
9251 }
9252
9253 return cCtrls;
9254}
9255
9256HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9257 MediaData::AttachmentList &atts)
9258{
9259 AutoCaller autoCaller(this);
9260 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9261
9262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9263
9264 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9265 it != mMediaData->mAttachments.end();
9266 ++it)
9267 {
9268 const ComObjPtr<MediumAttachment> &pAtt = *it;
9269 // should never happen, but deal with NULL pointers in the list.
9270 AssertStmt(!pAtt.isNull(), continue);
9271
9272 // getControllerName() needs caller+read lock
9273 AutoCaller autoAttCaller(pAtt);
9274 if (FAILED(autoAttCaller.rc()))
9275 {
9276 atts.clear();
9277 return autoAttCaller.rc();
9278 }
9279 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9280
9281 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9282 atts.push_back(pAtt);
9283 }
9284
9285 return S_OK;
9286}
9287
9288
9289/**
9290 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9291 * file if the machine name was changed and about creating a new settings file
9292 * if this is a new machine.
9293 *
9294 * @note Must be never called directly but only from #saveSettings().
9295 */
9296HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9297{
9298 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9299
9300 HRESULT rc = S_OK;
9301
9302 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9303
9304 /// @todo need to handle primary group change, too
9305
9306 /* attempt to rename the settings file if machine name is changed */
9307 if ( mUserData->s.fNameSync
9308 && mUserData.isBackedUp()
9309 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9310 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9311 )
9312 {
9313 bool dirRenamed = false;
9314 bool fileRenamed = false;
9315
9316 Utf8Str configFile, newConfigFile;
9317 Utf8Str configFilePrev, newConfigFilePrev;
9318 Utf8Str configDir, newConfigDir;
9319
9320 do
9321 {
9322 int vrc = VINF_SUCCESS;
9323
9324 Utf8Str name = mUserData.backedUpData()->s.strName;
9325 Utf8Str newName = mUserData->s.strName;
9326 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9327 if (group == "/")
9328 group.setNull();
9329 Utf8Str newGroup = mUserData->s.llGroups.front();
9330 if (newGroup == "/")
9331 newGroup.setNull();
9332
9333 configFile = mData->m_strConfigFileFull;
9334
9335 /* first, rename the directory if it matches the group and machine name */
9336 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9337 group.c_str(), RTPATH_DELIMITER, name.c_str());
9338 /** @todo hack, make somehow use of ComposeMachineFilename */
9339 if (mUserData->s.fDirectoryIncludesUUID)
9340 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9341 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9342 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9343 /** @todo hack, make somehow use of ComposeMachineFilename */
9344 if (mUserData->s.fDirectoryIncludesUUID)
9345 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9346 configDir = configFile;
9347 configDir.stripFilename();
9348 newConfigDir = configDir;
9349 if ( configDir.length() >= groupPlusName.length()
9350 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9351 groupPlusName.c_str()))
9352 {
9353 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9354 Utf8Str newConfigBaseDir(newConfigDir);
9355 newConfigDir.append(newGroupPlusName);
9356 /* consistency: use \ if appropriate on the platform */
9357 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9358 /* new dir and old dir cannot be equal here because of 'if'
9359 * above and because name != newName */
9360 Assert(configDir != newConfigDir);
9361 if (!fSettingsFileIsNew)
9362 {
9363 /* perform real rename only if the machine is not new */
9364 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9365 if ( vrc == VERR_FILE_NOT_FOUND
9366 || vrc == VERR_PATH_NOT_FOUND)
9367 {
9368 /* create the parent directory, then retry renaming */
9369 Utf8Str parent(newConfigDir);
9370 parent.stripFilename();
9371 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9372 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9373 }
9374 if (RT_FAILURE(vrc))
9375 {
9376 rc = setError(E_FAIL,
9377 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9378 configDir.c_str(),
9379 newConfigDir.c_str(),
9380 vrc);
9381 break;
9382 }
9383 /* delete subdirectories which are no longer needed */
9384 Utf8Str dir(configDir);
9385 dir.stripFilename();
9386 while (dir != newConfigBaseDir && dir != ".")
9387 {
9388 vrc = RTDirRemove(dir.c_str());
9389 if (RT_FAILURE(vrc))
9390 break;
9391 dir.stripFilename();
9392 }
9393 dirRenamed = true;
9394 }
9395 }
9396
9397 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9398 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9399
9400 /* then try to rename the settings file itself */
9401 if (newConfigFile != configFile)
9402 {
9403 /* get the path to old settings file in renamed directory */
9404 configFile = Utf8StrFmt("%s%c%s",
9405 newConfigDir.c_str(),
9406 RTPATH_DELIMITER,
9407 RTPathFilename(configFile.c_str()));
9408 if (!fSettingsFileIsNew)
9409 {
9410 /* perform real rename only if the machine is not new */
9411 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9412 if (RT_FAILURE(vrc))
9413 {
9414 rc = setError(E_FAIL,
9415 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9416 configFile.c_str(),
9417 newConfigFile.c_str(),
9418 vrc);
9419 break;
9420 }
9421 fileRenamed = true;
9422 configFilePrev = configFile;
9423 configFilePrev += "-prev";
9424 newConfigFilePrev = newConfigFile;
9425 newConfigFilePrev += "-prev";
9426 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9427 }
9428 }
9429
9430 // update m_strConfigFileFull amd mConfigFile
9431 mData->m_strConfigFileFull = newConfigFile;
9432 // compute the relative path too
9433 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9434
9435 // store the old and new so that VirtualBox::i_saveSettings() can update
9436 // the media registry
9437 if ( mData->mRegistered
9438 && (configDir != newConfigDir || configFile != newConfigFile))
9439 {
9440 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9441
9442 if (pfNeedsGlobalSaveSettings)
9443 *pfNeedsGlobalSaveSettings = true;
9444 }
9445
9446 // in the saved state file path, replace the old directory with the new directory
9447 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9448 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9449
9450 // and do the same thing for the saved state file paths of all the online snapshots
9451 if (mData->mFirstSnapshot)
9452 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9453 newConfigDir.c_str());
9454 }
9455 while (0);
9456
9457 if (FAILED(rc))
9458 {
9459 /* silently try to rename everything back */
9460 if (fileRenamed)
9461 {
9462 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9463 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9464 }
9465 if (dirRenamed)
9466 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9467 }
9468
9469 if (FAILED(rc)) return rc;
9470 }
9471
9472 if (fSettingsFileIsNew)
9473 {
9474 /* create a virgin config file */
9475 int vrc = VINF_SUCCESS;
9476
9477 /* ensure the settings directory exists */
9478 Utf8Str path(mData->m_strConfigFileFull);
9479 path.stripFilename();
9480 if (!RTDirExists(path.c_str()))
9481 {
9482 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9483 if (RT_FAILURE(vrc))
9484 {
9485 return setError(E_FAIL,
9486 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9487 path.c_str(),
9488 vrc);
9489 }
9490 }
9491
9492 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9493 path = Utf8Str(mData->m_strConfigFileFull);
9494 RTFILE f = NIL_RTFILE;
9495 vrc = RTFileOpen(&f, path.c_str(),
9496 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9497 if (RT_FAILURE(vrc))
9498 return setError(E_FAIL,
9499 tr("Could not create the settings file '%s' (%Rrc)"),
9500 path.c_str(),
9501 vrc);
9502 RTFileClose(f);
9503 }
9504
9505 return rc;
9506}
9507
9508/**
9509 * Saves and commits machine data, user data and hardware data.
9510 *
9511 * Note that on failure, the data remains uncommitted.
9512 *
9513 * @a aFlags may combine the following flags:
9514 *
9515 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9516 * Used when saving settings after an operation that makes them 100%
9517 * correspond to the settings from the current snapshot.
9518 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9519 * #isReallyModified() returns false. This is necessary for cases when we
9520 * change machine data directly, not through the backup()/commit() mechanism.
9521 * - SaveS_Force: settings will be saved without doing a deep compare of the
9522 * settings structures. This is used when this is called because snapshots
9523 * have changed to avoid the overhead of the deep compare.
9524 *
9525 * @note Must be called from under this object's write lock. Locks children for
9526 * writing.
9527 *
9528 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9529 * initialized to false and that will be set to true by this function if
9530 * the caller must invoke VirtualBox::i_saveSettings() because the global
9531 * settings have changed. This will happen if a machine rename has been
9532 * saved and the global machine and media registries will therefore need
9533 * updating.
9534 */
9535HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9536 int aFlags /*= 0*/)
9537{
9538 LogFlowThisFuncEnter();
9539
9540 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9541
9542 /* make sure child objects are unable to modify the settings while we are
9543 * saving them */
9544 i_ensureNoStateDependencies();
9545
9546 AssertReturn(!i_isSnapshotMachine(),
9547 E_FAIL);
9548
9549 HRESULT rc = S_OK;
9550 bool fNeedsWrite = false;
9551
9552 /* First, prepare to save settings. It will care about renaming the
9553 * settings directory and file if the machine name was changed and about
9554 * creating a new settings file if this is a new machine. */
9555 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9556 if (FAILED(rc)) return rc;
9557
9558 // keep a pointer to the current settings structures
9559 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9560 settings::MachineConfigFile *pNewConfig = NULL;
9561
9562 try
9563 {
9564 // make a fresh one to have everyone write stuff into
9565 pNewConfig = new settings::MachineConfigFile(NULL);
9566 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9567
9568 // now go and copy all the settings data from COM to the settings structures
9569 // (this calles i_saveSettings() on all the COM objects in the machine)
9570 i_copyMachineDataToSettings(*pNewConfig);
9571
9572 if (aFlags & SaveS_ResetCurStateModified)
9573 {
9574 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9575 mData->mCurrentStateModified = FALSE;
9576 fNeedsWrite = true; // always, no need to compare
9577 }
9578 else if (aFlags & SaveS_Force)
9579 {
9580 fNeedsWrite = true; // always, no need to compare
9581 }
9582 else
9583 {
9584 if (!mData->mCurrentStateModified)
9585 {
9586 // do a deep compare of the settings that we just saved with the settings
9587 // previously stored in the config file; this invokes MachineConfigFile::operator==
9588 // which does a deep compare of all the settings, which is expensive but less expensive
9589 // than writing out XML in vain
9590 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9591
9592 // could still be modified if any settings changed
9593 mData->mCurrentStateModified = fAnySettingsChanged;
9594
9595 fNeedsWrite = fAnySettingsChanged;
9596 }
9597 else
9598 fNeedsWrite = true;
9599 }
9600
9601 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9602
9603 if (fNeedsWrite)
9604 // now spit it all out!
9605 pNewConfig->write(mData->m_strConfigFileFull);
9606
9607 mData->pMachineConfigFile = pNewConfig;
9608 delete pOldConfig;
9609 i_commit();
9610
9611 // after saving settings, we are no longer different from the XML on disk
9612 mData->flModifications = 0;
9613 }
9614 catch (HRESULT err)
9615 {
9616 // we assume that error info is set by the thrower
9617 rc = err;
9618
9619 // restore old config
9620 delete pNewConfig;
9621 mData->pMachineConfigFile = pOldConfig;
9622 }
9623 catch (...)
9624 {
9625 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9626 }
9627
9628 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9629 {
9630 /* Fire the data change event, even on failure (since we've already
9631 * committed all data). This is done only for SessionMachines because
9632 * mutable Machine instances are always not registered (i.e. private
9633 * to the client process that creates them) and thus don't need to
9634 * inform callbacks. */
9635 if (i_isSessionMachine())
9636 mParent->i_onMachineDataChange(mData->mUuid);
9637 }
9638
9639 LogFlowThisFunc(("rc=%08X\n", rc));
9640 LogFlowThisFuncLeave();
9641 return rc;
9642}
9643
9644/**
9645 * Implementation for saving the machine settings into the given
9646 * settings::MachineConfigFile instance. This copies machine extradata
9647 * from the previous machine config file in the instance data, if any.
9648 *
9649 * This gets called from two locations:
9650 *
9651 * -- Machine::i_saveSettings(), during the regular XML writing;
9652 *
9653 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9654 * exported to OVF and we write the VirtualBox proprietary XML
9655 * into a <vbox:Machine> tag.
9656 *
9657 * This routine fills all the fields in there, including snapshots, *except*
9658 * for the following:
9659 *
9660 * -- fCurrentStateModified. There is some special logic associated with that.
9661 *
9662 * The caller can then call MachineConfigFile::write() or do something else
9663 * with it.
9664 *
9665 * Caller must hold the machine lock!
9666 *
9667 * This throws XML errors and HRESULT, so the caller must have a catch block!
9668 */
9669void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9670{
9671 // deep copy extradata
9672 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9673
9674 config.uuid = mData->mUuid;
9675
9676 // copy name, description, OS type, teleport, UTC etc.
9677 config.machineUserData = mUserData->s;
9678
9679 // Encode the Icon Override data from Machine and store on config userdata.
9680 com::SafeArray<BYTE> iconByte;
9681 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
9682 ssize_t cbData = iconByte.size();
9683 if (cbData > 0)
9684 {
9685 ssize_t cchOut = RTBase64EncodedLength(cbData);
9686 Utf8Str strIconData;
9687 strIconData.reserve(cchOut+1);
9688 int vrc = RTBase64Encode(iconByte.raw(), cbData,
9689 strIconData.mutableRaw(), strIconData.capacity(),
9690 NULL);
9691 if (RT_FAILURE(vrc))
9692 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9693 strIconData.jolt();
9694 config.machineUserData.ovIcon = strIconData;
9695 }
9696 else
9697 config.machineUserData.ovIcon.setNull();
9698
9699 if ( mData->mMachineState == MachineState_Saved
9700 || mData->mMachineState == MachineState_Restoring
9701 // when deleting a snapshot we may or may not have a saved state in the current state,
9702 // so let's not assert here please
9703 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9704 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9705 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9706 && (!mSSData->strStateFilePath.isEmpty())
9707 )
9708 )
9709 {
9710 Assert(!mSSData->strStateFilePath.isEmpty());
9711 /* try to make the file name relative to the settings file dir */
9712 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9713 }
9714 else
9715 {
9716 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9717 config.strStateFile.setNull();
9718 }
9719
9720 if (mData->mCurrentSnapshot)
9721 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9722 else
9723 config.uuidCurrentSnapshot.clear();
9724
9725 config.timeLastStateChange = mData->mLastStateChange;
9726 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9727 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9728
9729 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9730 if (FAILED(rc)) throw rc;
9731
9732 rc = i_saveStorageControllers(config.storageMachine);
9733 if (FAILED(rc)) throw rc;
9734
9735 // save machine's media registry if this is VirtualBox 4.0 or later
9736 if (config.canHaveOwnMediaRegistry())
9737 {
9738 // determine machine folder
9739 Utf8Str strMachineFolder = i_getSettingsFileFull();
9740 strMachineFolder.stripFilename();
9741 mParent->i_saveMediaRegistry(config.mediaRegistry,
9742 i_getId(), // only media with registry ID == machine UUID
9743 strMachineFolder);
9744 // this throws HRESULT
9745 }
9746
9747 // save snapshots
9748 rc = i_saveAllSnapshots(config);
9749 if (FAILED(rc)) throw rc;
9750}
9751
9752/**
9753 * Saves all snapshots of the machine into the given machine config file. Called
9754 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9755 * @param config
9756 * @return
9757 */
9758HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9759{
9760 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9761
9762 HRESULT rc = S_OK;
9763
9764 try
9765 {
9766 config.llFirstSnapshot.clear();
9767
9768 if (mData->mFirstSnapshot)
9769 {
9770 settings::Snapshot snapNew;
9771 config.llFirstSnapshot.push_back(snapNew);
9772
9773 // get reference to the fresh copy of the snapshot on the list and
9774 // work on that copy directly to avoid excessive copying later
9775 settings::Snapshot &snap = config.llFirstSnapshot.front();
9776
9777 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9778 if (FAILED(rc)) throw rc;
9779 }
9780
9781// if (mType == IsSessionMachine)
9782// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9783
9784 }
9785 catch (HRESULT err)
9786 {
9787 /* we assume that error info is set by the thrower */
9788 rc = err;
9789 }
9790 catch (...)
9791 {
9792 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9793 }
9794
9795 return rc;
9796}
9797
9798/**
9799 * Saves the VM hardware configuration. It is assumed that the
9800 * given node is empty.
9801 *
9802 * @param data Reference to the settings object for the hardware config.
9803 * @param pDbg Pointer to the settings object for the debugging config
9804 * which happens to live in mHWData.
9805 * @param pAutostart Pointer to the settings object for the autostart config
9806 * which happens to live in mHWData.
9807 */
9808HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9809 settings::Autostart *pAutostart)
9810{
9811 HRESULT rc = S_OK;
9812
9813 try
9814 {
9815 /* The hardware version attribute (optional).
9816 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9817 if ( mHWData->mHWVersion == "1"
9818 && mSSData->strStateFilePath.isEmpty()
9819 )
9820 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9821 other point needs to be found where this can be done. */
9822
9823 data.strVersion = mHWData->mHWVersion;
9824 data.uuid = mHWData->mHardwareUUID;
9825
9826 // CPU
9827 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9828 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9829 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9830 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9831 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9832 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9833 data.fPAE = !!mHWData->mPAEEnabled;
9834 data.enmLongMode = mHWData->mLongMode;
9835 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9836 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
9837
9838 /* Standard and Extended CPUID leafs. */
9839 data.llCpuIdLeafs.clear();
9840 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
9841 {
9842 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9843 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9844 }
9845 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
9846 {
9847 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9848 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9849 }
9850
9851 data.cCPUs = mHWData->mCPUCount;
9852 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9853 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9854
9855 data.llCpus.clear();
9856 if (data.fCpuHotPlug)
9857 {
9858 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
9859 {
9860 if (mHWData->mCPUAttached[idx])
9861 {
9862 settings::Cpu cpu;
9863 cpu.ulId = idx;
9864 data.llCpus.push_back(cpu);
9865 }
9866 }
9867 }
9868
9869 // memory
9870 data.ulMemorySizeMB = mHWData->mMemorySize;
9871 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9872
9873 // firmware
9874 data.firmwareType = mHWData->mFirmwareType;
9875
9876 // HID
9877 data.pointingHIDType = mHWData->mPointingHIDType;
9878 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9879
9880 // chipset
9881 data.chipsetType = mHWData->mChipsetType;
9882
9883 // paravirt
9884 data.paravirtProvider = mHWData->mParavirtProvider;
9885
9886
9887 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9888
9889 // HPET
9890 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9891
9892 // boot order
9893 data.mapBootOrder.clear();
9894 for (size_t i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9895 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9896
9897 // display
9898 data.graphicsControllerType = mHWData->mGraphicsControllerType;
9899 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9900 data.cMonitors = mHWData->mMonitorCount;
9901 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9902 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9903 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9904 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9905 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
9906 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
9907 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
9908 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
9909 {
9910 if (mHWData->maVideoCaptureScreens[i])
9911 ASMBitSet(&data.u64VideoCaptureScreens, i);
9912 else
9913 ASMBitClear(&data.u64VideoCaptureScreens, i);
9914 }
9915 /* store relative video capture file if possible */
9916 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
9917
9918 /* VRDEServer settings (optional) */
9919 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
9920 if (FAILED(rc)) throw rc;
9921
9922 /* BIOS (required) */
9923 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
9924 if (FAILED(rc)) throw rc;
9925
9926 /* USB Controller (required) */
9927 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
9928 {
9929 ComObjPtr<USBController> ctrl = *it;
9930 settings::USBController settingsCtrl;
9931
9932 settingsCtrl.strName = ctrl->i_getName();
9933 settingsCtrl.enmType = ctrl->i_getControllerType();
9934
9935 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
9936 }
9937
9938 /* USB device filters (required) */
9939 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
9940 if (FAILED(rc)) throw rc;
9941
9942 /* Network adapters (required) */
9943 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9944 data.llNetworkAdapters.clear();
9945 /* Write out only the nominal number of network adapters for this
9946 * chipset type. Since Machine::commit() hasn't been called there
9947 * may be extra NIC settings in the vector. */
9948 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9949 {
9950 settings::NetworkAdapter nic;
9951 nic.ulSlot = slot;
9952 /* paranoia check... must not be NULL, but must not crash either. */
9953 if (mNetworkAdapters[slot])
9954 {
9955 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
9956 if (FAILED(rc)) throw rc;
9957
9958 data.llNetworkAdapters.push_back(nic);
9959 }
9960 }
9961
9962 /* Serial ports */
9963 data.llSerialPorts.clear();
9964 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
9965 {
9966 settings::SerialPort s;
9967 s.ulSlot = slot;
9968 rc = mSerialPorts[slot]->i_saveSettings(s);
9969 if (FAILED(rc)) return rc;
9970
9971 data.llSerialPorts.push_back(s);
9972 }
9973
9974 /* Parallel ports */
9975 data.llParallelPorts.clear();
9976 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
9977 {
9978 settings::ParallelPort p;
9979 p.ulSlot = slot;
9980 rc = mParallelPorts[slot]->i_saveSettings(p);
9981 if (FAILED(rc)) return rc;
9982
9983 data.llParallelPorts.push_back(p);
9984 }
9985
9986 /* Audio adapter */
9987 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
9988 if (FAILED(rc)) return rc;
9989
9990 /* Shared folders */
9991 data.llSharedFolders.clear();
9992 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9993 it != mHWData->mSharedFolders.end();
9994 ++it)
9995 {
9996 SharedFolder *pSF = *it;
9997 AutoCaller sfCaller(pSF);
9998 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9999 settings::SharedFolder sf;
10000 sf.strName = pSF->i_getName();
10001 sf.strHostPath = pSF->i_getHostPath();
10002 sf.fWritable = !!pSF->i_isWritable();
10003 sf.fAutoMount = !!pSF->i_isAutoMounted();
10004
10005 data.llSharedFolders.push_back(sf);
10006 }
10007
10008 // clipboard
10009 data.clipboardMode = mHWData->mClipboardMode;
10010
10011 // drag'n'drop
10012 data.dndMode = mHWData->mDnDMode;
10013
10014 /* Guest */
10015 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10016
10017 // IO settings
10018 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10019 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10020
10021 /* BandwidthControl (required) */
10022 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10023 if (FAILED(rc)) throw rc;
10024
10025 /* Host PCI devices */
10026 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10027 it != mHWData->mPCIDeviceAssignments.end();
10028 ++it)
10029 {
10030 ComObjPtr<PCIDeviceAttachment> pda = *it;
10031 settings::HostPCIDeviceAttachment hpda;
10032
10033 rc = pda->i_saveSettings(hpda);
10034 if (FAILED(rc)) throw rc;
10035
10036 data.pciAttachments.push_back(hpda);
10037 }
10038
10039
10040 // guest properties
10041 data.llGuestProperties.clear();
10042#ifdef VBOX_WITH_GUEST_PROPS
10043 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10044 it != mHWData->mGuestProperties.end();
10045 ++it)
10046 {
10047 HWData::GuestProperty property = it->second;
10048
10049 /* Remove transient guest properties at shutdown unless we
10050 * are saving state */
10051 if ( ( mData->mMachineState == MachineState_PoweredOff
10052 || mData->mMachineState == MachineState_Aborted
10053 || mData->mMachineState == MachineState_Teleported)
10054 && ( property.mFlags & guestProp::TRANSIENT
10055 || property.mFlags & guestProp::TRANSRESET))
10056 continue;
10057 settings::GuestProperty prop;
10058 prop.strName = it->first;
10059 prop.strValue = property.strValue;
10060 prop.timestamp = property.mTimestamp;
10061 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10062 guestProp::writeFlags(property.mFlags, szFlags);
10063 prop.strFlags = szFlags;
10064
10065 data.llGuestProperties.push_back(prop);
10066 }
10067
10068 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10069 /* I presume this doesn't require a backup(). */
10070 mData->mGuestPropertiesModified = FALSE;
10071#endif /* VBOX_WITH_GUEST_PROPS defined */
10072
10073 *pDbg = mHWData->mDebugging;
10074 *pAutostart = mHWData->mAutostart;
10075
10076 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10077 }
10078 catch(std::bad_alloc &)
10079 {
10080 return E_OUTOFMEMORY;
10081 }
10082
10083 AssertComRC(rc);
10084 return rc;
10085}
10086
10087/**
10088 * Saves the storage controller configuration.
10089 *
10090 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10091 */
10092HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10093{
10094 data.llStorageControllers.clear();
10095
10096 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10097 it != mStorageControllers->end();
10098 ++it)
10099 {
10100 HRESULT rc;
10101 ComObjPtr<StorageController> pCtl = *it;
10102
10103 settings::StorageController ctl;
10104 ctl.strName = pCtl->i_getName();
10105 ctl.controllerType = pCtl->i_getControllerType();
10106 ctl.storageBus = pCtl->i_getStorageBus();
10107 ctl.ulInstance = pCtl->i_getInstance();
10108 ctl.fBootable = pCtl->i_getBootable();
10109
10110 /* Save the port count. */
10111 ULONG portCount;
10112 rc = pCtl->COMGETTER(PortCount)(&portCount);
10113 ComAssertComRCRet(rc, rc);
10114 ctl.ulPortCount = portCount;
10115
10116 /* Save fUseHostIOCache */
10117 BOOL fUseHostIOCache;
10118 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10119 ComAssertComRCRet(rc, rc);
10120 ctl.fUseHostIOCache = !!fUseHostIOCache;
10121
10122 /* Save IDE emulation settings. */
10123 if (ctl.controllerType == StorageControllerType_IntelAhci)
10124 {
10125 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10126 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10127 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10128 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10129 )
10130 ComAssertComRCRet(rc, rc);
10131 }
10132
10133 /* save the devices now. */
10134 rc = i_saveStorageDevices(pCtl, ctl);
10135 ComAssertComRCRet(rc, rc);
10136
10137 data.llStorageControllers.push_back(ctl);
10138 }
10139
10140 return S_OK;
10141}
10142
10143/**
10144 * Saves the hard disk configuration.
10145 */
10146HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10147 settings::StorageController &data)
10148{
10149 MediaData::AttachmentList atts;
10150
10151 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10152 if (FAILED(rc)) return rc;
10153
10154 data.llAttachedDevices.clear();
10155 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10156 it != atts.end();
10157 ++it)
10158 {
10159 settings::AttachedDevice dev;
10160 IMediumAttachment *iA = *it;
10161 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10162 Medium *pMedium = pAttach->i_getMedium();
10163
10164 dev.deviceType = pAttach->i_getType();
10165 dev.lPort = pAttach->i_getPort();
10166 dev.lDevice = pAttach->i_getDevice();
10167 dev.fPassThrough = pAttach->i_getPassthrough();
10168 dev.fHotPluggable = pAttach->i_getHotPluggable();
10169 if (pMedium)
10170 {
10171 if (pMedium->i_isHostDrive())
10172 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10173 else
10174 dev.uuid = pMedium->i_getId();
10175 dev.fTempEject = pAttach->i_getTempEject();
10176 dev.fNonRotational = pAttach->i_getNonRotational();
10177 dev.fDiscard = pAttach->i_getDiscard();
10178 }
10179
10180 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10181
10182 data.llAttachedDevices.push_back(dev);
10183 }
10184
10185 return S_OK;
10186}
10187
10188/**
10189 * Saves machine state settings as defined by aFlags
10190 * (SaveSTS_* values).
10191 *
10192 * @param aFlags Combination of SaveSTS_* flags.
10193 *
10194 * @note Locks objects for writing.
10195 */
10196HRESULT Machine::i_saveStateSettings(int aFlags)
10197{
10198 if (aFlags == 0)
10199 return S_OK;
10200
10201 AutoCaller autoCaller(this);
10202 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10203
10204 /* This object's write lock is also necessary to serialize file access
10205 * (prevent concurrent reads and writes) */
10206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10207
10208 HRESULT rc = S_OK;
10209
10210 Assert(mData->pMachineConfigFile);
10211
10212 try
10213 {
10214 if (aFlags & SaveSTS_CurStateModified)
10215 mData->pMachineConfigFile->fCurrentStateModified = true;
10216
10217 if (aFlags & SaveSTS_StateFilePath)
10218 {
10219 if (!mSSData->strStateFilePath.isEmpty())
10220 /* try to make the file name relative to the settings file dir */
10221 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10222 else
10223 mData->pMachineConfigFile->strStateFile.setNull();
10224 }
10225
10226 if (aFlags & SaveSTS_StateTimeStamp)
10227 {
10228 Assert( mData->mMachineState != MachineState_Aborted
10229 || mSSData->strStateFilePath.isEmpty());
10230
10231 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10232
10233 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10234//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10235 }
10236
10237 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10238 }
10239 catch (...)
10240 {
10241 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10242 }
10243
10244 return rc;
10245}
10246
10247/**
10248 * Ensures that the given medium is added to a media registry. If this machine
10249 * was created with 4.0 or later, then the machine registry is used. Otherwise
10250 * the global VirtualBox media registry is used.
10251 *
10252 * Caller must NOT hold machine lock, media tree or any medium locks!
10253 *
10254 * @param pMedium
10255 */
10256void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10257{
10258 /* Paranoia checks: do not hold machine or media tree locks. */
10259 AssertReturnVoid(!isWriteLockOnCurrentThread());
10260 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10261
10262 ComObjPtr<Medium> pBase;
10263 {
10264 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10265 pBase = pMedium->i_getBase();
10266 }
10267
10268 /* Paranoia checks: do not hold medium locks. */
10269 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10270 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10271
10272 // decide which medium registry to use now that the medium is attached:
10273 Guid uuid;
10274 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10275 // machine XML is VirtualBox 4.0 or higher:
10276 uuid = i_getId(); // machine UUID
10277 else
10278 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10279
10280 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10281 mParent->i_markRegistryModified(uuid);
10282
10283 /* For more complex hard disk structures it can happen that the base
10284 * medium isn't yet associated with any medium registry. Do that now. */
10285 if (pMedium != pBase)
10286 {
10287 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10288 mParent->i_markRegistryModified(uuid);
10289 }
10290}
10291
10292/**
10293 * Creates differencing hard disks for all normal hard disks attached to this
10294 * machine and a new set of attachments to refer to created disks.
10295 *
10296 * Used when taking a snapshot or when deleting the current state. Gets called
10297 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10298 *
10299 * This method assumes that mMediaData contains the original hard disk attachments
10300 * it needs to create diffs for. On success, these attachments will be replaced
10301 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10302 * called to delete created diffs which will also rollback mMediaData and restore
10303 * whatever was backed up before calling this method.
10304 *
10305 * Attachments with non-normal hard disks are left as is.
10306 *
10307 * If @a aOnline is @c false then the original hard disks that require implicit
10308 * diffs will be locked for reading. Otherwise it is assumed that they are
10309 * already locked for writing (when the VM was started). Note that in the latter
10310 * case it is responsibility of the caller to lock the newly created diffs for
10311 * writing if this method succeeds.
10312 *
10313 * @param aProgress Progress object to run (must contain at least as
10314 * many operations left as the number of hard disks
10315 * attached).
10316 * @param aOnline Whether the VM was online prior to this operation.
10317 *
10318 * @note The progress object is not marked as completed, neither on success nor
10319 * on failure. This is a responsibility of the caller.
10320 *
10321 * @note Locks this object and the media tree for writing.
10322 */
10323HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10324 ULONG aWeight,
10325 bool aOnline)
10326{
10327 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10328
10329 AutoCaller autoCaller(this);
10330 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10331
10332 AutoMultiWriteLock2 alock(this->lockHandle(),
10333 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10334
10335 /* must be in a protective state because we release the lock below */
10336 AssertReturn( mData->mMachineState == MachineState_Saving
10337 || mData->mMachineState == MachineState_LiveSnapshotting
10338 || mData->mMachineState == MachineState_RestoringSnapshot
10339 || mData->mMachineState == MachineState_DeletingSnapshot
10340 , E_FAIL);
10341
10342 HRESULT rc = S_OK;
10343
10344 // use appropriate locked media map (online or offline)
10345 MediumLockListMap lockedMediaOffline;
10346 MediumLockListMap *lockedMediaMap;
10347 if (aOnline)
10348 lockedMediaMap = &mData->mSession.mLockedMedia;
10349 else
10350 lockedMediaMap = &lockedMediaOffline;
10351
10352 try
10353 {
10354 if (!aOnline)
10355 {
10356 /* lock all attached hard disks early to detect "in use"
10357 * situations before creating actual diffs */
10358 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10359 it != mMediaData->mAttachments.end();
10360 ++it)
10361 {
10362 MediumAttachment* pAtt = *it;
10363 if (pAtt->i_getType() == DeviceType_HardDisk)
10364 {
10365 Medium* pMedium = pAtt->i_getMedium();
10366 Assert(pMedium);
10367
10368 MediumLockList *pMediumLockList(new MediumLockList());
10369 alock.release();
10370 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10371 false /* fMediumLockWrite */,
10372 NULL,
10373 *pMediumLockList);
10374 alock.acquire();
10375 if (FAILED(rc))
10376 {
10377 delete pMediumLockList;
10378 throw rc;
10379 }
10380 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10381 if (FAILED(rc))
10382 {
10383 throw setError(rc,
10384 tr("Collecting locking information for all attached media failed"));
10385 }
10386 }
10387 }
10388
10389 /* Now lock all media. If this fails, nothing is locked. */
10390 alock.release();
10391 rc = lockedMediaMap->Lock();
10392 alock.acquire();
10393 if (FAILED(rc))
10394 {
10395 throw setError(rc,
10396 tr("Locking of attached media failed"));
10397 }
10398 }
10399
10400 /* remember the current list (note that we don't use backup() since
10401 * mMediaData may be already backed up) */
10402 MediaData::AttachmentList atts = mMediaData->mAttachments;
10403
10404 /* start from scratch */
10405 mMediaData->mAttachments.clear();
10406
10407 /* go through remembered attachments and create diffs for normal hard
10408 * disks and attach them */
10409 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10410 it != atts.end();
10411 ++it)
10412 {
10413 MediumAttachment* pAtt = *it;
10414
10415 DeviceType_T devType = pAtt->i_getType();
10416 Medium* pMedium = pAtt->i_getMedium();
10417
10418 if ( devType != DeviceType_HardDisk
10419 || pMedium == NULL
10420 || pMedium->i_getType() != MediumType_Normal)
10421 {
10422 /* copy the attachment as is */
10423
10424 /** @todo the progress object created in Console::TakeSnaphot
10425 * only expects operations for hard disks. Later other
10426 * device types need to show up in the progress as well. */
10427 if (devType == DeviceType_HardDisk)
10428 {
10429 if (pMedium == NULL)
10430 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10431 aWeight); // weight
10432 else
10433 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10434 pMedium->i_getBase()->i_getName().c_str()).raw(),
10435 aWeight); // weight
10436 }
10437
10438 mMediaData->mAttachments.push_back(pAtt);
10439 continue;
10440 }
10441
10442 /* need a diff */
10443 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10444 pMedium->i_getBase()->i_getName().c_str()).raw(),
10445 aWeight); // weight
10446
10447 Utf8Str strFullSnapshotFolder;
10448 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10449
10450 ComObjPtr<Medium> diff;
10451 diff.createObject();
10452 // store the diff in the same registry as the parent
10453 // (this cannot fail here because we can't create implicit diffs for
10454 // unregistered images)
10455 Guid uuidRegistryParent;
10456 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10457 Assert(fInRegistry); NOREF(fInRegistry);
10458 rc = diff->init(mParent,
10459 pMedium->i_getPreferredDiffFormat(),
10460 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10461 uuidRegistryParent);
10462 if (FAILED(rc)) throw rc;
10463
10464 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10465 * the push_back? Looks like we're going to release medium with the
10466 * wrong kind of lock (general issue with if we fail anywhere at all)
10467 * and an orphaned VDI in the snapshots folder. */
10468
10469 /* update the appropriate lock list */
10470 MediumLockList *pMediumLockList;
10471 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10472 AssertComRCThrowRC(rc);
10473 if (aOnline)
10474 {
10475 alock.release();
10476 /* The currently attached medium will be read-only, change
10477 * the lock type to read. */
10478 rc = pMediumLockList->Update(pMedium, false);
10479 alock.acquire();
10480 AssertComRCThrowRC(rc);
10481 }
10482
10483 /* release the locks before the potentially lengthy operation */
10484 alock.release();
10485 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10486 pMediumLockList,
10487 NULL /* aProgress */,
10488 true /* aWait */);
10489 alock.acquire();
10490 if (FAILED(rc)) throw rc;
10491
10492 /* actual lock list update is done in Medium::commitMedia */
10493
10494 rc = diff->i_addBackReference(mData->mUuid);
10495 AssertComRCThrowRC(rc);
10496
10497 /* add a new attachment */
10498 ComObjPtr<MediumAttachment> attachment;
10499 attachment.createObject();
10500 rc = attachment->init(this,
10501 diff,
10502 pAtt->i_getControllerName(),
10503 pAtt->i_getPort(),
10504 pAtt->i_getDevice(),
10505 DeviceType_HardDisk,
10506 true /* aImplicit */,
10507 false /* aPassthrough */,
10508 false /* aTempEject */,
10509 pAtt->i_getNonRotational(),
10510 pAtt->i_getDiscard(),
10511 pAtt->i_getHotPluggable(),
10512 pAtt->i_getBandwidthGroup());
10513 if (FAILED(rc)) throw rc;
10514
10515 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10516 AssertComRCThrowRC(rc);
10517 mMediaData->mAttachments.push_back(attachment);
10518 }
10519 }
10520 catch (HRESULT aRC) { rc = aRC; }
10521
10522 /* unlock all hard disks we locked when there is no VM */
10523 if (!aOnline)
10524 {
10525 ErrorInfoKeeper eik;
10526
10527 HRESULT rc1 = lockedMediaMap->Clear();
10528 AssertComRC(rc1);
10529 }
10530
10531 return rc;
10532}
10533
10534/**
10535 * Deletes implicit differencing hard disks created either by
10536 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10537 *
10538 * Note that to delete hard disks created by #AttachDevice() this method is
10539 * called from #fixupMedia() when the changes are rolled back.
10540 *
10541 * @note Locks this object and the media tree for writing.
10542 */
10543HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10544{
10545 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10546
10547 AutoCaller autoCaller(this);
10548 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10549
10550 AutoMultiWriteLock2 alock(this->lockHandle(),
10551 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10552
10553 /* We absolutely must have backed up state. */
10554 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10555
10556 /* Check if there are any implicitly created diff images. */
10557 bool fImplicitDiffs = false;
10558 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10559 it != mMediaData->mAttachments.end();
10560 ++it)
10561 {
10562 const ComObjPtr<MediumAttachment> &pAtt = *it;
10563 if (pAtt->i_isImplicit())
10564 {
10565 fImplicitDiffs = true;
10566 break;
10567 }
10568 }
10569 /* If there is nothing to do, leave early. This saves lots of image locking
10570 * effort. It also avoids a MachineStateChanged event without real reason.
10571 * This is important e.g. when loading a VM config, because there should be
10572 * no events. Otherwise API clients can become thoroughly confused for
10573 * inaccessible VMs (the code for loading VM configs uses this method for
10574 * cleanup if the config makes no sense), as they take such events as an
10575 * indication that the VM is alive, and they would force the VM config to
10576 * be reread, leading to an endless loop. */
10577 if (!fImplicitDiffs)
10578 return S_OK;
10579
10580 HRESULT rc = S_OK;
10581 MachineState_T oldState = mData->mMachineState;
10582
10583 /* will release the lock before the potentially lengthy operation,
10584 * so protect with the special state (unless already protected) */
10585 if ( oldState != MachineState_Saving
10586 && oldState != MachineState_LiveSnapshotting
10587 && oldState != MachineState_RestoringSnapshot
10588 && oldState != MachineState_DeletingSnapshot
10589 && oldState != MachineState_DeletingSnapshotOnline
10590 && oldState != MachineState_DeletingSnapshotPaused
10591 )
10592 i_setMachineState(MachineState_SettingUp);
10593
10594 // use appropriate locked media map (online or offline)
10595 MediumLockListMap lockedMediaOffline;
10596 MediumLockListMap *lockedMediaMap;
10597 if (aOnline)
10598 lockedMediaMap = &mData->mSession.mLockedMedia;
10599 else
10600 lockedMediaMap = &lockedMediaOffline;
10601
10602 try
10603 {
10604 if (!aOnline)
10605 {
10606 /* lock all attached hard disks early to detect "in use"
10607 * situations before deleting actual diffs */
10608 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10609 it != mMediaData->mAttachments.end();
10610 ++it)
10611 {
10612 MediumAttachment* pAtt = *it;
10613 if (pAtt->i_getType() == DeviceType_HardDisk)
10614 {
10615 Medium* pMedium = pAtt->i_getMedium();
10616 Assert(pMedium);
10617
10618 MediumLockList *pMediumLockList(new MediumLockList());
10619 alock.release();
10620 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10621 false /* fMediumLockWrite */,
10622 NULL,
10623 *pMediumLockList);
10624 alock.acquire();
10625
10626 if (FAILED(rc))
10627 {
10628 delete pMediumLockList;
10629 throw rc;
10630 }
10631
10632 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10633 if (FAILED(rc))
10634 throw rc;
10635 }
10636 }
10637
10638 if (FAILED(rc))
10639 throw rc;
10640 } // end of offline
10641
10642 /* Lock lists are now up to date and include implicitly created media */
10643
10644 /* Go through remembered attachments and delete all implicitly created
10645 * diffs and fix up the attachment information */
10646 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10647 MediaData::AttachmentList implicitAtts;
10648 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10649 it != mMediaData->mAttachments.end();
10650 ++it)
10651 {
10652 ComObjPtr<MediumAttachment> pAtt = *it;
10653 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10654 if (pMedium.isNull())
10655 continue;
10656
10657 // Implicit attachments go on the list for deletion and back references are removed.
10658 if (pAtt->i_isImplicit())
10659 {
10660 /* Deassociate and mark for deletion */
10661 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10662 rc = pMedium->i_removeBackReference(mData->mUuid);
10663 if (FAILED(rc))
10664 throw rc;
10665 implicitAtts.push_back(pAtt);
10666 continue;
10667 }
10668
10669 /* Was this medium attached before? */
10670 if (!i_findAttachment(oldAtts, pMedium))
10671 {
10672 /* no: de-associate */
10673 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10674 rc = pMedium->i_removeBackReference(mData->mUuid);
10675 if (FAILED(rc))
10676 throw rc;
10677 continue;
10678 }
10679 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10680 }
10681
10682 /* If there are implicit attachments to delete, throw away the lock
10683 * map contents (which will unlock all media) since the medium
10684 * attachments will be rolled back. Below we need to completely
10685 * recreate the lock map anyway since it is infinitely complex to
10686 * do this incrementally (would need reconstructing each attachment
10687 * change, which would be extremely hairy). */
10688 if (implicitAtts.size() != 0)
10689 {
10690 ErrorInfoKeeper eik;
10691
10692 HRESULT rc1 = lockedMediaMap->Clear();
10693 AssertComRC(rc1);
10694 }
10695
10696 /* rollback hard disk changes */
10697 mMediaData.rollback();
10698
10699 MultiResult mrc(S_OK);
10700
10701 // Delete unused implicit diffs.
10702 if (implicitAtts.size() != 0)
10703 {
10704 alock.release();
10705
10706 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10707 {
10708 // Remove medium associated with this attachment.
10709 ComObjPtr<MediumAttachment> pAtt = *it;
10710 Assert(pAtt);
10711 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10712 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10713 Assert(pMedium);
10714
10715 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10716 // continue on delete failure, just collect error messages
10717 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10718 pMedium->i_getLocationFull().c_str() ));
10719 mrc = rc;
10720 }
10721
10722 alock.acquire();
10723
10724 /* if there is a VM recreate media lock map as mentioned above,
10725 * otherwise it is a waste of time and we leave things unlocked */
10726 if (aOnline)
10727 {
10728 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10729 /* must never be NULL, but better safe than sorry */
10730 if (!pMachine.isNull())
10731 {
10732 alock.release();
10733 rc = mData->mSession.mMachine->lockMedia();
10734 alock.acquire();
10735 if (FAILED(rc))
10736 throw rc;
10737 }
10738 }
10739 }
10740 }
10741 catch (HRESULT aRC) {rc = aRC;}
10742
10743 if (mData->mMachineState == MachineState_SettingUp)
10744 i_setMachineState(oldState);
10745
10746 /* unlock all hard disks we locked when there is no VM */
10747 if (!aOnline)
10748 {
10749 ErrorInfoKeeper eik;
10750
10751 HRESULT rc1 = lockedMediaMap->Clear();
10752 AssertComRC(rc1);
10753 }
10754
10755 return rc;
10756}
10757
10758
10759/**
10760 * Looks through the given list of media attachments for one with the given parameters
10761 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10762 * can be searched as well if needed.
10763 *
10764 * @param list
10765 * @param aControllerName
10766 * @param aControllerPort
10767 * @param aDevice
10768 * @return
10769 */
10770MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10771 IN_BSTR aControllerName,
10772 LONG aControllerPort,
10773 LONG aDevice)
10774{
10775 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10776 {
10777 MediumAttachment *pAttach = *it;
10778 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10779 return pAttach;
10780 }
10781
10782 return NULL;
10783}
10784
10785/**
10786 * Looks through the given list of media attachments for one with the given parameters
10787 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10788 * can be searched as well if needed.
10789 *
10790 * @param list
10791 * @param aControllerName
10792 * @param aControllerPort
10793 * @param aDevice
10794 * @return
10795 */
10796MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10797 ComObjPtr<Medium> pMedium)
10798{
10799 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10800 {
10801 MediumAttachment *pAttach = *it;
10802 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10803 if (pMediumThis == pMedium)
10804 return pAttach;
10805 }
10806
10807 return NULL;
10808}
10809
10810/**
10811 * Looks through the given list of media attachments for one with the given parameters
10812 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10813 * can be searched as well if needed.
10814 *
10815 * @param list
10816 * @param aControllerName
10817 * @param aControllerPort
10818 * @param aDevice
10819 * @return
10820 */
10821MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10822 Guid &id)
10823{
10824 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10825 {
10826 MediumAttachment *pAttach = *it;
10827 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10828 if (pMediumThis->i_getId() == id)
10829 return pAttach;
10830 }
10831
10832 return NULL;
10833}
10834
10835/**
10836 * Main implementation for Machine::DetachDevice. This also gets called
10837 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10838 *
10839 * @param pAttach Medium attachment to detach.
10840 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10841 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
10842 * SnapshotMachine, and this must be its snapshot.
10843 * @return
10844 */
10845HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
10846 AutoWriteLock &writeLock,
10847 Snapshot *pSnapshot)
10848{
10849 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
10850 DeviceType_T mediumType = pAttach->i_getType();
10851
10852 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
10853
10854 if (pAttach->i_isImplicit())
10855 {
10856 /* attempt to implicitly delete the implicitly created diff */
10857
10858 /// @todo move the implicit flag from MediumAttachment to Medium
10859 /// and forbid any hard disk operation when it is implicit. Or maybe
10860 /// a special media state for it to make it even more simple.
10861
10862 Assert(mMediaData.isBackedUp());
10863
10864 /* will release the lock before the potentially lengthy operation, so
10865 * protect with the special state */
10866 MachineState_T oldState = mData->mMachineState;
10867 i_setMachineState(MachineState_SettingUp);
10868
10869 writeLock.release();
10870
10871 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
10872 true /*aWait*/);
10873
10874 writeLock.acquire();
10875
10876 i_setMachineState(oldState);
10877
10878 if (FAILED(rc)) return rc;
10879 }
10880
10881 i_setModified(IsModified_Storage);
10882 mMediaData.backup();
10883 mMediaData->mAttachments.remove(pAttach);
10884
10885 if (!oldmedium.isNull())
10886 {
10887 // if this is from a snapshot, do not defer detachment to commitMedia()
10888 if (pSnapshot)
10889 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
10890 // else if non-hard disk media, do not defer detachment to commitMedia() either
10891 else if (mediumType != DeviceType_HardDisk)
10892 oldmedium->i_removeBackReference(mData->mUuid);
10893 }
10894
10895 return S_OK;
10896}
10897
10898/**
10899 * Goes thru all media of the given list and
10900 *
10901 * 1) calls i_detachDevice() on each of them for this machine and
10902 * 2) adds all Medium objects found in the process to the given list,
10903 * depending on cleanupMode.
10904 *
10905 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10906 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10907 * media to the list.
10908 *
10909 * This gets called from Machine::Unregister, both for the actual Machine and
10910 * the SnapshotMachine objects that might be found in the snapshots.
10911 *
10912 * Requires caller and locking. The machine lock must be passed in because it
10913 * will be passed on to i_detachDevice which needs it for temporary unlocking.
10914 *
10915 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
10916 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10917 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
10918 * Full, then all media get added;
10919 * otherwise no media get added.
10920 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10921 * @return
10922 */
10923HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
10924 Snapshot *pSnapshot,
10925 CleanupMode_T cleanupMode,
10926 MediaList &llMedia)
10927{
10928 Assert(isWriteLockOnCurrentThread());
10929
10930 HRESULT rc;
10931
10932 // make a temporary list because i_detachDevice invalidates iterators into
10933 // mMediaData->mAttachments
10934 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10935
10936 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
10937 {
10938 ComObjPtr<MediumAttachment> &pAttach = *it;
10939 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
10940
10941 if (!pMedium.isNull())
10942 {
10943 AutoCaller mac(pMedium);
10944 if (FAILED(mac.rc())) return mac.rc();
10945 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10946 DeviceType_T devType = pMedium->i_getDeviceType();
10947 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10948 && devType == DeviceType_HardDisk)
10949 || (cleanupMode == CleanupMode_Full)
10950 )
10951 {
10952 llMedia.push_back(pMedium);
10953 ComObjPtr<Medium> pParent = pMedium->i_getParent();
10954 /*
10955 * Search for medias which are not attached to any machine, but
10956 * in the chain to an attached disk. Mediums are only consided
10957 * if they are:
10958 * - have only one child
10959 * - no references to any machines
10960 * - are of normal medium type
10961 */
10962 while (!pParent.isNull())
10963 {
10964 AutoCaller mac1(pParent);
10965 if (FAILED(mac1.rc())) return mac1.rc();
10966 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10967 if (pParent->i_getChildren().size() == 1)
10968 {
10969 if ( pParent->i_getMachineBackRefCount() == 0
10970 && pParent->i_getType() == MediumType_Normal
10971 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10972 llMedia.push_back(pParent);
10973 }
10974 else
10975 break;
10976 pParent = pParent->i_getParent();
10977 }
10978 }
10979 }
10980
10981 // real machine: then we need to use the proper method
10982 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
10983
10984 if (FAILED(rc))
10985 return rc;
10986 }
10987
10988 return S_OK;
10989}
10990
10991/**
10992 * Perform deferred hard disk detachments.
10993 *
10994 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10995 * backed up).
10996 *
10997 * If @a aOnline is @c true then this method will also unlock the old hard disks
10998 * for which the new implicit diffs were created and will lock these new diffs for
10999 * writing.
11000 *
11001 * @param aOnline Whether the VM was online prior to this operation.
11002 *
11003 * @note Locks this object for writing!
11004 */
11005void Machine::i_commitMedia(bool aOnline /*= false*/)
11006{
11007 AutoCaller autoCaller(this);
11008 AssertComRCReturnVoid(autoCaller.rc());
11009
11010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11011
11012 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11013
11014 HRESULT rc = S_OK;
11015
11016 /* no attach/detach operations -- nothing to do */
11017 if (!mMediaData.isBackedUp())
11018 return;
11019
11020 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11021 bool fMediaNeedsLocking = false;
11022
11023 /* enumerate new attachments */
11024 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11025 it != mMediaData->mAttachments.end();
11026 ++it)
11027 {
11028 MediumAttachment *pAttach = *it;
11029
11030 pAttach->i_commit();
11031
11032 Medium* pMedium = pAttach->i_getMedium();
11033 bool fImplicit = pAttach->i_isImplicit();
11034
11035 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11036 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11037 fImplicit));
11038
11039 /** @todo convert all this Machine-based voodoo to MediumAttachment
11040 * based commit logic. */
11041 if (fImplicit)
11042 {
11043 /* convert implicit attachment to normal */
11044 pAttach->i_setImplicit(false);
11045
11046 if ( aOnline
11047 && pMedium
11048 && pAttach->i_getType() == DeviceType_HardDisk
11049 )
11050 {
11051 ComObjPtr<Medium> parent = pMedium->i_getParent();
11052 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11053
11054 /* update the appropriate lock list */
11055 MediumLockList *pMediumLockList;
11056 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11057 AssertComRC(rc);
11058 if (pMediumLockList)
11059 {
11060 /* unlock if there's a need to change the locking */
11061 if (!fMediaNeedsLocking)
11062 {
11063 rc = mData->mSession.mLockedMedia.Unlock();
11064 AssertComRC(rc);
11065 fMediaNeedsLocking = true;
11066 }
11067 rc = pMediumLockList->Update(parent, false);
11068 AssertComRC(rc);
11069 rc = pMediumLockList->Append(pMedium, true);
11070 AssertComRC(rc);
11071 }
11072 }
11073
11074 continue;
11075 }
11076
11077 if (pMedium)
11078 {
11079 /* was this medium attached before? */
11080 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11081 {
11082 MediumAttachment *pOldAttach = *oldIt;
11083 if (pOldAttach->i_getMedium() == pMedium)
11084 {
11085 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11086
11087 /* yes: remove from old to avoid de-association */
11088 oldAtts.erase(oldIt);
11089 break;
11090 }
11091 }
11092 }
11093 }
11094
11095 /* enumerate remaining old attachments and de-associate from the
11096 * current machine state */
11097 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11098 {
11099 MediumAttachment *pAttach = *it;
11100 Medium* pMedium = pAttach->i_getMedium();
11101
11102 /* Detach only hard disks, since DVD/floppy media is detached
11103 * instantly in MountMedium. */
11104 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11105 {
11106 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11107
11108 /* now de-associate from the current machine state */
11109 rc = pMedium->i_removeBackReference(mData->mUuid);
11110 AssertComRC(rc);
11111
11112 if (aOnline)
11113 {
11114 /* unlock since medium is not used anymore */
11115 MediumLockList *pMediumLockList;
11116 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11117 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11118 {
11119 /* this happens for online snapshots, there the attachment
11120 * is changing, but only to a diff image created under
11121 * the old one, so there is no separate lock list */
11122 Assert(!pMediumLockList);
11123 }
11124 else
11125 {
11126 AssertComRC(rc);
11127 if (pMediumLockList)
11128 {
11129 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11130 AssertComRC(rc);
11131 }
11132 }
11133 }
11134 }
11135 }
11136
11137 /* take media locks again so that the locking state is consistent */
11138 if (fMediaNeedsLocking)
11139 {
11140 Assert(aOnline);
11141 rc = mData->mSession.mLockedMedia.Lock();
11142 AssertComRC(rc);
11143 }
11144
11145 /* commit the hard disk changes */
11146 mMediaData.commit();
11147
11148 if (i_isSessionMachine())
11149 {
11150 /*
11151 * Update the parent machine to point to the new owner.
11152 * This is necessary because the stored parent will point to the
11153 * session machine otherwise and cause crashes or errors later
11154 * when the session machine gets invalid.
11155 */
11156 /** @todo Change the MediumAttachment class to behave like any other
11157 * class in this regard by creating peer MediumAttachment
11158 * objects for session machines and share the data with the peer
11159 * machine.
11160 */
11161 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11162 it != mMediaData->mAttachments.end();
11163 ++it)
11164 (*it)->i_updateParentMachine(mPeer);
11165
11166 /* attach new data to the primary machine and reshare it */
11167 mPeer->mMediaData.attach(mMediaData);
11168 }
11169
11170 return;
11171}
11172
11173/**
11174 * Perform deferred deletion of implicitly created diffs.
11175 *
11176 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11177 * backed up).
11178 *
11179 * @note Locks this object for writing!
11180 */
11181void Machine::i_rollbackMedia()
11182{
11183 AutoCaller autoCaller(this);
11184 AssertComRCReturnVoid(autoCaller.rc());
11185
11186 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11187 LogFlowThisFunc(("Entering rollbackMedia\n"));
11188
11189 HRESULT rc = S_OK;
11190
11191 /* no attach/detach operations -- nothing to do */
11192 if (!mMediaData.isBackedUp())
11193 return;
11194
11195 /* enumerate new attachments */
11196 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11197 it != mMediaData->mAttachments.end();
11198 ++it)
11199 {
11200 MediumAttachment *pAttach = *it;
11201 /* Fix up the backrefs for DVD/floppy media. */
11202 if (pAttach->i_getType() != DeviceType_HardDisk)
11203 {
11204 Medium* pMedium = pAttach->i_getMedium();
11205 if (pMedium)
11206 {
11207 rc = pMedium->i_removeBackReference(mData->mUuid);
11208 AssertComRC(rc);
11209 }
11210 }
11211
11212 (*it)->i_rollback();
11213
11214 pAttach = *it;
11215 /* Fix up the backrefs for DVD/floppy media. */
11216 if (pAttach->i_getType() != DeviceType_HardDisk)
11217 {
11218 Medium* pMedium = pAttach->i_getMedium();
11219 if (pMedium)
11220 {
11221 rc = pMedium->i_addBackReference(mData->mUuid);
11222 AssertComRC(rc);
11223 }
11224 }
11225 }
11226
11227 /** @todo convert all this Machine-based voodoo to MediumAttachment
11228 * based rollback logic. */
11229 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11230
11231 return;
11232}
11233
11234/**
11235 * Returns true if the settings file is located in the directory named exactly
11236 * as the machine; this means, among other things, that the machine directory
11237 * should be auto-renamed.
11238 *
11239 * @param aSettingsDir if not NULL, the full machine settings file directory
11240 * name will be assigned there.
11241 *
11242 * @note Doesn't lock anything.
11243 * @note Not thread safe (must be called from this object's lock).
11244 */
11245bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11246{
11247 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11248 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11249 if (aSettingsDir)
11250 *aSettingsDir = strMachineDirName;
11251 strMachineDirName.stripPath(); // vmname
11252 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11253 strConfigFileOnly.stripPath() // vmname.vbox
11254 .stripSuffix(); // vmname
11255 /** @todo hack, make somehow use of ComposeMachineFilename */
11256 if (mUserData->s.fDirectoryIncludesUUID)
11257 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11258
11259 AssertReturn(!strMachineDirName.isEmpty(), false);
11260 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11261
11262 return strMachineDirName == strConfigFileOnly;
11263}
11264
11265/**
11266 * Discards all changes to machine settings.
11267 *
11268 * @param aNotify Whether to notify the direct session about changes or not.
11269 *
11270 * @note Locks objects for writing!
11271 */
11272void Machine::i_rollback(bool aNotify)
11273{
11274 AutoCaller autoCaller(this);
11275 AssertComRCReturn(autoCaller.rc(), (void)0);
11276
11277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11278
11279 if (!mStorageControllers.isNull())
11280 {
11281 if (mStorageControllers.isBackedUp())
11282 {
11283 /* unitialize all new devices (absent in the backed up list). */
11284 StorageControllerList::const_iterator it = mStorageControllers->begin();
11285 StorageControllerList *backedList = mStorageControllers.backedUpData();
11286 while (it != mStorageControllers->end())
11287 {
11288 if ( std::find(backedList->begin(), backedList->end(), *it)
11289 == backedList->end()
11290 )
11291 {
11292 (*it)->uninit();
11293 }
11294 ++it;
11295 }
11296
11297 /* restore the list */
11298 mStorageControllers.rollback();
11299 }
11300
11301 /* rollback any changes to devices after restoring the list */
11302 if (mData->flModifications & IsModified_Storage)
11303 {
11304 StorageControllerList::const_iterator it = mStorageControllers->begin();
11305 while (it != mStorageControllers->end())
11306 {
11307 (*it)->i_rollback();
11308 ++it;
11309 }
11310 }
11311 }
11312
11313 if (!mUSBControllers.isNull())
11314 {
11315 if (mUSBControllers.isBackedUp())
11316 {
11317 /* unitialize all new devices (absent in the backed up list). */
11318 USBControllerList::const_iterator it = mUSBControllers->begin();
11319 USBControllerList *backedList = mUSBControllers.backedUpData();
11320 while (it != mUSBControllers->end())
11321 {
11322 if ( std::find(backedList->begin(), backedList->end(), *it)
11323 == backedList->end()
11324 )
11325 {
11326 (*it)->uninit();
11327 }
11328 ++it;
11329 }
11330
11331 /* restore the list */
11332 mUSBControllers.rollback();
11333 }
11334
11335 /* rollback any changes to devices after restoring the list */
11336 if (mData->flModifications & IsModified_USB)
11337 {
11338 USBControllerList::const_iterator it = mUSBControllers->begin();
11339 while (it != mUSBControllers->end())
11340 {
11341 (*it)->i_rollback();
11342 ++it;
11343 }
11344 }
11345 }
11346
11347 mUserData.rollback();
11348
11349 mHWData.rollback();
11350
11351 if (mData->flModifications & IsModified_Storage)
11352 i_rollbackMedia();
11353
11354 if (mBIOSSettings)
11355 mBIOSSettings->i_rollback();
11356
11357 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11358 mVRDEServer->i_rollback();
11359
11360 if (mAudioAdapter)
11361 mAudioAdapter->i_rollback();
11362
11363 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11364 mUSBDeviceFilters->i_rollback();
11365
11366 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11367 mBandwidthControl->i_rollback();
11368
11369 if (!mHWData.isNull())
11370 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11371 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11372 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11373 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11374
11375 if (mData->flModifications & IsModified_NetworkAdapters)
11376 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11377 if ( mNetworkAdapters[slot]
11378 && mNetworkAdapters[slot]->i_isModified())
11379 {
11380 mNetworkAdapters[slot]->i_rollback();
11381 networkAdapters[slot] = mNetworkAdapters[slot];
11382 }
11383
11384 if (mData->flModifications & IsModified_SerialPorts)
11385 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11386 if ( mSerialPorts[slot]
11387 && mSerialPorts[slot]->i_isModified())
11388 {
11389 mSerialPorts[slot]->i_rollback();
11390 serialPorts[slot] = mSerialPorts[slot];
11391 }
11392
11393 if (mData->flModifications & IsModified_ParallelPorts)
11394 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11395 if ( mParallelPorts[slot]
11396 && mParallelPorts[slot]->i_isModified())
11397 {
11398 mParallelPorts[slot]->i_rollback();
11399 parallelPorts[slot] = mParallelPorts[slot];
11400 }
11401
11402 if (aNotify)
11403 {
11404 /* inform the direct session about changes */
11405
11406 ComObjPtr<Machine> that = this;
11407 uint32_t flModifications = mData->flModifications;
11408 alock.release();
11409
11410 if (flModifications & IsModified_SharedFolders)
11411 that->i_onSharedFolderChange();
11412
11413 if (flModifications & IsModified_VRDEServer)
11414 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11415 if (flModifications & IsModified_USB)
11416 that->i_onUSBControllerChange();
11417
11418 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11419 if (networkAdapters[slot])
11420 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11421 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11422 if (serialPorts[slot])
11423 that->i_onSerialPortChange(serialPorts[slot]);
11424 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11425 if (parallelPorts[slot])
11426 that->i_onParallelPortChange(parallelPorts[slot]);
11427
11428 if (flModifications & IsModified_Storage)
11429 that->i_onStorageControllerChange();
11430
11431#if 0
11432 if (flModifications & IsModified_BandwidthControl)
11433 that->onBandwidthControlChange();
11434#endif
11435 }
11436}
11437
11438/**
11439 * Commits all the changes to machine settings.
11440 *
11441 * Note that this operation is supposed to never fail.
11442 *
11443 * @note Locks this object and children for writing.
11444 */
11445void Machine::i_commit()
11446{
11447 AutoCaller autoCaller(this);
11448 AssertComRCReturnVoid(autoCaller.rc());
11449
11450 AutoCaller peerCaller(mPeer);
11451 AssertComRCReturnVoid(peerCaller.rc());
11452
11453 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11454
11455 /*
11456 * use safe commit to ensure Snapshot machines (that share mUserData)
11457 * will still refer to a valid memory location
11458 */
11459 mUserData.commitCopy();
11460
11461 mHWData.commit();
11462
11463 if (mMediaData.isBackedUp())
11464 i_commitMedia(Global::IsOnline(mData->mMachineState));
11465
11466 mBIOSSettings->i_commit();
11467 mVRDEServer->i_commit();
11468 mAudioAdapter->i_commit();
11469 mUSBDeviceFilters->i_commit();
11470 mBandwidthControl->i_commit();
11471
11472 /* Since mNetworkAdapters is a list which might have been changed (resized)
11473 * without using the Backupable<> template we need to handle the copying
11474 * of the list entries manually, including the creation of peers for the
11475 * new objects. */
11476 bool commitNetworkAdapters = false;
11477 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11478 if (mPeer)
11479 {
11480 /* commit everything, even the ones which will go away */
11481 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11482 mNetworkAdapters[slot]->i_commit();
11483 /* copy over the new entries, creating a peer and uninit the original */
11484 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11485 for (size_t slot = 0; slot < newSize; slot++)
11486 {
11487 /* look if this adapter has a peer device */
11488 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11489 if (!peer)
11490 {
11491 /* no peer means the adapter is a newly created one;
11492 * create a peer owning data this data share it with */
11493 peer.createObject();
11494 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11495 }
11496 mPeer->mNetworkAdapters[slot] = peer;
11497 }
11498 /* uninit any no longer needed network adapters */
11499 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11500 mNetworkAdapters[slot]->uninit();
11501 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11502 {
11503 if (mPeer->mNetworkAdapters[slot])
11504 mPeer->mNetworkAdapters[slot]->uninit();
11505 }
11506 /* Keep the original network adapter count until this point, so that
11507 * discarding a chipset type change will not lose settings. */
11508 mNetworkAdapters.resize(newSize);
11509 mPeer->mNetworkAdapters.resize(newSize);
11510 }
11511 else
11512 {
11513 /* we have no peer (our parent is the newly created machine);
11514 * just commit changes to the network adapters */
11515 commitNetworkAdapters = true;
11516 }
11517 if (commitNetworkAdapters)
11518 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11519 mNetworkAdapters[slot]->i_commit();
11520
11521 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11522 mSerialPorts[slot]->i_commit();
11523 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11524 mParallelPorts[slot]->i_commit();
11525
11526 bool commitStorageControllers = false;
11527
11528 if (mStorageControllers.isBackedUp())
11529 {
11530 mStorageControllers.commit();
11531
11532 if (mPeer)
11533 {
11534 /* Commit all changes to new controllers (this will reshare data with
11535 * peers for those who have peers) */
11536 StorageControllerList *newList = new StorageControllerList();
11537 StorageControllerList::const_iterator it = mStorageControllers->begin();
11538 while (it != mStorageControllers->end())
11539 {
11540 (*it)->i_commit();
11541
11542 /* look if this controller has a peer device */
11543 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11544 if (!peer)
11545 {
11546 /* no peer means the device is a newly created one;
11547 * create a peer owning data this device share it with */
11548 peer.createObject();
11549 peer->init(mPeer, *it, true /* aReshare */);
11550 }
11551 else
11552 {
11553 /* remove peer from the old list */
11554 mPeer->mStorageControllers->remove(peer);
11555 }
11556 /* and add it to the new list */
11557 newList->push_back(peer);
11558
11559 ++it;
11560 }
11561
11562 /* uninit old peer's controllers that are left */
11563 it = mPeer->mStorageControllers->begin();
11564 while (it != mPeer->mStorageControllers->end())
11565 {
11566 (*it)->uninit();
11567 ++it;
11568 }
11569
11570 /* attach new list of controllers to our peer */
11571 mPeer->mStorageControllers.attach(newList);
11572 }
11573 else
11574 {
11575 /* we have no peer (our parent is the newly created machine);
11576 * just commit changes to devices */
11577 commitStorageControllers = true;
11578 }
11579 }
11580 else
11581 {
11582 /* the list of controllers itself is not changed,
11583 * just commit changes to controllers themselves */
11584 commitStorageControllers = true;
11585 }
11586
11587 if (commitStorageControllers)
11588 {
11589 StorageControllerList::const_iterator it = mStorageControllers->begin();
11590 while (it != mStorageControllers->end())
11591 {
11592 (*it)->i_commit();
11593 ++it;
11594 }
11595 }
11596
11597 bool commitUSBControllers = false;
11598
11599 if (mUSBControllers.isBackedUp())
11600 {
11601 mUSBControllers.commit();
11602
11603 if (mPeer)
11604 {
11605 /* Commit all changes to new controllers (this will reshare data with
11606 * peers for those who have peers) */
11607 USBControllerList *newList = new USBControllerList();
11608 USBControllerList::const_iterator it = mUSBControllers->begin();
11609 while (it != mUSBControllers->end())
11610 {
11611 (*it)->i_commit();
11612
11613 /* look if this controller has a peer device */
11614 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11615 if (!peer)
11616 {
11617 /* no peer means the device is a newly created one;
11618 * create a peer owning data this device share it with */
11619 peer.createObject();
11620 peer->init(mPeer, *it, true /* aReshare */);
11621 }
11622 else
11623 {
11624 /* remove peer from the old list */
11625 mPeer->mUSBControllers->remove(peer);
11626 }
11627 /* and add it to the new list */
11628 newList->push_back(peer);
11629
11630 ++it;
11631 }
11632
11633 /* uninit old peer's controllers that are left */
11634 it = mPeer->mUSBControllers->begin();
11635 while (it != mPeer->mUSBControllers->end())
11636 {
11637 (*it)->uninit();
11638 ++it;
11639 }
11640
11641 /* attach new list of controllers to our peer */
11642 mPeer->mUSBControllers.attach(newList);
11643 }
11644 else
11645 {
11646 /* we have no peer (our parent is the newly created machine);
11647 * just commit changes to devices */
11648 commitUSBControllers = true;
11649 }
11650 }
11651 else
11652 {
11653 /* the list of controllers itself is not changed,
11654 * just commit changes to controllers themselves */
11655 commitUSBControllers = true;
11656 }
11657
11658 if (commitUSBControllers)
11659 {
11660 USBControllerList::const_iterator it = mUSBControllers->begin();
11661 while (it != mUSBControllers->end())
11662 {
11663 (*it)->i_commit();
11664 ++it;
11665 }
11666 }
11667
11668 if (i_isSessionMachine())
11669 {
11670 /* attach new data to the primary machine and reshare it */
11671 mPeer->mUserData.attach(mUserData);
11672 mPeer->mHWData.attach(mHWData);
11673 /* mMediaData is reshared by fixupMedia */
11674 // mPeer->mMediaData.attach(mMediaData);
11675 Assert(mPeer->mMediaData.data() == mMediaData.data());
11676 }
11677}
11678
11679/**
11680 * Copies all the hardware data from the given machine.
11681 *
11682 * Currently, only called when the VM is being restored from a snapshot. In
11683 * particular, this implies that the VM is not running during this method's
11684 * call.
11685 *
11686 * @note This method must be called from under this object's lock.
11687 *
11688 * @note This method doesn't call #commit(), so all data remains backed up and
11689 * unsaved.
11690 */
11691void Machine::i_copyFrom(Machine *aThat)
11692{
11693 AssertReturnVoid(!i_isSnapshotMachine());
11694 AssertReturnVoid(aThat->i_isSnapshotMachine());
11695
11696 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11697
11698 mHWData.assignCopy(aThat->mHWData);
11699
11700 // create copies of all shared folders (mHWData after attaching a copy
11701 // contains just references to original objects)
11702 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11703 it != mHWData->mSharedFolders.end();
11704 ++it)
11705 {
11706 ComObjPtr<SharedFolder> folder;
11707 folder.createObject();
11708 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11709 AssertComRC(rc);
11710 *it = folder;
11711 }
11712
11713 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11714 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11715 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11716 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11717 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11718
11719 /* create private copies of all controllers */
11720 mStorageControllers.backup();
11721 mStorageControllers->clear();
11722 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11723 it != aThat->mStorageControllers->end();
11724 ++it)
11725 {
11726 ComObjPtr<StorageController> ctrl;
11727 ctrl.createObject();
11728 ctrl->initCopy(this, *it);
11729 mStorageControllers->push_back(ctrl);
11730 }
11731
11732 /* create private copies of all USB controllers */
11733 mUSBControllers.backup();
11734 mUSBControllers->clear();
11735 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11736 it != aThat->mUSBControllers->end();
11737 ++it)
11738 {
11739 ComObjPtr<USBController> ctrl;
11740 ctrl.createObject();
11741 ctrl->initCopy(this, *it);
11742 mUSBControllers->push_back(ctrl);
11743 }
11744
11745 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11746 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11747 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11748 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11749 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11750 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11751 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11752}
11753
11754/**
11755 * Returns whether the given storage controller is hotplug capable.
11756 *
11757 * @returns true if the controller supports hotplugging
11758 * false otherwise.
11759 * @param enmCtrlType The controller type to check for.
11760 */
11761bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11762{
11763 ComPtr<ISystemProperties> systemProperties;
11764 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11765 if (FAILED(rc))
11766 return false;
11767
11768 BOOL aHotplugCapable = FALSE;
11769 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11770
11771 return RT_BOOL(aHotplugCapable);
11772}
11773
11774#ifdef VBOX_WITH_RESOURCE_USAGE_API
11775
11776void Machine::i_getDiskList(MediaList &list)
11777{
11778 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11779 it != mMediaData->mAttachments.end();
11780 ++it)
11781 {
11782 MediumAttachment* pAttach = *it;
11783 /* just in case */
11784 AssertStmt(pAttach, continue);
11785
11786 AutoCaller localAutoCallerA(pAttach);
11787 if (FAILED(localAutoCallerA.rc())) continue;
11788
11789 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11790
11791 if (pAttach->i_getType() == DeviceType_HardDisk)
11792 list.push_back(pAttach->i_getMedium());
11793 }
11794}
11795
11796void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11797{
11798 AssertReturnVoid(isWriteLockOnCurrentThread());
11799 AssertPtrReturnVoid(aCollector);
11800
11801 pm::CollectorHAL *hal = aCollector->getHAL();
11802 /* Create sub metrics */
11803 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11804 "Percentage of processor time spent in user mode by the VM process.");
11805 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11806 "Percentage of processor time spent in kernel mode by the VM process.");
11807 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11808 "Size of resident portion of VM process in memory.");
11809 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11810 "Actual size of all VM disks combined.");
11811 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11812 "Network receive rate.");
11813 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11814 "Network transmit rate.");
11815 /* Create and register base metrics */
11816 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11817 cpuLoadUser, cpuLoadKernel);
11818 aCollector->registerBaseMetric(cpuLoad);
11819 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11820 ramUsageUsed);
11821 aCollector->registerBaseMetric(ramUsage);
11822 MediaList disks;
11823 i_getDiskList(disks);
11824 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11825 diskUsageUsed);
11826 aCollector->registerBaseMetric(diskUsage);
11827
11828 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11829 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11830 new pm::AggregateAvg()));
11831 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11832 new pm::AggregateMin()));
11833 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11834 new pm::AggregateMax()));
11835 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11836 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11837 new pm::AggregateAvg()));
11838 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11839 new pm::AggregateMin()));
11840 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11841 new pm::AggregateMax()));
11842
11843 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11844 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11845 new pm::AggregateAvg()));
11846 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11847 new pm::AggregateMin()));
11848 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11849 new pm::AggregateMax()));
11850
11851 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11852 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11853 new pm::AggregateAvg()));
11854 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11855 new pm::AggregateMin()));
11856 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11857 new pm::AggregateMax()));
11858
11859
11860 /* Guest metrics collector */
11861 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11862 aCollector->registerGuest(mCollectorGuest);
11863 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11864 this, __PRETTY_FUNCTION__, mCollectorGuest));
11865
11866 /* Create sub metrics */
11867 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11868 "Percentage of processor time spent in user mode as seen by the guest.");
11869 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11870 "Percentage of processor time spent in kernel mode as seen by the guest.");
11871 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11872 "Percentage of processor time spent idling as seen by the guest.");
11873
11874 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11875 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11876 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11877 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11878 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11879 pm::SubMetric *guestMemCache = new pm::SubMetric(
11880 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11881
11882 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
11883 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11884
11885 /* Create and register base metrics */
11886 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11887 machineNetRx, machineNetTx);
11888 aCollector->registerBaseMetric(machineNetRate);
11889
11890 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11891 guestLoadUser, guestLoadKernel, guestLoadIdle);
11892 aCollector->registerBaseMetric(guestCpuLoad);
11893
11894 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11895 guestMemTotal, guestMemFree,
11896 guestMemBalloon, guestMemShared,
11897 guestMemCache, guestPagedTotal);
11898 aCollector->registerBaseMetric(guestCpuMem);
11899
11900 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11901 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11902 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11903 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11904
11905 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11906 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11907 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11908 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11909
11910 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11911 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11912 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11913 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11914
11915 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11916 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11917 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11918 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11919
11920 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11921 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11922 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11923 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11924
11925 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11926 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11927 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11928 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11929
11930 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11931 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11932 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11933 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11934
11935 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11936 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11937 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11938 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11939
11940 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11941 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11942 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11943 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11944
11945 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11946 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11947 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11948 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11949
11950 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11951 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11952 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11953 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11954}
11955
11956void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11957{
11958 AssertReturnVoid(isWriteLockOnCurrentThread());
11959
11960 if (aCollector)
11961 {
11962 aCollector->unregisterMetricsFor(aMachine);
11963 aCollector->unregisterBaseMetricsFor(aMachine);
11964 }
11965}
11966
11967#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11968
11969
11970////////////////////////////////////////////////////////////////////////////////
11971
11972DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11973
11974HRESULT SessionMachine::FinalConstruct()
11975{
11976 LogFlowThisFunc(("\n"));
11977
11978 mClientToken = NULL;
11979
11980 return BaseFinalConstruct();
11981}
11982
11983void SessionMachine::FinalRelease()
11984{
11985 LogFlowThisFunc(("\n"));
11986
11987 Assert(!mClientToken);
11988 /* paranoia, should not hang around any more */
11989 if (mClientToken)
11990 {
11991 delete mClientToken;
11992 mClientToken = NULL;
11993 }
11994
11995 uninit(Uninit::Unexpected);
11996
11997 BaseFinalRelease();
11998}
11999
12000/**
12001 * @note Must be called only by Machine::LockMachine() from its own write lock.
12002 */
12003HRESULT SessionMachine::init(Machine *aMachine)
12004{
12005 LogFlowThisFuncEnter();
12006 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12007
12008 AssertReturn(aMachine, E_INVALIDARG);
12009
12010 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12011
12012 /* Enclose the state transition NotReady->InInit->Ready */
12013 AutoInitSpan autoInitSpan(this);
12014 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12015
12016 HRESULT rc = S_OK;
12017
12018 /* create the machine client token */
12019 try
12020 {
12021 mClientToken = new ClientToken(aMachine, this);
12022 if (!mClientToken->isReady())
12023 {
12024 delete mClientToken;
12025 mClientToken = NULL;
12026 rc = E_FAIL;
12027 }
12028 }
12029 catch (std::bad_alloc &)
12030 {
12031 rc = E_OUTOFMEMORY;
12032 }
12033 if (FAILED(rc))
12034 return rc;
12035
12036 /* memorize the peer Machine */
12037 unconst(mPeer) = aMachine;
12038 /* share the parent pointer */
12039 unconst(mParent) = aMachine->mParent;
12040
12041 /* take the pointers to data to share */
12042 mData.share(aMachine->mData);
12043 mSSData.share(aMachine->mSSData);
12044
12045 mUserData.share(aMachine->mUserData);
12046 mHWData.share(aMachine->mHWData);
12047 mMediaData.share(aMachine->mMediaData);
12048
12049 mStorageControllers.allocate();
12050 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12051 it != aMachine->mStorageControllers->end();
12052 ++it)
12053 {
12054 ComObjPtr<StorageController> ctl;
12055 ctl.createObject();
12056 ctl->init(this, *it);
12057 mStorageControllers->push_back(ctl);
12058 }
12059
12060 mUSBControllers.allocate();
12061 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12062 it != aMachine->mUSBControllers->end();
12063 ++it)
12064 {
12065 ComObjPtr<USBController> ctl;
12066 ctl.createObject();
12067 ctl->init(this, *it);
12068 mUSBControllers->push_back(ctl);
12069 }
12070
12071 unconst(mBIOSSettings).createObject();
12072 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12073 /* create another VRDEServer object that will be mutable */
12074 unconst(mVRDEServer).createObject();
12075 mVRDEServer->init(this, aMachine->mVRDEServer);
12076 /* create another audio adapter object that will be mutable */
12077 unconst(mAudioAdapter).createObject();
12078 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12079 /* create a list of serial ports that will be mutable */
12080 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12081 {
12082 unconst(mSerialPorts[slot]).createObject();
12083 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12084 }
12085 /* create a list of parallel ports that will be mutable */
12086 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12087 {
12088 unconst(mParallelPorts[slot]).createObject();
12089 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12090 }
12091
12092 /* create another USB device filters object that will be mutable */
12093 unconst(mUSBDeviceFilters).createObject();
12094 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12095
12096 /* create a list of network adapters that will be mutable */
12097 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12098 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12099 {
12100 unconst(mNetworkAdapters[slot]).createObject();
12101 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12102 }
12103
12104 /* create another bandwidth control object that will be mutable */
12105 unconst(mBandwidthControl).createObject();
12106 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12107
12108 /* default is to delete saved state on Saved -> PoweredOff transition */
12109 mRemoveSavedState = true;
12110
12111 /* Confirm a successful initialization when it's the case */
12112 autoInitSpan.setSucceeded();
12113
12114 miNATNetworksStarted = 0;
12115
12116 LogFlowThisFuncLeave();
12117 return rc;
12118}
12119
12120/**
12121 * Uninitializes this session object. If the reason is other than
12122 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12123 * or the client watcher code.
12124 *
12125 * @param aReason uninitialization reason
12126 *
12127 * @note Locks mParent + this object for writing.
12128 */
12129void SessionMachine::uninit(Uninit::Reason aReason)
12130{
12131 LogFlowThisFuncEnter();
12132 LogFlowThisFunc(("reason=%d\n", aReason));
12133
12134 /*
12135 * Strongly reference ourselves to prevent this object deletion after
12136 * mData->mSession.mMachine.setNull() below (which can release the last
12137 * reference and call the destructor). Important: this must be done before
12138 * accessing any members (and before AutoUninitSpan that does it as well).
12139 * This self reference will be released as the very last step on return.
12140 */
12141 ComObjPtr<SessionMachine> selfRef = this;
12142
12143 /* Enclose the state transition Ready->InUninit->NotReady */
12144 AutoUninitSpan autoUninitSpan(this);
12145 if (autoUninitSpan.uninitDone())
12146 {
12147 LogFlowThisFunc(("Already uninitialized\n"));
12148 LogFlowThisFuncLeave();
12149 return;
12150 }
12151
12152 if (autoUninitSpan.initFailed())
12153 {
12154 /* We've been called by init() because it's failed. It's not really
12155 * necessary (nor it's safe) to perform the regular uninit sequence
12156 * below, the following is enough.
12157 */
12158 LogFlowThisFunc(("Initialization failed.\n"));
12159 /* destroy the machine client token */
12160 if (mClientToken)
12161 {
12162 delete mClientToken;
12163 mClientToken = NULL;
12164 }
12165 uninitDataAndChildObjects();
12166 mData.free();
12167 unconst(mParent) = NULL;
12168 unconst(mPeer) = NULL;
12169 LogFlowThisFuncLeave();
12170 return;
12171 }
12172
12173 MachineState_T lastState;
12174 {
12175 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12176 lastState = mData->mMachineState;
12177 }
12178 NOREF(lastState);
12179
12180#ifdef VBOX_WITH_USB
12181 // release all captured USB devices, but do this before requesting the locks below
12182 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12183 {
12184 /* Console::captureUSBDevices() is called in the VM process only after
12185 * setting the machine state to Starting or Restoring.
12186 * Console::detachAllUSBDevices() will be called upon successful
12187 * termination. So, we need to release USB devices only if there was
12188 * an abnormal termination of a running VM.
12189 *
12190 * This is identical to SessionMachine::DetachAllUSBDevices except
12191 * for the aAbnormal argument. */
12192 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12193 AssertComRC(rc);
12194 NOREF(rc);
12195
12196 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12197 if (service)
12198 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12199 }
12200#endif /* VBOX_WITH_USB */
12201
12202 // we need to lock this object in uninit() because the lock is shared
12203 // with mPeer (as well as data we modify below). mParent lock is needed
12204 // by several calls to it, and USB needs host lock.
12205 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12206
12207#ifdef VBOX_WITH_RESOURCE_USAGE_API
12208 /*
12209 * It is safe to call Machine::i_unregisterMetrics() here because
12210 * PerformanceCollector::samplerCallback no longer accesses guest methods
12211 * holding the lock.
12212 */
12213 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12214 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12215 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12216 this, __PRETTY_FUNCTION__, mCollectorGuest));
12217 if (mCollectorGuest)
12218 {
12219 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12220 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12221 mCollectorGuest = NULL;
12222 }
12223#endif
12224
12225 if (aReason == Uninit::Abnormal)
12226 {
12227 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12228 Global::IsOnlineOrTransient(lastState)));
12229
12230 /* reset the state to Aborted */
12231 if (mData->mMachineState != MachineState_Aborted)
12232 i_setMachineState(MachineState_Aborted);
12233 }
12234
12235 // any machine settings modified?
12236 if (mData->flModifications)
12237 {
12238 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12239 i_rollback(false /* aNotify */);
12240 }
12241
12242 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12243 || !mConsoleTaskData.mSnapshot);
12244 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12245 {
12246 LogWarningThisFunc(("canceling failed save state request!\n"));
12247 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12248 }
12249 else if (!mConsoleTaskData.mSnapshot.isNull())
12250 {
12251 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12252
12253 /* delete all differencing hard disks created (this will also attach
12254 * their parents back by rolling back mMediaData) */
12255 i_rollbackMedia();
12256
12257 // delete the saved state file (it might have been already created)
12258 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12259 // think it's still in use
12260 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12261 mConsoleTaskData.mSnapshot->uninit();
12262 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12263 }
12264
12265 mData->mSession.mPID = NIL_RTPROCESS;
12266
12267 if (aReason == Uninit::Unexpected)
12268 {
12269 /* Uninitialization didn't come from #checkForDeath(), so tell the
12270 * client watcher thread to update the set of machines that have open
12271 * sessions. */
12272 mParent->i_updateClientWatcher();
12273 }
12274
12275 /* uninitialize all remote controls */
12276 if (mData->mSession.mRemoteControls.size())
12277 {
12278 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12279 mData->mSession.mRemoteControls.size()));
12280
12281 Data::Session::RemoteControlList::iterator it =
12282 mData->mSession.mRemoteControls.begin();
12283 while (it != mData->mSession.mRemoteControls.end())
12284 {
12285 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12286 HRESULT rc = (*it)->Uninitialize();
12287 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12288 if (FAILED(rc))
12289 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12290 ++it;
12291 }
12292 mData->mSession.mRemoteControls.clear();
12293 }
12294
12295 /* Remove all references to the NAT network service. The service will stop
12296 * if all references (also from other VMs) are removed. */
12297 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12298 {
12299 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12300 {
12301 NetworkAttachmentType_T type;
12302 HRESULT hrc;
12303
12304 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12305 if ( SUCCEEDED(hrc)
12306 && type == NetworkAttachmentType_NATNetwork)
12307 {
12308 Bstr name;
12309 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12310 if (SUCCEEDED(hrc))
12311 {
12312 multilock.release();
12313 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12314 mUserData->s.strName.c_str(), name.raw()));
12315 mParent->i_natNetworkRefDec(name.raw());
12316 multilock.acquire();
12317 }
12318 }
12319 }
12320 }
12321
12322 /*
12323 * An expected uninitialization can come only from #checkForDeath().
12324 * Otherwise it means that something's gone really wrong (for example,
12325 * the Session implementation has released the VirtualBox reference
12326 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12327 * etc). However, it's also possible, that the client releases the IPC
12328 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12329 * but the VirtualBox release event comes first to the server process.
12330 * This case is practically possible, so we should not assert on an
12331 * unexpected uninit, just log a warning.
12332 */
12333
12334 if ((aReason == Uninit::Unexpected))
12335 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12336
12337 if (aReason != Uninit::Normal)
12338 {
12339 mData->mSession.mDirectControl.setNull();
12340 }
12341 else
12342 {
12343 /* this must be null here (see #OnSessionEnd()) */
12344 Assert(mData->mSession.mDirectControl.isNull());
12345 Assert(mData->mSession.mState == SessionState_Unlocking);
12346 Assert(!mData->mSession.mProgress.isNull());
12347 }
12348 if (mData->mSession.mProgress)
12349 {
12350 if (aReason == Uninit::Normal)
12351 mData->mSession.mProgress->i_notifyComplete(S_OK);
12352 else
12353 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12354 COM_IIDOF(ISession),
12355 getComponentName(),
12356 tr("The VM session was aborted"));
12357 mData->mSession.mProgress.setNull();
12358 }
12359
12360 /* remove the association between the peer machine and this session machine */
12361 Assert( (SessionMachine*)mData->mSession.mMachine == this
12362 || aReason == Uninit::Unexpected);
12363
12364 /* reset the rest of session data */
12365 mData->mSession.mMachine.setNull();
12366 mData->mSession.mState = SessionState_Unlocked;
12367 mData->mSession.mType.setNull();
12368
12369 /* destroy the machine client token before leaving the exclusive lock */
12370 if (mClientToken)
12371 {
12372 delete mClientToken;
12373 mClientToken = NULL;
12374 }
12375
12376 /* fire an event */
12377 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12378
12379 uninitDataAndChildObjects();
12380
12381 /* free the essential data structure last */
12382 mData.free();
12383
12384 /* release the exclusive lock before setting the below two to NULL */
12385 multilock.release();
12386
12387 unconst(mParent) = NULL;
12388 unconst(mPeer) = NULL;
12389
12390 LogFlowThisFuncLeave();
12391}
12392
12393// util::Lockable interface
12394////////////////////////////////////////////////////////////////////////////////
12395
12396/**
12397 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12398 * with the primary Machine instance (mPeer).
12399 */
12400RWLockHandle *SessionMachine::lockHandle() const
12401{
12402 AssertReturn(mPeer != NULL, NULL);
12403 return mPeer->lockHandle();
12404}
12405
12406// IInternalMachineControl methods
12407////////////////////////////////////////////////////////////////////////////////
12408
12409/**
12410 * Passes collected guest statistics to performance collector object
12411 */
12412STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12413 ULONG aCpuKernel, ULONG aCpuIdle,
12414 ULONG aMemTotal, ULONG aMemFree,
12415 ULONG aMemBalloon, ULONG aMemShared,
12416 ULONG aMemCache, ULONG aPageTotal,
12417 ULONG aAllocVMM, ULONG aFreeVMM,
12418 ULONG aBalloonedVMM, ULONG aSharedVMM,
12419 ULONG aVmNetRx, ULONG aVmNetTx)
12420{
12421#ifdef VBOX_WITH_RESOURCE_USAGE_API
12422 if (mCollectorGuest)
12423 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12424 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12425 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12426 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12427
12428 return S_OK;
12429#else
12430 NOREF(aValidStats);
12431 NOREF(aCpuUser);
12432 NOREF(aCpuKernel);
12433 NOREF(aCpuIdle);
12434 NOREF(aMemTotal);
12435 NOREF(aMemFree);
12436 NOREF(aMemBalloon);
12437 NOREF(aMemShared);
12438 NOREF(aMemCache);
12439 NOREF(aPageTotal);
12440 NOREF(aAllocVMM);
12441 NOREF(aFreeVMM);
12442 NOREF(aBalloonedVMM);
12443 NOREF(aSharedVMM);
12444 NOREF(aVmNetRx);
12445 NOREF(aVmNetTx);
12446 return E_NOTIMPL;
12447#endif
12448}
12449
12450/**
12451 * @note Locks this object for writing.
12452 */
12453STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12454{
12455 AutoCaller autoCaller(this);
12456 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12457
12458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12459
12460 mRemoveSavedState = aRemove;
12461
12462 return S_OK;
12463}
12464
12465/**
12466 * @note Locks the same as #i_setMachineState() does.
12467 */
12468STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12469{
12470 return i_setMachineState(aMachineState);
12471}
12472
12473/**
12474 * @note Locks this object for writing.
12475 */
12476STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12477{
12478 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12479 AutoCaller autoCaller(this);
12480 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12481
12482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12483
12484 if (mData->mSession.mState != SessionState_Locked)
12485 return VBOX_E_INVALID_OBJECT_STATE;
12486
12487 if (!mData->mSession.mProgress.isNull())
12488 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12489
12490 /* If we didn't reference the NAT network service yet, add a reference to
12491 * force a start */
12492 if (miNATNetworksStarted < 1)
12493 {
12494 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12495 {
12496 NetworkAttachmentType_T type;
12497 HRESULT hrc;
12498 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12499 if ( SUCCEEDED(hrc)
12500 && type == NetworkAttachmentType_NATNetwork)
12501 {
12502 Bstr name;
12503 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12504 if (SUCCEEDED(hrc))
12505 {
12506 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12507 mUserData->s.strName.c_str(), name.raw()));
12508 mPeer->lockHandle()->unlockWrite();
12509 mParent->i_natNetworkRefInc(name.raw());
12510#ifdef RT_LOCK_STRICT
12511 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12512#else
12513 mPeer->lockHandle()->lockWrite();
12514#endif
12515 }
12516 }
12517 }
12518 miNATNetworksStarted++;
12519 }
12520
12521 LogFlowThisFunc(("returns S_OK.\n"));
12522 return S_OK;
12523}
12524
12525/**
12526 * @note Locks this object for writing.
12527 */
12528STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12529{
12530 AutoCaller autoCaller(this);
12531 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12532
12533 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12534
12535 if (mData->mSession.mState != SessionState_Locked)
12536 return VBOX_E_INVALID_OBJECT_STATE;
12537
12538 /* Finalize the LaunchVMProcess progress object. */
12539 if (mData->mSession.mProgress)
12540 {
12541 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12542 mData->mSession.mProgress.setNull();
12543 }
12544
12545 if (SUCCEEDED((HRESULT)iResult))
12546 {
12547#ifdef VBOX_WITH_RESOURCE_USAGE_API
12548 /* The VM has been powered up successfully, so it makes sense
12549 * now to offer the performance metrics for a running machine
12550 * object. Doing it earlier wouldn't be safe. */
12551 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12552 mData->mSession.mPID);
12553#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12554 }
12555
12556 return S_OK;
12557}
12558
12559/**
12560 * @note Locks this object for writing.
12561 */
12562STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12563{
12564 LogFlowThisFuncEnter();
12565
12566 AutoCaller autoCaller(this);
12567 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12568
12569 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12570
12571 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12572 E_FAIL);
12573
12574 /* create a progress object to track operation completion */
12575 ComObjPtr<Progress> pProgress;
12576 pProgress.createObject();
12577 pProgress->init(i_getVirtualBox(),
12578 static_cast<IMachine *>(this) /* aInitiator */,
12579 Bstr(tr("Stopping the virtual machine")).raw(),
12580 FALSE /* aCancelable */);
12581
12582 /* fill in the console task data */
12583 mConsoleTaskData.mLastState = mData->mMachineState;
12584 mConsoleTaskData.mProgress = pProgress;
12585
12586 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12587 i_setMachineState(MachineState_Stopping);
12588
12589 pProgress.queryInterfaceTo(aProgress);
12590
12591 return S_OK;
12592}
12593
12594/**
12595 * @note Locks this object for writing.
12596 */
12597STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12598{
12599 LogFlowThisFuncEnter();
12600
12601 AutoCaller autoCaller(this);
12602 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12603
12604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12605
12606 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12607 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12608 && mConsoleTaskData.mLastState != MachineState_Null,
12609 E_FAIL);
12610
12611 /*
12612 * On failure, set the state to the state we had when BeginPoweringDown()
12613 * was called (this is expected by Console::PowerDown() and the associated
12614 * task). On success the VM process already changed the state to
12615 * MachineState_PoweredOff, so no need to do anything.
12616 */
12617 if (FAILED(iResult))
12618 i_setMachineState(mConsoleTaskData.mLastState);
12619
12620 /* notify the progress object about operation completion */
12621 Assert(mConsoleTaskData.mProgress);
12622 if (SUCCEEDED(iResult))
12623 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12624 else
12625 {
12626 Utf8Str strErrMsg(aErrMsg);
12627 if (strErrMsg.length())
12628 mConsoleTaskData.mProgress->i_notifyComplete(iResult,
12629 COM_IIDOF(ISession),
12630 getComponentName(),
12631 strErrMsg.c_str());
12632 else
12633 mConsoleTaskData.mProgress->i_notifyComplete(iResult);
12634 }
12635
12636 /* clear out the temporary saved state data */
12637 mConsoleTaskData.mLastState = MachineState_Null;
12638 mConsoleTaskData.mProgress.setNull();
12639
12640 LogFlowThisFuncLeave();
12641 return S_OK;
12642}
12643
12644
12645/**
12646 * Goes through the USB filters of the given machine to see if the given
12647 * device matches any filter or not.
12648 *
12649 * @note Locks the same as USBController::hasMatchingFilter() does.
12650 */
12651STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12652 BOOL *aMatched,
12653 ULONG *aMaskedIfs)
12654{
12655 LogFlowThisFunc(("\n"));
12656
12657 AutoCaller autoCaller(this);
12658 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12659
12660#ifdef VBOX_WITH_USB
12661 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aUSBDevice, aMaskedIfs);
12662#else
12663 NOREF(aUSBDevice);
12664 NOREF(aMaskedIfs);
12665 *aMatched = FALSE;
12666#endif
12667
12668 return S_OK;
12669}
12670
12671/**
12672 * @note Locks the same as Host::captureUSBDevice() does.
12673 */
12674STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12675{
12676 LogFlowThisFunc(("\n"));
12677
12678 AutoCaller autoCaller(this);
12679 AssertComRCReturnRC(autoCaller.rc());
12680
12681#ifdef VBOX_WITH_USB
12682 /* if captureDeviceForVM() fails, it must have set extended error info */
12683 clearError();
12684 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12685 if (FAILED(rc)) return rc;
12686
12687 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12688 AssertReturn(service, E_FAIL);
12689 return service->captureDeviceForVM(this, Guid(aId).ref());
12690#else
12691 NOREF(aId);
12692 return E_NOTIMPL;
12693#endif
12694}
12695
12696/**
12697 * @note Locks the same as Host::detachUSBDevice() does.
12698 */
12699STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12700{
12701 LogFlowThisFunc(("\n"));
12702
12703 AutoCaller autoCaller(this);
12704 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12705
12706#ifdef VBOX_WITH_USB
12707 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12708 AssertReturn(service, E_FAIL);
12709 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12710#else
12711 NOREF(aId);
12712 NOREF(aDone);
12713 return E_NOTIMPL;
12714#endif
12715}
12716
12717/**
12718 * Inserts all machine filters to the USB proxy service and then calls
12719 * Host::autoCaptureUSBDevices().
12720 *
12721 * Called by Console from the VM process upon VM startup.
12722 *
12723 * @note Locks what called methods lock.
12724 */
12725STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12726{
12727 LogFlowThisFunc(("\n"));
12728
12729 AutoCaller autoCaller(this);
12730 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12731
12732#ifdef VBOX_WITH_USB
12733 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12734 AssertComRC(rc);
12735 NOREF(rc);
12736
12737 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12738 AssertReturn(service, E_FAIL);
12739 return service->autoCaptureDevicesForVM(this);
12740#else
12741 return S_OK;
12742#endif
12743}
12744
12745/**
12746 * Removes all machine filters from the USB proxy service and then calls
12747 * Host::detachAllUSBDevices().
12748 *
12749 * Called by Console from the VM process upon normal VM termination or by
12750 * SessionMachine::uninit() upon abnormal VM termination (from under the
12751 * Machine/SessionMachine lock).
12752 *
12753 * @note Locks what called methods lock.
12754 */
12755STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12756{
12757 LogFlowThisFunc(("\n"));
12758
12759 AutoCaller autoCaller(this);
12760 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12761
12762#ifdef VBOX_WITH_USB
12763 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12764 AssertComRC(rc);
12765 NOREF(rc);
12766
12767 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12768 AssertReturn(service, E_FAIL);
12769 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12770#else
12771 NOREF(aDone);
12772 return S_OK;
12773#endif
12774}
12775
12776/**
12777 * @note Locks this object for writing.
12778 */
12779STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12780 IProgress **aProgress)
12781{
12782 LogFlowThisFuncEnter();
12783
12784 AssertReturn(aSession, E_INVALIDARG);
12785 AssertReturn(aProgress, E_INVALIDARG);
12786
12787 AutoCaller autoCaller(this);
12788
12789 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12790 /*
12791 * We don't assert below because it might happen that a non-direct session
12792 * informs us it is closed right after we've been uninitialized -- it's ok.
12793 */
12794 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12795
12796 /* get IInternalSessionControl interface */
12797 ComPtr<IInternalSessionControl> control(aSession);
12798
12799 ComAssertRet(!control.isNull(), E_INVALIDARG);
12800
12801 /* Creating a Progress object requires the VirtualBox lock, and
12802 * thus locking it here is required by the lock order rules. */
12803 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12804
12805 if (control == mData->mSession.mDirectControl)
12806 {
12807 ComAssertRet(aProgress, E_POINTER);
12808
12809 /* The direct session is being normally closed by the client process
12810 * ----------------------------------------------------------------- */
12811
12812 /* go to the closing state (essential for all open*Session() calls and
12813 * for #checkForDeath()) */
12814 Assert(mData->mSession.mState == SessionState_Locked);
12815 mData->mSession.mState = SessionState_Unlocking;
12816
12817 /* set direct control to NULL to release the remote instance */
12818 mData->mSession.mDirectControl.setNull();
12819 LogFlowThisFunc(("Direct control is set to NULL\n"));
12820
12821 if (mData->mSession.mProgress)
12822 {
12823 /* finalize the progress, someone might wait if a frontend
12824 * closes the session before powering on the VM. */
12825 mData->mSession.mProgress->notifyComplete(E_FAIL,
12826 COM_IIDOF(ISession),
12827 getComponentName(),
12828 tr("The VM session was closed before any attempt to power it on"));
12829 mData->mSession.mProgress.setNull();
12830 }
12831
12832 /* Create the progress object the client will use to wait until
12833 * #checkForDeath() is called to uninitialize this session object after
12834 * it releases the IPC semaphore.
12835 * Note! Because we're "reusing" mProgress here, this must be a proxy
12836 * object just like for LaunchVMProcess. */
12837 Assert(mData->mSession.mProgress.isNull());
12838 ComObjPtr<ProgressProxy> progress;
12839 progress.createObject();
12840 ComPtr<IUnknown> pPeer(mPeer);
12841 progress->init(mParent, pPeer,
12842 Bstr(tr("Closing session")).raw(),
12843 FALSE /* aCancelable */);
12844 progress.queryInterfaceTo(aProgress);
12845 mData->mSession.mProgress = progress;
12846 }
12847 else
12848 {
12849 /* the remote session is being normally closed */
12850 Data::Session::RemoteControlList::iterator it =
12851 mData->mSession.mRemoteControls.begin();
12852 while (it != mData->mSession.mRemoteControls.end())
12853 {
12854 if (control == *it)
12855 break;
12856 ++it;
12857 }
12858 BOOL found = it != mData->mSession.mRemoteControls.end();
12859 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12860 E_INVALIDARG);
12861 // This MUST be erase(it), not remove(*it) as the latter triggers a
12862 // very nasty use after free due to the place where the value "lives".
12863 mData->mSession.mRemoteControls.erase(it);
12864 }
12865
12866 /* signal the client watcher thread, because the client is going away */
12867 mParent->i_updateClientWatcher();
12868
12869 LogFlowThisFuncLeave();
12870 return S_OK;
12871}
12872
12873/**
12874 * @note Locks this object for writing.
12875 */
12876STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12877{
12878 LogFlowThisFuncEnter();
12879
12880 CheckComArgOutPointerValid(aProgress);
12881 CheckComArgOutPointerValid(aStateFilePath);
12882
12883 AutoCaller autoCaller(this);
12884 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12885
12886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12887
12888 AssertReturn( mData->mMachineState == MachineState_Paused
12889 && mConsoleTaskData.mLastState == MachineState_Null
12890 && mConsoleTaskData.strStateFilePath.isEmpty(),
12891 E_FAIL);
12892
12893 /* create a progress object to track operation completion */
12894 ComObjPtr<Progress> pProgress;
12895 pProgress.createObject();
12896 pProgress->init(i_getVirtualBox(),
12897 static_cast<IMachine *>(this) /* aInitiator */,
12898 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12899 FALSE /* aCancelable */);
12900
12901 Utf8Str strStateFilePath;
12902 /* stateFilePath is null when the machine is not running */
12903 if (mData->mMachineState == MachineState_Paused)
12904 i_composeSavedStateFilename(strStateFilePath);
12905
12906 /* fill in the console task data */
12907 mConsoleTaskData.mLastState = mData->mMachineState;
12908 mConsoleTaskData.strStateFilePath = strStateFilePath;
12909 mConsoleTaskData.mProgress = pProgress;
12910
12911 /* set the state to Saving (this is expected by Console::SaveState()) */
12912 i_setMachineState(MachineState_Saving);
12913
12914 strStateFilePath.cloneTo(aStateFilePath);
12915 pProgress.queryInterfaceTo(aProgress);
12916
12917 return S_OK;
12918}
12919
12920/**
12921 * @note Locks mParent + this object for writing.
12922 */
12923STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12924{
12925 LogFlowThisFunc(("\n"));
12926
12927 AutoCaller autoCaller(this);
12928 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12929
12930 /* endSavingState() need mParent lock */
12931 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12932
12933 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12934 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12935 && mConsoleTaskData.mLastState != MachineState_Null
12936 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12937 E_FAIL);
12938
12939 /*
12940 * On failure, set the state to the state we had when BeginSavingState()
12941 * was called (this is expected by Console::SaveState() and the associated
12942 * task). On success the VM process already changed the state to
12943 * MachineState_Saved, so no need to do anything.
12944 */
12945 if (FAILED(iResult))
12946 i_setMachineState(mConsoleTaskData.mLastState);
12947
12948 return endSavingState(iResult, aErrMsg);
12949}
12950
12951/**
12952 * @note Locks this object for writing.
12953 */
12954STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12955{
12956 LogFlowThisFunc(("\n"));
12957
12958 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12959
12960 AutoCaller autoCaller(this);
12961 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12962
12963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12964
12965 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12966 || mData->mMachineState == MachineState_Teleported
12967 || mData->mMachineState == MachineState_Aborted
12968 , E_FAIL); /** @todo setError. */
12969
12970 Utf8Str stateFilePathFull = aSavedStateFile;
12971 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
12972 if (RT_FAILURE(vrc))
12973 return setError(VBOX_E_FILE_ERROR,
12974 tr("Invalid saved state file path '%ls' (%Rrc)"),
12975 aSavedStateFile,
12976 vrc);
12977
12978 mSSData->strStateFilePath = stateFilePathFull;
12979
12980 /* The below i_setMachineState() will detect the state transition and will
12981 * update the settings file */
12982
12983 return i_setMachineState(MachineState_Saved);
12984}
12985
12986STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12987 ComSafeArrayOut(BSTR, aValues),
12988 ComSafeArrayOut(LONG64, aTimestamps),
12989 ComSafeArrayOut(BSTR, aFlags))
12990{
12991 LogFlowThisFunc(("\n"));
12992
12993#ifdef VBOX_WITH_GUEST_PROPS
12994 using namespace guestProp;
12995
12996 AutoCaller autoCaller(this);
12997 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12998
12999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13000
13001 CheckComArgOutSafeArrayPointerValid(aNames);
13002 CheckComArgOutSafeArrayPointerValid(aValues);
13003 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13004 CheckComArgOutSafeArrayPointerValid(aFlags);
13005
13006 size_t cEntries = mHWData->mGuestProperties.size();
13007 com::SafeArray<BSTR> names(cEntries);
13008 com::SafeArray<BSTR> values(cEntries);
13009 com::SafeArray<LONG64> timestamps(cEntries);
13010 com::SafeArray<BSTR> flags(cEntries);
13011 unsigned i = 0;
13012 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13013 it != mHWData->mGuestProperties.end();
13014 ++it)
13015 {
13016 char szFlags[MAX_FLAGS_LEN + 1];
13017 it->first.cloneTo(&names[i]);
13018 it->second.strValue.cloneTo(&values[i]);
13019 timestamps[i] = it->second.mTimestamp;
13020 /* If it is NULL, keep it NULL. */
13021 if (it->second.mFlags)
13022 {
13023 writeFlags(it->second.mFlags, szFlags);
13024 Bstr(szFlags).cloneTo(&flags[i]);
13025 }
13026 else
13027 flags[i] = NULL;
13028 ++i;
13029 }
13030 names.detachTo(ComSafeArrayOutArg(aNames));
13031 values.detachTo(ComSafeArrayOutArg(aValues));
13032 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13033 flags.detachTo(ComSafeArrayOutArg(aFlags));
13034 return S_OK;
13035#else
13036 ReturnComNotImplemented();
13037#endif
13038}
13039
13040STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13041 IN_BSTR aValue,
13042 LONG64 aTimestamp,
13043 IN_BSTR aFlags)
13044{
13045 LogFlowThisFunc(("\n"));
13046
13047#ifdef VBOX_WITH_GUEST_PROPS
13048 using namespace guestProp;
13049
13050 CheckComArgStrNotEmptyOrNull(aName);
13051 CheckComArgNotNull(aValue);
13052 CheckComArgNotNull(aFlags);
13053
13054 try
13055 {
13056 /*
13057 * Convert input up front.
13058 */
13059 Utf8Str utf8Name(aName);
13060 uint32_t fFlags = NILFLAG;
13061 if (aFlags)
13062 {
13063 Utf8Str utf8Flags(aFlags);
13064 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13065 AssertRCReturn(vrc, E_INVALIDARG);
13066 }
13067
13068 /*
13069 * Now grab the object lock, validate the state and do the update.
13070 */
13071 AutoCaller autoCaller(this);
13072 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13073
13074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13075
13076 switch (mData->mMachineState)
13077 {
13078 case MachineState_Paused:
13079 case MachineState_Running:
13080 case MachineState_Teleporting:
13081 case MachineState_TeleportingPausedVM:
13082 case MachineState_LiveSnapshotting:
13083 case MachineState_DeletingSnapshotOnline:
13084 case MachineState_DeletingSnapshotPaused:
13085 case MachineState_Saving:
13086 case MachineState_Stopping:
13087 break;
13088
13089 default:
13090 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13091 VBOX_E_INVALID_VM_STATE);
13092 }
13093
13094 i_setModified(IsModified_MachineData);
13095 mHWData.backup();
13096
13097 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13098 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13099 if (it != mHWData->mGuestProperties.end())
13100 {
13101 if (!fDelete)
13102 {
13103 it->second.strValue = aValue;
13104 it->second.mTimestamp = aTimestamp;
13105 it->second.mFlags = fFlags;
13106 }
13107 else
13108 mHWData->mGuestProperties.erase(it);
13109
13110 mData->mGuestPropertiesModified = TRUE;
13111 }
13112 else if (!fDelete)
13113 {
13114 HWData::GuestProperty prop;
13115 prop.strValue = aValue;
13116 prop.mTimestamp = aTimestamp;
13117 prop.mFlags = fFlags;
13118
13119 mHWData->mGuestProperties[utf8Name] = prop;
13120 mData->mGuestPropertiesModified = TRUE;
13121 }
13122
13123 /*
13124 * Send a callback notification if appropriate
13125 */
13126 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13127 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13128 RTSTR_MAX,
13129 utf8Name.c_str(),
13130 RTSTR_MAX, NULL)
13131 )
13132 {
13133 alock.release();
13134
13135 mParent->i_onGuestPropertyChange(mData->mUuid,
13136 aName,
13137 aValue,
13138 aFlags);
13139 }
13140 }
13141 catch (...)
13142 {
13143 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13144 }
13145 return S_OK;
13146#else
13147 ReturnComNotImplemented();
13148#endif
13149}
13150
13151STDMETHODIMP SessionMachine::LockMedia()
13152{
13153 AutoCaller autoCaller(this);
13154 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13155
13156 AutoMultiWriteLock2 alock(this->lockHandle(),
13157 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13158
13159 AssertReturn( mData->mMachineState == MachineState_Starting
13160 || mData->mMachineState == MachineState_Restoring
13161 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13162
13163 clearError();
13164 alock.release();
13165 return lockMedia();
13166}
13167
13168STDMETHODIMP SessionMachine::UnlockMedia()
13169{
13170 HRESULT hrc = unlockMedia();
13171 return hrc;
13172}
13173
13174STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13175 IMediumAttachment **aNewAttachment)
13176{
13177 CheckComArgNotNull(aAttachment);
13178 CheckComArgOutPointerValid(aNewAttachment);
13179
13180 AutoCaller autoCaller(this);
13181 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13182
13183 // request the host lock first, since might be calling Host methods for getting host drives;
13184 // next, protect the media tree all the while we're in here, as well as our member variables
13185 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13186 this->lockHandle(),
13187 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13188
13189 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13190
13191 Bstr ctrlName;
13192 LONG lPort;
13193 LONG lDevice;
13194 bool fTempEject;
13195 {
13196 AutoCaller autoAttachCaller(this);
13197 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13198
13199 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13200
13201 /* Need to query the details first, as the IMediumAttachment reference
13202 * might be to the original settings, which we are going to change. */
13203 ctrlName = pAttach->i_getControllerName();
13204 lPort = pAttach->i_getPort();
13205 lDevice = pAttach->i_getDevice();
13206 fTempEject = pAttach->i_getTempEject();
13207 }
13208
13209 if (!fTempEject)
13210 {
13211 /* Remember previously mounted medium. The medium before taking the
13212 * backup is not necessarily the same thing. */
13213 ComObjPtr<Medium> oldmedium;
13214 oldmedium = pAttach->i_getMedium();
13215
13216 i_setModified(IsModified_Storage);
13217 mMediaData.backup();
13218
13219 // The backup operation makes the pAttach reference point to the
13220 // old settings. Re-get the correct reference.
13221 pAttach = i_findAttachment(mMediaData->mAttachments,
13222 ctrlName.raw(),
13223 lPort,
13224 lDevice);
13225
13226 {
13227 AutoCaller autoAttachCaller(this);
13228 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13229
13230 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13231 if (!oldmedium.isNull())
13232 oldmedium->i_removeBackReference(mData->mUuid);
13233
13234 pAttach->i_updateMedium(NULL);
13235 pAttach->i_updateEjected();
13236 }
13237
13238 i_setModified(IsModified_Storage);
13239 }
13240 else
13241 {
13242 {
13243 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13244 pAttach->i_updateEjected();
13245 }
13246 }
13247
13248 pAttach.queryInterfaceTo(aNewAttachment);
13249
13250 return S_OK;
13251}
13252
13253// public methods only for internal purposes
13254/////////////////////////////////////////////////////////////////////////////
13255
13256#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13257/**
13258 * Called from the client watcher thread to check for expected or unexpected
13259 * death of the client process that has a direct session to this machine.
13260 *
13261 * On Win32 and on OS/2, this method is called only when we've got the
13262 * mutex (i.e. the client has either died or terminated normally) so it always
13263 * returns @c true (the client is terminated, the session machine is
13264 * uninitialized).
13265 *
13266 * On other platforms, the method returns @c true if the client process has
13267 * terminated normally or abnormally and the session machine was uninitialized,
13268 * and @c false if the client process is still alive.
13269 *
13270 * @note Locks this object for writing.
13271 */
13272bool SessionMachine::i_checkForDeath()
13273{
13274 Uninit::Reason reason;
13275 bool terminated = false;
13276
13277 /* Enclose autoCaller with a block because calling uninit() from under it
13278 * will deadlock. */
13279 {
13280 AutoCaller autoCaller(this);
13281 if (!autoCaller.isOk())
13282 {
13283 /* return true if not ready, to cause the client watcher to exclude
13284 * the corresponding session from watching */
13285 LogFlowThisFunc(("Already uninitialized!\n"));
13286 return true;
13287 }
13288
13289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13290
13291 /* Determine the reason of death: if the session state is Closing here,
13292 * everything is fine. Otherwise it means that the client did not call
13293 * OnSessionEnd() before it released the IPC semaphore. This may happen
13294 * either because the client process has abnormally terminated, or
13295 * because it simply forgot to call ISession::Close() before exiting. We
13296 * threat the latter also as an abnormal termination (see
13297 * Session::uninit() for details). */
13298 reason = mData->mSession.mState == SessionState_Unlocking ?
13299 Uninit::Normal :
13300 Uninit::Abnormal;
13301
13302 if (mClientToken)
13303 terminated = mClientToken->release();
13304 } /* AutoCaller block */
13305
13306 if (terminated)
13307 uninit(reason);
13308
13309 return terminated;
13310}
13311
13312void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13313{
13314 LogFlowThisFunc(("\n"));
13315
13316 strTokenId.setNull();
13317
13318 AutoCaller autoCaller(this);
13319 AssertComRCReturnVoid(autoCaller.rc());
13320
13321 Assert(mClientToken);
13322 if (mClientToken)
13323 mClientToken->getId(strTokenId);
13324}
13325#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13326IToken *SessionMachine::i_getToken()
13327{
13328 LogFlowThisFunc(("\n"));
13329
13330 AutoCaller autoCaller(this);
13331 AssertComRCReturn(autoCaller.rc(), NULL);
13332
13333 Assert(mClientToken);
13334 if (mClientToken)
13335 return mClientToken->getToken();
13336 else
13337 return NULL;
13338}
13339#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13340
13341Machine::ClientToken *SessionMachine::i_getClientToken()
13342{
13343 LogFlowThisFunc(("\n"));
13344
13345 AutoCaller autoCaller(this);
13346 AssertComRCReturn(autoCaller.rc(), NULL);
13347
13348 return mClientToken;
13349}
13350
13351
13352/**
13353 * @note Locks this object for reading.
13354 */
13355HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13356{
13357 LogFlowThisFunc(("\n"));
13358
13359 AutoCaller autoCaller(this);
13360 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13361
13362 ComPtr<IInternalSessionControl> directControl;
13363 {
13364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13365 directControl = mData->mSession.mDirectControl;
13366 }
13367
13368 /* ignore notifications sent after #OnSessionEnd() is called */
13369 if (!directControl)
13370 return S_OK;
13371
13372 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13373}
13374
13375/**
13376 * @note Locks this object for reading.
13377 */
13378HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13379 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13380 IN_BSTR aGuestIp, LONG aGuestPort)
13381{
13382 LogFlowThisFunc(("\n"));
13383
13384 AutoCaller autoCaller(this);
13385 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13386
13387 ComPtr<IInternalSessionControl> directControl;
13388 {
13389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13390 directControl = mData->mSession.mDirectControl;
13391 }
13392
13393 /* ignore notifications sent after #OnSessionEnd() is called */
13394 if (!directControl)
13395 return S_OK;
13396 /*
13397 * instead acting like callback we ask IVirtualBox deliver corresponding event
13398 */
13399
13400 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13401 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13402 return S_OK;
13403}
13404
13405/**
13406 * @note Locks this object for reading.
13407 */
13408HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13409{
13410 LogFlowThisFunc(("\n"));
13411
13412 AutoCaller autoCaller(this);
13413 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13414
13415 ComPtr<IInternalSessionControl> directControl;
13416 {
13417 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13418 directControl = mData->mSession.mDirectControl;
13419 }
13420
13421 /* ignore notifications sent after #OnSessionEnd() is called */
13422 if (!directControl)
13423 return S_OK;
13424
13425 return directControl->OnSerialPortChange(serialPort);
13426}
13427
13428/**
13429 * @note Locks this object for reading.
13430 */
13431HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13432{
13433 LogFlowThisFunc(("\n"));
13434
13435 AutoCaller autoCaller(this);
13436 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13437
13438 ComPtr<IInternalSessionControl> directControl;
13439 {
13440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13441 directControl = mData->mSession.mDirectControl;
13442 }
13443
13444 /* ignore notifications sent after #OnSessionEnd() is called */
13445 if (!directControl)
13446 return S_OK;
13447
13448 return directControl->OnParallelPortChange(parallelPort);
13449}
13450
13451/**
13452 * @note Locks this object for reading.
13453 */
13454HRESULT SessionMachine::i_onStorageControllerChange()
13455{
13456 LogFlowThisFunc(("\n"));
13457
13458 AutoCaller autoCaller(this);
13459 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13460
13461 ComPtr<IInternalSessionControl> directControl;
13462 {
13463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13464 directControl = mData->mSession.mDirectControl;
13465 }
13466
13467 /* ignore notifications sent after #OnSessionEnd() is called */
13468 if (!directControl)
13469 return S_OK;
13470
13471 return directControl->OnStorageControllerChange();
13472}
13473
13474/**
13475 * @note Locks this object for reading.
13476 */
13477HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13478{
13479 LogFlowThisFunc(("\n"));
13480
13481 AutoCaller autoCaller(this);
13482 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13483
13484 ComPtr<IInternalSessionControl> directControl;
13485 {
13486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13487 directControl = mData->mSession.mDirectControl;
13488 }
13489
13490 /* ignore notifications sent after #OnSessionEnd() is called */
13491 if (!directControl)
13492 return S_OK;
13493
13494 return directControl->OnMediumChange(aAttachment, aForce);
13495}
13496
13497/**
13498 * @note Locks this object for reading.
13499 */
13500HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13501{
13502 LogFlowThisFunc(("\n"));
13503
13504 AutoCaller autoCaller(this);
13505 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13506
13507 ComPtr<IInternalSessionControl> directControl;
13508 {
13509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13510 directControl = mData->mSession.mDirectControl;
13511 }
13512
13513 /* ignore notifications sent after #OnSessionEnd() is called */
13514 if (!directControl)
13515 return S_OK;
13516
13517 return directControl->OnCPUChange(aCPU, aRemove);
13518}
13519
13520HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13521{
13522 LogFlowThisFunc(("\n"));
13523
13524 AutoCaller autoCaller(this);
13525 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13526
13527 ComPtr<IInternalSessionControl> directControl;
13528 {
13529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13530 directControl = mData->mSession.mDirectControl;
13531 }
13532
13533 /* ignore notifications sent after #OnSessionEnd() is called */
13534 if (!directControl)
13535 return S_OK;
13536
13537 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13538}
13539
13540/**
13541 * @note Locks this object for reading.
13542 */
13543HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13544{
13545 LogFlowThisFunc(("\n"));
13546
13547 AutoCaller autoCaller(this);
13548 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13549
13550 ComPtr<IInternalSessionControl> directControl;
13551 {
13552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13553 directControl = mData->mSession.mDirectControl;
13554 }
13555
13556 /* ignore notifications sent after #OnSessionEnd() is called */
13557 if (!directControl)
13558 return S_OK;
13559
13560 return directControl->OnVRDEServerChange(aRestart);
13561}
13562
13563/**
13564 * @note Locks this object for reading.
13565 */
13566HRESULT SessionMachine::i_onVideoCaptureChange()
13567{
13568 LogFlowThisFunc(("\n"));
13569
13570 AutoCaller autoCaller(this);
13571 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13572
13573 ComPtr<IInternalSessionControl> directControl;
13574 {
13575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13576 directControl = mData->mSession.mDirectControl;
13577 }
13578
13579 /* ignore notifications sent after #OnSessionEnd() is called */
13580 if (!directControl)
13581 return S_OK;
13582
13583 return directControl->OnVideoCaptureChange();
13584}
13585
13586/**
13587 * @note Locks this object for reading.
13588 */
13589HRESULT SessionMachine::i_onUSBControllerChange()
13590{
13591 LogFlowThisFunc(("\n"));
13592
13593 AutoCaller autoCaller(this);
13594 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13595
13596 ComPtr<IInternalSessionControl> directControl;
13597 {
13598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13599 directControl = mData->mSession.mDirectControl;
13600 }
13601
13602 /* ignore notifications sent after #OnSessionEnd() is called */
13603 if (!directControl)
13604 return S_OK;
13605
13606 return directControl->OnUSBControllerChange();
13607}
13608
13609/**
13610 * @note Locks this object for reading.
13611 */
13612HRESULT SessionMachine::i_onSharedFolderChange()
13613{
13614 LogFlowThisFunc(("\n"));
13615
13616 AutoCaller autoCaller(this);
13617 AssertComRCReturnRC(autoCaller.rc());
13618
13619 ComPtr<IInternalSessionControl> directControl;
13620 {
13621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13622 directControl = mData->mSession.mDirectControl;
13623 }
13624
13625 /* ignore notifications sent after #OnSessionEnd() is called */
13626 if (!directControl)
13627 return S_OK;
13628
13629 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13630}
13631
13632/**
13633 * @note Locks this object for reading.
13634 */
13635HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13636{
13637 LogFlowThisFunc(("\n"));
13638
13639 AutoCaller autoCaller(this);
13640 AssertComRCReturnRC(autoCaller.rc());
13641
13642 ComPtr<IInternalSessionControl> directControl;
13643 {
13644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13645 directControl = mData->mSession.mDirectControl;
13646 }
13647
13648 /* ignore notifications sent after #OnSessionEnd() is called */
13649 if (!directControl)
13650 return S_OK;
13651
13652 return directControl->OnClipboardModeChange(aClipboardMode);
13653}
13654
13655/**
13656 * @note Locks this object for reading.
13657 */
13658HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13659{
13660 LogFlowThisFunc(("\n"));
13661
13662 AutoCaller autoCaller(this);
13663 AssertComRCReturnRC(autoCaller.rc());
13664
13665 ComPtr<IInternalSessionControl> directControl;
13666 {
13667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13668 directControl = mData->mSession.mDirectControl;
13669 }
13670
13671 /* ignore notifications sent after #OnSessionEnd() is called */
13672 if (!directControl)
13673 return S_OK;
13674
13675 return directControl->OnDnDModeChange(aDnDMode);
13676}
13677
13678/**
13679 * @note Locks this object for reading.
13680 */
13681HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13682{
13683 LogFlowThisFunc(("\n"));
13684
13685 AutoCaller autoCaller(this);
13686 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13687
13688 ComPtr<IInternalSessionControl> directControl;
13689 {
13690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13691 directControl = mData->mSession.mDirectControl;
13692 }
13693
13694 /* ignore notifications sent after #OnSessionEnd() is called */
13695 if (!directControl)
13696 return S_OK;
13697
13698 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13699}
13700
13701/**
13702 * @note Locks this object for reading.
13703 */
13704HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13705{
13706 LogFlowThisFunc(("\n"));
13707
13708 AutoCaller autoCaller(this);
13709 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13710
13711 ComPtr<IInternalSessionControl> directControl;
13712 {
13713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13714 directControl = mData->mSession.mDirectControl;
13715 }
13716
13717 /* ignore notifications sent after #OnSessionEnd() is called */
13718 if (!directControl)
13719 return S_OK;
13720
13721 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13722}
13723
13724/**
13725 * Returns @c true if this machine's USB controller reports it has a matching
13726 * filter for the given USB device and @c false otherwise.
13727 *
13728 * @note locks this object for reading.
13729 */
13730bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13731{
13732 AutoCaller autoCaller(this);
13733 /* silently return if not ready -- this method may be called after the
13734 * direct machine session has been called */
13735 if (!autoCaller.isOk())
13736 return false;
13737
13738#ifdef VBOX_WITH_USB
13739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13740
13741 switch (mData->mMachineState)
13742 {
13743 case MachineState_Starting:
13744 case MachineState_Restoring:
13745 case MachineState_TeleportingIn:
13746 case MachineState_Paused:
13747 case MachineState_Running:
13748 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13749 * elsewhere... */
13750 alock.release();
13751 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13752 default: break;
13753 }
13754#else
13755 NOREF(aDevice);
13756 NOREF(aMaskedIfs);
13757#endif
13758 return false;
13759}
13760
13761/**
13762 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13763 */
13764HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13765 IVirtualBoxErrorInfo *aError,
13766 ULONG aMaskedIfs)
13767{
13768 LogFlowThisFunc(("\n"));
13769
13770 AutoCaller autoCaller(this);
13771
13772 /* This notification may happen after the machine object has been
13773 * uninitialized (the session was closed), so don't assert. */
13774 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13775
13776 ComPtr<IInternalSessionControl> directControl;
13777 {
13778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13779 directControl = mData->mSession.mDirectControl;
13780 }
13781
13782 /* fail on notifications sent after #OnSessionEnd() is called, it is
13783 * expected by the caller */
13784 if (!directControl)
13785 return E_FAIL;
13786
13787 /* No locks should be held at this point. */
13788 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13789 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13790
13791 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13792}
13793
13794/**
13795 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13796 */
13797HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13798 IVirtualBoxErrorInfo *aError)
13799{
13800 LogFlowThisFunc(("\n"));
13801
13802 AutoCaller autoCaller(this);
13803
13804 /* This notification may happen after the machine object has been
13805 * uninitialized (the session was closed), so don't assert. */
13806 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13807
13808 ComPtr<IInternalSessionControl> directControl;
13809 {
13810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13811 directControl = mData->mSession.mDirectControl;
13812 }
13813
13814 /* fail on notifications sent after #OnSessionEnd() is called, it is
13815 * expected by the caller */
13816 if (!directControl)
13817 return E_FAIL;
13818
13819 /* No locks should be held at this point. */
13820 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13821 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13822
13823 return directControl->OnUSBDeviceDetach(aId, aError);
13824}
13825
13826// protected methods
13827/////////////////////////////////////////////////////////////////////////////
13828
13829/**
13830 * Helper method to finalize saving the state.
13831 *
13832 * @note Must be called from under this object's lock.
13833 *
13834 * @param aRc S_OK if the snapshot has been taken successfully
13835 * @param aErrMsg human readable error message for failure
13836 *
13837 * @note Locks mParent + this objects for writing.
13838 */
13839HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13840{
13841 LogFlowThisFuncEnter();
13842
13843 AutoCaller autoCaller(this);
13844 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13845
13846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13847
13848 HRESULT rc = S_OK;
13849
13850 if (SUCCEEDED(aRc))
13851 {
13852 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13853
13854 /* save all VM settings */
13855 rc = i_saveSettings(NULL);
13856 // no need to check whether VirtualBox.xml needs saving also since
13857 // we can't have a name change pending at this point
13858 }
13859 else
13860 {
13861 // delete the saved state file (it might have been already created);
13862 // we need not check whether this is shared with a snapshot here because
13863 // we certainly created this saved state file here anew
13864 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13865 }
13866
13867 /* notify the progress object about operation completion */
13868 Assert(mConsoleTaskData.mProgress);
13869 if (SUCCEEDED(aRc))
13870 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13871 else
13872 {
13873 if (aErrMsg.length())
13874 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13875 COM_IIDOF(ISession),
13876 getComponentName(),
13877 aErrMsg.c_str());
13878 else
13879 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13880 }
13881
13882 /* clear out the temporary saved state data */
13883 mConsoleTaskData.mLastState = MachineState_Null;
13884 mConsoleTaskData.strStateFilePath.setNull();
13885 mConsoleTaskData.mProgress.setNull();
13886
13887 LogFlowThisFuncLeave();
13888 return rc;
13889}
13890
13891/**
13892 * Deletes the given file if it is no longer in use by either the current machine state
13893 * (if the machine is "saved") or any of the machine's snapshots.
13894 *
13895 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13896 * but is different for each SnapshotMachine. When calling this, the order of calling this
13897 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13898 * is therefore critical. I know, it's all rather messy.
13899 *
13900 * @param strStateFile
13901 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
13902 * the test for whether the saved state file is in use.
13903 */
13904void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
13905 Snapshot *pSnapshotToIgnore)
13906{
13907 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13908 if ( (strStateFile.isNotEmpty())
13909 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13910 )
13911 // ... and it must also not be shared with other snapshots
13912 if ( !mData->mFirstSnapshot
13913 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13914 // this checks the SnapshotMachine's state file paths
13915 )
13916 RTFileDelete(strStateFile.c_str());
13917}
13918
13919/**
13920 * Locks the attached media.
13921 *
13922 * All attached hard disks are locked for writing and DVD/floppy are locked for
13923 * reading. Parents of attached hard disks (if any) are locked for reading.
13924 *
13925 * This method also performs accessibility check of all media it locks: if some
13926 * media is inaccessible, the method will return a failure and a bunch of
13927 * extended error info objects per each inaccessible medium.
13928 *
13929 * Note that this method is atomic: if it returns a success, all media are
13930 * locked as described above; on failure no media is locked at all (all
13931 * succeeded individual locks will be undone).
13932 *
13933 * The caller is responsible for doing the necessary state sanity checks.
13934 *
13935 * The locks made by this method must be undone by calling #unlockMedia() when
13936 * no more needed.
13937 */
13938HRESULT SessionMachine::lockMedia()
13939{
13940 AutoCaller autoCaller(this);
13941 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13942
13943 AutoMultiWriteLock2 alock(this->lockHandle(),
13944 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13945
13946 /* bail out if trying to lock things with already set up locking */
13947 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13948
13949 MultiResult mrc(S_OK);
13950
13951 /* Collect locking information for all medium objects attached to the VM. */
13952 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13953 it != mMediaData->mAttachments.end();
13954 ++it)
13955 {
13956 MediumAttachment* pAtt = *it;
13957 DeviceType_T devType = pAtt->i_getType();
13958 Medium *pMedium = pAtt->i_getMedium();
13959
13960 MediumLockList *pMediumLockList(new MediumLockList());
13961 // There can be attachments without a medium (floppy/dvd), and thus
13962 // it's impossible to create a medium lock list. It still makes sense
13963 // to have the empty medium lock list in the map in case a medium is
13964 // attached later.
13965 if (pMedium != NULL)
13966 {
13967 MediumType_T mediumType = pMedium->i_getType();
13968 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13969 || mediumType == MediumType_Shareable;
13970 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13971
13972 alock.release();
13973 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13974 !fIsReadOnlyLock /* fMediumLockWrite */,
13975 NULL,
13976 *pMediumLockList);
13977 alock.acquire();
13978 if (FAILED(mrc))
13979 {
13980 delete pMediumLockList;
13981 mData->mSession.mLockedMedia.Clear();
13982 break;
13983 }
13984 }
13985
13986 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13987 if (FAILED(rc))
13988 {
13989 mData->mSession.mLockedMedia.Clear();
13990 mrc = setError(rc,
13991 tr("Collecting locking information for all attached media failed"));
13992 break;
13993 }
13994 }
13995
13996 if (SUCCEEDED(mrc))
13997 {
13998 /* Now lock all media. If this fails, nothing is locked. */
13999 alock.release();
14000 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14001 alock.acquire();
14002 if (FAILED(rc))
14003 {
14004 mrc = setError(rc,
14005 tr("Locking of attached media failed"));
14006 }
14007 }
14008
14009 return mrc;
14010}
14011
14012/**
14013 * Undoes the locks made by by #lockMedia().
14014 */
14015HRESULT SessionMachine::unlockMedia()
14016{
14017 AutoCaller autoCaller(this);
14018 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14019
14020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14021
14022 /* we may be holding important error info on the current thread;
14023 * preserve it */
14024 ErrorInfoKeeper eik;
14025
14026 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14027 AssertComRC(rc);
14028 return rc;
14029}
14030
14031/**
14032 * Helper to change the machine state (reimplementation).
14033 *
14034 * @note Locks this object for writing.
14035 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14036 * it can cause crashes in random places due to unexpectedly committing
14037 * the current settings. The caller is responsible for that. The call
14038 * to saveStateSettings is fine, because this method does not commit.
14039 */
14040HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14041{
14042 LogFlowThisFuncEnter();
14043 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14044
14045 AutoCaller autoCaller(this);
14046 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14047
14048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14049
14050 MachineState_T oldMachineState = mData->mMachineState;
14051
14052 AssertMsgReturn(oldMachineState != aMachineState,
14053 ("oldMachineState=%s, aMachineState=%s\n",
14054 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14055 E_FAIL);
14056
14057 HRESULT rc = S_OK;
14058
14059 int stsFlags = 0;
14060 bool deleteSavedState = false;
14061
14062 /* detect some state transitions */
14063
14064 if ( ( oldMachineState == MachineState_Saved
14065 && aMachineState == MachineState_Restoring)
14066 || ( ( oldMachineState == MachineState_PoweredOff
14067 || oldMachineState == MachineState_Teleported
14068 || oldMachineState == MachineState_Aborted
14069 )
14070 && ( aMachineState == MachineState_TeleportingIn
14071 || aMachineState == MachineState_Starting
14072 )
14073 )
14074 )
14075 {
14076 /* The EMT thread is about to start */
14077
14078 /* Nothing to do here for now... */
14079
14080 /// @todo NEWMEDIA don't let mDVDDrive and other children
14081 /// change anything when in the Starting/Restoring state
14082 }
14083 else if ( ( oldMachineState == MachineState_Running
14084 || oldMachineState == MachineState_Paused
14085 || oldMachineState == MachineState_Teleporting
14086 || oldMachineState == MachineState_LiveSnapshotting
14087 || oldMachineState == MachineState_Stuck
14088 || oldMachineState == MachineState_Starting
14089 || oldMachineState == MachineState_Stopping
14090 || oldMachineState == MachineState_Saving
14091 || oldMachineState == MachineState_Restoring
14092 || oldMachineState == MachineState_TeleportingPausedVM
14093 || oldMachineState == MachineState_TeleportingIn
14094 )
14095 && ( aMachineState == MachineState_PoweredOff
14096 || aMachineState == MachineState_Saved
14097 || aMachineState == MachineState_Teleported
14098 || aMachineState == MachineState_Aborted
14099 )
14100 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14101 * snapshot */
14102 && ( mConsoleTaskData.mSnapshot.isNull()
14103 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14104 )
14105 )
14106 {
14107 /* The EMT thread has just stopped, unlock attached media. Note that as
14108 * opposed to locking that is done from Console, we do unlocking here
14109 * because the VM process may have aborted before having a chance to
14110 * properly unlock all media it locked. */
14111
14112 unlockMedia();
14113 }
14114
14115 if (oldMachineState == MachineState_Restoring)
14116 {
14117 if (aMachineState != MachineState_Saved)
14118 {
14119 /*
14120 * delete the saved state file once the machine has finished
14121 * restoring from it (note that Console sets the state from
14122 * Restoring to Saved if the VM couldn't restore successfully,
14123 * to give the user an ability to fix an error and retry --
14124 * we keep the saved state file in this case)
14125 */
14126 deleteSavedState = true;
14127 }
14128 }
14129 else if ( oldMachineState == MachineState_Saved
14130 && ( aMachineState == MachineState_PoweredOff
14131 || aMachineState == MachineState_Aborted
14132 || aMachineState == MachineState_Teleported
14133 )
14134 )
14135 {
14136 /*
14137 * delete the saved state after Console::ForgetSavedState() is called
14138 * or if the VM process (owning a direct VM session) crashed while the
14139 * VM was Saved
14140 */
14141
14142 /// @todo (dmik)
14143 // Not sure that deleting the saved state file just because of the
14144 // client death before it attempted to restore the VM is a good
14145 // thing. But when it crashes we need to go to the Aborted state
14146 // which cannot have the saved state file associated... The only
14147 // way to fix this is to make the Aborted condition not a VM state
14148 // but a bool flag: i.e., when a crash occurs, set it to true and
14149 // change the state to PoweredOff or Saved depending on the
14150 // saved state presence.
14151
14152 deleteSavedState = true;
14153 mData->mCurrentStateModified = TRUE;
14154 stsFlags |= SaveSTS_CurStateModified;
14155 }
14156
14157 if ( aMachineState == MachineState_Starting
14158 || aMachineState == MachineState_Restoring
14159 || aMachineState == MachineState_TeleportingIn
14160 )
14161 {
14162 /* set the current state modified flag to indicate that the current
14163 * state is no more identical to the state in the
14164 * current snapshot */
14165 if (!mData->mCurrentSnapshot.isNull())
14166 {
14167 mData->mCurrentStateModified = TRUE;
14168 stsFlags |= SaveSTS_CurStateModified;
14169 }
14170 }
14171
14172 if (deleteSavedState)
14173 {
14174 if (mRemoveSavedState)
14175 {
14176 Assert(!mSSData->strStateFilePath.isEmpty());
14177
14178 // it is safe to delete the saved state file if ...
14179 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14180 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14181 // ... none of the snapshots share the saved state file
14182 )
14183 RTFileDelete(mSSData->strStateFilePath.c_str());
14184 }
14185
14186 mSSData->strStateFilePath.setNull();
14187 stsFlags |= SaveSTS_StateFilePath;
14188 }
14189
14190 /* redirect to the underlying peer machine */
14191 mPeer->i_setMachineState(aMachineState);
14192
14193 if ( aMachineState == MachineState_PoweredOff
14194 || aMachineState == MachineState_Teleported
14195 || aMachineState == MachineState_Aborted
14196 || aMachineState == MachineState_Saved)
14197 {
14198 /* the machine has stopped execution
14199 * (or the saved state file was adopted) */
14200 stsFlags |= SaveSTS_StateTimeStamp;
14201 }
14202
14203 if ( ( oldMachineState == MachineState_PoweredOff
14204 || oldMachineState == MachineState_Aborted
14205 || oldMachineState == MachineState_Teleported
14206 )
14207 && aMachineState == MachineState_Saved)
14208 {
14209 /* the saved state file was adopted */
14210 Assert(!mSSData->strStateFilePath.isEmpty());
14211 stsFlags |= SaveSTS_StateFilePath;
14212 }
14213
14214#ifdef VBOX_WITH_GUEST_PROPS
14215 if ( aMachineState == MachineState_PoweredOff
14216 || aMachineState == MachineState_Aborted
14217 || aMachineState == MachineState_Teleported)
14218 {
14219 /* Make sure any transient guest properties get removed from the
14220 * property store on shutdown. */
14221
14222 HWData::GuestPropertyMap::const_iterator it;
14223 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14224 if (!fNeedsSaving)
14225 for (it = mHWData->mGuestProperties.begin();
14226 it != mHWData->mGuestProperties.end(); ++it)
14227 if ( (it->second.mFlags & guestProp::TRANSIENT)
14228 || (it->second.mFlags & guestProp::TRANSRESET))
14229 {
14230 fNeedsSaving = true;
14231 break;
14232 }
14233 if (fNeedsSaving)
14234 {
14235 mData->mCurrentStateModified = TRUE;
14236 stsFlags |= SaveSTS_CurStateModified;
14237 }
14238 }
14239#endif
14240
14241 rc = i_saveStateSettings(stsFlags);
14242
14243 if ( ( oldMachineState != MachineState_PoweredOff
14244 && oldMachineState != MachineState_Aborted
14245 && oldMachineState != MachineState_Teleported
14246 )
14247 && ( aMachineState == MachineState_PoweredOff
14248 || aMachineState == MachineState_Aborted
14249 || aMachineState == MachineState_Teleported
14250 )
14251 )
14252 {
14253 /* we've been shut down for any reason */
14254 /* no special action so far */
14255 }
14256
14257 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14258 LogFlowThisFuncLeave();
14259 return rc;
14260}
14261
14262/**
14263 * Sends the current machine state value to the VM process.
14264 *
14265 * @note Locks this object for reading, then calls a client process.
14266 */
14267HRESULT SessionMachine::i_updateMachineStateOnClient()
14268{
14269 AutoCaller autoCaller(this);
14270 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14271
14272 ComPtr<IInternalSessionControl> directControl;
14273 {
14274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14275 AssertReturn(!!mData, E_FAIL);
14276 directControl = mData->mSession.mDirectControl;
14277
14278 /* directControl may be already set to NULL here in #OnSessionEnd()
14279 * called too early by the direct session process while there is still
14280 * some operation (like deleting the snapshot) in progress. The client
14281 * process in this case is waiting inside Session::close() for the
14282 * "end session" process object to complete, while #uninit() called by
14283 * #checkForDeath() on the Watcher thread is waiting for the pending
14284 * operation to complete. For now, we accept this inconsistent behavior
14285 * and simply do nothing here. */
14286
14287 if (mData->mSession.mState == SessionState_Unlocking)
14288 return S_OK;
14289
14290 AssertReturn(!directControl.isNull(), E_FAIL);
14291 }
14292
14293 return directControl->UpdateMachineState(mData->mMachineState);
14294}
14295
14296HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14297{
14298 NOREF(aRemove);
14299 ReturnComNotImplemented();
14300}
14301
14302HRESULT Machine::updateState(MachineState_T aState)
14303{
14304 NOREF(aState);
14305 ReturnComNotImplemented();
14306}
14307
14308HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14309{
14310 NOREF(aProgress);
14311 ReturnComNotImplemented();
14312}
14313
14314HRESULT Machine::endPowerUp(LONG aResult)
14315{
14316 NOREF(aResult);
14317 ReturnComNotImplemented();
14318}
14319
14320HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14321{
14322 NOREF(aProgress);
14323 ReturnComNotImplemented();
14324}
14325
14326HRESULT Machine::endPoweringDown(LONG aResult,
14327 const com::Utf8Str &aErrMsg)
14328{
14329 NOREF(aResult);
14330 NOREF(aErrMsg);
14331 ReturnComNotImplemented();
14332}
14333
14334HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14335 BOOL *aMatched,
14336 ULONG *aMaskedInterfaces)
14337{
14338 NOREF(aDevice);
14339 NOREF(aMatched);
14340 NOREF(aMaskedInterfaces);
14341 ReturnComNotImplemented();
14342
14343}
14344
14345HRESULT Machine::captureUSBDevice(const com::Guid &aId)
14346{
14347 NOREF(aId);
14348 ReturnComNotImplemented();
14349}
14350
14351HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14352 BOOL aDone)
14353{
14354 NOREF(aId);
14355 NOREF(aDone);
14356 ReturnComNotImplemented();
14357}
14358
14359HRESULT Machine::autoCaptureUSBDevices()
14360{
14361 ReturnComNotImplemented();
14362}
14363
14364HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14365{
14366 NOREF(aDone);
14367 ReturnComNotImplemented();
14368}
14369
14370HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14371 ComPtr<IProgress> &aProgress)
14372{
14373 NOREF(aSession);
14374 NOREF(aProgress);
14375 ReturnComNotImplemented();
14376}
14377
14378HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14379 com::Utf8Str &aStateFilePath)
14380{
14381 NOREF(aProgress);
14382 NOREF(aStateFilePath);
14383 ReturnComNotImplemented();
14384}
14385
14386HRESULT Machine::endSavingState(LONG aResult,
14387 const com::Utf8Str &aErrMsg)
14388{
14389 NOREF(aResult);
14390 NOREF(aErrMsg);
14391 ReturnComNotImplemented();
14392}
14393
14394HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14395{
14396 NOREF(aSavedStateFile);
14397 ReturnComNotImplemented();
14398}
14399
14400HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14401 const com::Utf8Str &aName,
14402 const com::Utf8Str &aDescription,
14403 const ComPtr<IProgress> &aConsoleProgress,
14404 BOOL aFTakingSnapshotOnline,
14405 com::Utf8Str &aStateFilePath)
14406{
14407 NOREF(aInitiator);
14408 NOREF(aName);
14409 NOREF(aDescription);
14410 NOREF(aConsoleProgress);
14411 NOREF(aFTakingSnapshotOnline);
14412 NOREF(aStateFilePath);
14413 ReturnComNotImplemented();
14414}
14415
14416HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14417{
14418 NOREF(aSuccess);
14419 ReturnComNotImplemented();
14420}
14421
14422HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14423 const com::Guid &aStartId,
14424 const com::Guid &aEndId,
14425 BOOL aDeleteAllChildren,
14426 MachineState_T *aMachineState,
14427 ComPtr<IProgress> &aProgress)
14428{
14429 NOREF(aInitiator);
14430 NOREF(aStartId);
14431 NOREF(aEndId);
14432 NOREF(aDeleteAllChildren);
14433 NOREF(aMachineState);
14434 NOREF(aProgress);
14435 ReturnComNotImplemented();
14436}
14437
14438HRESULT Machine::finishOnlineMergeMedium()
14439{
14440 ReturnComNotImplemented();
14441}
14442
14443HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14444 const ComPtr<ISnapshot> &aSnapshot,
14445 MachineState_T *aMachineState,
14446 ComPtr<IProgress> &aProgress)
14447{
14448 NOREF(aInitiator);
14449 NOREF(aSnapshot);
14450 NOREF(aMachineState);
14451 NOREF(aProgress);
14452 ReturnComNotImplemented();
14453}
14454
14455HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14456 std::vector<com::Utf8Str> &aValues,
14457 std::vector<LONG64> &aTimestamps,
14458 std::vector<com::Utf8Str> &aFlags)
14459{
14460 NOREF(aNames);
14461 NOREF(aValues);
14462 NOREF(aTimestamps);
14463 NOREF(aFlags);
14464 ReturnComNotImplemented();
14465}
14466
14467HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14468 const com::Utf8Str &aValue,
14469 LONG64 aTimestamp,
14470 const com::Utf8Str &aFlags)
14471{
14472 NOREF(aName);
14473 NOREF(aValue);
14474 NOREF(aTimestamp);
14475 NOREF(aFlags);
14476 ReturnComNotImplemented();
14477}
14478
14479HRESULT Machine::lockMedia()
14480{
14481 ReturnComNotImplemented();
14482}
14483
14484HRESULT Machine::unlockMedia()
14485{
14486 ReturnComNotImplemented();
14487}
14488
14489HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14490 ComPtr<IMediumAttachment> &aNewAttachment)
14491{
14492 NOREF(aAttachment);
14493 NOREF(aNewAttachment);
14494 ReturnComNotImplemented();
14495}
14496
14497HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14498 ULONG aCpuUser,
14499 ULONG aCpuKernel,
14500 ULONG aCpuIdle,
14501 ULONG aMemTotal,
14502 ULONG aMemFree,
14503 ULONG aMemBalloon,
14504 ULONG aMemShared,
14505 ULONG aMemCache,
14506 ULONG aPagedTotal,
14507 ULONG aMemAllocTotal,
14508 ULONG aMemFreeTotal,
14509 ULONG aMemBalloonTotal,
14510 ULONG aMemSharedTotal,
14511 ULONG aVmNetRx,
14512 ULONG aVmNetTx)
14513{
14514 NOREF(aValidStats);
14515 NOREF(aCpuUser);
14516 NOREF(aCpuKernel);
14517 NOREF(aCpuIdle);
14518 NOREF(aMemTotal);
14519 NOREF(aMemFree);
14520 NOREF(aMemBalloon);
14521 NOREF(aMemShared);
14522 NOREF(aMemCache);
14523 NOREF(aPagedTotal);
14524 NOREF(aMemAllocTotal);
14525 NOREF(aMemFreeTotal);
14526 NOREF(aMemBalloonTotal);
14527 NOREF(aMemSharedTotal);
14528 NOREF(aVmNetRx);
14529 NOREF(aVmNetTx);
14530 ReturnComNotImplemented();
14531}
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