VirtualBox

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

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

Main: update guest properties API to work with API wrappers, removed unnecesary BSTR conversion.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 499.0 KB
Line 
1/* $Id: MachineImpl.cpp 52251 2014-08-01 06:07:28Z 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.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
90# define HOSTSUFF_EXE ".exe"
91#else /* !RT_OS_WINDOWS */
92# define HOSTSUFF_EXE ""
93#endif /* !RT_OS_WINDOWS */
94
95// defines / prototypes
96/////////////////////////////////////////////////////////////////////////////
97
98/////////////////////////////////////////////////////////////////////////////
99// Machine::Data structure
100/////////////////////////////////////////////////////////////////////////////
101
102Machine::Data::Data()
103{
104 mRegistered = FALSE;
105 pMachineConfigFile = NULL;
106 /* Contains hints on what has changed when the user is using the VM (config
107 * changes, running the VM, ...). This is used to decide if a config needs
108 * to be written to disk. */
109 flModifications = 0;
110 /* VM modification usually also trigger setting the current state to
111 * "Modified". Although this is not always the case. An e.g. is the VM
112 * initialization phase or when snapshot related data is changed. The
113 * actually behavior is controlled by the following flag. */
114 m_fAllowStateModification = false;
115 mAccessible = FALSE;
116 /* mUuid is initialized in Machine::init() */
117
118 mMachineState = MachineState_PoweredOff;
119 RTTimeNow(&mLastStateChange);
120
121 mMachineStateDeps = 0;
122 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
123 mMachineStateChangePending = 0;
124
125 mCurrentStateModified = TRUE;
126 mGuestPropertiesModified = FALSE;
127
128 mSession.mPID = NIL_RTPROCESS;
129 mSession.mState = SessionState_Unlocked;
130}
131
132Machine::Data::~Data()
133{
134 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
135 {
136 RTSemEventMultiDestroy(mMachineStateDepsSem);
137 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
138 }
139 if (pMachineConfigFile)
140 {
141 delete pMachineConfigFile;
142 pMachineConfigFile = NULL;
143 }
144}
145
146/////////////////////////////////////////////////////////////////////////////
147// Machine::HWData structure
148/////////////////////////////////////////////////////////////////////////////
149
150Machine::HWData::HWData()
151{
152 /* default values for a newly created machine */
153 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
154 mMemorySize = 128;
155 mCPUCount = 1;
156 mCPUHotPlugEnabled = false;
157 mMemoryBalloonSize = 0;
158 mPageFusionEnabled = false;
159 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
160 mVRAMSize = 8;
161 mAccelerate3DEnabled = false;
162 mAccelerate2DVideoEnabled = false;
163 mMonitorCount = 1;
164 mVideoCaptureWidth = 1024;
165 mVideoCaptureHeight = 768;
166 mVideoCaptureRate = 512;
167 mVideoCaptureFPS = 25;
168 mVideoCaptureEnabled = false;
169 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
170 maVideoCaptureScreens[i] = true;
171
172 mHWVirtExEnabled = true;
173 mHWVirtExNestedPagingEnabled = true;
174#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
175 mHWVirtExLargePagesEnabled = true;
176#else
177 /* Not supported on 32 bits hosts. */
178 mHWVirtExLargePagesEnabled = false;
179#endif
180 mHWVirtExVPIDEnabled = true;
181 mHWVirtExUXEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if 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 spawns three processes when a VM is
3241 launched, the 3rd one is the one that will end up here. */
3242 RTPROCESS ppid;
3243 int rc = RTProcQueryParent(pid, &ppid);
3244 if (RT_SUCCESS(rc))
3245 rc = RTProcQueryParent(ppid, &ppid);
3246 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3247 || rc == VERR_ACCESS_DENIED)
3248 {
3249 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3250 mData->mSession.mPID = pid;
3251 }
3252#endif
3253
3254 if (mData->mSession.mPID != pid)
3255 return setError(E_ACCESSDENIED,
3256 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3257 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3258 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3259 }
3260
3261 // create the mutable SessionMachine from the current machine
3262 ComObjPtr<SessionMachine> sessionMachine;
3263 sessionMachine.createObject();
3264 rc = sessionMachine->init(this);
3265 AssertComRC(rc);
3266
3267 /* NOTE: doing return from this function after this point but
3268 * before the end is forbidden since it may call SessionMachine::uninit()
3269 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3270 * lock while still holding the Machine lock in alock so that a deadlock
3271 * is possible due to the wrong lock order. */
3272
3273 if (SUCCEEDED(rc))
3274 {
3275 /*
3276 * Set the session state to Spawning to protect against subsequent
3277 * attempts to open a session and to unregister the machine after
3278 * we release the lock.
3279 */
3280 SessionState_T origState = mData->mSession.mState;
3281 mData->mSession.mState = SessionState_Spawning;
3282
3283#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3284 /* Get the client token ID to be passed to the client process */
3285 Utf8Str strTokenId;
3286 sessionMachine->i_getTokenId(strTokenId);
3287 Assert(!strTokenId.isEmpty());
3288#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3289 /* Get the client token to be passed to the client process */
3290 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3291 /* The token is now "owned" by pToken, fix refcount */
3292 if (!pToken.isNull())
3293 pToken->Release();
3294#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3295
3296 /*
3297 * Release the lock before calling the client process -- it will call
3298 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3299 * because the state is Spawning, so that LaunchVMProcess() and
3300 * LockMachine() calls will fail. This method, called before we
3301 * acquire the lock again, will fail because of the wrong PID.
3302 *
3303 * Note that mData->mSession.mRemoteControls accessed outside
3304 * the lock may not be modified when state is Spawning, so it's safe.
3305 */
3306 alock.release();
3307
3308 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3309#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3310 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3311#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3312 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3313 /* Now the token is owned by the client process. */
3314 pToken.setNull();
3315#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3316 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3317
3318 /* The failure may occur w/o any error info (from RPC), so provide one */
3319 if (FAILED(rc))
3320 setError(VBOX_E_VM_ERROR,
3321 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3322
3323 if ( SUCCEEDED(rc)
3324 && fLaunchingVMProcess
3325 )
3326 {
3327 /* complete the remote session initialization */
3328
3329 /* get the console from the direct session */
3330 ComPtr<IConsole> console;
3331 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3332 ComAssertComRC(rc);
3333
3334 if (SUCCEEDED(rc) && !console)
3335 {
3336 ComAssert(!!console);
3337 rc = E_FAIL;
3338 }
3339
3340 /* assign machine & console to the remote session */
3341 if (SUCCEEDED(rc))
3342 {
3343 /*
3344 * after LaunchVMProcess(), the first and the only
3345 * entry in remoteControls is that remote session
3346 */
3347 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3348 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3349 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3350
3351 /* The failure may occur w/o any error info (from RPC), so provide one */
3352 if (FAILED(rc))
3353 setError(VBOX_E_VM_ERROR,
3354 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3355 }
3356
3357 if (FAILED(rc))
3358 pSessionControl->Uninitialize();
3359 }
3360
3361 /* acquire the lock again */
3362 alock.acquire();
3363
3364 /* Restore the session state */
3365 mData->mSession.mState = origState;
3366 }
3367
3368 // finalize spawning anyway (this is why we don't return on errors above)
3369 if (fLaunchingVMProcess)
3370 {
3371 /* Note that the progress object is finalized later */
3372 /** @todo Consider checking mData->mSession.mProgress for cancellation
3373 * around here. */
3374
3375 /* We don't reset mSession.mPID here because it is necessary for
3376 * SessionMachine::uninit() to reap the child process later. */
3377
3378 if (FAILED(rc))
3379 {
3380 /* Close the remote session, remove the remote control from the list
3381 * and reset session state to Closed (@note keep the code in sync
3382 * with the relevant part in checkForSpawnFailure()). */
3383
3384 Assert(mData->mSession.mRemoteControls.size() == 1);
3385 if (mData->mSession.mRemoteControls.size() == 1)
3386 {
3387 ErrorInfoKeeper eik;
3388 mData->mSession.mRemoteControls.front()->Uninitialize();
3389 }
3390
3391 mData->mSession.mRemoteControls.clear();
3392 mData->mSession.mState = SessionState_Unlocked;
3393 }
3394 }
3395 else
3396 {
3397 /* memorize PID of the directly opened session */
3398 if (SUCCEEDED(rc))
3399 mData->mSession.mPID = pid;
3400 }
3401
3402 if (SUCCEEDED(rc))
3403 {
3404 /* memorize the direct session control and cache IUnknown for it */
3405 mData->mSession.mDirectControl = pSessionControl;
3406 mData->mSession.mState = SessionState_Locked;
3407 /* associate the SessionMachine with this Machine */
3408 mData->mSession.mMachine = sessionMachine;
3409
3410 /* request an IUnknown pointer early from the remote party for later
3411 * identity checks (it will be internally cached within mDirectControl
3412 * at least on XPCOM) */
3413 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3414 NOREF(unk);
3415 }
3416
3417 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3418 * would break the lock order */
3419 alock.release();
3420
3421 /* uninitialize the created session machine on failure */
3422 if (FAILED(rc))
3423 sessionMachine->uninit();
3424
3425 }
3426
3427 if (SUCCEEDED(rc))
3428 {
3429 /*
3430 * tell the client watcher thread to update the set of
3431 * machines that have open sessions
3432 */
3433 mParent->i_updateClientWatcher();
3434
3435 if (oldState != SessionState_Locked)
3436 /* fire an event */
3437 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3438 }
3439
3440 return rc;
3441}
3442
3443/**
3444 * @note Locks objects!
3445 */
3446HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3447 const com::Utf8Str &aType,
3448 const com::Utf8Str &aEnvironment,
3449 ComPtr<IProgress> &aProgress)
3450{
3451 Utf8Str strFrontend(aType);
3452 Utf8Str strEnvironment(aEnvironment);
3453 /* "emergencystop" doesn't need the session, so skip the checks/interface
3454 * retrieval. This code doesn't quite fit in here, but introducing a
3455 * special API method would be even more effort, and would require explicit
3456 * support by every API client. It's better to hide the feature a bit. */
3457 if (strFrontend != "emergencystop")
3458 CheckComArgNotNull(aSession);
3459
3460 HRESULT rc = S_OK;
3461 if (strFrontend.isEmpty())
3462 {
3463 Bstr bstrFrontend;
3464 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3465 if (FAILED(rc))
3466 return rc;
3467 strFrontend = bstrFrontend;
3468 if (strFrontend.isEmpty())
3469 {
3470 ComPtr<ISystemProperties> systemProperties;
3471 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3472 if (FAILED(rc))
3473 return rc;
3474 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3475 if (FAILED(rc))
3476 return rc;
3477 strFrontend = bstrFrontend;
3478 }
3479 /* paranoia - emergencystop is not a valid default */
3480 if (strFrontend == "emergencystop")
3481 strFrontend = Utf8Str::Empty;
3482 }
3483 /* default frontend: Qt GUI */
3484 if (strFrontend.isEmpty())
3485 strFrontend = "GUI/Qt";
3486
3487 if (strFrontend != "emergencystop")
3488 {
3489 /* check the session state */
3490 SessionState_T state;
3491 rc = aSession->COMGETTER(State)(&state);
3492 if (FAILED(rc))
3493 return rc;
3494
3495 if (state != SessionState_Unlocked)
3496 return setError(VBOX_E_INVALID_OBJECT_STATE,
3497 tr("The given session is busy"));
3498
3499 /* get the IInternalSessionControl interface */
3500 ComPtr<IInternalSessionControl> control(aSession);
3501 ComAssertMsgRet(!control.isNull(),
3502 ("No IInternalSessionControl interface"),
3503 E_INVALIDARG);
3504
3505 /* get the teleporter enable state for the progress object init. */
3506 BOOL fTeleporterEnabled;
3507 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3508 if (FAILED(rc))
3509 return rc;
3510
3511 /* create a progress object */
3512 ComObjPtr<ProgressProxy> progress;
3513 progress.createObject();
3514 rc = progress->init(mParent,
3515 static_cast<IMachine*>(this),
3516 Bstr(tr("Starting VM")).raw(),
3517 TRUE /* aCancelable */,
3518 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3519 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3520 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3521 2 /* uFirstOperationWeight */,
3522 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3523
3524 if (SUCCEEDED(rc))
3525 {
3526 rc = i_launchVMProcess(control, strFrontend, strEnvironment, progress);
3527 if (SUCCEEDED(rc))
3528 {
3529 progress.queryInterfaceTo(aProgress.asOutParam());
3530
3531 /* signal the client watcher thread */
3532 mParent->i_updateClientWatcher();
3533
3534 /* fire an event */
3535 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3536 }
3537 }
3538 }
3539 else
3540 {
3541 /* no progress object - either instant success or failure */
3542 aProgress = NULL;
3543
3544 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3545
3546 if (mData->mSession.mState != SessionState_Locked)
3547 return setError(VBOX_E_INVALID_OBJECT_STATE,
3548 tr("The machine '%s' is not locked by a session"),
3549 mUserData->s.strName.c_str());
3550
3551 /* must have a VM process associated - do not kill normal API clients
3552 * with an open session */
3553 if (!Global::IsOnline(mData->mMachineState))
3554 return setError(VBOX_E_INVALID_OBJECT_STATE,
3555 tr("The machine '%s' does not have a VM process"),
3556 mUserData->s.strName.c_str());
3557
3558 /* forcibly terminate the VM process */
3559 if (mData->mSession.mPID != NIL_RTPROCESS)
3560 RTProcTerminate(mData->mSession.mPID);
3561
3562 /* signal the client watcher thread, as most likely the client has
3563 * been terminated */
3564 mParent->i_updateClientWatcher();
3565 }
3566
3567 return rc;
3568}
3569
3570HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3571{
3572 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3573 return setError(E_INVALIDARG,
3574 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3575 aPosition, SchemaDefs::MaxBootPosition);
3576
3577 if (aDevice == DeviceType_USB)
3578 return setError(E_NOTIMPL,
3579 tr("Booting from USB device is currently not supported"));
3580
3581 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3582
3583 HRESULT rc = i_checkStateDependency(MutableStateDep);
3584 if (FAILED(rc)) return rc;
3585
3586 i_setModified(IsModified_MachineData);
3587 mHWData.backup();
3588 mHWData->mBootOrder[aPosition - 1] = aDevice;
3589
3590 return S_OK;
3591}
3592
3593HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3594{
3595 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3596 return setError(E_INVALIDARG,
3597 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3598 aPosition, SchemaDefs::MaxBootPosition);
3599
3600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3601
3602 *aDevice = mHWData->mBootOrder[aPosition - 1];
3603
3604 return S_OK;
3605}
3606
3607HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3608 LONG aControllerPort,
3609 LONG aDevice,
3610 DeviceType_T aType,
3611 const ComPtr<IMedium> &aMedium)
3612{
3613 IMedium *aM = aMedium;
3614 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3615 aName.c_str(), aControllerPort, aDevice, aType, aM));
3616
3617 // request the host lock first, since might be calling Host methods for getting host drives;
3618 // next, protect the media tree all the while we're in here, as well as our member variables
3619 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3620 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3621
3622 HRESULT rc = i_checkStateDependency(MutableStateDep);
3623 if (FAILED(rc)) return rc;
3624
3625 /// @todo NEWMEDIA implicit machine registration
3626 if (!mData->mRegistered)
3627 return setError(VBOX_E_INVALID_OBJECT_STATE,
3628 tr("Cannot attach storage devices to an unregistered machine"));
3629
3630 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3631
3632 /* Check for an existing controller. */
3633 ComObjPtr<StorageController> ctl;
3634 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3635 if (FAILED(rc)) return rc;
3636
3637 StorageControllerType_T ctrlType;
3638 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3639 if (FAILED(rc))
3640 return setError(E_FAIL,
3641 tr("Could not get type of controller '%s'"),
3642 aName.c_str());
3643
3644 bool fSilent = false;
3645 Utf8Str strReconfig;
3646
3647 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3648 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3649 if ( mData->mMachineState == MachineState_Paused
3650 && strReconfig == "1")
3651 fSilent = true;
3652
3653 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3654 bool fHotplug = false;
3655 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3656 fHotplug = true;
3657
3658 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3659 return setError(VBOX_E_INVALID_VM_STATE,
3660 tr("Controller '%s' does not support hotplugging"),
3661 aName.c_str());
3662
3663 // check that the port and device are not out of range
3664 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3665 if (FAILED(rc)) return rc;
3666
3667 /* check if the device slot is already busy */
3668 MediumAttachment *pAttachTemp;
3669 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3670 Bstr(aName).raw(),
3671 aControllerPort,
3672 aDevice)))
3673 {
3674 Medium *pMedium = pAttachTemp->i_getMedium();
3675 if (pMedium)
3676 {
3677 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3678 return setError(VBOX_E_OBJECT_IN_USE,
3679 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3680 pMedium->i_getLocationFull().c_str(),
3681 aControllerPort,
3682 aDevice,
3683 aName.c_str());
3684 }
3685 else
3686 return setError(VBOX_E_OBJECT_IN_USE,
3687 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3688 aControllerPort, aDevice, aName.c_str());
3689 }
3690
3691 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3692 if (aMedium && medium.isNull())
3693 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3694
3695 AutoCaller mediumCaller(medium);
3696 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3697
3698 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3699
3700 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3701 && !medium.isNull()
3702 )
3703 return setError(VBOX_E_OBJECT_IN_USE,
3704 tr("Medium '%s' is already attached to this virtual machine"),
3705 medium->i_getLocationFull().c_str());
3706
3707 if (!medium.isNull())
3708 {
3709 MediumType_T mtype = medium->i_getType();
3710 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3711 // For DVDs it's not written to the config file, so needs no global config
3712 // version bump. For floppies it's a new attribute "type", which is ignored
3713 // by older VirtualBox version, so needs no global config version bump either.
3714 // For hard disks this type is not accepted.
3715 if (mtype == MediumType_MultiAttach)
3716 {
3717 // This type is new with VirtualBox 4.0 and therefore requires settings
3718 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3719 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3720 // two reasons: The medium type is a property of the media registry tree, which
3721 // can reside in the global config file (for pre-4.0 media); we would therefore
3722 // possibly need to bump the global config version. We don't want to do that though
3723 // because that might make downgrading to pre-4.0 impossible.
3724 // As a result, we can only use these two new types if the medium is NOT in the
3725 // global registry:
3726 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3727 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3728 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3729 )
3730 return setError(VBOX_E_INVALID_OBJECT_STATE,
3731 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3732 "to machines that were created with VirtualBox 4.0 or later"),
3733 medium->i_getLocationFull().c_str());
3734 }
3735 }
3736
3737 bool fIndirect = false;
3738 if (!medium.isNull())
3739 fIndirect = medium->i_isReadOnly();
3740 bool associate = true;
3741
3742 do
3743 {
3744 if ( aType == DeviceType_HardDisk
3745 && mMediaData.isBackedUp())
3746 {
3747 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3748
3749 /* check if the medium was attached to the VM before we started
3750 * changing attachments in which case the attachment just needs to
3751 * be restored */
3752 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3753 {
3754 AssertReturn(!fIndirect, E_FAIL);
3755
3756 /* see if it's the same bus/channel/device */
3757 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3758 {
3759 /* the simplest case: restore the whole attachment
3760 * and return, nothing else to do */
3761 mMediaData->mAttachments.push_back(pAttachTemp);
3762
3763 /* Reattach the medium to the VM. */
3764 if (fHotplug || fSilent)
3765 {
3766 mediumLock.release();
3767 treeLock.release();
3768 alock.release();
3769
3770 MediumLockList *pMediumLockList(new MediumLockList());
3771
3772 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3773 true /* fMediumLockWrite */,
3774 NULL,
3775 *pMediumLockList);
3776 alock.acquire();
3777 if (FAILED(rc))
3778 delete pMediumLockList;
3779 else
3780 {
3781 mData->mSession.mLockedMedia.Unlock();
3782 alock.release();
3783 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3784 mData->mSession.mLockedMedia.Lock();
3785 alock.acquire();
3786 }
3787 alock.release();
3788
3789 if (SUCCEEDED(rc))
3790 {
3791 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3792 /* Remove lock list in case of error. */
3793 if (FAILED(rc))
3794 {
3795 mData->mSession.mLockedMedia.Unlock();
3796 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3797 mData->mSession.mLockedMedia.Lock();
3798 }
3799 }
3800 }
3801
3802 return S_OK;
3803 }
3804
3805 /* bus/channel/device differ; we need a new attachment object,
3806 * but don't try to associate it again */
3807 associate = false;
3808 break;
3809 }
3810 }
3811
3812 /* go further only if the attachment is to be indirect */
3813 if (!fIndirect)
3814 break;
3815
3816 /* perform the so called smart attachment logic for indirect
3817 * attachments. Note that smart attachment is only applicable to base
3818 * hard disks. */
3819
3820 if (medium->i_getParent().isNull())
3821 {
3822 /* first, investigate the backup copy of the current hard disk
3823 * attachments to make it possible to re-attach existing diffs to
3824 * another device slot w/o losing their contents */
3825 if (mMediaData.isBackedUp())
3826 {
3827 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3828
3829 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3830 uint32_t foundLevel = 0;
3831
3832 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3833 {
3834 uint32_t level = 0;
3835 MediumAttachment *pAttach = *it;
3836 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3837 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3838 if (pMedium.isNull())
3839 continue;
3840
3841 if (pMedium->i_getBase(&level) == medium)
3842 {
3843 /* skip the hard disk if its currently attached (we
3844 * cannot attach the same hard disk twice) */
3845 if (i_findAttachment(mMediaData->mAttachments,
3846 pMedium))
3847 continue;
3848
3849 /* matched device, channel and bus (i.e. attached to the
3850 * same place) will win and immediately stop the search;
3851 * otherwise the attachment that has the youngest
3852 * descendant of medium will be used
3853 */
3854 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3855 {
3856 /* the simplest case: restore the whole attachment
3857 * and return, nothing else to do */
3858 mMediaData->mAttachments.push_back(*it);
3859
3860 /* Reattach the medium to the VM. */
3861 if (fHotplug || fSilent)
3862 {
3863 mediumLock.release();
3864 treeLock.release();
3865 alock.release();
3866
3867 MediumLockList *pMediumLockList(new MediumLockList());
3868
3869 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3870 true /* fMediumLockWrite */,
3871 NULL,
3872 *pMediumLockList);
3873 alock.acquire();
3874 if (FAILED(rc))
3875 delete pMediumLockList;
3876 else
3877 {
3878 mData->mSession.mLockedMedia.Unlock();
3879 alock.release();
3880 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3881 mData->mSession.mLockedMedia.Lock();
3882 alock.acquire();
3883 }
3884 alock.release();
3885
3886 if (SUCCEEDED(rc))
3887 {
3888 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3889 /* Remove lock list in case of error. */
3890 if (FAILED(rc))
3891 {
3892 mData->mSession.mLockedMedia.Unlock();
3893 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3894 mData->mSession.mLockedMedia.Lock();
3895 }
3896 }
3897 }
3898
3899 return S_OK;
3900 }
3901 else if ( foundIt == oldAtts.end()
3902 || level > foundLevel /* prefer younger */
3903 )
3904 {
3905 foundIt = it;
3906 foundLevel = level;
3907 }
3908 }
3909 }
3910
3911 if (foundIt != oldAtts.end())
3912 {
3913 /* use the previously attached hard disk */
3914 medium = (*foundIt)->i_getMedium();
3915 mediumCaller.attach(medium);
3916 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3917 mediumLock.attach(medium);
3918 /* not implicit, doesn't require association with this VM */
3919 fIndirect = false;
3920 associate = false;
3921 /* go right to the MediumAttachment creation */
3922 break;
3923 }
3924 }
3925
3926 /* must give up the medium lock and medium tree lock as below we
3927 * go over snapshots, which needs a lock with higher lock order. */
3928 mediumLock.release();
3929 treeLock.release();
3930
3931 /* then, search through snapshots for the best diff in the given
3932 * hard disk's chain to base the new diff on */
3933
3934 ComObjPtr<Medium> base;
3935 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3936 while (snap)
3937 {
3938 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3939
3940 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
3941
3942 MediumAttachment *pAttachFound = NULL;
3943 uint32_t foundLevel = 0;
3944
3945 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
3946 {
3947 MediumAttachment *pAttach = *it;
3948 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3949 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3950 if (pMedium.isNull())
3951 continue;
3952
3953 uint32_t level = 0;
3954 if (pMedium->i_getBase(&level) == medium)
3955 {
3956 /* matched device, channel and bus (i.e. attached to the
3957 * same place) will win and immediately stop the search;
3958 * otherwise the attachment that has the youngest
3959 * descendant of medium will be used
3960 */
3961 if ( pAttach->i_getDevice() == aDevice
3962 && pAttach->i_getPort() == aControllerPort
3963 && pAttach->i_getControllerName() == aName
3964 )
3965 {
3966 pAttachFound = pAttach;
3967 break;
3968 }
3969 else if ( !pAttachFound
3970 || level > foundLevel /* prefer younger */
3971 )
3972 {
3973 pAttachFound = pAttach;
3974 foundLevel = level;
3975 }
3976 }
3977 }
3978
3979 if (pAttachFound)
3980 {
3981 base = pAttachFound->i_getMedium();
3982 break;
3983 }
3984
3985 snap = snap->i_getParent();
3986 }
3987
3988 /* re-lock medium tree and the medium, as we need it below */
3989 treeLock.acquire();
3990 mediumLock.acquire();
3991
3992 /* found a suitable diff, use it as a base */
3993 if (!base.isNull())
3994 {
3995 medium = base;
3996 mediumCaller.attach(medium);
3997 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3998 mediumLock.attach(medium);
3999 }
4000 }
4001
4002 Utf8Str strFullSnapshotFolder;
4003 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4004
4005 ComObjPtr<Medium> diff;
4006 diff.createObject();
4007 // store this diff in the same registry as the parent
4008 Guid uuidRegistryParent;
4009 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4010 {
4011 // parent image has no registry: this can happen if we're attaching a new immutable
4012 // image that has not yet been attached (medium then points to the base and we're
4013 // creating the diff image for the immutable, and the parent is not yet registered);
4014 // put the parent in the machine registry then
4015 mediumLock.release();
4016 treeLock.release();
4017 alock.release();
4018 i_addMediumToRegistry(medium);
4019 alock.acquire();
4020 treeLock.acquire();
4021 mediumLock.acquire();
4022 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4023 }
4024 rc = diff->init(mParent,
4025 medium->i_getPreferredDiffFormat(),
4026 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4027 uuidRegistryParent);
4028 if (FAILED(rc)) return rc;
4029
4030 /* Apply the normal locking logic to the entire chain. */
4031 MediumLockList *pMediumLockList(new MediumLockList());
4032 mediumLock.release();
4033 treeLock.release();
4034 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4035 true /* fMediumLockWrite */,
4036 medium,
4037 *pMediumLockList);
4038 treeLock.acquire();
4039 mediumLock.acquire();
4040 if (SUCCEEDED(rc))
4041 {
4042 mediumLock.release();
4043 treeLock.release();
4044 rc = pMediumLockList->Lock();
4045 treeLock.acquire();
4046 mediumLock.acquire();
4047 if (FAILED(rc))
4048 setError(rc,
4049 tr("Could not lock medium when creating diff '%s'"),
4050 diff->i_getLocationFull().c_str());
4051 else
4052 {
4053 /* will release the lock before the potentially lengthy
4054 * operation, so protect with the special state */
4055 MachineState_T oldState = mData->mMachineState;
4056 i_setMachineState(MachineState_SettingUp);
4057
4058 mediumLock.release();
4059 treeLock.release();
4060 alock.release();
4061
4062 rc = medium->i_createDiffStorage(diff,
4063 MediumVariant_Standard,
4064 pMediumLockList,
4065 NULL /* aProgress */,
4066 true /* aWait */);
4067
4068 alock.acquire();
4069 treeLock.acquire();
4070 mediumLock.acquire();
4071
4072 i_setMachineState(oldState);
4073 }
4074 }
4075
4076 /* Unlock the media and free the associated memory. */
4077 delete pMediumLockList;
4078
4079 if (FAILED(rc)) return rc;
4080
4081 /* use the created diff for the actual attachment */
4082 medium = diff;
4083 mediumCaller.attach(medium);
4084 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4085 mediumLock.attach(medium);
4086 }
4087 while (0);
4088
4089 ComObjPtr<MediumAttachment> attachment;
4090 attachment.createObject();
4091 rc = attachment->init(this,
4092 medium,
4093 aName,
4094 aControllerPort,
4095 aDevice,
4096 aType,
4097 fIndirect,
4098 false /* fPassthrough */,
4099 false /* fTempEject */,
4100 false /* fNonRotational */,
4101 false /* fDiscard */,
4102 fHotplug /* fHotPluggable */,
4103 Utf8Str::Empty);
4104 if (FAILED(rc)) return rc;
4105
4106 if (associate && !medium.isNull())
4107 {
4108 // as the last step, associate the medium to the VM
4109 rc = medium->i_addBackReference(mData->mUuid);
4110 // here we can fail because of Deleting, or being in process of creating a Diff
4111 if (FAILED(rc)) return rc;
4112
4113 mediumLock.release();
4114 treeLock.release();
4115 alock.release();
4116 i_addMediumToRegistry(medium);
4117 alock.acquire();
4118 treeLock.acquire();
4119 mediumLock.acquire();
4120 }
4121
4122 /* success: finally remember the attachment */
4123 i_setModified(IsModified_Storage);
4124 mMediaData.backup();
4125 mMediaData->mAttachments.push_back(attachment);
4126
4127 mediumLock.release();
4128 treeLock.release();
4129 alock.release();
4130
4131 if (fHotplug || fSilent)
4132 {
4133 if (!medium.isNull())
4134 {
4135 MediumLockList *pMediumLockList(new MediumLockList());
4136
4137 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4138 true /* fMediumLockWrite */,
4139 NULL,
4140 *pMediumLockList);
4141 alock.acquire();
4142 if (FAILED(rc))
4143 delete pMediumLockList;
4144 else
4145 {
4146 mData->mSession.mLockedMedia.Unlock();
4147 alock.release();
4148 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4149 mData->mSession.mLockedMedia.Lock();
4150 alock.acquire();
4151 }
4152 alock.release();
4153 }
4154
4155 if (SUCCEEDED(rc))
4156 {
4157 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4158 /* Remove lock list in case of error. */
4159 if (FAILED(rc))
4160 {
4161 mData->mSession.mLockedMedia.Unlock();
4162 mData->mSession.mLockedMedia.Remove(attachment);
4163 mData->mSession.mLockedMedia.Lock();
4164 }
4165 }
4166 }
4167
4168 mParent->i_saveModifiedRegistries();
4169
4170 return rc;
4171}
4172
4173HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4174 LONG aDevice)
4175{
4176 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4177 aName.c_str(), aControllerPort, aDevice));
4178
4179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4180
4181 HRESULT rc = i_checkStateDependency(MutableStateDep);
4182 if (FAILED(rc)) return rc;
4183
4184 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4185
4186 /* Check for an existing controller. */
4187 ComObjPtr<StorageController> ctl;
4188 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4189 if (FAILED(rc)) return rc;
4190
4191 StorageControllerType_T ctrlType;
4192 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4193 if (FAILED(rc))
4194 return setError(E_FAIL,
4195 tr("Could not get type of controller '%s'"),
4196 aName.c_str());
4197
4198 bool fSilent = false;
4199 Utf8Str strReconfig;
4200
4201 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4202 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4203 if ( mData->mMachineState == MachineState_Paused
4204 && strReconfig == "1")
4205 fSilent = true;
4206
4207 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4208 bool fHotplug = false;
4209 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4210 fHotplug = true;
4211
4212 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4213 return setError(VBOX_E_INVALID_VM_STATE,
4214 tr("Controller '%s' does not support hotplugging"),
4215 aName.c_str());
4216
4217 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4218 Bstr(aName).raw(),
4219 aControllerPort,
4220 aDevice);
4221 if (!pAttach)
4222 return setError(VBOX_E_OBJECT_NOT_FOUND,
4223 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4224 aDevice, aControllerPort, aName.c_str());
4225
4226 if (fHotplug && !pAttach->i_getHotPluggable())
4227 return setError(VBOX_E_NOT_SUPPORTED,
4228 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4229 aDevice, aControllerPort, aName.c_str());
4230
4231 /*
4232 * The VM has to detach the device before we delete any implicit diffs.
4233 * If this fails we can roll back without loosing data.
4234 */
4235 if (fHotplug || fSilent)
4236 {
4237 alock.release();
4238 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4239 alock.acquire();
4240 }
4241 if (FAILED(rc)) return rc;
4242
4243 /* If we are here everything went well and we can delete the implicit now. */
4244 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4245
4246 alock.release();
4247
4248 mParent->i_saveModifiedRegistries();
4249
4250 return rc;
4251}
4252
4253HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4254 LONG aDevice, BOOL aPassthrough)
4255{
4256 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4257 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4258
4259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4260
4261 HRESULT rc = i_checkStateDependency(MutableStateDep);
4262 if (FAILED(rc)) return rc;
4263
4264 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4265
4266 if (Global::IsOnlineOrTransient(mData->mMachineState))
4267 return setError(VBOX_E_INVALID_VM_STATE,
4268 tr("Invalid machine state: %s"),
4269 Global::stringifyMachineState(mData->mMachineState));
4270
4271 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4272 Bstr(aName).raw(),
4273 aControllerPort,
4274 aDevice);
4275 if (!pAttach)
4276 return setError(VBOX_E_OBJECT_NOT_FOUND,
4277 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4278 aDevice, aControllerPort, aName.c_str());
4279
4280
4281 i_setModified(IsModified_Storage);
4282 mMediaData.backup();
4283
4284 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4285
4286 if (pAttach->i_getType() != DeviceType_DVD)
4287 return setError(E_INVALIDARG,
4288 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4289 aDevice, aControllerPort, aName.c_str());
4290 pAttach->i_updatePassthrough(!!aPassthrough);
4291
4292 return S_OK;
4293}
4294
4295HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4296 LONG aDevice, BOOL aTemporaryEject)
4297{
4298
4299 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4300 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4301
4302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4303
4304 HRESULT rc = i_checkStateDependency(MutableStateDep);
4305 if (FAILED(rc)) return rc;
4306
4307 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4308 Bstr(aName).raw(),
4309 aControllerPort,
4310 aDevice);
4311 if (!pAttach)
4312 return setError(VBOX_E_OBJECT_NOT_FOUND,
4313 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4314 aDevice, aControllerPort, aName.c_str());
4315
4316
4317 i_setModified(IsModified_Storage);
4318 mMediaData.backup();
4319
4320 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4321
4322 if (pAttach->i_getType() != DeviceType_DVD)
4323 return setError(E_INVALIDARG,
4324 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4325 aDevice, aControllerPort, aName.c_str());
4326 pAttach->i_updateTempEject(!!aTemporaryEject);
4327
4328 return S_OK;
4329}
4330
4331HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4332 LONG aDevice, BOOL aNonRotational)
4333{
4334
4335 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4336 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4337
4338 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4339
4340 HRESULT rc = i_checkStateDependency(MutableStateDep);
4341 if (FAILED(rc)) return rc;
4342
4343 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4344
4345 if (Global::IsOnlineOrTransient(mData->mMachineState))
4346 return setError(VBOX_E_INVALID_VM_STATE,
4347 tr("Invalid machine state: %s"),
4348 Global::stringifyMachineState(mData->mMachineState));
4349
4350 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4351 Bstr(aName).raw(),
4352 aControllerPort,
4353 aDevice);
4354 if (!pAttach)
4355 return setError(VBOX_E_OBJECT_NOT_FOUND,
4356 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4357 aDevice, aControllerPort, aName.c_str());
4358
4359
4360 i_setModified(IsModified_Storage);
4361 mMediaData.backup();
4362
4363 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4364
4365 if (pAttach->i_getType() != DeviceType_HardDisk)
4366 return setError(E_INVALIDARG,
4367 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"),
4368 aDevice, aControllerPort, aName.c_str());
4369 pAttach->i_updateNonRotational(!!aNonRotational);
4370
4371 return S_OK;
4372}
4373
4374HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4375 LONG aDevice, BOOL aDiscard)
4376{
4377
4378 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4379 aName.c_str(), aControllerPort, aDevice, aDiscard));
4380
4381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4382
4383 HRESULT rc = i_checkStateDependency(MutableStateDep);
4384 if (FAILED(rc)) return rc;
4385
4386 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4387
4388 if (Global::IsOnlineOrTransient(mData->mMachineState))
4389 return setError(VBOX_E_INVALID_VM_STATE,
4390 tr("Invalid machine state: %s"),
4391 Global::stringifyMachineState(mData->mMachineState));
4392
4393 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4394 Bstr(aName).raw(),
4395 aControllerPort,
4396 aDevice);
4397 if (!pAttach)
4398 return setError(VBOX_E_OBJECT_NOT_FOUND,
4399 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4400 aDevice, aControllerPort, aName.c_str());
4401
4402
4403 i_setModified(IsModified_Storage);
4404 mMediaData.backup();
4405
4406 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4407
4408 if (pAttach->i_getType() != DeviceType_HardDisk)
4409 return setError(E_INVALIDARG,
4410 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"),
4411 aDevice, aControllerPort, aName.c_str());
4412 pAttach->i_updateDiscard(!!aDiscard);
4413
4414 return S_OK;
4415}
4416
4417HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4418 LONG aDevice, BOOL aHotPluggable)
4419{
4420 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4421 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4422
4423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4424
4425 HRESULT rc = i_checkStateDependency(MutableStateDep);
4426 if (FAILED(rc)) return rc;
4427
4428 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4429
4430 if (Global::IsOnlineOrTransient(mData->mMachineState))
4431 return setError(VBOX_E_INVALID_VM_STATE,
4432 tr("Invalid machine state: %s"),
4433 Global::stringifyMachineState(mData->mMachineState));
4434
4435 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4436 Bstr(aName).raw(),
4437 aControllerPort,
4438 aDevice);
4439 if (!pAttach)
4440 return setError(VBOX_E_OBJECT_NOT_FOUND,
4441 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4442 aDevice, aControllerPort, aName.c_str());
4443
4444 /* Check for an existing controller. */
4445 ComObjPtr<StorageController> ctl;
4446 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4447 if (FAILED(rc)) return rc;
4448
4449 StorageControllerType_T ctrlType;
4450 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4451 if (FAILED(rc))
4452 return setError(E_FAIL,
4453 tr("Could not get type of controller '%s'"),
4454 aName.c_str());
4455
4456 if (!i_isControllerHotplugCapable(ctrlType))
4457 return setError(VBOX_E_NOT_SUPPORTED,
4458 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4459 aName.c_str());
4460
4461 i_setModified(IsModified_Storage);
4462 mMediaData.backup();
4463
4464 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4465
4466 if (pAttach->i_getType() == DeviceType_Floppy)
4467 return setError(E_INVALIDARG,
4468 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"),
4469 aDevice, aControllerPort, aName.c_str());
4470 pAttach->i_updateHotPluggable(!!aHotPluggable);
4471
4472 return S_OK;
4473}
4474
4475HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4476 LONG aDevice)
4477{
4478 int rc = S_OK;
4479 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4480 aName.c_str(), aControllerPort, aDevice));
4481
4482 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4483
4484 return rc;
4485}
4486
4487HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4488 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4489{
4490 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4491 aName.c_str(), aControllerPort, aDevice));
4492
4493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4494
4495 HRESULT rc = i_checkStateDependency(MutableStateDep);
4496 if (FAILED(rc)) return rc;
4497
4498 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4499
4500 if (Global::IsOnlineOrTransient(mData->mMachineState))
4501 return setError(VBOX_E_INVALID_VM_STATE,
4502 tr("Invalid machine state: %s"),
4503 Global::stringifyMachineState(mData->mMachineState));
4504
4505 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4506 Bstr(aName).raw(),
4507 aControllerPort,
4508 aDevice);
4509 if (!pAttach)
4510 return setError(VBOX_E_OBJECT_NOT_FOUND,
4511 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4512 aDevice, aControllerPort, aName.c_str());
4513
4514
4515 i_setModified(IsModified_Storage);
4516 mMediaData.backup();
4517
4518 IBandwidthGroup *iB = aBandwidthGroup;
4519 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4520 if (aBandwidthGroup && group.isNull())
4521 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4522
4523 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4524
4525 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4526 if (strBandwidthGroupOld.isNotEmpty())
4527 {
4528 /* Get the bandwidth group object and release it - this must not fail. */
4529 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4530 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4531 Assert(SUCCEEDED(rc));
4532
4533 pBandwidthGroupOld->i_release();
4534 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4535 }
4536
4537 if (!group.isNull())
4538 {
4539 group->i_reference();
4540 pAttach->i_updateBandwidthGroup(group->i_getName());
4541 }
4542
4543 return S_OK;
4544}
4545
4546HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4547 LONG aControllerPort,
4548 LONG aDevice,
4549 DeviceType_T aType)
4550{
4551 HRESULT rc = S_OK;
4552
4553 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4554 aName.c_str(), aControllerPort, aDevice, aType));
4555
4556 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4557
4558 return rc;
4559}
4560
4561
4562HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4563 LONG aControllerPort,
4564 LONG aDevice,
4565 BOOL aForce)
4566{
4567 int rc = S_OK;
4568 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4569 aName.c_str(), aControllerPort, aForce));
4570
4571 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4572
4573 return rc;
4574}
4575
4576HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4577 LONG aControllerPort,
4578 LONG aDevice,
4579 const ComPtr<IMedium> &aMedium,
4580 BOOL aForce)
4581{
4582 int rc = S_OK;
4583 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4584 aName.c_str(), aControllerPort, aDevice, aForce));
4585
4586 // request the host lock first, since might be calling Host methods for getting host drives;
4587 // next, protect the media tree all the while we're in here, as well as our member variables
4588 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4589 this->lockHandle(),
4590 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4591
4592 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4593 Bstr(aName).raw(),
4594 aControllerPort,
4595 aDevice);
4596 if (pAttach.isNull())
4597 return setError(VBOX_E_OBJECT_NOT_FOUND,
4598 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4599 aDevice, aControllerPort, aName.c_str());
4600
4601 /* Remember previously mounted medium. The medium before taking the
4602 * backup is not necessarily the same thing. */
4603 ComObjPtr<Medium> oldmedium;
4604 oldmedium = pAttach->i_getMedium();
4605
4606 IMedium *iM = aMedium;
4607 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4608 if (aMedium && pMedium.isNull())
4609 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4610
4611 AutoCaller mediumCaller(pMedium);
4612 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4613
4614 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4615 if (pMedium)
4616 {
4617 DeviceType_T mediumType = pAttach->i_getType();
4618 switch (mediumType)
4619 {
4620 case DeviceType_DVD:
4621 case DeviceType_Floppy:
4622 break;
4623
4624 default:
4625 return setError(VBOX_E_INVALID_OBJECT_STATE,
4626 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4627 aControllerPort,
4628 aDevice,
4629 aName.c_str());
4630 }
4631 }
4632
4633 i_setModified(IsModified_Storage);
4634 mMediaData.backup();
4635
4636 {
4637 // The backup operation makes the pAttach reference point to the
4638 // old settings. Re-get the correct reference.
4639 pAttach = i_findAttachment(mMediaData->mAttachments,
4640 Bstr(aName).raw(),
4641 aControllerPort,
4642 aDevice);
4643 if (!oldmedium.isNull())
4644 oldmedium->i_removeBackReference(mData->mUuid);
4645 if (!pMedium.isNull())
4646 {
4647 pMedium->i_addBackReference(mData->mUuid);
4648
4649 mediumLock.release();
4650 multiLock.release();
4651 i_addMediumToRegistry(pMedium);
4652 multiLock.acquire();
4653 mediumLock.acquire();
4654 }
4655
4656 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4657 pAttach->i_updateMedium(pMedium);
4658 }
4659
4660 i_setModified(IsModified_Storage);
4661
4662 mediumLock.release();
4663 multiLock.release();
4664 rc = i_onMediumChange(pAttach, aForce);
4665 multiLock.acquire();
4666 mediumLock.acquire();
4667
4668 /* On error roll back this change only. */
4669 if (FAILED(rc))
4670 {
4671 if (!pMedium.isNull())
4672 pMedium->i_removeBackReference(mData->mUuid);
4673 pAttach = i_findAttachment(mMediaData->mAttachments,
4674 Bstr(aName).raw(),
4675 aControllerPort,
4676 aDevice);
4677 /* If the attachment is gone in the meantime, bail out. */
4678 if (pAttach.isNull())
4679 return rc;
4680 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4681 if (!oldmedium.isNull())
4682 oldmedium->i_addBackReference(mData->mUuid);
4683 pAttach->i_updateMedium(oldmedium);
4684 }
4685
4686 mediumLock.release();
4687 multiLock.release();
4688
4689 mParent->i_saveModifiedRegistries();
4690
4691 return rc;
4692}
4693HRESULT Machine::getMedium(const com::Utf8Str &aName,
4694 LONG aControllerPort,
4695 LONG aDevice,
4696 ComPtr<IMedium> &aMedium)
4697{
4698 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4699 aName.c_str(), aControllerPort, aDevice));
4700
4701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4702
4703 aMedium = NULL;
4704
4705 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4706 Bstr(aName).raw(),
4707 aControllerPort,
4708 aDevice);
4709 if (pAttach.isNull())
4710 return setError(VBOX_E_OBJECT_NOT_FOUND,
4711 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4712 aDevice, aControllerPort, aName.c_str());
4713
4714 pAttach->i_getMedium().queryInterfaceTo(aMedium.asOutParam());
4715
4716 return S_OK;
4717}
4718
4719HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4720{
4721
4722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4723
4724 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4725
4726 return S_OK;
4727}
4728
4729HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4730{
4731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4732
4733 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4734
4735 return S_OK;
4736}
4737
4738HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4739{
4740 /* Do not assert if slot is out of range, just return the advertised
4741 status. testdriver/vbox.py triggers this in logVmInfo. */
4742 if (aSlot >= mNetworkAdapters.size())
4743 return setError(E_INVALIDARG,
4744 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4745 aSlot, mNetworkAdapters.size());
4746
4747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4748
4749 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4750
4751 return S_OK;
4752}
4753
4754HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4755{
4756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4757
4758 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4759 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4760 size_t i = 0;
4761 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4762 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4763 ++it, ++i)
4764 aKeys[i] = it->first;
4765
4766 return S_OK;
4767}
4768
4769 /**
4770 * @note Locks this object for reading.
4771 */
4772HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4773 com::Utf8Str &aValue)
4774{
4775 /* start with nothing found */
4776 aValue = "";
4777
4778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4779
4780 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4781 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4782 // found:
4783 aValue = it->second; // source is a Utf8Str
4784
4785 /* return the result to caller (may be empty) */
4786 return S_OK;
4787}
4788
4789 /**
4790 * @note Locks mParent for writing + this object for writing.
4791 */
4792HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4793{
4794 Utf8Str strOldValue; // empty
4795
4796 // locking note: we only hold the read lock briefly to look up the old value,
4797 // then release it and call the onExtraCanChange callbacks. There is a small
4798 // chance of a race insofar as the callback might be called twice if two callers
4799 // change the same key at the same time, but that's a much better solution
4800 // than the deadlock we had here before. The actual changing of the extradata
4801 // is then performed under the write lock and race-free.
4802
4803 // look up the old value first; if nothing has changed then we need not do anything
4804 {
4805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4806 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4807 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4808 strOldValue = it->second;
4809 }
4810
4811 bool fChanged;
4812 if ((fChanged = (strOldValue != aValue)))
4813 {
4814 // ask for permission from all listeners outside the locks;
4815 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4816 // lock to copy the list of callbacks to invoke
4817 Bstr error;
4818 Bstr bstrValue(aValue);
4819
4820 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4821 {
4822 const char *sep = error.isEmpty() ? "" : ": ";
4823 CBSTR err = error.raw();
4824 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4825 sep, err));
4826 return setError(E_ACCESSDENIED,
4827 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4828 aKey.c_str(),
4829 aValue.c_str(),
4830 sep,
4831 err);
4832 }
4833
4834 // data is changing and change not vetoed: then write it out under the lock
4835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4836
4837 if (i_isSnapshotMachine())
4838 {
4839 HRESULT rc = i_checkStateDependency(MutableStateDep);
4840 if (FAILED(rc)) return rc;
4841 }
4842
4843 if (aValue.isEmpty())
4844 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4845 else
4846 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4847 // creates a new key if needed
4848
4849 bool fNeedsGlobalSaveSettings = false;
4850 i_saveSettings(&fNeedsGlobalSaveSettings);
4851
4852 if (fNeedsGlobalSaveSettings)
4853 {
4854 // save the global settings; for that we should hold only the VirtualBox lock
4855 alock.release();
4856 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4857 mParent->i_saveSettings();
4858 }
4859 }
4860
4861 // fire notification outside the lock
4862 if (fChanged)
4863 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4864
4865 return S_OK;
4866}
4867
4868HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4869{
4870 aProgress = NULL;
4871 NOREF(aSettingsFilePath);
4872 ReturnComNotImplemented();
4873}
4874
4875HRESULT Machine::saveSettings()
4876{
4877 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4878
4879 /* when there was auto-conversion, we want to save the file even if
4880 * the VM is saved */
4881 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4882 if (FAILED(rc)) return rc;
4883
4884 /* the settings file path may never be null */
4885 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4886
4887 /* save all VM data excluding snapshots */
4888 bool fNeedsGlobalSaveSettings = false;
4889 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4890 mlock.release();
4891
4892 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4893 {
4894 // save the global settings; for that we should hold only the VirtualBox lock
4895 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4896 rc = mParent->i_saveSettings();
4897 }
4898
4899 return rc;
4900}
4901
4902
4903HRESULT Machine::discardSettings()
4904{
4905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4906
4907 HRESULT rc = i_checkStateDependency(MutableStateDep);
4908 if (FAILED(rc)) return rc;
4909
4910 /*
4911 * during this rollback, the session will be notified if data has
4912 * been actually changed
4913 */
4914 i_rollback(true /* aNotify */);
4915
4916 return S_OK;
4917}
4918
4919/** @note Locks objects! */
4920HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
4921 std::vector<ComPtr<IMedium> > &aMedia)
4922{
4923 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4924 AutoLimitedCaller autoCaller(this);
4925 AssertComRCReturnRC(autoCaller.rc());
4926
4927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4928
4929 Guid id(i_getId());
4930
4931 if (mData->mSession.mState != SessionState_Unlocked)
4932 return setError(VBOX_E_INVALID_OBJECT_STATE,
4933 tr("Cannot unregister the machine '%s' while it is locked"),
4934 mUserData->s.strName.c_str());
4935
4936 // wait for state dependents to drop to zero
4937 i_ensureNoStateDependencies();
4938
4939 if (!mData->mAccessible)
4940 {
4941 // inaccessible maschines can only be unregistered; uninitialize ourselves
4942 // here because currently there may be no unregistered that are inaccessible
4943 // (this state combination is not supported). Note releasing the caller and
4944 // leaving the lock before calling uninit()
4945 alock.release();
4946 autoCaller.release();
4947
4948 uninit();
4949
4950 mParent->i_unregisterMachine(this, id);
4951 // calls VirtualBox::i_saveSettings()
4952
4953 return S_OK;
4954 }
4955
4956 HRESULT rc = S_OK;
4957
4958 // discard saved state
4959 if (mData->mMachineState == MachineState_Saved)
4960 {
4961 // add the saved state file to the list of files the caller should delete
4962 Assert(!mSSData->strStateFilePath.isEmpty());
4963 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4964
4965 mSSData->strStateFilePath.setNull();
4966
4967 // unconditionally set the machine state to powered off, we now
4968 // know no session has locked the machine
4969 mData->mMachineState = MachineState_PoweredOff;
4970 }
4971
4972 size_t cSnapshots = 0;
4973 if (mData->mFirstSnapshot)
4974 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
4975 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
4976 // fail now before we start detaching media
4977 return setError(VBOX_E_INVALID_OBJECT_STATE,
4978 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4979 mUserData->s.strName.c_str(), cSnapshots);
4980
4981 // This list collects the medium objects from all medium attachments
4982 // which we will detach from the machine and its snapshots, in a specific
4983 // order which allows for closing all media without getting "media in use"
4984 // errors, simply by going through the list from the front to the back:
4985 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4986 // and must be closed before the parent media from the snapshots, or closing the parents
4987 // will fail because they still have children);
4988 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4989 // the root ("first") snapshot of the machine.
4990 MediaList llMedia;
4991
4992 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4993 && mMediaData->mAttachments.size()
4994 )
4995 {
4996 // we have media attachments: detach them all and add the Medium objects to our list
4997 if (aCleanupMode != CleanupMode_UnregisterOnly)
4998 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4999 else
5000 return setError(VBOX_E_INVALID_OBJECT_STATE,
5001 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5002 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5003 }
5004
5005 if (cSnapshots)
5006 {
5007 // autoCleanup must be true here, or we would have failed above
5008
5009 // add the media from the medium attachments of the snapshots to llMedia
5010 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5011 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5012 // into the children first
5013
5014 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5015 MachineState_T oldState = mData->mMachineState;
5016 mData->mMachineState = MachineState_DeletingSnapshot;
5017
5018 // make a copy of the first snapshot so the refcount does not drop to 0
5019 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5020 // because of the AutoCaller voodoo)
5021 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5022
5023 // GO!
5024 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5025
5026 mData->mMachineState = oldState;
5027 }
5028
5029 if (FAILED(rc))
5030 {
5031 i_rollbackMedia();
5032 return rc;
5033 }
5034
5035 // commit all the media changes made above
5036 i_commitMedia();
5037
5038 mData->mRegistered = false;
5039
5040 // machine lock no longer needed
5041 alock.release();
5042
5043 // return media to caller
5044 size_t i = 0;
5045 aMedia.resize(llMedia.size());
5046 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5047 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5048
5049 mParent->i_unregisterMachine(this, id);
5050 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5051
5052 return S_OK;
5053}
5054
5055struct Machine::DeleteTask
5056{
5057 ComObjPtr<Machine> pMachine;
5058 RTCList<ComPtr<IMedium> > llMediums;
5059 StringsList llFilesToDelete;
5060 ComObjPtr<Progress> pProgress;
5061};
5062
5063HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5064{
5065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5066
5067 HRESULT rc = i_checkStateDependency(MutableStateDep);
5068 if (FAILED(rc)) return rc;
5069
5070 if (mData->mRegistered)
5071 return setError(VBOX_E_INVALID_VM_STATE,
5072 tr("Cannot delete settings of a registered machine"));
5073
5074 DeleteTask *pTask = new DeleteTask;
5075 pTask->pMachine = this;
5076
5077 // collect files to delete
5078 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5079
5080 for (size_t i = 0; i < aMedia.size(); ++i)
5081 {
5082 IMedium *pIMedium(aMedia[i]);
5083 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5084 if (pMedium.isNull())
5085 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5086 SafeArray<BSTR> ids;
5087 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5088 if (FAILED(rc)) return rc;
5089 /* At this point the medium should not have any back references
5090 * anymore. If it has it is attached to another VM and *must* not
5091 * deleted. */
5092 if (ids.size() < 1)
5093 pTask->llMediums.append(pMedium);
5094 }
5095 if (mData->pMachineConfigFile->fileExists())
5096 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5097
5098 pTask->pProgress.createObject();
5099 pTask->pProgress->init(i_getVirtualBox(),
5100 static_cast<IMachine*>(this) /* aInitiator */,
5101 Bstr(tr("Deleting files")).raw(),
5102 true /* fCancellable */,
5103 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5104 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5105
5106 int vrc = RTThreadCreate(NULL,
5107 Machine::deleteThread,
5108 (void*)pTask,
5109 0,
5110 RTTHREADTYPE_MAIN_WORKER,
5111 0,
5112 "MachineDelete");
5113
5114 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5115
5116 if (RT_FAILURE(vrc))
5117 {
5118 delete pTask;
5119 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5120 }
5121
5122 LogFlowFuncLeave();
5123
5124 return S_OK;
5125}
5126
5127/**
5128 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5129 * calls Machine::deleteTaskWorker() on the actual machine object.
5130 * @param Thread
5131 * @param pvUser
5132 * @return
5133 */
5134/*static*/
5135DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5136{
5137 LogFlowFuncEnter();
5138
5139 DeleteTask *pTask = (DeleteTask*)pvUser;
5140 Assert(pTask);
5141 Assert(pTask->pMachine);
5142 Assert(pTask->pProgress);
5143
5144 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5145 pTask->pProgress->i_notifyComplete(rc);
5146
5147 delete pTask;
5148
5149 LogFlowFuncLeave();
5150
5151 NOREF(Thread);
5152
5153 return VINF_SUCCESS;
5154}
5155
5156/**
5157 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5158 * @param task
5159 * @return
5160 */
5161HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5162{
5163 AutoCaller autoCaller(this);
5164 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5165
5166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5167
5168 HRESULT rc = S_OK;
5169
5170 try
5171 {
5172 ULONG uLogHistoryCount = 3;
5173 ComPtr<ISystemProperties> systemProperties;
5174 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5175 if (FAILED(rc)) throw rc;
5176
5177 if (!systemProperties.isNull())
5178 {
5179 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5180 if (FAILED(rc)) throw rc;
5181 }
5182
5183 MachineState_T oldState = mData->mMachineState;
5184 i_setMachineState(MachineState_SettingUp);
5185 alock.release();
5186 for (size_t i = 0; i < task.llMediums.size(); ++i)
5187 {
5188 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5189 {
5190 AutoCaller mac(pMedium);
5191 if (FAILED(mac.rc())) throw mac.rc();
5192 Utf8Str strLocation = pMedium->i_getLocationFull();
5193 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5194 if (FAILED(rc)) throw rc;
5195 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5196 }
5197 ComPtr<IProgress> pProgress2;
5198 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5199 if (FAILED(rc)) throw rc;
5200 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5201 if (FAILED(rc)) throw rc;
5202 /* Check the result of the asynchronous process. */
5203 LONG iRc;
5204 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5205 if (FAILED(rc)) throw rc;
5206 /* If the thread of the progress object has an error, then
5207 * retrieve the error info from there, or it'll be lost. */
5208 if (FAILED(iRc))
5209 throw setError(ProgressErrorInfo(pProgress2));
5210
5211 /* Close the medium, deliberately without checking the return
5212- * code, and without leaving any trace in the error info, as
5213- * a failure here is a very minor issue, which shouldn't happen
5214- * as above we even managed to delete the medium. */
5215 {
5216 ErrorInfoKeeper eik;
5217 pMedium->Close();
5218 }
5219 }
5220 i_setMachineState(oldState);
5221 alock.acquire();
5222
5223 // delete the files pushed on the task list by Machine::Delete()
5224 // (this includes saved states of the machine and snapshots and
5225 // medium storage files from the IMedium list passed in, and the
5226 // machine XML file)
5227 StringsList::const_iterator it = task.llFilesToDelete.begin();
5228 while (it != task.llFilesToDelete.end())
5229 {
5230 const Utf8Str &strFile = *it;
5231 LogFunc(("Deleting file %s\n", strFile.c_str()));
5232 int vrc = RTFileDelete(strFile.c_str());
5233 if (RT_FAILURE(vrc))
5234 throw setError(VBOX_E_IPRT_ERROR,
5235 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5236
5237 ++it;
5238 if (it == task.llFilesToDelete.end())
5239 {
5240 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5241 if (FAILED(rc)) throw rc;
5242 break;
5243 }
5244
5245 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5246 if (FAILED(rc)) throw rc;
5247 }
5248
5249 /* delete the settings only when the file actually exists */
5250 if (mData->pMachineConfigFile->fileExists())
5251 {
5252 /* Delete any backup or uncommitted XML files. Ignore failures.
5253 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5254 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5255 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5256 RTFileDelete(otherXml.c_str());
5257 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5258 RTFileDelete(otherXml.c_str());
5259
5260 /* delete the Logs folder, nothing important should be left
5261 * there (we don't check for errors because the user might have
5262 * some private files there that we don't want to delete) */
5263 Utf8Str logFolder;
5264 getLogFolder(logFolder);
5265 Assert(logFolder.length());
5266 if (RTDirExists(logFolder.c_str()))
5267 {
5268 /* Delete all VBox.log[.N] files from the Logs folder
5269 * (this must be in sync with the rotation logic in
5270 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5271 * files that may have been created by the GUI. */
5272 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5273 logFolder.c_str(), RTPATH_DELIMITER);
5274 RTFileDelete(log.c_str());
5275 log = Utf8StrFmt("%s%cVBox.png",
5276 logFolder.c_str(), RTPATH_DELIMITER);
5277 RTFileDelete(log.c_str());
5278 for (int i = uLogHistoryCount; i > 0; i--)
5279 {
5280 log = Utf8StrFmt("%s%cVBox.log.%d",
5281 logFolder.c_str(), RTPATH_DELIMITER, i);
5282 RTFileDelete(log.c_str());
5283 log = Utf8StrFmt("%s%cVBox.png.%d",
5284 logFolder.c_str(), RTPATH_DELIMITER, i);
5285 RTFileDelete(log.c_str());
5286 }
5287
5288 RTDirRemove(logFolder.c_str());
5289 }
5290
5291 /* delete the Snapshots folder, nothing important should be left
5292 * there (we don't check for errors because the user might have
5293 * some private files there that we don't want to delete) */
5294 Utf8Str strFullSnapshotFolder;
5295 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5296 Assert(!strFullSnapshotFolder.isEmpty());
5297 if (RTDirExists(strFullSnapshotFolder.c_str()))
5298 RTDirRemove(strFullSnapshotFolder.c_str());
5299
5300 // delete the directory that contains the settings file, but only
5301 // if it matches the VM name
5302 Utf8Str settingsDir;
5303 if (i_isInOwnDir(&settingsDir))
5304 RTDirRemove(settingsDir.c_str());
5305 }
5306
5307 alock.release();
5308
5309 mParent->i_saveModifiedRegistries();
5310 }
5311 catch (HRESULT aRC) { rc = aRC; }
5312
5313 return rc;
5314}
5315
5316HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5317{
5318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5319
5320 ComObjPtr<Snapshot> pSnapshot;
5321 HRESULT rc;
5322
5323 if (aNameOrId.isEmpty())
5324 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5325 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5326 else
5327 {
5328 Guid uuid(aNameOrId);
5329 if (uuid.isValid())
5330 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5331 else
5332 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5333 }
5334 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5335
5336 return rc;
5337}
5338
5339HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5340{
5341 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5342
5343 HRESULT rc = i_checkStateDependency(MutableStateDep);
5344 if (FAILED(rc)) return rc;
5345
5346 ComObjPtr<SharedFolder> sharedFolder;
5347 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5348 if (SUCCEEDED(rc))
5349 return setError(VBOX_E_OBJECT_IN_USE,
5350 tr("Shared folder named '%s' already exists"),
5351 aName.c_str());
5352
5353 sharedFolder.createObject();
5354 rc = sharedFolder->init(i_getMachine(),
5355 aName,
5356 aHostPath,
5357 !!aWritable,
5358 !!aAutomount,
5359 true /* fFailOnError */);
5360 if (FAILED(rc)) return rc;
5361
5362 i_setModified(IsModified_SharedFolders);
5363 mHWData.backup();
5364 mHWData->mSharedFolders.push_back(sharedFolder);
5365
5366 /* inform the direct session if any */
5367 alock.release();
5368 i_onSharedFolderChange();
5369
5370 return S_OK;
5371}
5372
5373HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5374{
5375 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5376
5377 HRESULT rc = i_checkStateDependency(MutableStateDep);
5378 if (FAILED(rc)) return rc;
5379
5380 ComObjPtr<SharedFolder> sharedFolder;
5381 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5382 if (FAILED(rc)) return rc;
5383
5384 i_setModified(IsModified_SharedFolders);
5385 mHWData.backup();
5386 mHWData->mSharedFolders.remove(sharedFolder);
5387
5388 /* inform the direct session if any */
5389 alock.release();
5390 i_onSharedFolderChange();
5391
5392 return S_OK;
5393}
5394
5395HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5396{
5397 /* start with No */
5398 *aCanShow = FALSE;
5399
5400 ComPtr<IInternalSessionControl> directControl;
5401 {
5402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5403
5404 if (mData->mSession.mState != SessionState_Locked)
5405 return setError(VBOX_E_INVALID_VM_STATE,
5406 tr("Machine is not locked for session (session state: %s)"),
5407 Global::stringifySessionState(mData->mSession.mState));
5408
5409 directControl = mData->mSession.mDirectControl;
5410 }
5411
5412 /* ignore calls made after #OnSessionEnd() is called */
5413 if (!directControl)
5414 return S_OK;
5415
5416 LONG64 dummy;
5417 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5418}
5419
5420HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5421{
5422 ComPtr<IInternalSessionControl> directControl;
5423 {
5424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5425
5426 if (mData->mSession.mState != SessionState_Locked)
5427 return setError(E_FAIL,
5428 tr("Machine is not locked for session (session state: %s)"),
5429 Global::stringifySessionState(mData->mSession.mState));
5430
5431 directControl = mData->mSession.mDirectControl;
5432 }
5433
5434 /* ignore calls made after #OnSessionEnd() is called */
5435 if (!directControl)
5436 return S_OK;
5437
5438 BOOL dummy;
5439 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5440}
5441
5442#ifdef VBOX_WITH_GUEST_PROPS
5443/**
5444 * Look up a guest property in VBoxSVC's internal structures.
5445 */
5446HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5447 com::Utf8Str &aValue,
5448 LONG64 *aTimestamp,
5449 com::Utf8Str &aFlags) const
5450{
5451 using namespace guestProp;
5452
5453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5454 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5455
5456 if (it != mHWData->mGuestProperties.end())
5457 {
5458 char szFlags[MAX_FLAGS_LEN + 1];
5459 aValue = it->second.strValue;
5460 *aTimestamp = it->second.mTimestamp;
5461 writeFlags(it->second.mFlags, szFlags);
5462 aFlags = Utf8Str(szFlags);
5463 }
5464
5465 return S_OK;
5466}
5467
5468/**
5469 * Query the VM that a guest property belongs to for the property.
5470 * @returns E_ACCESSDENIED if the VM process is not available or not
5471 * currently handling queries and the lookup should then be done in
5472 * VBoxSVC.
5473 */
5474HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5475 com::Utf8Str &aValue,
5476 LONG64 *aTimestamp,
5477 com::Utf8Str &aFlags) const
5478{
5479 HRESULT rc = S_OK;
5480 BSTR bValue = NULL;
5481 BSTR bFlags = NULL;
5482
5483 ComPtr<IInternalSessionControl> directControl;
5484 directControl = mData->mSession.mDirectControl;
5485
5486 /* fail if we were called after #OnSessionEnd() is called. This is a
5487 * silly race condition. */
5488
5489 /** @todo This code is bothering API clients (like python script clients) with
5490 * the AccessGuestProperty call, creating unncessary IPC. Need to
5491 * have a way of figuring out which kind of direct session it is... */
5492 if (!directControl)
5493 rc = E_ACCESSDENIED;
5494 else
5495 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5496 0 /* accessMode */,
5497 &bValue, aTimestamp, &bFlags);
5498
5499 aValue = bValue;
5500 aFlags = bFlags;
5501
5502 return rc;
5503}
5504#endif // VBOX_WITH_GUEST_PROPS
5505
5506HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5507 com::Utf8Str &aValue,
5508 LONG64 *aTimestamp,
5509 com::Utf8Str &aFlags)
5510{
5511#ifndef VBOX_WITH_GUEST_PROPS
5512 ReturnComNotImplemented();
5513#else // VBOX_WITH_GUEST_PROPS
5514
5515 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5516
5517 if (rc == E_ACCESSDENIED)
5518 /* The VM is not running or the service is not (yet) accessible */
5519 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5520 return rc;
5521#endif // VBOX_WITH_GUEST_PROPS
5522}
5523
5524HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5525{
5526 LONG64 dummyTimestamp;
5527 com::Utf8Str dummyFlags;
5528 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5529 return rc;
5530
5531}
5532HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5533{
5534 com::Utf8Str dummyFlags;
5535 com::Utf8Str dummyValue;
5536 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5537 return rc;
5538}
5539
5540#ifdef VBOX_WITH_GUEST_PROPS
5541/**
5542 * Set a guest property in VBoxSVC's internal structures.
5543 */
5544HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5545 const com::Utf8Str &aFlags, bool fDelete)
5546{
5547 using namespace guestProp;
5548
5549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5550 HRESULT rc = S_OK;
5551
5552 rc = i_checkStateDependency(MutableStateDep);
5553 if (FAILED(rc)) return rc;
5554
5555 try
5556 {
5557 uint32_t fFlags = NILFLAG;
5558 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5559 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5560
5561 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5562 if (it == mHWData->mGuestProperties.end())
5563 {
5564 if (!fDelete)
5565 {
5566 i_setModified(IsModified_MachineData);
5567 mHWData.backupEx();
5568
5569 RTTIMESPEC time;
5570 HWData::GuestProperty prop;
5571 prop.strValue = Bstr(aValue).raw();
5572 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5573 prop.mFlags = fFlags;
5574 mHWData->mGuestProperties[aName] = prop;
5575 }
5576 }
5577 else
5578 {
5579 if (it->second.mFlags & (RDONLYHOST))
5580 {
5581 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5582 }
5583 else
5584 {
5585 i_setModified(IsModified_MachineData);
5586 mHWData.backupEx();
5587
5588 /* The backupEx() operation invalidates our iterator,
5589 * so get a new one. */
5590 it = mHWData->mGuestProperties.find(aName);
5591 Assert(it != mHWData->mGuestProperties.end());
5592
5593 if (!fDelete)
5594 {
5595 RTTIMESPEC time;
5596 it->second.strValue = aValue;
5597 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5598 it->second.mFlags = fFlags;
5599 }
5600 else
5601 mHWData->mGuestProperties.erase(it);
5602 }
5603 }
5604
5605 if ( SUCCEEDED(rc)
5606 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5607 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5608 RTSTR_MAX,
5609 aName.c_str(),
5610 RTSTR_MAX,
5611 NULL)
5612 )
5613 )
5614 {
5615 alock.release();
5616
5617 mParent->i_onGuestPropertyChange(mData->mUuid,
5618 Bstr(aName).raw(),
5619 Bstr(aValue).raw(),
5620 Bstr(aFlags).raw());
5621 }
5622 }
5623 catch (std::bad_alloc &)
5624 {
5625 rc = E_OUTOFMEMORY;
5626 }
5627
5628 return rc;
5629}
5630
5631/**
5632 * Set a property on the VM that that property belongs to.
5633 * @returns E_ACCESSDENIED if the VM process is not available or not
5634 * currently handling queries and the setting should then be done in
5635 * VBoxSVC.
5636 */
5637HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5638 const com::Utf8Str &aFlags, bool fDelete)
5639{
5640 HRESULT rc;
5641
5642 try
5643 {
5644 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5645
5646 BSTR dummy = NULL; /* will not be changed (setter) */
5647 LONG64 dummy64;
5648 if (!directControl)
5649 rc = E_ACCESSDENIED;
5650 else
5651 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5652 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5653 fDelete? 2: 1 /* accessMode */,
5654 &dummy, &dummy64, &dummy);
5655 }
5656 catch (std::bad_alloc &)
5657 {
5658 rc = E_OUTOFMEMORY;
5659 }
5660
5661 return rc;
5662}
5663#endif // VBOX_WITH_GUEST_PROPS
5664
5665HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5666 const com::Utf8Str &aFlags)
5667{
5668#ifndef VBOX_WITH_GUEST_PROPS
5669 ReturnComNotImplemented();
5670#else // VBOX_WITH_GUEST_PROPS
5671 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5672 if (rc == E_ACCESSDENIED)
5673 /* The VM is not running or the service is not (yet) accessible */
5674 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5675 return rc;
5676#endif // VBOX_WITH_GUEST_PROPS
5677}
5678
5679HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5680{
5681 return setGuestProperty(aProperty, aValue, "");
5682}
5683
5684HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5685{
5686#ifndef VBOX_WITH_GUEST_PROPS
5687 ReturnComNotImplemented();
5688#else // VBOX_WITH_GUEST_PROPS
5689 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5690 if (rc == E_ACCESSDENIED)
5691 /* The VM is not running or the service is not (yet) accessible */
5692 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5693 return rc;
5694#endif // VBOX_WITH_GUEST_PROPS
5695}
5696
5697#ifdef VBOX_WITH_GUEST_PROPS
5698/**
5699 * Enumerate the guest properties in VBoxSVC's internal structures.
5700 */
5701HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5702 std::vector<com::Utf8Str> &aNames,
5703 std::vector<com::Utf8Str> &aValues,
5704 std::vector<LONG64> &aTimestamps,
5705 std::vector<com::Utf8Str> &aFlags)
5706{
5707 using namespace guestProp;
5708
5709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5710 Utf8Str strPatterns(aPatterns);
5711
5712 HWData::GuestPropertyMap propMap;
5713
5714 /*
5715 * Look for matching patterns and build up a list.
5716 */
5717 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5718 while (it != mHWData->mGuestProperties.end())
5719 {
5720 if ( strPatterns.isEmpty()
5721 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5722 RTSTR_MAX,
5723 it->first.c_str(),
5724 RTSTR_MAX,
5725 NULL)
5726 )
5727 propMap.insert(*it);
5728 it++;
5729 }
5730
5731 alock.release();
5732
5733 /*
5734 * And build up the arrays for returning the property information.
5735 */
5736 size_t cEntries = propMap.size();
5737
5738 aNames.resize(cEntries);
5739 aValues.resize(cEntries);
5740 aTimestamps.resize(cEntries);
5741 aFlags.resize(cEntries);
5742
5743 char szFlags[MAX_FLAGS_LEN + 1];
5744 size_t i= 0;
5745 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5746 {
5747 aNames[i] = it->first;
5748 aValues[i] = it->second.strValue;
5749 aTimestamps[i] = it->second.mTimestamp;
5750 writeFlags(it->second.mFlags, szFlags);
5751 aFlags[i] = Utf8Str(szFlags);
5752 }
5753
5754 return S_OK;
5755}
5756
5757/**
5758 * Enumerate the properties managed by a VM.
5759 * @returns E_ACCESSDENIED if the VM process is not available or not
5760 * currently handling queries and the setting should then be done in
5761 * VBoxSVC.
5762 */
5763HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5764 std::vector<com::Utf8Str> &aNames,
5765 std::vector<com::Utf8Str> &aValues,
5766 std::vector<LONG64> &aTimestamps,
5767 std::vector<com::Utf8Str> &aFlags)
5768{
5769 HRESULT rc;
5770 ComPtr<IInternalSessionControl> directControl;
5771 directControl = mData->mSession.mDirectControl;
5772
5773
5774 com::SafeArray<BSTR> bNames;
5775 com::SafeArray<BSTR> bValues;
5776 com::SafeArray<LONG64> bTimestamps;
5777 com::SafeArray<BSTR> bFlags;
5778
5779 if (!directControl)
5780 rc = E_ACCESSDENIED;
5781 else
5782 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5783 ComSafeArrayAsOutParam(bNames),
5784 ComSafeArrayAsOutParam(bValues),
5785 ComSafeArrayAsOutParam(bTimestamps),
5786 ComSafeArrayAsOutParam(bFlags));
5787 size_t i;
5788 aNames.resize(bNames.size());
5789 for (i = 0; i < bNames.size(); ++i)
5790 aNames[i] = Utf8Str(bNames[i]);
5791 aValues.resize(bValues.size());
5792 for (i = 0; i < bValues.size(); ++i)
5793 aValues[i] = Utf8Str(bValues[i]);
5794 aTimestamps.resize(bTimestamps.size());
5795 for (i = 0; i < bTimestamps.size(); ++i)
5796 aTimestamps[i] = bTimestamps[i];
5797 aFlags.resize(bFlags.size());
5798 for (i = 0; i < bFlags.size(); ++i)
5799 aFlags[i] = Utf8Str(bFlags[i]);
5800
5801 return rc;
5802}
5803#endif // VBOX_WITH_GUEST_PROPS
5804HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5805 std::vector<com::Utf8Str> &aNames,
5806 std::vector<com::Utf8Str> &aValues,
5807 std::vector<LONG64> &aTimestamps,
5808 std::vector<com::Utf8Str> &aFlags)
5809{
5810#ifndef VBOX_WITH_GUEST_PROPS
5811 ReturnComNotImplemented();
5812#else // VBOX_WITH_GUEST_PROPS
5813
5814 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5815
5816 if (rc == E_ACCESSDENIED)
5817 /* The VM is not running or the service is not (yet) accessible */
5818 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5819 return rc;
5820#endif // VBOX_WITH_GUEST_PROPS
5821}
5822
5823HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5824 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5825{
5826 MediaData::AttachmentList atts;
5827
5828 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5829 if (FAILED(rc)) return rc;
5830
5831 size_t i = 0;
5832 aMediumAttachments.resize(atts.size());
5833 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5834 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5835
5836 return S_OK;
5837}
5838
5839HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5840 LONG aControllerPort,
5841 LONG aDevice,
5842 ComPtr<IMediumAttachment> &aAttachment)
5843{
5844 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5845 aName.c_str(), aControllerPort, aDevice));
5846
5847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5848
5849 aAttachment = NULL;
5850
5851 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5852 Bstr(aName).raw(),
5853 aControllerPort,
5854 aDevice);
5855 if (pAttach.isNull())
5856 return setError(VBOX_E_OBJECT_NOT_FOUND,
5857 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5858 aDevice, aControllerPort, aName.c_str());
5859
5860 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5861
5862 return S_OK;
5863}
5864
5865
5866HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5867 StorageBus_T aConnectionType,
5868 ComPtr<IStorageController> &aController)
5869{
5870 if ( (aConnectionType <= StorageBus_Null)
5871 || (aConnectionType > StorageBus_USB))
5872 return setError(E_INVALIDARG,
5873 tr("Invalid connection type: %d"),
5874 aConnectionType);
5875
5876 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5877
5878 HRESULT rc = i_checkStateDependency(MutableStateDep);
5879 if (FAILED(rc)) return rc;
5880
5881 /* try to find one with the name first. */
5882 ComObjPtr<StorageController> ctrl;
5883
5884 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5885 if (SUCCEEDED(rc))
5886 return setError(VBOX_E_OBJECT_IN_USE,
5887 tr("Storage controller named '%s' already exists"),
5888 aName.c_str());
5889
5890 ctrl.createObject();
5891
5892 /* get a new instance number for the storage controller */
5893 ULONG ulInstance = 0;
5894 bool fBootable = true;
5895 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5896 it != mStorageControllers->end();
5897 ++it)
5898 {
5899 if ((*it)->i_getStorageBus() == aConnectionType)
5900 {
5901 ULONG ulCurInst = (*it)->i_getInstance();
5902
5903 if (ulCurInst >= ulInstance)
5904 ulInstance = ulCurInst + 1;
5905
5906 /* Only one controller of each type can be marked as bootable. */
5907 if ((*it)->i_getBootable())
5908 fBootable = false;
5909 }
5910 }
5911
5912 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5913 if (FAILED(rc)) return rc;
5914
5915 i_setModified(IsModified_Storage);
5916 mStorageControllers.backup();
5917 mStorageControllers->push_back(ctrl);
5918
5919 ctrl.queryInterfaceTo(aController.asOutParam());
5920
5921 /* inform the direct session if any */
5922 alock.release();
5923 i_onStorageControllerChange();
5924
5925 return S_OK;
5926}
5927
5928HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5929 ComPtr<IStorageController> &aStorageController)
5930{
5931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5932
5933 ComObjPtr<StorageController> ctrl;
5934
5935 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5936 if (SUCCEEDED(rc))
5937 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5938
5939 return rc;
5940}
5941
5942HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
5943 ComPtr<IStorageController> &aStorageController)
5944{
5945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5946
5947 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5948 it != mStorageControllers->end();
5949 ++it)
5950 {
5951 if ((*it)->i_getInstance() == aInstance)
5952 {
5953 (*it).queryInterfaceTo(aStorageController.asOutParam());
5954 return S_OK;
5955 }
5956 }
5957
5958 return setError(VBOX_E_OBJECT_NOT_FOUND,
5959 tr("Could not find a storage controller with instance number '%lu'"),
5960 aInstance);
5961}
5962
5963HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5964{
5965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5966
5967 HRESULT rc = i_checkStateDependency(MutableStateDep);
5968 if (FAILED(rc)) return rc;
5969
5970 ComObjPtr<StorageController> ctrl;
5971
5972 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5973 if (SUCCEEDED(rc))
5974 {
5975 /* Ensure that only one controller of each type is marked as bootable. */
5976 if (aBootable == TRUE)
5977 {
5978 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5979 it != mStorageControllers->end();
5980 ++it)
5981 {
5982 ComObjPtr<StorageController> aCtrl = (*it);
5983
5984 if ( (aCtrl->i_getName() != aName)
5985 && aCtrl->i_getBootable() == TRUE
5986 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5987 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5988 {
5989 aCtrl->i_setBootable(FALSE);
5990 break;
5991 }
5992 }
5993 }
5994
5995 if (SUCCEEDED(rc))
5996 {
5997 ctrl->i_setBootable(aBootable);
5998 i_setModified(IsModified_Storage);
5999 }
6000 }
6001
6002 if (SUCCEEDED(rc))
6003 {
6004 /* inform the direct session if any */
6005 alock.release();
6006 i_onStorageControllerChange();
6007 }
6008
6009 return rc;
6010}
6011
6012HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6013{
6014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6015
6016 HRESULT rc = i_checkStateDependency(MutableStateDep);
6017 if (FAILED(rc)) return rc;
6018
6019 ComObjPtr<StorageController> ctrl;
6020 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6021 if (FAILED(rc)) return rc;
6022
6023 {
6024 /* find all attached devices to the appropriate storage controller and detach them all */
6025 // make a temporary list because detachDevice invalidates iterators into
6026 // mMediaData->mAttachments
6027 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6028
6029 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6030 it != llAttachments2.end();
6031 ++it)
6032 {
6033 MediumAttachment *pAttachTemp = *it;
6034
6035 AutoCaller localAutoCaller(pAttachTemp);
6036 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6037
6038 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6039
6040 if (pAttachTemp->i_getControllerName() == aName)
6041 {
6042 rc = i_detachDevice(pAttachTemp, alock, NULL);
6043 if (FAILED(rc)) return rc;
6044 }
6045 }
6046 }
6047
6048 /* We can remove it now. */
6049 i_setModified(IsModified_Storage);
6050 mStorageControllers.backup();
6051
6052 ctrl->i_unshare();
6053
6054 mStorageControllers->remove(ctrl);
6055
6056 /* inform the direct session if any */
6057 alock.release();
6058 i_onStorageControllerChange();
6059
6060 return S_OK;
6061}
6062
6063HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6064 ComPtr<IUSBController> &aController)
6065{
6066 if ( (aType <= USBControllerType_Null)
6067 || (aType >= USBControllerType_Last))
6068 return setError(E_INVALIDARG,
6069 tr("Invalid USB controller type: %d"),
6070 aType);
6071
6072 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6073
6074 HRESULT rc = i_checkStateDependency(MutableStateDep);
6075 if (FAILED(rc)) return rc;
6076
6077 /* try to find one with the same type first. */
6078 ComObjPtr<USBController> ctrl;
6079
6080 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6081 if (SUCCEEDED(rc))
6082 return setError(VBOX_E_OBJECT_IN_USE,
6083 tr("USB controller named '%s' already exists"),
6084 aName.c_str());
6085
6086 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6087 ULONG maxInstances;
6088 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6089 if (FAILED(rc))
6090 return rc;
6091
6092 ULONG cInstances = i_getUSBControllerCountByType(aType);
6093 if (cInstances >= maxInstances)
6094 return setError(E_INVALIDARG,
6095 tr("Too many USB controllers of this type"));
6096
6097 ctrl.createObject();
6098
6099 rc = ctrl->init(this, aName, aType);
6100 if (FAILED(rc)) return rc;
6101
6102 i_setModified(IsModified_USB);
6103 mUSBControllers.backup();
6104 mUSBControllers->push_back(ctrl);
6105
6106 ctrl.queryInterfaceTo(aController.asOutParam());
6107
6108 /* inform the direct session if any */
6109 alock.release();
6110 i_onUSBControllerChange();
6111
6112 return S_OK;
6113}
6114
6115HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6116{
6117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6118
6119 ComObjPtr<USBController> ctrl;
6120
6121 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6122 if (SUCCEEDED(rc))
6123 ctrl.queryInterfaceTo(aController.asOutParam());
6124
6125 return rc;
6126}
6127
6128HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6129 ULONG *aControllers)
6130{
6131 if ( (aType <= USBControllerType_Null)
6132 || (aType >= USBControllerType_Last))
6133 return setError(E_INVALIDARG,
6134 tr("Invalid USB controller type: %d"),
6135 aType);
6136
6137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6138
6139 ComObjPtr<USBController> ctrl;
6140
6141 *aControllers = i_getUSBControllerCountByType(aType);
6142
6143 return S_OK;
6144}
6145
6146HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6147{
6148
6149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6150
6151 HRESULT rc = i_checkStateDependency(MutableStateDep);
6152 if (FAILED(rc)) return rc;
6153
6154 ComObjPtr<USBController> ctrl;
6155 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6156 if (FAILED(rc)) return rc;
6157
6158 i_setModified(IsModified_USB);
6159 mUSBControllers.backup();
6160
6161 ctrl->i_unshare();
6162
6163 mUSBControllers->remove(ctrl);
6164
6165 /* inform the direct session if any */
6166 alock.release();
6167 i_onUSBControllerChange();
6168
6169 return S_OK;
6170}
6171
6172HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6173 ULONG *aOriginX,
6174 ULONG *aOriginY,
6175 ULONG *aWidth,
6176 ULONG *aHeight,
6177 BOOL *aEnabled)
6178{
6179 uint32_t u32OriginX= 0;
6180 uint32_t u32OriginY= 0;
6181 uint32_t u32Width = 0;
6182 uint32_t u32Height = 0;
6183 uint16_t u16Flags = 0;
6184
6185 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6186 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6187 if (RT_FAILURE(vrc))
6188 {
6189#ifdef RT_OS_WINDOWS
6190 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6191 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6192 * So just assign fEnable to TRUE again.
6193 * The right fix would be to change GUI API wrappers to make sure that parameters
6194 * are changed only if API succeeds.
6195 */
6196 *aEnabled = TRUE;
6197#endif
6198 return setError(VBOX_E_IPRT_ERROR,
6199 tr("Saved guest size is not available (%Rrc)"),
6200 vrc);
6201 }
6202
6203 *aOriginX = u32OriginX;
6204 *aOriginY = u32OriginY;
6205 *aWidth = u32Width;
6206 *aHeight = u32Height;
6207 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6208
6209 return S_OK;
6210}
6211
6212HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6213{
6214 if (aScreenId != 0)
6215 return E_NOTIMPL;
6216
6217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6218
6219 uint8_t *pu8Data = NULL;
6220 uint32_t cbData = 0;
6221 uint32_t u32Width = 0;
6222 uint32_t u32Height = 0;
6223
6224 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6225
6226 if (RT_FAILURE(vrc))
6227 return setError(VBOX_E_IPRT_ERROR,
6228 tr("Saved screenshot data is not available (%Rrc)"),
6229 vrc);
6230
6231 *aSize = cbData;
6232 *aWidth = u32Width;
6233 *aHeight = u32Height;
6234
6235 freeSavedDisplayScreenshot(pu8Data);
6236
6237 return S_OK;
6238}
6239
6240
6241HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6242{
6243 if (aScreenId != 0)
6244 return E_NOTIMPL;
6245
6246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6247
6248 uint8_t *pu8Data = NULL;
6249 uint32_t cbData = 0;
6250 uint32_t u32Width = 0;
6251 uint32_t u32Height = 0;
6252
6253 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6254
6255 if (RT_FAILURE(vrc))
6256 return setError(VBOX_E_IPRT_ERROR,
6257 tr("Saved screenshot data is not available (%Rrc)"),
6258 vrc);
6259
6260 *aWidth = u32Width;
6261 *aHeight = u32Height;
6262
6263 com::SafeArray<BYTE> bitmap(cbData);
6264 /* Convert pixels to format expected by the API caller. */
6265 if (aBGR)
6266 {
6267 /* [0] B, [1] G, [2] R, [3] A. */
6268 for (unsigned i = 0; i < cbData; i += 4)
6269 {
6270 bitmap[i] = pu8Data[i];
6271 bitmap[i + 1] = pu8Data[i + 1];
6272 bitmap[i + 2] = pu8Data[i + 2];
6273 bitmap[i + 3] = 0xff;
6274 }
6275 }
6276 else
6277 {
6278 /* [0] R, [1] G, [2] B, [3] A. */
6279 for (unsigned i = 0; i < cbData; i += 4)
6280 {
6281 bitmap[i] = pu8Data[i + 2];
6282 bitmap[i + 1] = pu8Data[i + 1];
6283 bitmap[i + 2] = pu8Data[i];
6284 bitmap[i + 3] = 0xff;
6285 }
6286 }
6287 aData.resize(bitmap.size());
6288 for (size_t i = 0; i < bitmap.size(); ++i)
6289 aData[i] = bitmap[i];
6290
6291 freeSavedDisplayScreenshot(pu8Data);
6292
6293 return S_OK;
6294}
6295
6296HRESULT Machine::readSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6297{
6298 if (aScreenId != 0)
6299 return E_NOTIMPL;
6300
6301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6302
6303 uint8_t *pu8Data = NULL;
6304 uint32_t cbData = 0;
6305 uint32_t u32Width = 0;
6306 uint32_t u32Height = 0;
6307
6308 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6309
6310 if (RT_FAILURE(vrc))
6311 return setError(VBOX_E_IPRT_ERROR,
6312 tr("Saved screenshot data is not available (%Rrc)"),
6313 vrc);
6314
6315 *aWidth = u32Width;
6316 *aHeight = u32Height;
6317
6318 HRESULT rc = S_OK;
6319 uint8_t *pu8PNG = NULL;
6320 uint32_t cbPNG = 0;
6321 uint32_t cxPNG = 0;
6322 uint32_t cyPNG = 0;
6323
6324 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6325
6326 if (RT_SUCCESS(vrc))
6327 {
6328 com::SafeArray<BYTE> screenData(cbPNG);
6329 screenData.initFrom(pu8PNG, cbPNG);
6330 if (pu8PNG)
6331 RTMemFree(pu8PNG);
6332 aData.resize(screenData.size());
6333 for (size_t i = 0; i < screenData.size(); ++i)
6334 aData[i] = screenData[i];
6335 }
6336 else
6337 {
6338 if (pu8PNG)
6339 RTMemFree(pu8PNG);
6340 return setError(VBOX_E_IPRT_ERROR,
6341 tr("Could not convert screenshot to PNG (%Rrc)"),
6342 vrc);
6343 }
6344
6345 freeSavedDisplayScreenshot(pu8Data);
6346
6347 return rc;
6348}
6349
6350HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6351{
6352 if (aScreenId != 0)
6353 return E_NOTIMPL;
6354
6355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6356
6357 uint8_t *pu8Data = NULL;
6358 uint32_t cbData = 0;
6359 uint32_t u32Width = 0;
6360 uint32_t u32Height = 0;
6361
6362 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6363
6364 if (RT_FAILURE(vrc))
6365 return setError(VBOX_E_IPRT_ERROR,
6366 tr("Saved screenshot data is not available (%Rrc)"),
6367 vrc);
6368
6369 *aSize = cbData;
6370 *aWidth = u32Width;
6371 *aHeight = u32Height;
6372
6373 freeSavedDisplayScreenshot(pu8Data);
6374
6375 return S_OK;
6376}
6377
6378HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6379{
6380 if (aScreenId != 0)
6381 return E_NOTIMPL;
6382
6383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6384
6385 uint8_t *pu8Data = NULL;
6386 uint32_t cbData = 0;
6387 uint32_t u32Width = 0;
6388 uint32_t u32Height = 0;
6389
6390 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6391
6392 if (RT_FAILURE(vrc))
6393 return setError(VBOX_E_IPRT_ERROR,
6394 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6395 vrc);
6396
6397 *aWidth = u32Width;
6398 *aHeight = u32Height;
6399
6400 com::SafeArray<BYTE> png(cbData);
6401 png.initFrom(pu8Data, cbData);
6402 aData.resize(png.size());
6403 for (size_t i = 0; i < png.size(); ++i)
6404 aData[i] = png[i];
6405
6406 freeSavedDisplayScreenshot(pu8Data);
6407
6408 return S_OK;
6409}
6410
6411HRESULT Machine::hotPlugCPU(ULONG aCpu)
6412{
6413 HRESULT rc = S_OK;
6414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6415
6416 if (!mHWData->mCPUHotPlugEnabled)
6417 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6418
6419 if (aCpu >= mHWData->mCPUCount)
6420 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6421
6422 if (mHWData->mCPUAttached[aCpu])
6423 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6424
6425 alock.release();
6426 rc = i_onCPUChange(aCpu, false);
6427 alock.acquire();
6428 if (FAILED(rc)) return rc;
6429
6430 i_setModified(IsModified_MachineData);
6431 mHWData.backup();
6432 mHWData->mCPUAttached[aCpu] = true;
6433
6434 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6435 if (Global::IsOnline(mData->mMachineState))
6436 i_saveSettings(NULL);
6437
6438 return S_OK;
6439}
6440
6441HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6442{
6443 HRESULT rc = S_OK;
6444
6445 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6446
6447 if (!mHWData->mCPUHotPlugEnabled)
6448 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6449
6450 if (aCpu >= SchemaDefs::MaxCPUCount)
6451 return setError(E_INVALIDARG,
6452 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6453 SchemaDefs::MaxCPUCount);
6454
6455 if (!mHWData->mCPUAttached[aCpu])
6456 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6457
6458 /* CPU 0 can't be detached */
6459 if (aCpu == 0)
6460 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6461
6462 alock.release();
6463 rc = i_onCPUChange(aCpu, true);
6464 alock.acquire();
6465 if (FAILED(rc)) return rc;
6466
6467 i_setModified(IsModified_MachineData);
6468 mHWData.backup();
6469 mHWData->mCPUAttached[aCpu] = false;
6470
6471 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6472 if (Global::IsOnline(mData->mMachineState))
6473 i_saveSettings(NULL);
6474
6475 return S_OK;
6476}
6477
6478HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6479{
6480 *aAttached = false;
6481
6482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6483
6484 /* If hotplug is enabled the CPU is always enabled. */
6485 if (!mHWData->mCPUHotPlugEnabled)
6486 {
6487 if (aCpu < mHWData->mCPUCount)
6488 *aAttached = true;
6489 }
6490 else
6491 {
6492 if (aCpu < SchemaDefs::MaxCPUCount)
6493 *aAttached = mHWData->mCPUAttached[aCpu];
6494 }
6495
6496 return S_OK;
6497}
6498
6499HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6500{
6501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6502
6503 Utf8Str log = i_queryLogFilename(aIdx);
6504 if (!RTFileExists(log.c_str()))
6505 log.setNull();
6506 aFilename = log;
6507
6508 return S_OK;
6509}
6510
6511HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6512{
6513 if (aSize < 0)
6514 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6515
6516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6517
6518 HRESULT rc = S_OK;
6519 Utf8Str log = i_queryLogFilename(aIdx);
6520
6521 /* do not unnecessarily hold the lock while doing something which does
6522 * not need the lock and potentially takes a long time. */
6523 alock.release();
6524
6525 /* Limit the chunk size to 32K for now, as that gives better performance
6526 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6527 * One byte expands to approx. 25 bytes of breathtaking XML. */
6528 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6529 com::SafeArray<BYTE> logData(cbData);
6530
6531 RTFILE LogFile;
6532 int vrc = RTFileOpen(&LogFile, log.c_str(),
6533 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6534 if (RT_SUCCESS(vrc))
6535 {
6536 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6537 if (RT_SUCCESS(vrc))
6538 logData.resize(cbData);
6539 else
6540 rc = setError(VBOX_E_IPRT_ERROR,
6541 tr("Could not read log file '%s' (%Rrc)"),
6542 log.c_str(), vrc);
6543 RTFileClose(LogFile);
6544 }
6545 else
6546 rc = setError(VBOX_E_IPRT_ERROR,
6547 tr("Could not open log file '%s' (%Rrc)"),
6548 log.c_str(), vrc);
6549
6550 if (FAILED(rc))
6551 logData.resize(0);
6552
6553 aData.resize(logData.size());
6554 for (size_t i = 0; i < logData.size(); ++i)
6555 aData[i] = logData[i];
6556
6557 return rc;
6558}
6559
6560
6561/**
6562 * Currently this method doesn't attach device to the running VM,
6563 * just makes sure it's plugged on next VM start.
6564 */
6565HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6566{
6567 // lock scope
6568 {
6569 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6570
6571 HRESULT rc = i_checkStateDependency(MutableStateDep);
6572 if (FAILED(rc)) return rc;
6573
6574 ChipsetType_T aChipset = ChipsetType_PIIX3;
6575 COMGETTER(ChipsetType)(&aChipset);
6576
6577 if (aChipset != ChipsetType_ICH9)
6578 {
6579 return setError(E_INVALIDARG,
6580 tr("Host PCI attachment only supported with ICH9 chipset"));
6581 }
6582
6583 // check if device with this host PCI address already attached
6584 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6585 it != mHWData->mPCIDeviceAssignments.end();
6586 ++it)
6587 {
6588 LONG iHostAddress = -1;
6589 ComPtr<PCIDeviceAttachment> pAttach;
6590 pAttach = *it;
6591 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6592 if (iHostAddress == aHostAddress)
6593 return setError(E_INVALIDARG,
6594 tr("Device with host PCI address already attached to this VM"));
6595 }
6596
6597 ComObjPtr<PCIDeviceAttachment> pda;
6598 char name[32];
6599
6600 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6601 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6602 Bstr bname(name);
6603 pda.createObject();
6604 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6605 i_setModified(IsModified_MachineData);
6606 mHWData.backup();
6607 mHWData->mPCIDeviceAssignments.push_back(pda);
6608 }
6609
6610 return S_OK;
6611}
6612
6613/**
6614 * Currently this method doesn't detach device from the running VM,
6615 * just makes sure it's not plugged on next VM start.
6616 */
6617HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6618{
6619 ComObjPtr<PCIDeviceAttachment> pAttach;
6620 bool fRemoved = false;
6621 HRESULT rc;
6622
6623 // lock scope
6624 {
6625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6626
6627 rc = i_checkStateDependency(MutableStateDep);
6628 if (FAILED(rc)) return rc;
6629
6630 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6631 it != mHWData->mPCIDeviceAssignments.end();
6632 ++it)
6633 {
6634 LONG iHostAddress = -1;
6635 pAttach = *it;
6636 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6637 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6638 {
6639 i_setModified(IsModified_MachineData);
6640 mHWData.backup();
6641 mHWData->mPCIDeviceAssignments.remove(pAttach);
6642 fRemoved = true;
6643 break;
6644 }
6645 }
6646 }
6647
6648
6649 /* Fire event outside of the lock */
6650 if (fRemoved)
6651 {
6652 Assert(!pAttach.isNull());
6653 ComPtr<IEventSource> es;
6654 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6655 Assert(SUCCEEDED(rc));
6656 Bstr mid;
6657 rc = this->COMGETTER(Id)(mid.asOutParam());
6658 Assert(SUCCEEDED(rc));
6659 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6660 }
6661
6662 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6663 tr("No host PCI device %08x attached"),
6664 aHostAddress
6665 );
6666}
6667
6668HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6669{
6670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6671
6672 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6673
6674 size_t i = 0;
6675 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6676 it != mHWData->mPCIDeviceAssignments.end();
6677 ++i, ++it)
6678 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6679
6680 return S_OK;
6681}
6682
6683HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6684{
6685 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6686
6687 return S_OK;
6688}
6689
6690HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6691{
6692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6693
6694 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6695
6696 return S_OK;
6697}
6698
6699HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6700{
6701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6702 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6703 if (SUCCEEDED(hrc))
6704 {
6705 hrc = mHWData.backupEx();
6706 if (SUCCEEDED(hrc))
6707 {
6708 i_setModified(IsModified_MachineData);
6709 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6710 }
6711 }
6712 return hrc;
6713}
6714
6715HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6716{
6717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6718 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6719 return S_OK;
6720}
6721
6722HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6723{
6724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6725 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6726 if (SUCCEEDED(hrc))
6727 {
6728 hrc = mHWData.backupEx();
6729 if (SUCCEEDED(hrc))
6730 {
6731 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6732 if (SUCCEEDED(hrc))
6733 i_setModified(IsModified_MachineData);
6734 }
6735 }
6736 return hrc;
6737}
6738
6739HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6740{
6741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6742
6743 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6744
6745 return S_OK;
6746}
6747
6748HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6749{
6750 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6751 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6752 if (SUCCEEDED(hrc))
6753 {
6754 hrc = mHWData.backupEx();
6755 if (SUCCEEDED(hrc))
6756 {
6757 i_setModified(IsModified_MachineData);
6758 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6759 }
6760 }
6761 return hrc;
6762}
6763
6764HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6765{
6766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6767
6768 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6769
6770 return S_OK;
6771}
6772
6773HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6774{
6775 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6776
6777 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6778 if ( SUCCEEDED(hrc)
6779 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6780 {
6781 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6782 int vrc;
6783
6784 if (aAutostartEnabled)
6785 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6786 else
6787 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6788
6789 if (RT_SUCCESS(vrc))
6790 {
6791 hrc = mHWData.backupEx();
6792 if (SUCCEEDED(hrc))
6793 {
6794 i_setModified(IsModified_MachineData);
6795 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6796 }
6797 }
6798 else if (vrc == VERR_NOT_SUPPORTED)
6799 hrc = setError(VBOX_E_NOT_SUPPORTED,
6800 tr("The VM autostart feature is not supported on this platform"));
6801 else if (vrc == VERR_PATH_NOT_FOUND)
6802 hrc = setError(E_FAIL,
6803 tr("The path to the autostart database is not set"));
6804 else
6805 hrc = setError(E_UNEXPECTED,
6806 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6807 aAutostartEnabled ? "Adding" : "Removing",
6808 mUserData->s.strName.c_str(), vrc);
6809 }
6810 return hrc;
6811}
6812
6813HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6814{
6815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6816
6817 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6818
6819 return S_OK;
6820}
6821
6822HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6823{
6824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6825 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6826 if (SUCCEEDED(hrc))
6827 {
6828 hrc = mHWData.backupEx();
6829 if (SUCCEEDED(hrc))
6830 {
6831 i_setModified(IsModified_MachineData);
6832 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6833 }
6834 }
6835 return hrc;
6836}
6837
6838HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6839{
6840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6841
6842 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6843
6844 return S_OK;
6845}
6846
6847HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6848{
6849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6850 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6851 if ( SUCCEEDED(hrc)
6852 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6853 {
6854 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6855 int vrc;
6856
6857 if (aAutostopType != AutostopType_Disabled)
6858 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6859 else
6860 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6861
6862 if (RT_SUCCESS(vrc))
6863 {
6864 hrc = mHWData.backupEx();
6865 if (SUCCEEDED(hrc))
6866 {
6867 i_setModified(IsModified_MachineData);
6868 mHWData->mAutostart.enmAutostopType = aAutostopType;
6869 }
6870 }
6871 else if (vrc == VERR_NOT_SUPPORTED)
6872 hrc = setError(VBOX_E_NOT_SUPPORTED,
6873 tr("The VM autostop feature is not supported on this platform"));
6874 else if (vrc == VERR_PATH_NOT_FOUND)
6875 hrc = setError(E_FAIL,
6876 tr("The path to the autostart database is not set"));
6877 else
6878 hrc = setError(E_UNEXPECTED,
6879 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6880 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6881 mUserData->s.strName.c_str(), vrc);
6882 }
6883 return hrc;
6884}
6885
6886HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6887{
6888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6889
6890 aDefaultFrontend = mHWData->mDefaultFrontend;
6891
6892 return S_OK;
6893}
6894
6895HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6896{
6897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6898 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6899 if (SUCCEEDED(hrc))
6900 {
6901 hrc = mHWData.backupEx();
6902 if (SUCCEEDED(hrc))
6903 {
6904 i_setModified(IsModified_MachineData);
6905 mHWData->mDefaultFrontend = aDefaultFrontend;
6906 }
6907 }
6908 return hrc;
6909}
6910
6911HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6912{
6913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6914 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
6915 aIcon.resize(mUserData->mIcon.size());
6916 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
6917 aIcon.resize(icon.size());
6918 for (size_t i = 0; i < icon.size(); ++i)
6919 aIcon[i] = icon[i];
6920 return S_OK;
6921}
6922
6923HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6924{
6925 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6926 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6927 if (SUCCEEDED(hrc))
6928 {
6929 i_setModified(IsModified_MachineData);
6930 mUserData.backup();
6931 com::SafeArray<BYTE> icon(aIcon);
6932 mUserData->mIcon.resize(aIcon.size());
6933 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
6934 }
6935 return hrc;
6936}
6937
6938HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6939{
6940
6941#ifdef VBOX_WITH_USB
6942 *aUSBProxyAvailable = true;
6943#else
6944 *aUSBProxyAvailable = false;
6945#endif
6946 return S_OK;
6947}
6948
6949HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6950 ComPtr<IProgress> &aProgress)
6951{
6952 ComObjPtr<Progress> pP;
6953 Progress *ppP = pP;
6954 IProgress *iP = static_cast<IProgress *>(ppP);
6955 IProgress **pProgress = &iP;
6956
6957 IMachine *pTarget = aTarget;
6958
6959 /* Convert the options. */
6960 RTCList<CloneOptions_T> optList;
6961 if (aOptions.size())
6962 for (size_t i = 0; i < aOptions.size(); ++i)
6963 optList.append(aOptions[i]);
6964
6965 if (optList.contains(CloneOptions_Link))
6966 {
6967 if (!i_isSnapshotMachine())
6968 return setError(E_INVALIDARG,
6969 tr("Linked clone can only be created from a snapshot"));
6970 if (aMode != CloneMode_MachineState)
6971 return setError(E_INVALIDARG,
6972 tr("Linked clone can only be created for a single machine state"));
6973 }
6974 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6975
6976 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6977
6978 HRESULT rc = pWorker->start(pProgress);
6979
6980 pP = static_cast<Progress *>(*pProgress);
6981 pP.queryInterfaceTo(aProgress.asOutParam());
6982
6983 return rc;
6984
6985}
6986
6987// public methods for internal purposes
6988/////////////////////////////////////////////////////////////////////////////
6989
6990/**
6991 * Adds the given IsModified_* flag to the dirty flags of the machine.
6992 * This must be called either during i_loadSettings or under the machine write lock.
6993 * @param fl
6994 */
6995void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6996{
6997 mData->flModifications |= fl;
6998 if (fAllowStateModification && i_isStateModificationAllowed())
6999 mData->mCurrentStateModified = true;
7000}
7001
7002/**
7003 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7004 * care of the write locking.
7005 *
7006 * @param fModifications The flag to add.
7007 */
7008void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7009{
7010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7011 i_setModified(fModification, fAllowStateModification);
7012}
7013
7014/**
7015 * Saves the registry entry of this machine to the given configuration node.
7016 *
7017 * @param aEntryNode Node to save the registry entry to.
7018 *
7019 * @note locks this object for reading.
7020 */
7021HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7022{
7023 AutoLimitedCaller autoCaller(this);
7024 AssertComRCReturnRC(autoCaller.rc());
7025
7026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7027
7028 data.uuid = mData->mUuid;
7029 data.strSettingsFile = mData->m_strConfigFile;
7030
7031 return S_OK;
7032}
7033
7034/**
7035 * Calculates the absolute path of the given path taking the directory of the
7036 * machine settings file as the current directory.
7037 *
7038 * @param aPath Path to calculate the absolute path for.
7039 * @param aResult Where to put the result (used only on success, can be the
7040 * same Utf8Str instance as passed in @a aPath).
7041 * @return IPRT result.
7042 *
7043 * @note Locks this object for reading.
7044 */
7045int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7046{
7047 AutoCaller autoCaller(this);
7048 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7049
7050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7051
7052 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7053
7054 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7055
7056 strSettingsDir.stripFilename();
7057 char folder[RTPATH_MAX];
7058 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7059 if (RT_SUCCESS(vrc))
7060 aResult = folder;
7061
7062 return vrc;
7063}
7064
7065/**
7066 * Copies strSource to strTarget, making it relative to the machine folder
7067 * if it is a subdirectory thereof, or simply copying it otherwise.
7068 *
7069 * @param strSource Path to evaluate and copy.
7070 * @param strTarget Buffer to receive target path.
7071 *
7072 * @note Locks this object for reading.
7073 */
7074void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7075 Utf8Str &strTarget)
7076{
7077 AutoCaller autoCaller(this);
7078 AssertComRCReturn(autoCaller.rc(), (void)0);
7079
7080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7081
7082 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7083 // use strTarget as a temporary buffer to hold the machine settings dir
7084 strTarget = mData->m_strConfigFileFull;
7085 strTarget.stripFilename();
7086 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7087 {
7088 // is relative: then append what's left
7089 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7090 // for empty paths (only possible for subdirs) use "." to avoid
7091 // triggering default settings for not present config attributes.
7092 if (strTarget.isEmpty())
7093 strTarget = ".";
7094 }
7095 else
7096 // is not relative: then overwrite
7097 strTarget = strSource;
7098}
7099
7100/**
7101 * Returns the full path to the machine's log folder in the
7102 * \a aLogFolder argument.
7103 */
7104void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7105{
7106 AutoCaller autoCaller(this);
7107 AssertComRCReturnVoid(autoCaller.rc());
7108
7109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7110
7111 char szTmp[RTPATH_MAX];
7112 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7113 if (RT_SUCCESS(vrc))
7114 {
7115 if (szTmp[0] && !mUserData.isNull())
7116 {
7117 char szTmp2[RTPATH_MAX];
7118 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7119 if (RT_SUCCESS(vrc))
7120 aLogFolder = BstrFmt("%s%c%s",
7121 szTmp2,
7122 RTPATH_DELIMITER,
7123 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7124 }
7125 else
7126 vrc = VERR_PATH_IS_RELATIVE;
7127 }
7128
7129 if (RT_FAILURE(vrc))
7130 {
7131 // fallback if VBOX_USER_LOGHOME is not set or invalid
7132 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7133 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7134 aLogFolder.append(RTPATH_DELIMITER);
7135 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7136 }
7137}
7138
7139/**
7140 * Returns the full path to the machine's log file for an given index.
7141 */
7142Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail. See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7143{
7144 Utf8Str logFolder;
7145 getLogFolder(logFolder);
7146 Assert(logFolder.length());
7147 Utf8Str log;
7148 if (idx == 0)
7149 log = Utf8StrFmt("%s%cVBox.log",
7150 logFolder.c_str(), RTPATH_DELIMITER);
7151 else
7152 log = Utf8StrFmt("%s%cVBox.log.%d",
7153 logFolder.c_str(), RTPATH_DELIMITER, idx);
7154 return log;
7155}
7156
7157/**
7158 * Returns the full path to the machine's (hardened) startup log file.
7159 */
7160Utf8Str Machine::i_getStartupLogFilename(void)
7161{
7162 Utf8Str strFilename;
7163 getLogFolder(strFilename);
7164 Assert(strFilename.length());
7165 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7166 return strFilename;
7167}
7168
7169
7170/**
7171 * Composes a unique saved state filename based on the current system time. The filename is
7172 * granular to the second so this will work so long as no more than one snapshot is taken on
7173 * a machine per second.
7174 *
7175 * Before version 4.1, we used this formula for saved state files:
7176 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7177 * which no longer works because saved state files can now be shared between the saved state of the
7178 * "saved" machine and an online snapshot, and the following would cause problems:
7179 * 1) save machine
7180 * 2) create online snapshot from that machine state --> reusing saved state file
7181 * 3) save machine again --> filename would be reused, breaking the online snapshot
7182 *
7183 * So instead we now use a timestamp.
7184 *
7185 * @param str
7186 */
7187
7188void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7189{
7190 AutoCaller autoCaller(this);
7191 AssertComRCReturnVoid(autoCaller.rc());
7192
7193 {
7194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7195 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7196 }
7197
7198 RTTIMESPEC ts;
7199 RTTimeNow(&ts);
7200 RTTIME time;
7201 RTTimeExplode(&time, &ts);
7202
7203 strStateFilePath += RTPATH_DELIMITER;
7204 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7205 time.i32Year, time.u8Month, time.u8MonthDay,
7206 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7207}
7208
7209/**
7210 * Returns the full path to the default video capture file.
7211 */
7212void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7213{
7214 AutoCaller autoCaller(this);
7215 AssertComRCReturnVoid(autoCaller.rc());
7216
7217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7218
7219 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7220 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7221 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7222}
7223
7224/**
7225 * Returns whether at least one USB controller is present for the VM.
7226 */
7227bool Machine::i_isUSBControllerPresent()
7228{
7229 AutoCaller autoCaller(this);
7230 AssertComRCReturn(autoCaller.rc(), false);
7231
7232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7233
7234 return (mUSBControllers->size() > 0);
7235}
7236
7237/**
7238 * @note Locks this object for writing, calls the client process
7239 * (inside the lock).
7240 */
7241HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7242 const Utf8Str &strFrontend,
7243 const Utf8Str &strEnvironment,
7244 ProgressProxy *aProgress)
7245{
7246 LogFlowThisFuncEnter();
7247
7248 AssertReturn(aControl, E_FAIL);
7249 AssertReturn(aProgress, E_FAIL);
7250 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7251
7252 AutoCaller autoCaller(this);
7253 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7254
7255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7256
7257 if (!mData->mRegistered)
7258 return setError(E_UNEXPECTED,
7259 tr("The machine '%s' is not registered"),
7260 mUserData->s.strName.c_str());
7261
7262 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7263
7264 if ( mData->mSession.mState == SessionState_Locked
7265 || mData->mSession.mState == SessionState_Spawning
7266 || mData->mSession.mState == SessionState_Unlocking)
7267 return setError(VBOX_E_INVALID_OBJECT_STATE,
7268 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7269 mUserData->s.strName.c_str());
7270
7271 /* may not be busy */
7272 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7273
7274 /* get the path to the executable */
7275 char szPath[RTPATH_MAX];
7276 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7277 size_t cchBufLeft = strlen(szPath);
7278 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7279 szPath[cchBufLeft] = 0;
7280 char *pszNamePart = szPath + cchBufLeft;
7281 cchBufLeft = sizeof(szPath) - cchBufLeft;
7282
7283 int vrc = VINF_SUCCESS;
7284 RTPROCESS pid = NIL_RTPROCESS;
7285
7286 RTENV env = RTENV_DEFAULT;
7287
7288 if (!strEnvironment.isEmpty())
7289 {
7290 char *newEnvStr = NULL;
7291
7292 do
7293 {
7294 /* clone the current environment */
7295 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7296 AssertRCBreakStmt(vrc2, vrc = vrc2);
7297
7298 newEnvStr = RTStrDup(strEnvironment.c_str());
7299 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7300
7301 /* put new variables to the environment
7302 * (ignore empty variable names here since RTEnv API
7303 * intentionally doesn't do that) */
7304 char *var = newEnvStr;
7305 for (char *p = newEnvStr; *p; ++p)
7306 {
7307 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7308 {
7309 *p = '\0';
7310 if (*var)
7311 {
7312 char *val = strchr(var, '=');
7313 if (val)
7314 {
7315 *val++ = '\0';
7316 vrc2 = RTEnvSetEx(env, var, val);
7317 }
7318 else
7319 vrc2 = RTEnvUnsetEx(env, var);
7320 if (RT_FAILURE(vrc2))
7321 break;
7322 }
7323 var = p + 1;
7324 }
7325 }
7326 if (RT_SUCCESS(vrc2) && *var)
7327 vrc2 = RTEnvPutEx(env, var);
7328
7329 AssertRCBreakStmt(vrc2, vrc = vrc2);
7330 }
7331 while (0);
7332
7333 if (newEnvStr != NULL)
7334 RTStrFree(newEnvStr);
7335 }
7336
7337 /* Hardened startup logging */
7338#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7339 Utf8Str strSupStartLogArg("--sup-startup-log=");
7340 {
7341 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7342 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7343 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7344 {
7345 Utf8Str strStartupLogDir = strStartupLogFile;
7346 strStartupLogDir.stripFilename();
7347 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a file without stripping the file. */
7348 }
7349 strSupStartLogArg.append(strStartupLogFile);
7350 }
7351 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7352#else
7353 const char *pszSupStartupLogArg = NULL;
7354#endif
7355
7356
7357#ifdef VBOX_WITH_QTGUI
7358 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7359 {
7360# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7361 /* Modify the base path so that we don't need to use ".." below. */
7362 RTPathStripTrailingSlash(szPath);
7363 RTPathStripFilename(szPath);
7364 cchBufLeft = strlen(szPath);
7365 pszNamePart = szPath + cchBufLeft;
7366 cchBufLeft = sizeof(szPath) - cchBufLeft;
7367
7368# define OSX_APP_NAME "VirtualBoxVM"
7369# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7370
7371 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7372 if ( strAppOverride.contains(".")
7373 || strAppOverride.contains("/")
7374 || strAppOverride.contains("\\")
7375 || strAppOverride.contains(":"))
7376 strAppOverride.setNull();
7377 Utf8Str strAppPath;
7378 if (!strAppOverride.isEmpty())
7379 {
7380 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7381 Utf8Str strFullPath(szPath);
7382 strFullPath.append(strAppPath);
7383 /* there is a race, but people using this deserve the failure */
7384 if (!RTFileExists(strFullPath.c_str()))
7385 strAppOverride.setNull();
7386 }
7387 if (strAppOverride.isEmpty())
7388 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7389 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7390 strcpy(pszNamePart, strAppPath.c_str());
7391# else
7392 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7393 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7394 strcpy(pszNamePart, s_szVirtualBox_exe);
7395# endif
7396
7397 Utf8Str idStr = mData->mUuid.toString();
7398 const char *apszArgs[] =
7399 {
7400 szPath,
7401 "--comment", mUserData->s.strName.c_str(),
7402 "--startvm", idStr.c_str(),
7403 "--no-startvm-errormsgbox",
7404 pszSupStartupLogArg,
7405 NULL
7406 };
7407 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7408 }
7409#else /* !VBOX_WITH_QTGUI */
7410 if (0)
7411 ;
7412#endif /* VBOX_WITH_QTGUI */
7413
7414 else
7415
7416#ifdef VBOX_WITH_VBOXSDL
7417 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7418 {
7419 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7420 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7421 strcpy(pszNamePart, s_szVBoxSDL_exe);
7422
7423 Utf8Str idStr = mData->mUuid.toString();
7424 const char *apszArgs[] =
7425 {
7426 szPath,
7427 "--comment", mUserData->s.strName.c_str(),
7428 "--startvm", idStr.c_str(),
7429 pszSupStartupLogArg,
7430 NULL
7431 };
7432 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7433 }
7434#else /* !VBOX_WITH_VBOXSDL */
7435 if (0)
7436 ;
7437#endif /* !VBOX_WITH_VBOXSDL */
7438
7439 else
7440
7441#ifdef VBOX_WITH_HEADLESS
7442 if ( strFrontend == "headless"
7443 || strFrontend == "capture"
7444 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7445 )
7446 {
7447 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7448 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7449 * and a VM works even if the server has not been installed.
7450 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7451 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7452 * differently in 4.0 and 3.x.
7453 */
7454 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7455 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7456 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7457
7458 Utf8Str idStr = mData->mUuid.toString();
7459 const char *apszArgs[] =
7460 {
7461 szPath,
7462 "--comment", mUserData->s.strName.c_str(),
7463 "--startvm", idStr.c_str(),
7464 "--vrde", "config",
7465 0, /* For "--capture". */
7466 0, /* For "--sup-startup-log". */
7467 0
7468 };
7469 unsigned iArg = 7;
7470 if (strFrontend == "capture")
7471 apszArgs[iArg++] = "--capture";
7472 apszArgs[iArg++] = pszSupStartupLogArg;
7473
7474# ifdef RT_OS_WINDOWS
7475 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7476# else
7477 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7478# endif
7479 }
7480#else /* !VBOX_WITH_HEADLESS */
7481 if (0)
7482 ;
7483#endif /* !VBOX_WITH_HEADLESS */
7484 else
7485 {
7486 RTEnvDestroy(env);
7487 return setError(E_INVALIDARG,
7488 tr("Invalid frontend name: '%s'"),
7489 strFrontend.c_str());
7490 }
7491
7492 RTEnvDestroy(env);
7493
7494 if (RT_FAILURE(vrc))
7495 return setError(VBOX_E_IPRT_ERROR,
7496 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7497 mUserData->s.strName.c_str(), vrc);
7498
7499 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7500
7501 /*
7502 * Note that we don't release the lock here before calling the client,
7503 * because it doesn't need to call us back if called with a NULL argument.
7504 * Releasing the lock here is dangerous because we didn't prepare the
7505 * launch data yet, but the client we've just started may happen to be
7506 * too fast and call LockMachine() that will fail (because of PID, etc.),
7507 * so that the Machine will never get out of the Spawning session state.
7508 */
7509
7510 /* inform the session that it will be a remote one */
7511 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7512#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7513 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7514#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7515 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7516#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7517 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7518
7519 if (FAILED(rc))
7520 {
7521 /* restore the session state */
7522 mData->mSession.mState = SessionState_Unlocked;
7523 alock.release();
7524 mParent->i_addProcessToReap(pid);
7525 /* The failure may occur w/o any error info (from RPC), so provide one */
7526 return setError(VBOX_E_VM_ERROR,
7527 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7528 }
7529
7530 /* attach launch data to the machine */
7531 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7532 mData->mSession.mRemoteControls.push_back(aControl);
7533 mData->mSession.mProgress = aProgress;
7534 mData->mSession.mPID = pid;
7535 mData->mSession.mState = SessionState_Spawning;
7536 mData->mSession.mType = strFrontend;
7537
7538 alock.release();
7539 mParent->i_addProcessToReap(pid);
7540
7541 LogFlowThisFuncLeave();
7542 return S_OK;
7543}
7544
7545/**
7546 * Returns @c true if the given session machine instance has an open direct
7547 * session (and optionally also for direct sessions which are closing) and
7548 * returns the session control machine instance if so.
7549 *
7550 * Note that when the method returns @c false, the arguments remain unchanged.
7551 *
7552 * @param aMachine Session machine object.
7553 * @param aControl Direct session control object (optional).
7554 * @param aAllowClosing If true then additionally a session which is currently
7555 * being closed will also be allowed.
7556 *
7557 * @note locks this object for reading.
7558 */
7559bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7560 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7561 bool aAllowClosing /*= false*/)
7562{
7563 AutoLimitedCaller autoCaller(this);
7564 AssertComRCReturn(autoCaller.rc(), false);
7565
7566 /* just return false for inaccessible machines */
7567 if (getObjectState().getState() != ObjectState::Ready)
7568 return false;
7569
7570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7571
7572 if ( mData->mSession.mState == SessionState_Locked
7573 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7574 )
7575 {
7576 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7577
7578 aMachine = mData->mSession.mMachine;
7579
7580 if (aControl != NULL)
7581 *aControl = mData->mSession.mDirectControl;
7582
7583 return true;
7584 }
7585
7586 return false;
7587}
7588
7589/**
7590 * Returns @c true if the given machine has an spawning direct session.
7591 *
7592 * @note locks this object for reading.
7593 */
7594bool Machine::i_isSessionSpawning()
7595{
7596 AutoLimitedCaller autoCaller(this);
7597 AssertComRCReturn(autoCaller.rc(), false);
7598
7599 /* just return false for inaccessible machines */
7600 if (getObjectState().getState() != ObjectState::Ready)
7601 return false;
7602
7603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7604
7605 if (mData->mSession.mState == SessionState_Spawning)
7606 return true;
7607
7608 return false;
7609}
7610
7611/**
7612 * Called from the client watcher thread to check for unexpected client process
7613 * death during Session_Spawning state (e.g. before it successfully opened a
7614 * direct session).
7615 *
7616 * On Win32 and on OS/2, this method is called only when we've got the
7617 * direct client's process termination notification, so it always returns @c
7618 * true.
7619 *
7620 * On other platforms, this method returns @c true if the client process is
7621 * terminated and @c false if it's still alive.
7622 *
7623 * @note Locks this object for writing.
7624 */
7625bool Machine::i_checkForSpawnFailure()
7626{
7627 AutoCaller autoCaller(this);
7628 if (!autoCaller.isOk())
7629 {
7630 /* nothing to do */
7631 LogFlowThisFunc(("Already uninitialized!\n"));
7632 return true;
7633 }
7634
7635 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7636
7637 if (mData->mSession.mState != SessionState_Spawning)
7638 {
7639 /* nothing to do */
7640 LogFlowThisFunc(("Not spawning any more!\n"));
7641 return true;
7642 }
7643
7644 HRESULT rc = S_OK;
7645
7646 /* PID not yet initialized, skip check. */
7647 if (mData->mSession.mPID == NIL_RTPROCESS)
7648 return false;
7649
7650 RTPROCSTATUS status;
7651 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7652
7653 if (vrc != VERR_PROCESS_RUNNING)
7654 {
7655 Utf8Str strExtraInfo;
7656
7657#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7658 /* If the startup logfile exists and is of non-zero length, tell the
7659 user to look there for more details to encourage them to attach it
7660 when reporting startup issues. */
7661 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7662 uint64_t cbStartupLogFile = 0;
7663 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7664 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7665 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7666#endif
7667
7668 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7669 rc = setError(E_FAIL,
7670 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7671 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7672 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7673 rc = setError(E_FAIL,
7674 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7675 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7676 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7677 rc = setError(E_FAIL,
7678 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7679 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7680 else
7681 rc = setError(E_FAIL,
7682 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7683 i_getName().c_str(), vrc, strExtraInfo.c_str());
7684 }
7685
7686 if (FAILED(rc))
7687 {
7688 /* Close the remote session, remove the remote control from the list
7689 * and reset session state to Closed (@note keep the code in sync with
7690 * the relevant part in LockMachine()). */
7691
7692 Assert(mData->mSession.mRemoteControls.size() == 1);
7693 if (mData->mSession.mRemoteControls.size() == 1)
7694 {
7695 ErrorInfoKeeper eik;
7696 mData->mSession.mRemoteControls.front()->Uninitialize();
7697 }
7698
7699 mData->mSession.mRemoteControls.clear();
7700 mData->mSession.mState = SessionState_Unlocked;
7701
7702 /* finalize the progress after setting the state */
7703 if (!mData->mSession.mProgress.isNull())
7704 {
7705 mData->mSession.mProgress->notifyComplete(rc);
7706 mData->mSession.mProgress.setNull();
7707 }
7708
7709 mData->mSession.mPID = NIL_RTPROCESS;
7710
7711 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7712 return true;
7713 }
7714
7715 return false;
7716}
7717
7718/**
7719 * Checks whether the machine can be registered. If so, commits and saves
7720 * all settings.
7721 *
7722 * @note Must be called from mParent's write lock. Locks this object and
7723 * children for writing.
7724 */
7725HRESULT Machine::i_prepareRegister()
7726{
7727 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7728
7729 AutoLimitedCaller autoCaller(this);
7730 AssertComRCReturnRC(autoCaller.rc());
7731
7732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7733
7734 /* wait for state dependents to drop to zero */
7735 i_ensureNoStateDependencies();
7736
7737 if (!mData->mAccessible)
7738 return setError(VBOX_E_INVALID_OBJECT_STATE,
7739 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7740 mUserData->s.strName.c_str(),
7741 mData->mUuid.toString().c_str());
7742
7743 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7744
7745 if (mData->mRegistered)
7746 return setError(VBOX_E_INVALID_OBJECT_STATE,
7747 tr("The machine '%s' with UUID {%s} is already registered"),
7748 mUserData->s.strName.c_str(),
7749 mData->mUuid.toString().c_str());
7750
7751 HRESULT rc = S_OK;
7752
7753 // Ensure the settings are saved. If we are going to be registered and
7754 // no config file exists yet, create it by calling i_saveSettings() too.
7755 if ( (mData->flModifications)
7756 || (!mData->pMachineConfigFile->fileExists())
7757 )
7758 {
7759 rc = i_saveSettings(NULL);
7760 // no need to check whether VirtualBox.xml needs saving too since
7761 // we can't have a machine XML file rename pending
7762 if (FAILED(rc)) return rc;
7763 }
7764
7765 /* more config checking goes here */
7766
7767 if (SUCCEEDED(rc))
7768 {
7769 /* we may have had implicit modifications we want to fix on success */
7770 i_commit();
7771
7772 mData->mRegistered = true;
7773 }
7774 else
7775 {
7776 /* we may have had implicit modifications we want to cancel on failure*/
7777 i_rollback(false /* aNotify */);
7778 }
7779
7780 return rc;
7781}
7782
7783/**
7784 * Increases the number of objects dependent on the machine state or on the
7785 * registered state. Guarantees that these two states will not change at least
7786 * until #releaseStateDependency() is called.
7787 *
7788 * Depending on the @a aDepType value, additional state checks may be made.
7789 * These checks will set extended error info on failure. See
7790 * #checkStateDependency() for more info.
7791 *
7792 * If this method returns a failure, the dependency is not added and the caller
7793 * is not allowed to rely on any particular machine state or registration state
7794 * value and may return the failed result code to the upper level.
7795 *
7796 * @param aDepType Dependency type to add.
7797 * @param aState Current machine state (NULL if not interested).
7798 * @param aRegistered Current registered state (NULL if not interested).
7799 *
7800 * @note Locks this object for writing.
7801 */
7802HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7803 MachineState_T *aState /* = NULL */,
7804 BOOL *aRegistered /* = NULL */)
7805{
7806 AutoCaller autoCaller(this);
7807 AssertComRCReturnRC(autoCaller.rc());
7808
7809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7810
7811 HRESULT rc = i_checkStateDependency(aDepType);
7812 if (FAILED(rc)) return rc;
7813
7814 {
7815 if (mData->mMachineStateChangePending != 0)
7816 {
7817 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7818 * drop to zero so don't add more. It may make sense to wait a bit
7819 * and retry before reporting an error (since the pending state
7820 * transition should be really quick) but let's just assert for
7821 * now to see if it ever happens on practice. */
7822
7823 AssertFailed();
7824
7825 return setError(E_ACCESSDENIED,
7826 tr("Machine state change is in progress. Please retry the operation later."));
7827 }
7828
7829 ++mData->mMachineStateDeps;
7830 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7831 }
7832
7833 if (aState)
7834 *aState = mData->mMachineState;
7835 if (aRegistered)
7836 *aRegistered = mData->mRegistered;
7837
7838 return S_OK;
7839}
7840
7841/**
7842 * Decreases the number of objects dependent on the machine state.
7843 * Must always complete the #addStateDependency() call after the state
7844 * dependency is no more necessary.
7845 */
7846void Machine::i_releaseStateDependency()
7847{
7848 AutoCaller autoCaller(this);
7849 AssertComRCReturnVoid(autoCaller.rc());
7850
7851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7852
7853 /* releaseStateDependency() w/o addStateDependency()? */
7854 AssertReturnVoid(mData->mMachineStateDeps != 0);
7855 -- mData->mMachineStateDeps;
7856
7857 if (mData->mMachineStateDeps == 0)
7858 {
7859 /* inform i_ensureNoStateDependencies() that there are no more deps */
7860 if (mData->mMachineStateChangePending != 0)
7861 {
7862 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7863 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7864 }
7865 }
7866}
7867
7868Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7869{
7870 /* start with nothing found */
7871 Utf8Str strResult("");
7872
7873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7874
7875 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7876 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7877 // found:
7878 strResult = it->second; // source is a Utf8Str
7879
7880 return strResult;
7881}
7882
7883// protected methods
7884/////////////////////////////////////////////////////////////////////////////
7885
7886/**
7887 * Performs machine state checks based on the @a aDepType value. If a check
7888 * fails, this method will set extended error info, otherwise it will return
7889 * S_OK. It is supposed, that on failure, the caller will immediately return
7890 * the return value of this method to the upper level.
7891 *
7892 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7893 *
7894 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7895 * current state of this machine object allows to change settings of the
7896 * machine (i.e. the machine is not registered, or registered but not running
7897 * and not saved). It is useful to call this method from Machine setters
7898 * before performing any change.
7899 *
7900 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7901 * as for MutableStateDep except that if the machine is saved, S_OK is also
7902 * returned. This is useful in setters which allow changing machine
7903 * properties when it is in the saved state.
7904 *
7905 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7906 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7907 * Aborted).
7908 *
7909 * @param aDepType Dependency type to check.
7910 *
7911 * @note Non Machine based classes should use #addStateDependency() and
7912 * #releaseStateDependency() methods or the smart AutoStateDependency
7913 * template.
7914 *
7915 * @note This method must be called from under this object's read or write
7916 * lock.
7917 */
7918HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7919{
7920 switch (aDepType)
7921 {
7922 case AnyStateDep:
7923 {
7924 break;
7925 }
7926 case MutableStateDep:
7927 {
7928 if ( mData->mRegistered
7929 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7930 Paused should actually be included here... (Live Migration) */
7931 || ( mData->mMachineState != MachineState_Paused
7932 && mData->mMachineState != MachineState_Running
7933 && mData->mMachineState != MachineState_Aborted
7934 && mData->mMachineState != MachineState_Teleported
7935 && mData->mMachineState != MachineState_PoweredOff
7936 )
7937 )
7938 )
7939 return setError(VBOX_E_INVALID_VM_STATE,
7940 tr("The machine is not mutable (state is %s)"),
7941 Global::stringifyMachineState(mData->mMachineState));
7942 break;
7943 }
7944 case MutableOrSavedStateDep:
7945 {
7946 if ( mData->mRegistered
7947 && ( !i_isSessionMachine() /** @todo This was just converted raw; Check if Running and
7948 Paused should actually be included here... (Live Migration) */
7949 || ( mData->mMachineState != MachineState_Paused
7950 && mData->mMachineState != MachineState_Running
7951 && mData->mMachineState != MachineState_Aborted
7952 && mData->mMachineState != MachineState_Teleported
7953 && mData->mMachineState != MachineState_Saved
7954 && mData->mMachineState != MachineState_PoweredOff
7955 )
7956 )
7957 )
7958 return setError(VBOX_E_INVALID_VM_STATE,
7959 tr("The machine is not mutable (state is %s)"),
7960 Global::stringifyMachineState(mData->mMachineState));
7961 break;
7962 }
7963 case OfflineStateDep:
7964 {
7965 if ( mData->mRegistered
7966 && ( !i_isSessionMachine()
7967 || ( mData->mMachineState != MachineState_PoweredOff
7968 && mData->mMachineState != MachineState_Saved
7969 && mData->mMachineState != MachineState_Aborted
7970 && mData->mMachineState != MachineState_Teleported
7971 )
7972 )
7973 )
7974 return setError(VBOX_E_INVALID_VM_STATE,
7975 tr("The machine is not offline (state is %s)"),
7976 Global::stringifyMachineState(mData->mMachineState));
7977 break;
7978 }
7979 }
7980
7981 return S_OK;
7982}
7983
7984/**
7985 * Helper to initialize all associated child objects and allocate data
7986 * structures.
7987 *
7988 * This method must be called as a part of the object's initialization procedure
7989 * (usually done in the #init() method).
7990 *
7991 * @note Must be called only from #init() or from #registeredInit().
7992 */
7993HRESULT Machine::initDataAndChildObjects()
7994{
7995 AutoCaller autoCaller(this);
7996 AssertComRCReturnRC(autoCaller.rc());
7997 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
7998 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
7999
8000 AssertReturn(!mData->mAccessible, E_FAIL);
8001
8002 /* allocate data structures */
8003 mSSData.allocate();
8004 mUserData.allocate();
8005 mHWData.allocate();
8006 mMediaData.allocate();
8007 mStorageControllers.allocate();
8008 mUSBControllers.allocate();
8009
8010 /* initialize mOSTypeId */
8011 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8012
8013 /* create associated BIOS settings object */
8014 unconst(mBIOSSettings).createObject();
8015 mBIOSSettings->init(this);
8016
8017 /* create an associated VRDE object (default is disabled) */
8018 unconst(mVRDEServer).createObject();
8019 mVRDEServer->init(this);
8020
8021 /* create associated serial port objects */
8022 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8023 {
8024 unconst(mSerialPorts[slot]).createObject();
8025 mSerialPorts[slot]->init(this, slot);
8026 }
8027
8028 /* create associated parallel port objects */
8029 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8030 {
8031 unconst(mParallelPorts[slot]).createObject();
8032 mParallelPorts[slot]->init(this, slot);
8033 }
8034
8035 /* create the audio adapter object (always present, default is disabled) */
8036 unconst(mAudioAdapter).createObject();
8037 mAudioAdapter->init(this);
8038
8039 /* create the USB device filters object (always present) */
8040 unconst(mUSBDeviceFilters).createObject();
8041 mUSBDeviceFilters->init(this);
8042
8043 /* create associated network adapter objects */
8044 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8045 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8046 {
8047 unconst(mNetworkAdapters[slot]).createObject();
8048 mNetworkAdapters[slot]->init(this, slot);
8049 }
8050
8051 /* create the bandwidth control */
8052 unconst(mBandwidthControl).createObject();
8053 mBandwidthControl->init(this);
8054
8055 return S_OK;
8056}
8057
8058/**
8059 * Helper to uninitialize all associated child objects and to free all data
8060 * structures.
8061 *
8062 * This method must be called as a part of the object's uninitialization
8063 * procedure (usually done in the #uninit() method).
8064 *
8065 * @note Must be called only from #uninit() or from #registeredInit().
8066 */
8067void Machine::uninitDataAndChildObjects()
8068{
8069 AutoCaller autoCaller(this);
8070 AssertComRCReturnVoid(autoCaller.rc());
8071 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8072 || getObjectState().getState() == ObjectState::Limited);
8073
8074 /* tell all our other child objects we've been uninitialized */
8075 if (mBandwidthControl)
8076 {
8077 mBandwidthControl->uninit();
8078 unconst(mBandwidthControl).setNull();
8079 }
8080
8081 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8082 {
8083 if (mNetworkAdapters[slot])
8084 {
8085 mNetworkAdapters[slot]->uninit();
8086 unconst(mNetworkAdapters[slot]).setNull();
8087 }
8088 }
8089
8090 if (mUSBDeviceFilters)
8091 {
8092 mUSBDeviceFilters->uninit();
8093 unconst(mUSBDeviceFilters).setNull();
8094 }
8095
8096 if (mAudioAdapter)
8097 {
8098 mAudioAdapter->uninit();
8099 unconst(mAudioAdapter).setNull();
8100 }
8101
8102 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8103 {
8104 if (mParallelPorts[slot])
8105 {
8106 mParallelPorts[slot]->uninit();
8107 unconst(mParallelPorts[slot]).setNull();
8108 }
8109 }
8110
8111 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8112 {
8113 if (mSerialPorts[slot])
8114 {
8115 mSerialPorts[slot]->uninit();
8116 unconst(mSerialPorts[slot]).setNull();
8117 }
8118 }
8119
8120 if (mVRDEServer)
8121 {
8122 mVRDEServer->uninit();
8123 unconst(mVRDEServer).setNull();
8124 }
8125
8126 if (mBIOSSettings)
8127 {
8128 mBIOSSettings->uninit();
8129 unconst(mBIOSSettings).setNull();
8130 }
8131
8132 /* Deassociate media (only when a real Machine or a SnapshotMachine
8133 * instance is uninitialized; SessionMachine instances refer to real
8134 * Machine media). This is necessary for a clean re-initialization of
8135 * the VM after successfully re-checking the accessibility state. Note
8136 * that in case of normal Machine or SnapshotMachine uninitialization (as
8137 * a result of unregistering or deleting the snapshot), outdated media
8138 * attachments will already be uninitialized and deleted, so this
8139 * code will not affect them. */
8140 if ( !!mMediaData
8141 && (!i_isSessionMachine())
8142 )
8143 {
8144 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8145 it != mMediaData->mAttachments.end();
8146 ++it)
8147 {
8148 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8149 if (pMedium.isNull())
8150 continue;
8151 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8152 AssertComRC(rc);
8153 }
8154 }
8155
8156 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8157 {
8158 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8159 if (mData->mFirstSnapshot)
8160 {
8161 // snapshots tree is protected by machine write lock; strictly
8162 // this isn't necessary here since we're deleting the entire
8163 // machine, but otherwise we assert in Snapshot::uninit()
8164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8165 mData->mFirstSnapshot->uninit();
8166 mData->mFirstSnapshot.setNull();
8167 }
8168
8169 mData->mCurrentSnapshot.setNull();
8170 }
8171
8172 /* free data structures (the essential mData structure is not freed here
8173 * since it may be still in use) */
8174 mMediaData.free();
8175 mStorageControllers.free();
8176 mUSBControllers.free();
8177 mHWData.free();
8178 mUserData.free();
8179 mSSData.free();
8180}
8181
8182/**
8183 * Returns a pointer to the Machine object for this machine that acts like a
8184 * parent for complex machine data objects such as shared folders, etc.
8185 *
8186 * For primary Machine objects and for SnapshotMachine objects, returns this
8187 * object's pointer itself. For SessionMachine objects, returns the peer
8188 * (primary) machine pointer.
8189 */
8190Machine* Machine::i_getMachine()
8191{
8192 if (i_isSessionMachine())
8193 return (Machine*)mPeer;
8194 return this;
8195}
8196
8197/**
8198 * Makes sure that there are no machine state dependents. If necessary, waits
8199 * for the number of dependents to drop to zero.
8200 *
8201 * Make sure this method is called from under this object's write lock to
8202 * guarantee that no new dependents may be added when this method returns
8203 * control to the caller.
8204 *
8205 * @note Locks this object for writing. The lock will be released while waiting
8206 * (if necessary).
8207 *
8208 * @warning To be used only in methods that change the machine state!
8209 */
8210void Machine::i_ensureNoStateDependencies()
8211{
8212 AssertReturnVoid(isWriteLockOnCurrentThread());
8213
8214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8215
8216 /* Wait for all state dependents if necessary */
8217 if (mData->mMachineStateDeps != 0)
8218 {
8219 /* lazy semaphore creation */
8220 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8221 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8222
8223 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8224 mData->mMachineStateDeps));
8225
8226 ++mData->mMachineStateChangePending;
8227
8228 /* reset the semaphore before waiting, the last dependent will signal
8229 * it */
8230 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8231
8232 alock.release();
8233
8234 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8235
8236 alock.acquire();
8237
8238 -- mData->mMachineStateChangePending;
8239 }
8240}
8241
8242/**
8243 * Changes the machine state and informs callbacks.
8244 *
8245 * This method is not intended to fail so it either returns S_OK or asserts (and
8246 * returns a failure).
8247 *
8248 * @note Locks this object for writing.
8249 */
8250HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8251{
8252 LogFlowThisFuncEnter();
8253 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8254
8255 AutoCaller autoCaller(this);
8256 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8257
8258 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8259
8260 /* wait for state dependents to drop to zero */
8261 i_ensureNoStateDependencies();
8262
8263 if (mData->mMachineState != aMachineState)
8264 {
8265 mData->mMachineState = aMachineState;
8266
8267 RTTimeNow(&mData->mLastStateChange);
8268
8269 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8270 }
8271
8272 LogFlowThisFuncLeave();
8273 return S_OK;
8274}
8275
8276/**
8277 * Searches for a shared folder with the given logical name
8278 * in the collection of shared folders.
8279 *
8280 * @param aName logical name of the shared folder
8281 * @param aSharedFolder where to return the found object
8282 * @param aSetError whether to set the error info if the folder is
8283 * not found
8284 * @return
8285 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8286 *
8287 * @note
8288 * must be called from under the object's lock!
8289 */
8290HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8291 ComObjPtr<SharedFolder> &aSharedFolder,
8292 bool aSetError /* = false */)
8293{
8294 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8295 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8296 it != mHWData->mSharedFolders.end();
8297 ++it)
8298 {
8299 SharedFolder *pSF = *it;
8300 AutoCaller autoCaller(pSF);
8301 if (pSF->i_getName() == aName)
8302 {
8303 aSharedFolder = pSF;
8304 rc = S_OK;
8305 break;
8306 }
8307 }
8308
8309 if (aSetError && FAILED(rc))
8310 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8311
8312 return rc;
8313}
8314
8315/**
8316 * Initializes all machine instance data from the given settings structures
8317 * from XML. The exception is the machine UUID which needs special handling
8318 * depending on the caller's use case, so the caller needs to set that herself.
8319 *
8320 * This gets called in several contexts during machine initialization:
8321 *
8322 * -- When machine XML exists on disk already and needs to be loaded into memory,
8323 * for example, from registeredInit() to load all registered machines on
8324 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8325 * attached to the machine should be part of some media registry already.
8326 *
8327 * -- During OVF import, when a machine config has been constructed from an
8328 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8329 * ensure that the media listed as attachments in the config (which have
8330 * been imported from the OVF) receive the correct registry ID.
8331 *
8332 * -- During VM cloning.
8333 *
8334 * @param config Machine settings from XML.
8335 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8336 * for each attached medium in the config.
8337 * @return
8338 */
8339HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8340 const Guid *puuidRegistry)
8341{
8342 // copy name, description, OS type, teleporter, UTC etc.
8343 mUserData->s = config.machineUserData;
8344
8345 // Decode the Icon overide data from config userdata and set onto Machine.
8346 #define DECODE_STR_MAX _1M
8347 const char* pszStr = config.machineUserData.ovIcon.c_str();
8348 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8349 if (cbOut > DECODE_STR_MAX)
8350 return setError(E_FAIL,
8351 tr("Icon Data too long.'%d' > '%d'"),
8352 cbOut,
8353 DECODE_STR_MAX);
8354 com::SafeArray<BYTE> iconByte(cbOut);
8355 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8356 if (FAILED(rc))
8357 return setError(E_FAIL,
8358 tr("Failure to Decode Icon Data. '%s' (%d)"),
8359 pszStr,
8360 rc);
8361 mUserData->mIcon.resize(iconByte.size());
8362 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
8363
8364 // look up the object by Id to check it is valid
8365 ComPtr<IGuestOSType> guestOSType;
8366 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8367 guestOSType.asOutParam());
8368 if (FAILED(rc)) return rc;
8369
8370 // stateFile (optional)
8371 if (config.strStateFile.isEmpty())
8372 mSSData->strStateFilePath.setNull();
8373 else
8374 {
8375 Utf8Str stateFilePathFull(config.strStateFile);
8376 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8377 if (RT_FAILURE(vrc))
8378 return setError(E_FAIL,
8379 tr("Invalid saved state file path '%s' (%Rrc)"),
8380 config.strStateFile.c_str(),
8381 vrc);
8382 mSSData->strStateFilePath = stateFilePathFull;
8383 }
8384
8385 // snapshot folder needs special processing so set it again
8386 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8387 if (FAILED(rc)) return rc;
8388
8389 /* Copy the extra data items (Not in any case config is already the same as
8390 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8391 * make sure the extra data map is copied). */
8392 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8393
8394 /* currentStateModified (optional, default is true) */
8395 mData->mCurrentStateModified = config.fCurrentStateModified;
8396
8397 mData->mLastStateChange = config.timeLastStateChange;
8398
8399 /*
8400 * note: all mUserData members must be assigned prior this point because
8401 * we need to commit changes in order to let mUserData be shared by all
8402 * snapshot machine instances.
8403 */
8404 mUserData.commitCopy();
8405
8406 // machine registry, if present (must be loaded before snapshots)
8407 if (config.canHaveOwnMediaRegistry())
8408 {
8409 // determine machine folder
8410 Utf8Str strMachineFolder = i_getSettingsFileFull();
8411 strMachineFolder.stripFilename();
8412 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8413 config.mediaRegistry,
8414 strMachineFolder);
8415 if (FAILED(rc)) return rc;
8416 }
8417
8418 /* Snapshot node (optional) */
8419 size_t cRootSnapshots;
8420 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8421 {
8422 // there must be only one root snapshot
8423 Assert(cRootSnapshots == 1);
8424
8425 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8426
8427 rc = i_loadSnapshot(snap,
8428 config.uuidCurrentSnapshot,
8429 NULL); // no parent == first snapshot
8430 if (FAILED(rc)) return rc;
8431 }
8432
8433 // hardware data
8434 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8435 if (FAILED(rc)) return rc;
8436
8437 // load storage controllers
8438 rc = i_loadStorageControllers(config.storageMachine,
8439 puuidRegistry,
8440 NULL /* puuidSnapshot */);
8441 if (FAILED(rc)) return rc;
8442
8443 /*
8444 * NOTE: the assignment below must be the last thing to do,
8445 * otherwise it will be not possible to change the settings
8446 * somewhere in the code above because all setters will be
8447 * blocked by i_checkStateDependency(MutableStateDep).
8448 */
8449
8450 /* set the machine state to Aborted or Saved when appropriate */
8451 if (config.fAborted)
8452 {
8453 mSSData->strStateFilePath.setNull();
8454
8455 /* no need to use i_setMachineState() during init() */
8456 mData->mMachineState = MachineState_Aborted;
8457 }
8458 else if (!mSSData->strStateFilePath.isEmpty())
8459 {
8460 /* no need to use i_setMachineState() during init() */
8461 mData->mMachineState = MachineState_Saved;
8462 }
8463
8464 // after loading settings, we are no longer different from the XML on disk
8465 mData->flModifications = 0;
8466
8467 return S_OK;
8468}
8469
8470/**
8471 * Recursively loads all snapshots starting from the given.
8472 *
8473 * @param aNode <Snapshot> node.
8474 * @param aCurSnapshotId Current snapshot ID from the settings file.
8475 * @param aParentSnapshot Parent snapshot.
8476 */
8477HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8478 const Guid &aCurSnapshotId,
8479 Snapshot *aParentSnapshot)
8480{
8481 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8482 AssertReturn(!i_isSessionMachine(), E_FAIL);
8483
8484 HRESULT rc = S_OK;
8485
8486 Utf8Str strStateFile;
8487 if (!data.strStateFile.isEmpty())
8488 {
8489 /* optional */
8490 strStateFile = data.strStateFile;
8491 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8492 if (RT_FAILURE(vrc))
8493 return setError(E_FAIL,
8494 tr("Invalid saved state file path '%s' (%Rrc)"),
8495 strStateFile.c_str(),
8496 vrc);
8497 }
8498
8499 /* create a snapshot machine object */
8500 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8501 pSnapshotMachine.createObject();
8502 rc = pSnapshotMachine->initFromSettings(this,
8503 data.hardware,
8504 &data.debugging,
8505 &data.autostart,
8506 data.storage,
8507 data.uuid.ref(),
8508 strStateFile);
8509 if (FAILED(rc)) return rc;
8510
8511 /* create a snapshot object */
8512 ComObjPtr<Snapshot> pSnapshot;
8513 pSnapshot.createObject();
8514 /* initialize the snapshot */
8515 rc = pSnapshot->init(mParent, // VirtualBox object
8516 data.uuid,
8517 data.strName,
8518 data.strDescription,
8519 data.timestamp,
8520 pSnapshotMachine,
8521 aParentSnapshot);
8522 if (FAILED(rc)) return rc;
8523
8524 /* memorize the first snapshot if necessary */
8525 if (!mData->mFirstSnapshot)
8526 mData->mFirstSnapshot = pSnapshot;
8527
8528 /* memorize the current snapshot when appropriate */
8529 if ( !mData->mCurrentSnapshot
8530 && pSnapshot->i_getId() == aCurSnapshotId
8531 )
8532 mData->mCurrentSnapshot = pSnapshot;
8533
8534 // now create the children
8535 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8536 it != data.llChildSnapshots.end();
8537 ++it)
8538 {
8539 const settings::Snapshot &childData = *it;
8540 // recurse
8541 rc = i_loadSnapshot(childData,
8542 aCurSnapshotId,
8543 pSnapshot); // parent = the one we created above
8544 if (FAILED(rc)) return rc;
8545 }
8546
8547 return rc;
8548}
8549
8550/**
8551 * Loads settings into mHWData.
8552 *
8553 * @param data Reference to the hardware settings.
8554 * @param pDbg Pointer to the debugging settings.
8555 * @param pAutostart Pointer to the autostart settings.
8556 */
8557HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8558 const settings::Autostart *pAutostart)
8559{
8560 AssertReturn(!i_isSessionMachine(), E_FAIL);
8561
8562 HRESULT rc = S_OK;
8563
8564 try
8565 {
8566 /* The hardware version attribute (optional). */
8567 mHWData->mHWVersion = data.strVersion;
8568 mHWData->mHardwareUUID = data.uuid;
8569
8570 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8571 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8572 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8573 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8574 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8575 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8576 mHWData->mPAEEnabled = data.fPAE;
8577 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8578 mHWData->mLongMode = data.enmLongMode;
8579 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8580 mHWData->mCPUCount = data.cCPUs;
8581 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8582 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8583
8584 // cpu
8585 if (mHWData->mCPUHotPlugEnabled)
8586 {
8587 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8588 it != data.llCpus.end();
8589 ++it)
8590 {
8591 const settings::Cpu &cpu = *it;
8592
8593 mHWData->mCPUAttached[cpu.ulId] = true;
8594 }
8595 }
8596
8597 // cpuid leafs
8598 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8599 it != data.llCpuIdLeafs.end();
8600 ++it)
8601 {
8602 const settings::CpuIdLeaf &leaf = *it;
8603
8604 switch (leaf.ulId)
8605 {
8606 case 0x0:
8607 case 0x1:
8608 case 0x2:
8609 case 0x3:
8610 case 0x4:
8611 case 0x5:
8612 case 0x6:
8613 case 0x7:
8614 case 0x8:
8615 case 0x9:
8616 case 0xA:
8617 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8618 break;
8619
8620 case 0x80000000:
8621 case 0x80000001:
8622 case 0x80000002:
8623 case 0x80000003:
8624 case 0x80000004:
8625 case 0x80000005:
8626 case 0x80000006:
8627 case 0x80000007:
8628 case 0x80000008:
8629 case 0x80000009:
8630 case 0x8000000A:
8631 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8632 break;
8633
8634 default:
8635 /* just ignore */
8636 break;
8637 }
8638 }
8639
8640 mHWData->mMemorySize = data.ulMemorySizeMB;
8641 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8642
8643 // boot order
8644 for (size_t i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8645 {
8646 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8647 if (it == data.mapBootOrder.end())
8648 mHWData->mBootOrder[i] = DeviceType_Null;
8649 else
8650 mHWData->mBootOrder[i] = it->second;
8651 }
8652
8653 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8654 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8655 mHWData->mMonitorCount = data.cMonitors;
8656 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8657 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8658 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8659 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8660 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8661 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8662 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8663 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8664 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8665 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8666 if (!data.strVideoCaptureFile.isEmpty())
8667 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8668 else
8669 mHWData->mVideoCaptureFile.setNull();
8670 mHWData->mFirmwareType = data.firmwareType;
8671 mHWData->mPointingHIDType = data.pointingHIDType;
8672 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8673 mHWData->mChipsetType = data.chipsetType;
8674 mHWData->mParavirtProvider = data.paravirtProvider;
8675 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8676 mHWData->mHPETEnabled = data.fHPETEnabled;
8677
8678 /* VRDEServer */
8679 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8680 if (FAILED(rc)) return rc;
8681
8682 /* BIOS */
8683 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8684 if (FAILED(rc)) return rc;
8685
8686 // Bandwidth control (must come before network adapters)
8687 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8688 if (FAILED(rc)) return rc;
8689
8690 /* Shared folders */
8691 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8692 it != data.usbSettings.llUSBControllers.end();
8693 ++it)
8694 {
8695 const settings::USBController &settingsCtrl = *it;
8696 ComObjPtr<USBController> newCtrl;
8697
8698 newCtrl.createObject();
8699 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8700 mUSBControllers->push_back(newCtrl);
8701 }
8702
8703 /* USB device filters */
8704 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8705 if (FAILED(rc)) return rc;
8706
8707 // network adapters
8708 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8709 uint32_t oldCount = mNetworkAdapters.size();
8710 if (newCount > oldCount)
8711 {
8712 mNetworkAdapters.resize(newCount);
8713 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8714 {
8715 unconst(mNetworkAdapters[slot]).createObject();
8716 mNetworkAdapters[slot]->init(this, slot);
8717 }
8718 }
8719 else if (newCount < oldCount)
8720 mNetworkAdapters.resize(newCount);
8721 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8722 it != data.llNetworkAdapters.end();
8723 ++it)
8724 {
8725 const settings::NetworkAdapter &nic = *it;
8726
8727 /* slot unicity is guaranteed by XML Schema */
8728 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8729 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8730 if (FAILED(rc)) return rc;
8731 }
8732
8733 // serial ports
8734 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8735 it != data.llSerialPorts.end();
8736 ++it)
8737 {
8738 const settings::SerialPort &s = *it;
8739
8740 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8741 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8742 if (FAILED(rc)) return rc;
8743 }
8744
8745 // parallel ports (optional)
8746 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8747 it != data.llParallelPorts.end();
8748 ++it)
8749 {
8750 const settings::ParallelPort &p = *it;
8751
8752 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8753 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8754 if (FAILED(rc)) return rc;
8755 }
8756
8757 /* AudioAdapter */
8758 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8759 if (FAILED(rc)) return rc;
8760
8761 /* Shared folders */
8762 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8763 it != data.llSharedFolders.end();
8764 ++it)
8765 {
8766 const settings::SharedFolder &sf = *it;
8767
8768 ComObjPtr<SharedFolder> sharedFolder;
8769 /* Check for double entries. Not allowed! */
8770 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8771 if (SUCCEEDED(rc))
8772 return setError(VBOX_E_OBJECT_IN_USE,
8773 tr("Shared folder named '%s' already exists"),
8774 sf.strName.c_str());
8775
8776 /* Create the new shared folder. Don't break on error. This will be
8777 * reported when the machine starts. */
8778 sharedFolder.createObject();
8779 rc = sharedFolder->init(i_getMachine(),
8780 sf.strName,
8781 sf.strHostPath,
8782 RT_BOOL(sf.fWritable),
8783 RT_BOOL(sf.fAutoMount),
8784 false /* fFailOnError */);
8785 if (FAILED(rc)) return rc;
8786 mHWData->mSharedFolders.push_back(sharedFolder);
8787 }
8788
8789 // Clipboard
8790 mHWData->mClipboardMode = data.clipboardMode;
8791
8792 // drag'n'drop
8793 mHWData->mDnDMode = data.dndMode;
8794
8795 // guest settings
8796 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8797
8798 // IO settings
8799 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8800 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8801
8802 // Host PCI devices
8803 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8804 it != data.pciAttachments.end();
8805 ++it)
8806 {
8807 const settings::HostPCIDeviceAttachment &hpda = *it;
8808 ComObjPtr<PCIDeviceAttachment> pda;
8809
8810 pda.createObject();
8811 pda->i_loadSettings(this, hpda);
8812 mHWData->mPCIDeviceAssignments.push_back(pda);
8813 }
8814
8815 /*
8816 * (The following isn't really real hardware, but it lives in HWData
8817 * for reasons of convenience.)
8818 */
8819
8820#ifdef VBOX_WITH_GUEST_PROPS
8821 /* Guest properties (optional) */
8822 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8823 it != data.llGuestProperties.end();
8824 ++it)
8825 {
8826 const settings::GuestProperty &prop = *it;
8827 uint32_t fFlags = guestProp::NILFLAG;
8828 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8829 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8830 mHWData->mGuestProperties[prop.strName] = property;
8831 }
8832
8833 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8834#endif /* VBOX_WITH_GUEST_PROPS defined */
8835
8836 rc = i_loadDebugging(pDbg);
8837 if (FAILED(rc))
8838 return rc;
8839
8840 mHWData->mAutostart = *pAutostart;
8841
8842 /* default frontend */
8843 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8844 }
8845 catch(std::bad_alloc &)
8846 {
8847 return E_OUTOFMEMORY;
8848 }
8849
8850 AssertComRC(rc);
8851 return rc;
8852}
8853
8854/**
8855 * Called from Machine::loadHardware() to load the debugging settings of the
8856 * machine.
8857 *
8858 * @param pDbg Pointer to the settings.
8859 */
8860HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8861{
8862 mHWData->mDebugging = *pDbg;
8863 /* no more processing currently required, this will probably change. */
8864 return S_OK;
8865}
8866
8867/**
8868 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8869 *
8870 * @param data
8871 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8872 * @param puuidSnapshot
8873 * @return
8874 */
8875HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8876 const Guid *puuidRegistry,
8877 const Guid *puuidSnapshot)
8878{
8879 AssertReturn(!i_isSessionMachine(), E_FAIL);
8880
8881 HRESULT rc = S_OK;
8882
8883 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8884 it != data.llStorageControllers.end();
8885 ++it)
8886 {
8887 const settings::StorageController &ctlData = *it;
8888
8889 ComObjPtr<StorageController> pCtl;
8890 /* Try to find one with the name first. */
8891 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8892 if (SUCCEEDED(rc))
8893 return setError(VBOX_E_OBJECT_IN_USE,
8894 tr("Storage controller named '%s' already exists"),
8895 ctlData.strName.c_str());
8896
8897 pCtl.createObject();
8898 rc = pCtl->init(this,
8899 ctlData.strName,
8900 ctlData.storageBus,
8901 ctlData.ulInstance,
8902 ctlData.fBootable);
8903 if (FAILED(rc)) return rc;
8904
8905 mStorageControllers->push_back(pCtl);
8906
8907 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8908 if (FAILED(rc)) return rc;
8909
8910 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8911 if (FAILED(rc)) return rc;
8912
8913 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8914 if (FAILED(rc)) return rc;
8915
8916 /* Set IDE emulation settings (only for AHCI controller). */
8917 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8918 {
8919 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8920 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8921 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8922 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8923 )
8924 return rc;
8925 }
8926
8927 /* Load the attached devices now. */
8928 rc = i_loadStorageDevices(pCtl,
8929 ctlData,
8930 puuidRegistry,
8931 puuidSnapshot);
8932 if (FAILED(rc)) return rc;
8933 }
8934
8935 return S_OK;
8936}
8937
8938/**
8939 * Called from i_loadStorageControllers for a controller's devices.
8940 *
8941 * @param aStorageController
8942 * @param data
8943 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8944 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8945 * @return
8946 */
8947HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
8948 const settings::StorageController &data,
8949 const Guid *puuidRegistry,
8950 const Guid *puuidSnapshot)
8951{
8952 HRESULT rc = S_OK;
8953
8954 /* paranoia: detect duplicate attachments */
8955 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8956 it != data.llAttachedDevices.end();
8957 ++it)
8958 {
8959 const settings::AttachedDevice &ad = *it;
8960
8961 for (settings::AttachedDevicesList::const_iterator it2 = it;
8962 it2 != data.llAttachedDevices.end();
8963 ++it2)
8964 {
8965 if (it == it2)
8966 continue;
8967
8968 const settings::AttachedDevice &ad2 = *it2;
8969
8970 if ( ad.lPort == ad2.lPort
8971 && ad.lDevice == ad2.lDevice)
8972 {
8973 return setError(E_FAIL,
8974 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8975 aStorageController->i_getName().c_str(),
8976 ad.lPort,
8977 ad.lDevice,
8978 mUserData->s.strName.c_str());
8979 }
8980 }
8981 }
8982
8983 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8984 it != data.llAttachedDevices.end();
8985 ++it)
8986 {
8987 const settings::AttachedDevice &dev = *it;
8988 ComObjPtr<Medium> medium;
8989
8990 switch (dev.deviceType)
8991 {
8992 case DeviceType_Floppy:
8993 case DeviceType_DVD:
8994 if (dev.strHostDriveSrc.isNotEmpty())
8995 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
8996 false /* fRefresh */, medium);
8997 else
8998 rc = mParent->i_findRemoveableMedium(dev.deviceType,
8999 dev.uuid,
9000 false /* fRefresh */,
9001 false /* aSetError */,
9002 medium);
9003 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9004 // This is not an error. The host drive or UUID might have vanished, so just go
9005 // ahead without this removeable medium attachment
9006 rc = S_OK;
9007 break;
9008
9009 case DeviceType_HardDisk:
9010 {
9011 /* find a hard disk by UUID */
9012 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9013 if (FAILED(rc))
9014 {
9015 if (i_isSnapshotMachine())
9016 {
9017 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9018 // so the user knows that the bad disk is in a snapshot somewhere
9019 com::ErrorInfo info;
9020 return setError(E_FAIL,
9021 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9022 puuidSnapshot->raw(),
9023 info.getText().raw());
9024 }
9025 else
9026 return rc;
9027 }
9028
9029 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9030
9031 if (medium->i_getType() == MediumType_Immutable)
9032 {
9033 if (i_isSnapshotMachine())
9034 return setError(E_FAIL,
9035 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9036 "of the virtual machine '%s' ('%s')"),
9037 medium->i_getLocationFull().c_str(),
9038 dev.uuid.raw(),
9039 puuidSnapshot->raw(),
9040 mUserData->s.strName.c_str(),
9041 mData->m_strConfigFileFull.c_str());
9042
9043 return setError(E_FAIL,
9044 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9045 medium->i_getLocationFull().c_str(),
9046 dev.uuid.raw(),
9047 mUserData->s.strName.c_str(),
9048 mData->m_strConfigFileFull.c_str());
9049 }
9050
9051 if (medium->i_getType() == MediumType_MultiAttach)
9052 {
9053 if (i_isSnapshotMachine())
9054 return setError(E_FAIL,
9055 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9056 "of the virtual machine '%s' ('%s')"),
9057 medium->i_getLocationFull().c_str(),
9058 dev.uuid.raw(),
9059 puuidSnapshot->raw(),
9060 mUserData->s.strName.c_str(),
9061 mData->m_strConfigFileFull.c_str());
9062
9063 return setError(E_FAIL,
9064 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9065 medium->i_getLocationFull().c_str(),
9066 dev.uuid.raw(),
9067 mUserData->s.strName.c_str(),
9068 mData->m_strConfigFileFull.c_str());
9069 }
9070
9071 if ( !i_isSnapshotMachine()
9072 && medium->i_getChildren().size() != 0
9073 )
9074 return setError(E_FAIL,
9075 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9076 "because it has %d differencing child hard disks"),
9077 medium->i_getLocationFull().c_str(),
9078 dev.uuid.raw(),
9079 mUserData->s.strName.c_str(),
9080 mData->m_strConfigFileFull.c_str(),
9081 medium->i_getChildren().size());
9082
9083 if (i_findAttachment(mMediaData->mAttachments,
9084 medium))
9085 return setError(E_FAIL,
9086 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9087 medium->i_getLocationFull().c_str(),
9088 dev.uuid.raw(),
9089 mUserData->s.strName.c_str(),
9090 mData->m_strConfigFileFull.c_str());
9091
9092 break;
9093 }
9094
9095 default:
9096 return setError(E_FAIL,
9097 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9098 medium->i_getLocationFull().c_str(),
9099 mUserData->s.strName.c_str(),
9100 mData->m_strConfigFileFull.c_str());
9101 }
9102
9103 if (FAILED(rc))
9104 break;
9105
9106 /* Bandwidth groups are loaded at this point. */
9107 ComObjPtr<BandwidthGroup> pBwGroup;
9108
9109 if (!dev.strBwGroup.isEmpty())
9110 {
9111 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9112 if (FAILED(rc))
9113 return setError(E_FAIL,
9114 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9115 medium->i_getLocationFull().c_str(),
9116 dev.strBwGroup.c_str(),
9117 mUserData->s.strName.c_str(),
9118 mData->m_strConfigFileFull.c_str());
9119 pBwGroup->i_reference();
9120 }
9121
9122 const Bstr controllerName = aStorageController->i_getName();
9123 ComObjPtr<MediumAttachment> pAttachment;
9124 pAttachment.createObject();
9125 rc = pAttachment->init(this,
9126 medium,
9127 controllerName,
9128 dev.lPort,
9129 dev.lDevice,
9130 dev.deviceType,
9131 false,
9132 dev.fPassThrough,
9133 dev.fTempEject,
9134 dev.fNonRotational,
9135 dev.fDiscard,
9136 dev.fHotPluggable,
9137 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9138 if (FAILED(rc)) break;
9139
9140 /* associate the medium with this machine and snapshot */
9141 if (!medium.isNull())
9142 {
9143 AutoCaller medCaller(medium);
9144 if (FAILED(medCaller.rc())) return medCaller.rc();
9145 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9146
9147 if (i_isSnapshotMachine())
9148 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9149 else
9150 rc = medium->i_addBackReference(mData->mUuid);
9151 /* If the medium->addBackReference fails it sets an appropriate
9152 * error message, so no need to do any guesswork here. */
9153
9154 if (puuidRegistry)
9155 // caller wants registry ID to be set on all attached media (OVF import case)
9156 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9157 }
9158
9159 if (FAILED(rc))
9160 break;
9161
9162 /* back up mMediaData to let registeredInit() properly rollback on failure
9163 * (= limited accessibility) */
9164 i_setModified(IsModified_Storage);
9165 mMediaData.backup();
9166 mMediaData->mAttachments.push_back(pAttachment);
9167 }
9168
9169 return rc;
9170}
9171
9172/**
9173 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9174 *
9175 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9176 * @param aSnapshot where to return the found snapshot
9177 * @param aSetError true to set extended error info on failure
9178 */
9179HRESULT Machine::i_findSnapshotById(const Guid &aId,
9180 ComObjPtr<Snapshot> &aSnapshot,
9181 bool aSetError /* = false */)
9182{
9183 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9184
9185 if (!mData->mFirstSnapshot)
9186 {
9187 if (aSetError)
9188 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9189 return E_FAIL;
9190 }
9191
9192 if (aId.isZero())
9193 aSnapshot = mData->mFirstSnapshot;
9194 else
9195 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9196
9197 if (!aSnapshot)
9198 {
9199 if (aSetError)
9200 return setError(E_FAIL,
9201 tr("Could not find a snapshot with UUID {%s}"),
9202 aId.toString().c_str());
9203 return E_FAIL;
9204 }
9205
9206 return S_OK;
9207}
9208
9209/**
9210 * Returns the snapshot with the given name or fails of no such snapshot.
9211 *
9212 * @param aName snapshot name to find
9213 * @param aSnapshot where to return the found snapshot
9214 * @param aSetError true to set extended error info on failure
9215 */
9216HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9217 ComObjPtr<Snapshot> &aSnapshot,
9218 bool aSetError /* = false */)
9219{
9220 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9221
9222 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9223
9224 if (!mData->mFirstSnapshot)
9225 {
9226 if (aSetError)
9227 return setError(VBOX_E_OBJECT_NOT_FOUND,
9228 tr("This machine does not have any snapshots"));
9229 return VBOX_E_OBJECT_NOT_FOUND;
9230 }
9231
9232 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9233
9234 if (!aSnapshot)
9235 {
9236 if (aSetError)
9237 return setError(VBOX_E_OBJECT_NOT_FOUND,
9238 tr("Could not find a snapshot named '%s'"), strName.c_str());
9239 return VBOX_E_OBJECT_NOT_FOUND;
9240 }
9241
9242 return S_OK;
9243}
9244
9245/**
9246 * Returns a storage controller object with the given name.
9247 *
9248 * @param aName storage controller name to find
9249 * @param aStorageController where to return the found storage controller
9250 * @param aSetError true to set extended error info on failure
9251 */
9252HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9253 ComObjPtr<StorageController> &aStorageController,
9254 bool aSetError /* = false */)
9255{
9256 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9257
9258 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9259 it != mStorageControllers->end();
9260 ++it)
9261 {
9262 if ((*it)->i_getName() == aName)
9263 {
9264 aStorageController = (*it);
9265 return S_OK;
9266 }
9267 }
9268
9269 if (aSetError)
9270 return setError(VBOX_E_OBJECT_NOT_FOUND,
9271 tr("Could not find a storage controller named '%s'"),
9272 aName.c_str());
9273 return VBOX_E_OBJECT_NOT_FOUND;
9274}
9275
9276/**
9277 * Returns a USB controller object with the given name.
9278 *
9279 * @param aName USB controller name to find
9280 * @param aUSBController where to return the found USB controller
9281 * @param aSetError true to set extended error info on failure
9282 */
9283HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9284 ComObjPtr<USBController> &aUSBController,
9285 bool aSetError /* = false */)
9286{
9287 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9288
9289 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9290 it != mUSBControllers->end();
9291 ++it)
9292 {
9293 if ((*it)->i_getName() == aName)
9294 {
9295 aUSBController = (*it);
9296 return S_OK;
9297 }
9298 }
9299
9300 if (aSetError)
9301 return setError(VBOX_E_OBJECT_NOT_FOUND,
9302 tr("Could not find a storage controller named '%s'"),
9303 aName.c_str());
9304 return VBOX_E_OBJECT_NOT_FOUND;
9305}
9306
9307/**
9308 * Returns the number of USB controller instance of the given type.
9309 *
9310 * @param enmType USB controller type.
9311 */
9312ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9313{
9314 ULONG cCtrls = 0;
9315
9316 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9317 it != mUSBControllers->end();
9318 ++it)
9319 {
9320 if ((*it)->i_getControllerType() == enmType)
9321 cCtrls++;
9322 }
9323
9324 return cCtrls;
9325}
9326
9327HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9328 MediaData::AttachmentList &atts)
9329{
9330 AutoCaller autoCaller(this);
9331 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9332
9333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9334
9335 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9336 it != mMediaData->mAttachments.end();
9337 ++it)
9338 {
9339 const ComObjPtr<MediumAttachment> &pAtt = *it;
9340 // should never happen, but deal with NULL pointers in the list.
9341 AssertStmt(!pAtt.isNull(), continue);
9342
9343 // getControllerName() needs caller+read lock
9344 AutoCaller autoAttCaller(pAtt);
9345 if (FAILED(autoAttCaller.rc()))
9346 {
9347 atts.clear();
9348 return autoAttCaller.rc();
9349 }
9350 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9351
9352 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9353 atts.push_back(pAtt);
9354 }
9355
9356 return S_OK;
9357}
9358
9359
9360/**
9361 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9362 * file if the machine name was changed and about creating a new settings file
9363 * if this is a new machine.
9364 *
9365 * @note Must be never called directly but only from #saveSettings().
9366 */
9367HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9368{
9369 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9370
9371 HRESULT rc = S_OK;
9372
9373 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9374
9375 /// @todo need to handle primary group change, too
9376
9377 /* attempt to rename the settings file if machine name is changed */
9378 if ( mUserData->s.fNameSync
9379 && mUserData.isBackedUp()
9380 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9381 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9382 )
9383 {
9384 bool dirRenamed = false;
9385 bool fileRenamed = false;
9386
9387 Utf8Str configFile, newConfigFile;
9388 Utf8Str configFilePrev, newConfigFilePrev;
9389 Utf8Str configDir, newConfigDir;
9390
9391 do
9392 {
9393 int vrc = VINF_SUCCESS;
9394
9395 Utf8Str name = mUserData.backedUpData()->s.strName;
9396 Utf8Str newName = mUserData->s.strName;
9397 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9398 if (group == "/")
9399 group.setNull();
9400 Utf8Str newGroup = mUserData->s.llGroups.front();
9401 if (newGroup == "/")
9402 newGroup.setNull();
9403
9404 configFile = mData->m_strConfigFileFull;
9405
9406 /* first, rename the directory if it matches the group and machine name */
9407 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9408 group.c_str(), RTPATH_DELIMITER, name.c_str());
9409 /** @todo hack, make somehow use of ComposeMachineFilename */
9410 if (mUserData->s.fDirectoryIncludesUUID)
9411 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9412 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9413 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9414 /** @todo hack, make somehow use of ComposeMachineFilename */
9415 if (mUserData->s.fDirectoryIncludesUUID)
9416 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9417 configDir = configFile;
9418 configDir.stripFilename();
9419 newConfigDir = configDir;
9420 if ( configDir.length() >= groupPlusName.length()
9421 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9422 groupPlusName.c_str()))
9423 {
9424 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9425 Utf8Str newConfigBaseDir(newConfigDir);
9426 newConfigDir.append(newGroupPlusName);
9427 /* consistency: use \ if appropriate on the platform */
9428 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9429 /* new dir and old dir cannot be equal here because of 'if'
9430 * above and because name != newName */
9431 Assert(configDir != newConfigDir);
9432 if (!fSettingsFileIsNew)
9433 {
9434 /* perform real rename only if the machine is not new */
9435 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9436 if ( vrc == VERR_FILE_NOT_FOUND
9437 || vrc == VERR_PATH_NOT_FOUND)
9438 {
9439 /* create the parent directory, then retry renaming */
9440 Utf8Str parent(newConfigDir);
9441 parent.stripFilename();
9442 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9443 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9444 }
9445 if (RT_FAILURE(vrc))
9446 {
9447 rc = setError(E_FAIL,
9448 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9449 configDir.c_str(),
9450 newConfigDir.c_str(),
9451 vrc);
9452 break;
9453 }
9454 /* delete subdirectories which are no longer needed */
9455 Utf8Str dir(configDir);
9456 dir.stripFilename();
9457 while (dir != newConfigBaseDir && dir != ".")
9458 {
9459 vrc = RTDirRemove(dir.c_str());
9460 if (RT_FAILURE(vrc))
9461 break;
9462 dir.stripFilename();
9463 }
9464 dirRenamed = true;
9465 }
9466 }
9467
9468 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9469 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9470
9471 /* then try to rename the settings file itself */
9472 if (newConfigFile != configFile)
9473 {
9474 /* get the path to old settings file in renamed directory */
9475 configFile = Utf8StrFmt("%s%c%s",
9476 newConfigDir.c_str(),
9477 RTPATH_DELIMITER,
9478 RTPathFilename(configFile.c_str()));
9479 if (!fSettingsFileIsNew)
9480 {
9481 /* perform real rename only if the machine is not new */
9482 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9483 if (RT_FAILURE(vrc))
9484 {
9485 rc = setError(E_FAIL,
9486 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9487 configFile.c_str(),
9488 newConfigFile.c_str(),
9489 vrc);
9490 break;
9491 }
9492 fileRenamed = true;
9493 configFilePrev = configFile;
9494 configFilePrev += "-prev";
9495 newConfigFilePrev = newConfigFile;
9496 newConfigFilePrev += "-prev";
9497 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9498 }
9499 }
9500
9501 // update m_strConfigFileFull amd mConfigFile
9502 mData->m_strConfigFileFull = newConfigFile;
9503 // compute the relative path too
9504 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9505
9506 // store the old and new so that VirtualBox::i_saveSettings() can update
9507 // the media registry
9508 if ( mData->mRegistered
9509 && (configDir != newConfigDir || configFile != newConfigFile))
9510 {
9511 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9512
9513 if (pfNeedsGlobalSaveSettings)
9514 *pfNeedsGlobalSaveSettings = true;
9515 }
9516
9517 // in the saved state file path, replace the old directory with the new directory
9518 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9519 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9520
9521 // and do the same thing for the saved state file paths of all the online snapshots
9522 if (mData->mFirstSnapshot)
9523 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9524 newConfigDir.c_str());
9525 }
9526 while (0);
9527
9528 if (FAILED(rc))
9529 {
9530 /* silently try to rename everything back */
9531 if (fileRenamed)
9532 {
9533 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9534 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9535 }
9536 if (dirRenamed)
9537 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9538 }
9539
9540 if (FAILED(rc)) return rc;
9541 }
9542
9543 if (fSettingsFileIsNew)
9544 {
9545 /* create a virgin config file */
9546 int vrc = VINF_SUCCESS;
9547
9548 /* ensure the settings directory exists */
9549 Utf8Str path(mData->m_strConfigFileFull);
9550 path.stripFilename();
9551 if (!RTDirExists(path.c_str()))
9552 {
9553 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9554 if (RT_FAILURE(vrc))
9555 {
9556 return setError(E_FAIL,
9557 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9558 path.c_str(),
9559 vrc);
9560 }
9561 }
9562
9563 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9564 path = Utf8Str(mData->m_strConfigFileFull);
9565 RTFILE f = NIL_RTFILE;
9566 vrc = RTFileOpen(&f, path.c_str(),
9567 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9568 if (RT_FAILURE(vrc))
9569 return setError(E_FAIL,
9570 tr("Could not create the settings file '%s' (%Rrc)"),
9571 path.c_str(),
9572 vrc);
9573 RTFileClose(f);
9574 }
9575
9576 return rc;
9577}
9578
9579/**
9580 * Saves and commits machine data, user data and hardware data.
9581 *
9582 * Note that on failure, the data remains uncommitted.
9583 *
9584 * @a aFlags may combine the following flags:
9585 *
9586 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9587 * Used when saving settings after an operation that makes them 100%
9588 * correspond to the settings from the current snapshot.
9589 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9590 * #isReallyModified() returns false. This is necessary for cases when we
9591 * change machine data directly, not through the backup()/commit() mechanism.
9592 * - SaveS_Force: settings will be saved without doing a deep compare of the
9593 * settings structures. This is used when this is called because snapshots
9594 * have changed to avoid the overhead of the deep compare.
9595 *
9596 * @note Must be called from under this object's write lock. Locks children for
9597 * writing.
9598 *
9599 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9600 * initialized to false and that will be set to true by this function if
9601 * the caller must invoke VirtualBox::i_saveSettings() because the global
9602 * settings have changed. This will happen if a machine rename has been
9603 * saved and the global machine and media registries will therefore need
9604 * updating.
9605 */
9606HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9607 int aFlags /*= 0*/)
9608{
9609 LogFlowThisFuncEnter();
9610
9611 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9612
9613 /* make sure child objects are unable to modify the settings while we are
9614 * saving them */
9615 i_ensureNoStateDependencies();
9616
9617 AssertReturn(!i_isSnapshotMachine(),
9618 E_FAIL);
9619
9620 HRESULT rc = S_OK;
9621 bool fNeedsWrite = false;
9622
9623 /* First, prepare to save settings. It will care about renaming the
9624 * settings directory and file if the machine name was changed and about
9625 * creating a new settings file if this is a new machine. */
9626 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9627 if (FAILED(rc)) return rc;
9628
9629 // keep a pointer to the current settings structures
9630 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9631 settings::MachineConfigFile *pNewConfig = NULL;
9632
9633 try
9634 {
9635 // make a fresh one to have everyone write stuff into
9636 pNewConfig = new settings::MachineConfigFile(NULL);
9637 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9638
9639 // now go and copy all the settings data from COM to the settings structures
9640 // (this calles i_saveSettings() on all the COM objects in the machine)
9641 i_copyMachineDataToSettings(*pNewConfig);
9642
9643 if (aFlags & SaveS_ResetCurStateModified)
9644 {
9645 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9646 mData->mCurrentStateModified = FALSE;
9647 fNeedsWrite = true; // always, no need to compare
9648 }
9649 else if (aFlags & SaveS_Force)
9650 {
9651 fNeedsWrite = true; // always, no need to compare
9652 }
9653 else
9654 {
9655 if (!mData->mCurrentStateModified)
9656 {
9657 // do a deep compare of the settings that we just saved with the settings
9658 // previously stored in the config file; this invokes MachineConfigFile::operator==
9659 // which does a deep compare of all the settings, which is expensive but less expensive
9660 // than writing out XML in vain
9661 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9662
9663 // could still be modified if any settings changed
9664 mData->mCurrentStateModified = fAnySettingsChanged;
9665
9666 fNeedsWrite = fAnySettingsChanged;
9667 }
9668 else
9669 fNeedsWrite = true;
9670 }
9671
9672 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9673
9674 if (fNeedsWrite)
9675 // now spit it all out!
9676 pNewConfig->write(mData->m_strConfigFileFull);
9677
9678 mData->pMachineConfigFile = pNewConfig;
9679 delete pOldConfig;
9680 i_commit();
9681
9682 // after saving settings, we are no longer different from the XML on disk
9683 mData->flModifications = 0;
9684 }
9685 catch (HRESULT err)
9686 {
9687 // we assume that error info is set by the thrower
9688 rc = err;
9689
9690 // restore old config
9691 delete pNewConfig;
9692 mData->pMachineConfigFile = pOldConfig;
9693 }
9694 catch (...)
9695 {
9696 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9697 }
9698
9699 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9700 {
9701 /* Fire the data change event, even on failure (since we've already
9702 * committed all data). This is done only for SessionMachines because
9703 * mutable Machine instances are always not registered (i.e. private
9704 * to the client process that creates them) and thus don't need to
9705 * inform callbacks. */
9706 if (i_isSessionMachine())
9707 mParent->i_onMachineDataChange(mData->mUuid);
9708 }
9709
9710 LogFlowThisFunc(("rc=%08X\n", rc));
9711 LogFlowThisFuncLeave();
9712 return rc;
9713}
9714
9715/**
9716 * Implementation for saving the machine settings into the given
9717 * settings::MachineConfigFile instance. This copies machine extradata
9718 * from the previous machine config file in the instance data, if any.
9719 *
9720 * This gets called from two locations:
9721 *
9722 * -- Machine::i_saveSettings(), during the regular XML writing;
9723 *
9724 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9725 * exported to OVF and we write the VirtualBox proprietary XML
9726 * into a <vbox:Machine> tag.
9727 *
9728 * This routine fills all the fields in there, including snapshots, *except*
9729 * for the following:
9730 *
9731 * -- fCurrentStateModified. There is some special logic associated with that.
9732 *
9733 * The caller can then call MachineConfigFile::write() or do something else
9734 * with it.
9735 *
9736 * Caller must hold the machine lock!
9737 *
9738 * This throws XML errors and HRESULT, so the caller must have a catch block!
9739 */
9740void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9741{
9742 // deep copy extradata
9743 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9744
9745 config.uuid = mData->mUuid;
9746
9747 // copy name, description, OS type, teleport, UTC etc.
9748 config.machineUserData = mUserData->s;
9749
9750 // Encode the Icon Override data from Machine and store on config userdata.
9751 com::SafeArray<BYTE> iconByte;
9752 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
9753 ssize_t cbData = iconByte.size();
9754 if (cbData > 0)
9755 {
9756 ssize_t cchOut = RTBase64EncodedLength(cbData);
9757 Utf8Str strIconData;
9758 strIconData.reserve(cchOut+1);
9759 int vrc = RTBase64Encode(iconByte.raw(), cbData,
9760 strIconData.mutableRaw(), strIconData.capacity(),
9761 NULL);
9762 if (RT_FAILURE(vrc))
9763 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9764 strIconData.jolt();
9765 config.machineUserData.ovIcon = strIconData;
9766 }
9767 else
9768 config.machineUserData.ovIcon.setNull();
9769
9770 if ( mData->mMachineState == MachineState_Saved
9771 || mData->mMachineState == MachineState_Restoring
9772 // when deleting a snapshot we may or may not have a saved state in the current state,
9773 // so let's not assert here please
9774 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9775 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9776 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9777 && (!mSSData->strStateFilePath.isEmpty())
9778 )
9779 )
9780 {
9781 Assert(!mSSData->strStateFilePath.isEmpty());
9782 /* try to make the file name relative to the settings file dir */
9783 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9784 }
9785 else
9786 {
9787 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9788 config.strStateFile.setNull();
9789 }
9790
9791 if (mData->mCurrentSnapshot)
9792 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9793 else
9794 config.uuidCurrentSnapshot.clear();
9795
9796 config.timeLastStateChange = mData->mLastStateChange;
9797 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9798 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9799
9800 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9801 if (FAILED(rc)) throw rc;
9802
9803 rc = i_saveStorageControllers(config.storageMachine);
9804 if (FAILED(rc)) throw rc;
9805
9806 // save machine's media registry if this is VirtualBox 4.0 or later
9807 if (config.canHaveOwnMediaRegistry())
9808 {
9809 // determine machine folder
9810 Utf8Str strMachineFolder = i_getSettingsFileFull();
9811 strMachineFolder.stripFilename();
9812 mParent->i_saveMediaRegistry(config.mediaRegistry,
9813 i_getId(), // only media with registry ID == machine UUID
9814 strMachineFolder);
9815 // this throws HRESULT
9816 }
9817
9818 // save snapshots
9819 rc = i_saveAllSnapshots(config);
9820 if (FAILED(rc)) throw rc;
9821}
9822
9823/**
9824 * Saves all snapshots of the machine into the given machine config file. Called
9825 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9826 * @param config
9827 * @return
9828 */
9829HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9830{
9831 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9832
9833 HRESULT rc = S_OK;
9834
9835 try
9836 {
9837 config.llFirstSnapshot.clear();
9838
9839 if (mData->mFirstSnapshot)
9840 {
9841 settings::Snapshot snapNew;
9842 config.llFirstSnapshot.push_back(snapNew);
9843
9844 // get reference to the fresh copy of the snapshot on the list and
9845 // work on that copy directly to avoid excessive copying later
9846 settings::Snapshot &snap = config.llFirstSnapshot.front();
9847
9848 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9849 if (FAILED(rc)) throw rc;
9850 }
9851
9852// if (mType == IsSessionMachine)
9853// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9854
9855 }
9856 catch (HRESULT err)
9857 {
9858 /* we assume that error info is set by the thrower */
9859 rc = err;
9860 }
9861 catch (...)
9862 {
9863 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9864 }
9865
9866 return rc;
9867}
9868
9869/**
9870 * Saves the VM hardware configuration. It is assumed that the
9871 * given node is empty.
9872 *
9873 * @param data Reference to the settings object for the hardware config.
9874 * @param pDbg Pointer to the settings object for the debugging config
9875 * which happens to live in mHWData.
9876 * @param pAutostart Pointer to the settings object for the autostart config
9877 * which happens to live in mHWData.
9878 */
9879HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9880 settings::Autostart *pAutostart)
9881{
9882 HRESULT rc = S_OK;
9883
9884 try
9885 {
9886 /* The hardware version attribute (optional).
9887 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9888 if ( mHWData->mHWVersion == "1"
9889 && mSSData->strStateFilePath.isEmpty()
9890 )
9891 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9892 other point needs to be found where this can be done. */
9893
9894 data.strVersion = mHWData->mHWVersion;
9895 data.uuid = mHWData->mHardwareUUID;
9896
9897 // CPU
9898 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9899 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9900 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9901 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9902 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
9903 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9904 data.fPAE = !!mHWData->mPAEEnabled;
9905 data.enmLongMode = mHWData->mLongMode;
9906 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9907 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
9908
9909 /* Standard and Extended CPUID leafs. */
9910 data.llCpuIdLeafs.clear();
9911 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
9912 {
9913 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9914 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9915 }
9916 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
9917 {
9918 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9919 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9920 }
9921
9922 data.cCPUs = mHWData->mCPUCount;
9923 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9924 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9925
9926 data.llCpus.clear();
9927 if (data.fCpuHotPlug)
9928 {
9929 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
9930 {
9931 if (mHWData->mCPUAttached[idx])
9932 {
9933 settings::Cpu cpu;
9934 cpu.ulId = idx;
9935 data.llCpus.push_back(cpu);
9936 }
9937 }
9938 }
9939
9940 // memory
9941 data.ulMemorySizeMB = mHWData->mMemorySize;
9942 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9943
9944 // firmware
9945 data.firmwareType = mHWData->mFirmwareType;
9946
9947 // HID
9948 data.pointingHIDType = mHWData->mPointingHIDType;
9949 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9950
9951 // chipset
9952 data.chipsetType = mHWData->mChipsetType;
9953
9954 // paravirt
9955 data.paravirtProvider = mHWData->mParavirtProvider;
9956
9957
9958 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9959
9960 // HPET
9961 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9962
9963 // boot order
9964 data.mapBootOrder.clear();
9965 for (size_t i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9966 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9967
9968 // display
9969 data.graphicsControllerType = mHWData->mGraphicsControllerType;
9970 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9971 data.cMonitors = mHWData->mMonitorCount;
9972 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9973 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9974 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9975 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9976 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
9977 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
9978 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
9979 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
9980 {
9981 if (mHWData->maVideoCaptureScreens[i])
9982 ASMBitSet(&data.u64VideoCaptureScreens, i);
9983 else
9984 ASMBitClear(&data.u64VideoCaptureScreens, i);
9985 }
9986 /* store relative video capture file if possible */
9987 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
9988
9989 /* VRDEServer settings (optional) */
9990 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
9991 if (FAILED(rc)) throw rc;
9992
9993 /* BIOS (required) */
9994 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
9995 if (FAILED(rc)) throw rc;
9996
9997 /* USB Controller (required) */
9998 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
9999 {
10000 ComObjPtr<USBController> ctrl = *it;
10001 settings::USBController settingsCtrl;
10002
10003 settingsCtrl.strName = ctrl->i_getName();
10004 settingsCtrl.enmType = ctrl->i_getControllerType();
10005
10006 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10007 }
10008
10009 /* USB device filters (required) */
10010 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10011 if (FAILED(rc)) throw rc;
10012
10013 /* Network adapters (required) */
10014 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10015 data.llNetworkAdapters.clear();
10016 /* Write out only the nominal number of network adapters for this
10017 * chipset type. Since Machine::commit() hasn't been called there
10018 * may be extra NIC settings in the vector. */
10019 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10020 {
10021 settings::NetworkAdapter nic;
10022 nic.ulSlot = slot;
10023 /* paranoia check... must not be NULL, but must not crash either. */
10024 if (mNetworkAdapters[slot])
10025 {
10026 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10027 if (FAILED(rc)) throw rc;
10028
10029 data.llNetworkAdapters.push_back(nic);
10030 }
10031 }
10032
10033 /* Serial ports */
10034 data.llSerialPorts.clear();
10035 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10036 {
10037 settings::SerialPort s;
10038 s.ulSlot = slot;
10039 rc = mSerialPorts[slot]->i_saveSettings(s);
10040 if (FAILED(rc)) return rc;
10041
10042 data.llSerialPorts.push_back(s);
10043 }
10044
10045 /* Parallel ports */
10046 data.llParallelPorts.clear();
10047 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10048 {
10049 settings::ParallelPort p;
10050 p.ulSlot = slot;
10051 rc = mParallelPorts[slot]->i_saveSettings(p);
10052 if (FAILED(rc)) return rc;
10053
10054 data.llParallelPorts.push_back(p);
10055 }
10056
10057 /* Audio adapter */
10058 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10059 if (FAILED(rc)) return rc;
10060
10061 /* Shared folders */
10062 data.llSharedFolders.clear();
10063 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10064 it != mHWData->mSharedFolders.end();
10065 ++it)
10066 {
10067 SharedFolder *pSF = *it;
10068 AutoCaller sfCaller(pSF);
10069 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10070 settings::SharedFolder sf;
10071 sf.strName = pSF->i_getName();
10072 sf.strHostPath = pSF->i_getHostPath();
10073 sf.fWritable = !!pSF->i_isWritable();
10074 sf.fAutoMount = !!pSF->i_isAutoMounted();
10075
10076 data.llSharedFolders.push_back(sf);
10077 }
10078
10079 // clipboard
10080 data.clipboardMode = mHWData->mClipboardMode;
10081
10082 // drag'n'drop
10083 data.dndMode = mHWData->mDnDMode;
10084
10085 /* Guest */
10086 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10087
10088 // IO settings
10089 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10090 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10091
10092 /* BandwidthControl (required) */
10093 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10094 if (FAILED(rc)) throw rc;
10095
10096 /* Host PCI devices */
10097 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10098 it != mHWData->mPCIDeviceAssignments.end();
10099 ++it)
10100 {
10101 ComObjPtr<PCIDeviceAttachment> pda = *it;
10102 settings::HostPCIDeviceAttachment hpda;
10103
10104 rc = pda->i_saveSettings(hpda);
10105 if (FAILED(rc)) throw rc;
10106
10107 data.pciAttachments.push_back(hpda);
10108 }
10109
10110
10111 // guest properties
10112 data.llGuestProperties.clear();
10113#ifdef VBOX_WITH_GUEST_PROPS
10114 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10115 it != mHWData->mGuestProperties.end();
10116 ++it)
10117 {
10118 HWData::GuestProperty property = it->second;
10119
10120 /* Remove transient guest properties at shutdown unless we
10121 * are saving state */
10122 if ( ( mData->mMachineState == MachineState_PoweredOff
10123 || mData->mMachineState == MachineState_Aborted
10124 || mData->mMachineState == MachineState_Teleported)
10125 && ( property.mFlags & guestProp::TRANSIENT
10126 || property.mFlags & guestProp::TRANSRESET))
10127 continue;
10128 settings::GuestProperty prop;
10129 prop.strName = it->first;
10130 prop.strValue = property.strValue;
10131 prop.timestamp = property.mTimestamp;
10132 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10133 guestProp::writeFlags(property.mFlags, szFlags);
10134 prop.strFlags = szFlags;
10135
10136 data.llGuestProperties.push_back(prop);
10137 }
10138
10139 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10140 /* I presume this doesn't require a backup(). */
10141 mData->mGuestPropertiesModified = FALSE;
10142#endif /* VBOX_WITH_GUEST_PROPS defined */
10143
10144 *pDbg = mHWData->mDebugging;
10145 *pAutostart = mHWData->mAutostart;
10146
10147 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10148 }
10149 catch(std::bad_alloc &)
10150 {
10151 return E_OUTOFMEMORY;
10152 }
10153
10154 AssertComRC(rc);
10155 return rc;
10156}
10157
10158/**
10159 * Saves the storage controller configuration.
10160 *
10161 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10162 */
10163HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10164{
10165 data.llStorageControllers.clear();
10166
10167 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10168 it != mStorageControllers->end();
10169 ++it)
10170 {
10171 HRESULT rc;
10172 ComObjPtr<StorageController> pCtl = *it;
10173
10174 settings::StorageController ctl;
10175 ctl.strName = pCtl->i_getName();
10176 ctl.controllerType = pCtl->i_getControllerType();
10177 ctl.storageBus = pCtl->i_getStorageBus();
10178 ctl.ulInstance = pCtl->i_getInstance();
10179 ctl.fBootable = pCtl->i_getBootable();
10180
10181 /* Save the port count. */
10182 ULONG portCount;
10183 rc = pCtl->COMGETTER(PortCount)(&portCount);
10184 ComAssertComRCRet(rc, rc);
10185 ctl.ulPortCount = portCount;
10186
10187 /* Save fUseHostIOCache */
10188 BOOL fUseHostIOCache;
10189 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10190 ComAssertComRCRet(rc, rc);
10191 ctl.fUseHostIOCache = !!fUseHostIOCache;
10192
10193 /* Save IDE emulation settings. */
10194 if (ctl.controllerType == StorageControllerType_IntelAhci)
10195 {
10196 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10197 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10198 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10199 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10200 )
10201 ComAssertComRCRet(rc, rc);
10202 }
10203
10204 /* save the devices now. */
10205 rc = i_saveStorageDevices(pCtl, ctl);
10206 ComAssertComRCRet(rc, rc);
10207
10208 data.llStorageControllers.push_back(ctl);
10209 }
10210
10211 return S_OK;
10212}
10213
10214/**
10215 * Saves the hard disk configuration.
10216 */
10217HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10218 settings::StorageController &data)
10219{
10220 MediaData::AttachmentList atts;
10221
10222 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10223 if (FAILED(rc)) return rc;
10224
10225 data.llAttachedDevices.clear();
10226 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10227 it != atts.end();
10228 ++it)
10229 {
10230 settings::AttachedDevice dev;
10231 IMediumAttachment *iA = *it;
10232 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10233 Medium *pMedium = pAttach->i_getMedium();
10234
10235 dev.deviceType = pAttach->i_getType();
10236 dev.lPort = pAttach->i_getPort();
10237 dev.lDevice = pAttach->i_getDevice();
10238 dev.fPassThrough = pAttach->i_getPassthrough();
10239 dev.fHotPluggable = pAttach->i_getHotPluggable();
10240 if (pMedium)
10241 {
10242 if (pMedium->i_isHostDrive())
10243 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10244 else
10245 dev.uuid = pMedium->i_getId();
10246 dev.fTempEject = pAttach->i_getTempEject();
10247 dev.fNonRotational = pAttach->i_getNonRotational();
10248 dev.fDiscard = pAttach->i_getDiscard();
10249 }
10250
10251 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10252
10253 data.llAttachedDevices.push_back(dev);
10254 }
10255
10256 return S_OK;
10257}
10258
10259/**
10260 * Saves machine state settings as defined by aFlags
10261 * (SaveSTS_* values).
10262 *
10263 * @param aFlags Combination of SaveSTS_* flags.
10264 *
10265 * @note Locks objects for writing.
10266 */
10267HRESULT Machine::i_saveStateSettings(int aFlags)
10268{
10269 if (aFlags == 0)
10270 return S_OK;
10271
10272 AutoCaller autoCaller(this);
10273 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10274
10275 /* This object's write lock is also necessary to serialize file access
10276 * (prevent concurrent reads and writes) */
10277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10278
10279 HRESULT rc = S_OK;
10280
10281 Assert(mData->pMachineConfigFile);
10282
10283 try
10284 {
10285 if (aFlags & SaveSTS_CurStateModified)
10286 mData->pMachineConfigFile->fCurrentStateModified = true;
10287
10288 if (aFlags & SaveSTS_StateFilePath)
10289 {
10290 if (!mSSData->strStateFilePath.isEmpty())
10291 /* try to make the file name relative to the settings file dir */
10292 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10293 else
10294 mData->pMachineConfigFile->strStateFile.setNull();
10295 }
10296
10297 if (aFlags & SaveSTS_StateTimeStamp)
10298 {
10299 Assert( mData->mMachineState != MachineState_Aborted
10300 || mSSData->strStateFilePath.isEmpty());
10301
10302 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10303
10304 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10305//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10306 }
10307
10308 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10309 }
10310 catch (...)
10311 {
10312 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10313 }
10314
10315 return rc;
10316}
10317
10318/**
10319 * Ensures that the given medium is added to a media registry. If this machine
10320 * was created with 4.0 or later, then the machine registry is used. Otherwise
10321 * the global VirtualBox media registry is used.
10322 *
10323 * Caller must NOT hold machine lock, media tree or any medium locks!
10324 *
10325 * @param pMedium
10326 */
10327void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10328{
10329 /* Paranoia checks: do not hold machine or media tree locks. */
10330 AssertReturnVoid(!isWriteLockOnCurrentThread());
10331 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10332
10333 ComObjPtr<Medium> pBase;
10334 {
10335 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10336 pBase = pMedium->i_getBase();
10337 }
10338
10339 /* Paranoia checks: do not hold medium locks. */
10340 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10341 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10342
10343 // decide which medium registry to use now that the medium is attached:
10344 Guid uuid;
10345 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10346 // machine XML is VirtualBox 4.0 or higher:
10347 uuid = i_getId(); // machine UUID
10348 else
10349 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10350
10351 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10352 mParent->i_markRegistryModified(uuid);
10353
10354 /* For more complex hard disk structures it can happen that the base
10355 * medium isn't yet associated with any medium registry. Do that now. */
10356 if (pMedium != pBase)
10357 {
10358 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10359 mParent->i_markRegistryModified(uuid);
10360 }
10361}
10362
10363/**
10364 * Creates differencing hard disks for all normal hard disks attached to this
10365 * machine and a new set of attachments to refer to created disks.
10366 *
10367 * Used when taking a snapshot or when deleting the current state. Gets called
10368 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10369 *
10370 * This method assumes that mMediaData contains the original hard disk attachments
10371 * it needs to create diffs for. On success, these attachments will be replaced
10372 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10373 * called to delete created diffs which will also rollback mMediaData and restore
10374 * whatever was backed up before calling this method.
10375 *
10376 * Attachments with non-normal hard disks are left as is.
10377 *
10378 * If @a aOnline is @c false then the original hard disks that require implicit
10379 * diffs will be locked for reading. Otherwise it is assumed that they are
10380 * already locked for writing (when the VM was started). Note that in the latter
10381 * case it is responsibility of the caller to lock the newly created diffs for
10382 * writing if this method succeeds.
10383 *
10384 * @param aProgress Progress object to run (must contain at least as
10385 * many operations left as the number of hard disks
10386 * attached).
10387 * @param aOnline Whether the VM was online prior to this operation.
10388 *
10389 * @note The progress object is not marked as completed, neither on success nor
10390 * on failure. This is a responsibility of the caller.
10391 *
10392 * @note Locks this object and the media tree for writing.
10393 */
10394HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10395 ULONG aWeight,
10396 bool aOnline)
10397{
10398 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10399
10400 AutoCaller autoCaller(this);
10401 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10402
10403 AutoMultiWriteLock2 alock(this->lockHandle(),
10404 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10405
10406 /* must be in a protective state because we release the lock below */
10407 AssertReturn( mData->mMachineState == MachineState_Saving
10408 || mData->mMachineState == MachineState_LiveSnapshotting
10409 || mData->mMachineState == MachineState_RestoringSnapshot
10410 || mData->mMachineState == MachineState_DeletingSnapshot
10411 , E_FAIL);
10412
10413 HRESULT rc = S_OK;
10414
10415 // use appropriate locked media map (online or offline)
10416 MediumLockListMap lockedMediaOffline;
10417 MediumLockListMap *lockedMediaMap;
10418 if (aOnline)
10419 lockedMediaMap = &mData->mSession.mLockedMedia;
10420 else
10421 lockedMediaMap = &lockedMediaOffline;
10422
10423 try
10424 {
10425 if (!aOnline)
10426 {
10427 /* lock all attached hard disks early to detect "in use"
10428 * situations before creating actual diffs */
10429 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10430 it != mMediaData->mAttachments.end();
10431 ++it)
10432 {
10433 MediumAttachment* pAtt = *it;
10434 if (pAtt->i_getType() == DeviceType_HardDisk)
10435 {
10436 Medium* pMedium = pAtt->i_getMedium();
10437 Assert(pMedium);
10438
10439 MediumLockList *pMediumLockList(new MediumLockList());
10440 alock.release();
10441 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10442 false /* fMediumLockWrite */,
10443 NULL,
10444 *pMediumLockList);
10445 alock.acquire();
10446 if (FAILED(rc))
10447 {
10448 delete pMediumLockList;
10449 throw rc;
10450 }
10451 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10452 if (FAILED(rc))
10453 {
10454 throw setError(rc,
10455 tr("Collecting locking information for all attached media failed"));
10456 }
10457 }
10458 }
10459
10460 /* Now lock all media. If this fails, nothing is locked. */
10461 alock.release();
10462 rc = lockedMediaMap->Lock();
10463 alock.acquire();
10464 if (FAILED(rc))
10465 {
10466 throw setError(rc,
10467 tr("Locking of attached media failed"));
10468 }
10469 }
10470
10471 /* remember the current list (note that we don't use backup() since
10472 * mMediaData may be already backed up) */
10473 MediaData::AttachmentList atts = mMediaData->mAttachments;
10474
10475 /* start from scratch */
10476 mMediaData->mAttachments.clear();
10477
10478 /* go through remembered attachments and create diffs for normal hard
10479 * disks and attach them */
10480 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10481 it != atts.end();
10482 ++it)
10483 {
10484 MediumAttachment* pAtt = *it;
10485
10486 DeviceType_T devType = pAtt->i_getType();
10487 Medium* pMedium = pAtt->i_getMedium();
10488
10489 if ( devType != DeviceType_HardDisk
10490 || pMedium == NULL
10491 || pMedium->i_getType() != MediumType_Normal)
10492 {
10493 /* copy the attachment as is */
10494
10495 /** @todo the progress object created in Console::TakeSnaphot
10496 * only expects operations for hard disks. Later other
10497 * device types need to show up in the progress as well. */
10498 if (devType == DeviceType_HardDisk)
10499 {
10500 if (pMedium == NULL)
10501 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10502 aWeight); // weight
10503 else
10504 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10505 pMedium->i_getBase()->i_getName().c_str()).raw(),
10506 aWeight); // weight
10507 }
10508
10509 mMediaData->mAttachments.push_back(pAtt);
10510 continue;
10511 }
10512
10513 /* need a diff */
10514 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10515 pMedium->i_getBase()->i_getName().c_str()).raw(),
10516 aWeight); // weight
10517
10518 Utf8Str strFullSnapshotFolder;
10519 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10520
10521 ComObjPtr<Medium> diff;
10522 diff.createObject();
10523 // store the diff in the same registry as the parent
10524 // (this cannot fail here because we can't create implicit diffs for
10525 // unregistered images)
10526 Guid uuidRegistryParent;
10527 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10528 Assert(fInRegistry); NOREF(fInRegistry);
10529 rc = diff->init(mParent,
10530 pMedium->i_getPreferredDiffFormat(),
10531 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10532 uuidRegistryParent);
10533 if (FAILED(rc)) throw rc;
10534
10535 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10536 * the push_back? Looks like we're going to release medium with the
10537 * wrong kind of lock (general issue with if we fail anywhere at all)
10538 * and an orphaned VDI in the snapshots folder. */
10539
10540 /* update the appropriate lock list */
10541 MediumLockList *pMediumLockList;
10542 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10543 AssertComRCThrowRC(rc);
10544 if (aOnline)
10545 {
10546 alock.release();
10547 /* The currently attached medium will be read-only, change
10548 * the lock type to read. */
10549 rc = pMediumLockList->Update(pMedium, false);
10550 alock.acquire();
10551 AssertComRCThrowRC(rc);
10552 }
10553
10554 /* release the locks before the potentially lengthy operation */
10555 alock.release();
10556 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10557 pMediumLockList,
10558 NULL /* aProgress */,
10559 true /* aWait */);
10560 alock.acquire();
10561 if (FAILED(rc)) throw rc;
10562
10563 /* actual lock list update is done in Medium::commitMedia */
10564
10565 rc = diff->i_addBackReference(mData->mUuid);
10566 AssertComRCThrowRC(rc);
10567
10568 /* add a new attachment */
10569 ComObjPtr<MediumAttachment> attachment;
10570 attachment.createObject();
10571 rc = attachment->init(this,
10572 diff,
10573 pAtt->i_getControllerName(),
10574 pAtt->i_getPort(),
10575 pAtt->i_getDevice(),
10576 DeviceType_HardDisk,
10577 true /* aImplicit */,
10578 false /* aPassthrough */,
10579 false /* aTempEject */,
10580 pAtt->i_getNonRotational(),
10581 pAtt->i_getDiscard(),
10582 pAtt->i_getHotPluggable(),
10583 pAtt->i_getBandwidthGroup());
10584 if (FAILED(rc)) throw rc;
10585
10586 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10587 AssertComRCThrowRC(rc);
10588 mMediaData->mAttachments.push_back(attachment);
10589 }
10590 }
10591 catch (HRESULT aRC) { rc = aRC; }
10592
10593 /* unlock all hard disks we locked when there is no VM */
10594 if (!aOnline)
10595 {
10596 ErrorInfoKeeper eik;
10597
10598 HRESULT rc1 = lockedMediaMap->Clear();
10599 AssertComRC(rc1);
10600 }
10601
10602 return rc;
10603}
10604
10605/**
10606 * Deletes implicit differencing hard disks created either by
10607 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10608 *
10609 * Note that to delete hard disks created by #AttachDevice() this method is
10610 * called from #fixupMedia() when the changes are rolled back.
10611 *
10612 * @note Locks this object and the media tree for writing.
10613 */
10614HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10615{
10616 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10617
10618 AutoCaller autoCaller(this);
10619 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10620
10621 AutoMultiWriteLock2 alock(this->lockHandle(),
10622 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10623
10624 /* We absolutely must have backed up state. */
10625 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10626
10627 /* Check if there are any implicitly created diff images. */
10628 bool fImplicitDiffs = false;
10629 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10630 it != mMediaData->mAttachments.end();
10631 ++it)
10632 {
10633 const ComObjPtr<MediumAttachment> &pAtt = *it;
10634 if (pAtt->i_isImplicit())
10635 {
10636 fImplicitDiffs = true;
10637 break;
10638 }
10639 }
10640 /* If there is nothing to do, leave early. This saves lots of image locking
10641 * effort. It also avoids a MachineStateChanged event without real reason.
10642 * This is important e.g. when loading a VM config, because there should be
10643 * no events. Otherwise API clients can become thoroughly confused for
10644 * inaccessible VMs (the code for loading VM configs uses this method for
10645 * cleanup if the config makes no sense), as they take such events as an
10646 * indication that the VM is alive, and they would force the VM config to
10647 * be reread, leading to an endless loop. */
10648 if (!fImplicitDiffs)
10649 return S_OK;
10650
10651 HRESULT rc = S_OK;
10652 MachineState_T oldState = mData->mMachineState;
10653
10654 /* will release the lock before the potentially lengthy operation,
10655 * so protect with the special state (unless already protected) */
10656 if ( oldState != MachineState_Saving
10657 && oldState != MachineState_LiveSnapshotting
10658 && oldState != MachineState_RestoringSnapshot
10659 && oldState != MachineState_DeletingSnapshot
10660 && oldState != MachineState_DeletingSnapshotOnline
10661 && oldState != MachineState_DeletingSnapshotPaused
10662 )
10663 i_setMachineState(MachineState_SettingUp);
10664
10665 // use appropriate locked media map (online or offline)
10666 MediumLockListMap lockedMediaOffline;
10667 MediumLockListMap *lockedMediaMap;
10668 if (aOnline)
10669 lockedMediaMap = &mData->mSession.mLockedMedia;
10670 else
10671 lockedMediaMap = &lockedMediaOffline;
10672
10673 try
10674 {
10675 if (!aOnline)
10676 {
10677 /* lock all attached hard disks early to detect "in use"
10678 * situations before deleting actual diffs */
10679 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10680 it != mMediaData->mAttachments.end();
10681 ++it)
10682 {
10683 MediumAttachment* pAtt = *it;
10684 if (pAtt->i_getType() == DeviceType_HardDisk)
10685 {
10686 Medium* pMedium = pAtt->i_getMedium();
10687 Assert(pMedium);
10688
10689 MediumLockList *pMediumLockList(new MediumLockList());
10690 alock.release();
10691 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10692 false /* fMediumLockWrite */,
10693 NULL,
10694 *pMediumLockList);
10695 alock.acquire();
10696
10697 if (FAILED(rc))
10698 {
10699 delete pMediumLockList;
10700 throw rc;
10701 }
10702
10703 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10704 if (FAILED(rc))
10705 throw rc;
10706 }
10707 }
10708
10709 if (FAILED(rc))
10710 throw rc;
10711 } // end of offline
10712
10713 /* Lock lists are now up to date and include implicitly created media */
10714
10715 /* Go through remembered attachments and delete all implicitly created
10716 * diffs and fix up the attachment information */
10717 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10718 MediaData::AttachmentList implicitAtts;
10719 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10720 it != mMediaData->mAttachments.end();
10721 ++it)
10722 {
10723 ComObjPtr<MediumAttachment> pAtt = *it;
10724 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10725 if (pMedium.isNull())
10726 continue;
10727
10728 // Implicit attachments go on the list for deletion and back references are removed.
10729 if (pAtt->i_isImplicit())
10730 {
10731 /* Deassociate and mark for deletion */
10732 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10733 rc = pMedium->i_removeBackReference(mData->mUuid);
10734 if (FAILED(rc))
10735 throw rc;
10736 implicitAtts.push_back(pAtt);
10737 continue;
10738 }
10739
10740 /* Was this medium attached before? */
10741 if (!i_findAttachment(oldAtts, pMedium))
10742 {
10743 /* no: de-associate */
10744 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10745 rc = pMedium->i_removeBackReference(mData->mUuid);
10746 if (FAILED(rc))
10747 throw rc;
10748 continue;
10749 }
10750 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10751 }
10752
10753 /* If there are implicit attachments to delete, throw away the lock
10754 * map contents (which will unlock all media) since the medium
10755 * attachments will be rolled back. Below we need to completely
10756 * recreate the lock map anyway since it is infinitely complex to
10757 * do this incrementally (would need reconstructing each attachment
10758 * change, which would be extremely hairy). */
10759 if (implicitAtts.size() != 0)
10760 {
10761 ErrorInfoKeeper eik;
10762
10763 HRESULT rc1 = lockedMediaMap->Clear();
10764 AssertComRC(rc1);
10765 }
10766
10767 /* rollback hard disk changes */
10768 mMediaData.rollback();
10769
10770 MultiResult mrc(S_OK);
10771
10772 // Delete unused implicit diffs.
10773 if (implicitAtts.size() != 0)
10774 {
10775 alock.release();
10776
10777 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10778 {
10779 // Remove medium associated with this attachment.
10780 ComObjPtr<MediumAttachment> pAtt = *it;
10781 Assert(pAtt);
10782 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10783 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10784 Assert(pMedium);
10785
10786 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10787 // continue on delete failure, just collect error messages
10788 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10789 pMedium->i_getLocationFull().c_str() ));
10790 mrc = rc;
10791 }
10792
10793 alock.acquire();
10794
10795 /* if there is a VM recreate media lock map as mentioned above,
10796 * otherwise it is a waste of time and we leave things unlocked */
10797 if (aOnline)
10798 {
10799 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10800 /* must never be NULL, but better safe than sorry */
10801 if (!pMachine.isNull())
10802 {
10803 alock.release();
10804 rc = mData->mSession.mMachine->lockMedia();
10805 alock.acquire();
10806 if (FAILED(rc))
10807 throw rc;
10808 }
10809 }
10810 }
10811 }
10812 catch (HRESULT aRC) {rc = aRC;}
10813
10814 if (mData->mMachineState == MachineState_SettingUp)
10815 i_setMachineState(oldState);
10816
10817 /* unlock all hard disks we locked when there is no VM */
10818 if (!aOnline)
10819 {
10820 ErrorInfoKeeper eik;
10821
10822 HRESULT rc1 = lockedMediaMap->Clear();
10823 AssertComRC(rc1);
10824 }
10825
10826 return rc;
10827}
10828
10829
10830/**
10831 * Looks through the given list of media attachments for one with the given parameters
10832 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10833 * can be searched as well if needed.
10834 *
10835 * @param list
10836 * @param aControllerName
10837 * @param aControllerPort
10838 * @param aDevice
10839 * @return
10840 */
10841MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10842 IN_BSTR aControllerName,
10843 LONG aControllerPort,
10844 LONG aDevice)
10845{
10846 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10847 {
10848 MediumAttachment *pAttach = *it;
10849 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10850 return pAttach;
10851 }
10852
10853 return NULL;
10854}
10855
10856/**
10857 * Looks through the given list of media attachments for one with the given parameters
10858 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10859 * can be searched as well if needed.
10860 *
10861 * @param list
10862 * @param aControllerName
10863 * @param aControllerPort
10864 * @param aDevice
10865 * @return
10866 */
10867MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10868 ComObjPtr<Medium> pMedium)
10869{
10870 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10871 {
10872 MediumAttachment *pAttach = *it;
10873 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10874 if (pMediumThis == pMedium)
10875 return pAttach;
10876 }
10877
10878 return NULL;
10879}
10880
10881/**
10882 * Looks through the given list of media attachments for one with the given parameters
10883 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10884 * can be searched as well if needed.
10885 *
10886 * @param list
10887 * @param aControllerName
10888 * @param aControllerPort
10889 * @param aDevice
10890 * @return
10891 */
10892MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10893 Guid &id)
10894{
10895 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10896 {
10897 MediumAttachment *pAttach = *it;
10898 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10899 if (pMediumThis->i_getId() == id)
10900 return pAttach;
10901 }
10902
10903 return NULL;
10904}
10905
10906/**
10907 * Main implementation for Machine::DetachDevice. This also gets called
10908 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10909 *
10910 * @param pAttach Medium attachment to detach.
10911 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10912 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
10913 * SnapshotMachine, and this must be its snapshot.
10914 * @return
10915 */
10916HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
10917 AutoWriteLock &writeLock,
10918 Snapshot *pSnapshot)
10919{
10920 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
10921 DeviceType_T mediumType = pAttach->i_getType();
10922
10923 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
10924
10925 if (pAttach->i_isImplicit())
10926 {
10927 /* attempt to implicitly delete the implicitly created diff */
10928
10929 /// @todo move the implicit flag from MediumAttachment to Medium
10930 /// and forbid any hard disk operation when it is implicit. Or maybe
10931 /// a special media state for it to make it even more simple.
10932
10933 Assert(mMediaData.isBackedUp());
10934
10935 /* will release the lock before the potentially lengthy operation, so
10936 * protect with the special state */
10937 MachineState_T oldState = mData->mMachineState;
10938 i_setMachineState(MachineState_SettingUp);
10939
10940 writeLock.release();
10941
10942 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
10943 true /*aWait*/);
10944
10945 writeLock.acquire();
10946
10947 i_setMachineState(oldState);
10948
10949 if (FAILED(rc)) return rc;
10950 }
10951
10952 i_setModified(IsModified_Storage);
10953 mMediaData.backup();
10954 mMediaData->mAttachments.remove(pAttach);
10955
10956 if (!oldmedium.isNull())
10957 {
10958 // if this is from a snapshot, do not defer detachment to commitMedia()
10959 if (pSnapshot)
10960 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
10961 // else if non-hard disk media, do not defer detachment to commitMedia() either
10962 else if (mediumType != DeviceType_HardDisk)
10963 oldmedium->i_removeBackReference(mData->mUuid);
10964 }
10965
10966 return S_OK;
10967}
10968
10969/**
10970 * Goes thru all media of the given list and
10971 *
10972 * 1) calls i_detachDevice() on each of them for this machine and
10973 * 2) adds all Medium objects found in the process to the given list,
10974 * depending on cleanupMode.
10975 *
10976 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10977 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10978 * media to the list.
10979 *
10980 * This gets called from Machine::Unregister, both for the actual Machine and
10981 * the SnapshotMachine objects that might be found in the snapshots.
10982 *
10983 * Requires caller and locking. The machine lock must be passed in because it
10984 * will be passed on to i_detachDevice which needs it for temporary unlocking.
10985 *
10986 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
10987 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10988 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
10989 * Full, then all media get added;
10990 * otherwise no media get added.
10991 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10992 * @return
10993 */
10994HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
10995 Snapshot *pSnapshot,
10996 CleanupMode_T cleanupMode,
10997 MediaList &llMedia)
10998{
10999 Assert(isWriteLockOnCurrentThread());
11000
11001 HRESULT rc;
11002
11003 // make a temporary list because i_detachDevice invalidates iterators into
11004 // mMediaData->mAttachments
11005 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11006
11007 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11008 {
11009 ComObjPtr<MediumAttachment> &pAttach = *it;
11010 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11011
11012 if (!pMedium.isNull())
11013 {
11014 AutoCaller mac(pMedium);
11015 if (FAILED(mac.rc())) return mac.rc();
11016 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11017 DeviceType_T devType = pMedium->i_getDeviceType();
11018 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11019 && devType == DeviceType_HardDisk)
11020 || (cleanupMode == CleanupMode_Full)
11021 )
11022 {
11023 llMedia.push_back(pMedium);
11024 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11025 /*
11026 * Search for medias which are not attached to any machine, but
11027 * in the chain to an attached disk. Mediums are only consided
11028 * if they are:
11029 * - have only one child
11030 * - no references to any machines
11031 * - are of normal medium type
11032 */
11033 while (!pParent.isNull())
11034 {
11035 AutoCaller mac1(pParent);
11036 if (FAILED(mac1.rc())) return mac1.rc();
11037 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11038 if (pParent->i_getChildren().size() == 1)
11039 {
11040 if ( pParent->i_getMachineBackRefCount() == 0
11041 && pParent->i_getType() == MediumType_Normal
11042 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11043 llMedia.push_back(pParent);
11044 }
11045 else
11046 break;
11047 pParent = pParent->i_getParent();
11048 }
11049 }
11050 }
11051
11052 // real machine: then we need to use the proper method
11053 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11054
11055 if (FAILED(rc))
11056 return rc;
11057 }
11058
11059 return S_OK;
11060}
11061
11062/**
11063 * Perform deferred hard disk detachments.
11064 *
11065 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11066 * backed up).
11067 *
11068 * If @a aOnline is @c true then this method will also unlock the old hard disks
11069 * for which the new implicit diffs were created and will lock these new diffs for
11070 * writing.
11071 *
11072 * @param aOnline Whether the VM was online prior to this operation.
11073 *
11074 * @note Locks this object for writing!
11075 */
11076void Machine::i_commitMedia(bool aOnline /*= false*/)
11077{
11078 AutoCaller autoCaller(this);
11079 AssertComRCReturnVoid(autoCaller.rc());
11080
11081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11082
11083 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11084
11085 HRESULT rc = S_OK;
11086
11087 /* no attach/detach operations -- nothing to do */
11088 if (!mMediaData.isBackedUp())
11089 return;
11090
11091 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11092 bool fMediaNeedsLocking = false;
11093
11094 /* enumerate new attachments */
11095 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11096 it != mMediaData->mAttachments.end();
11097 ++it)
11098 {
11099 MediumAttachment *pAttach = *it;
11100
11101 pAttach->i_commit();
11102
11103 Medium* pMedium = pAttach->i_getMedium();
11104 bool fImplicit = pAttach->i_isImplicit();
11105
11106 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11107 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11108 fImplicit));
11109
11110 /** @todo convert all this Machine-based voodoo to MediumAttachment
11111 * based commit logic. */
11112 if (fImplicit)
11113 {
11114 /* convert implicit attachment to normal */
11115 pAttach->i_setImplicit(false);
11116
11117 if ( aOnline
11118 && pMedium
11119 && pAttach->i_getType() == DeviceType_HardDisk
11120 )
11121 {
11122 ComObjPtr<Medium> parent = pMedium->i_getParent();
11123 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11124
11125 /* update the appropriate lock list */
11126 MediumLockList *pMediumLockList;
11127 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11128 AssertComRC(rc);
11129 if (pMediumLockList)
11130 {
11131 /* unlock if there's a need to change the locking */
11132 if (!fMediaNeedsLocking)
11133 {
11134 rc = mData->mSession.mLockedMedia.Unlock();
11135 AssertComRC(rc);
11136 fMediaNeedsLocking = true;
11137 }
11138 rc = pMediumLockList->Update(parent, false);
11139 AssertComRC(rc);
11140 rc = pMediumLockList->Append(pMedium, true);
11141 AssertComRC(rc);
11142 }
11143 }
11144
11145 continue;
11146 }
11147
11148 if (pMedium)
11149 {
11150 /* was this medium attached before? */
11151 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11152 {
11153 MediumAttachment *pOldAttach = *oldIt;
11154 if (pOldAttach->i_getMedium() == pMedium)
11155 {
11156 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11157
11158 /* yes: remove from old to avoid de-association */
11159 oldAtts.erase(oldIt);
11160 break;
11161 }
11162 }
11163 }
11164 }
11165
11166 /* enumerate remaining old attachments and de-associate from the
11167 * current machine state */
11168 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11169 {
11170 MediumAttachment *pAttach = *it;
11171 Medium* pMedium = pAttach->i_getMedium();
11172
11173 /* Detach only hard disks, since DVD/floppy media is detached
11174 * instantly in MountMedium. */
11175 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11176 {
11177 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11178
11179 /* now de-associate from the current machine state */
11180 rc = pMedium->i_removeBackReference(mData->mUuid);
11181 AssertComRC(rc);
11182
11183 if (aOnline)
11184 {
11185 /* unlock since medium is not used anymore */
11186 MediumLockList *pMediumLockList;
11187 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11188 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11189 {
11190 /* this happens for online snapshots, there the attachment
11191 * is changing, but only to a diff image created under
11192 * the old one, so there is no separate lock list */
11193 Assert(!pMediumLockList);
11194 }
11195 else
11196 {
11197 AssertComRC(rc);
11198 if (pMediumLockList)
11199 {
11200 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11201 AssertComRC(rc);
11202 }
11203 }
11204 }
11205 }
11206 }
11207
11208 /* take media locks again so that the locking state is consistent */
11209 if (fMediaNeedsLocking)
11210 {
11211 Assert(aOnline);
11212 rc = mData->mSession.mLockedMedia.Lock();
11213 AssertComRC(rc);
11214 }
11215
11216 /* commit the hard disk changes */
11217 mMediaData.commit();
11218
11219 if (i_isSessionMachine())
11220 {
11221 /*
11222 * Update the parent machine to point to the new owner.
11223 * This is necessary because the stored parent will point to the
11224 * session machine otherwise and cause crashes or errors later
11225 * when the session machine gets invalid.
11226 */
11227 /** @todo Change the MediumAttachment class to behave like any other
11228 * class in this regard by creating peer MediumAttachment
11229 * objects for session machines and share the data with the peer
11230 * machine.
11231 */
11232 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11233 it != mMediaData->mAttachments.end();
11234 ++it)
11235 (*it)->i_updateParentMachine(mPeer);
11236
11237 /* attach new data to the primary machine and reshare it */
11238 mPeer->mMediaData.attach(mMediaData);
11239 }
11240
11241 return;
11242}
11243
11244/**
11245 * Perform deferred deletion of implicitly created diffs.
11246 *
11247 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11248 * backed up).
11249 *
11250 * @note Locks this object for writing!
11251 */
11252void Machine::i_rollbackMedia()
11253{
11254 AutoCaller autoCaller(this);
11255 AssertComRCReturnVoid(autoCaller.rc());
11256
11257 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11258 LogFlowThisFunc(("Entering rollbackMedia\n"));
11259
11260 HRESULT rc = S_OK;
11261
11262 /* no attach/detach operations -- nothing to do */
11263 if (!mMediaData.isBackedUp())
11264 return;
11265
11266 /* enumerate new attachments */
11267 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11268 it != mMediaData->mAttachments.end();
11269 ++it)
11270 {
11271 MediumAttachment *pAttach = *it;
11272 /* Fix up the backrefs for DVD/floppy media. */
11273 if (pAttach->i_getType() != DeviceType_HardDisk)
11274 {
11275 Medium* pMedium = pAttach->i_getMedium();
11276 if (pMedium)
11277 {
11278 rc = pMedium->i_removeBackReference(mData->mUuid);
11279 AssertComRC(rc);
11280 }
11281 }
11282
11283 (*it)->i_rollback();
11284
11285 pAttach = *it;
11286 /* Fix up the backrefs for DVD/floppy media. */
11287 if (pAttach->i_getType() != DeviceType_HardDisk)
11288 {
11289 Medium* pMedium = pAttach->i_getMedium();
11290 if (pMedium)
11291 {
11292 rc = pMedium->i_addBackReference(mData->mUuid);
11293 AssertComRC(rc);
11294 }
11295 }
11296 }
11297
11298 /** @todo convert all this Machine-based voodoo to MediumAttachment
11299 * based rollback logic. */
11300 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11301
11302 return;
11303}
11304
11305/**
11306 * Returns true if the settings file is located in the directory named exactly
11307 * as the machine; this means, among other things, that the machine directory
11308 * should be auto-renamed.
11309 *
11310 * @param aSettingsDir if not NULL, the full machine settings file directory
11311 * name will be assigned there.
11312 *
11313 * @note Doesn't lock anything.
11314 * @note Not thread safe (must be called from this object's lock).
11315 */
11316bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11317{
11318 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11319 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11320 if (aSettingsDir)
11321 *aSettingsDir = strMachineDirName;
11322 strMachineDirName.stripPath(); // vmname
11323 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11324 strConfigFileOnly.stripPath() // vmname.vbox
11325 .stripSuffix(); // vmname
11326 /** @todo hack, make somehow use of ComposeMachineFilename */
11327 if (mUserData->s.fDirectoryIncludesUUID)
11328 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11329
11330 AssertReturn(!strMachineDirName.isEmpty(), false);
11331 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11332
11333 return strMachineDirName == strConfigFileOnly;
11334}
11335
11336/**
11337 * Discards all changes to machine settings.
11338 *
11339 * @param aNotify Whether to notify the direct session about changes or not.
11340 *
11341 * @note Locks objects for writing!
11342 */
11343void Machine::i_rollback(bool aNotify)
11344{
11345 AutoCaller autoCaller(this);
11346 AssertComRCReturn(autoCaller.rc(), (void)0);
11347
11348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11349
11350 if (!mStorageControllers.isNull())
11351 {
11352 if (mStorageControllers.isBackedUp())
11353 {
11354 /* unitialize all new devices (absent in the backed up list). */
11355 StorageControllerList::const_iterator it = mStorageControllers->begin();
11356 StorageControllerList *backedList = mStorageControllers.backedUpData();
11357 while (it != mStorageControllers->end())
11358 {
11359 if ( std::find(backedList->begin(), backedList->end(), *it)
11360 == backedList->end()
11361 )
11362 {
11363 (*it)->uninit();
11364 }
11365 ++it;
11366 }
11367
11368 /* restore the list */
11369 mStorageControllers.rollback();
11370 }
11371
11372 /* rollback any changes to devices after restoring the list */
11373 if (mData->flModifications & IsModified_Storage)
11374 {
11375 StorageControllerList::const_iterator it = mStorageControllers->begin();
11376 while (it != mStorageControllers->end())
11377 {
11378 (*it)->i_rollback();
11379 ++it;
11380 }
11381 }
11382 }
11383
11384 if (!mUSBControllers.isNull())
11385 {
11386 if (mUSBControllers.isBackedUp())
11387 {
11388 /* unitialize all new devices (absent in the backed up list). */
11389 USBControllerList::const_iterator it = mUSBControllers->begin();
11390 USBControllerList *backedList = mUSBControllers.backedUpData();
11391 while (it != mUSBControllers->end())
11392 {
11393 if ( std::find(backedList->begin(), backedList->end(), *it)
11394 == backedList->end()
11395 )
11396 {
11397 (*it)->uninit();
11398 }
11399 ++it;
11400 }
11401
11402 /* restore the list */
11403 mUSBControllers.rollback();
11404 }
11405
11406 /* rollback any changes to devices after restoring the list */
11407 if (mData->flModifications & IsModified_USB)
11408 {
11409 USBControllerList::const_iterator it = mUSBControllers->begin();
11410 while (it != mUSBControllers->end())
11411 {
11412 (*it)->i_rollback();
11413 ++it;
11414 }
11415 }
11416 }
11417
11418 mUserData.rollback();
11419
11420 mHWData.rollback();
11421
11422 if (mData->flModifications & IsModified_Storage)
11423 i_rollbackMedia();
11424
11425 if (mBIOSSettings)
11426 mBIOSSettings->i_rollback();
11427
11428 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11429 mVRDEServer->i_rollback();
11430
11431 if (mAudioAdapter)
11432 mAudioAdapter->i_rollback();
11433
11434 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11435 mUSBDeviceFilters->i_rollback();
11436
11437 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11438 mBandwidthControl->i_rollback();
11439
11440 if (!mHWData.isNull())
11441 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11442 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11443 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11444 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11445
11446 if (mData->flModifications & IsModified_NetworkAdapters)
11447 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11448 if ( mNetworkAdapters[slot]
11449 && mNetworkAdapters[slot]->i_isModified())
11450 {
11451 mNetworkAdapters[slot]->i_rollback();
11452 networkAdapters[slot] = mNetworkAdapters[slot];
11453 }
11454
11455 if (mData->flModifications & IsModified_SerialPorts)
11456 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11457 if ( mSerialPorts[slot]
11458 && mSerialPorts[slot]->i_isModified())
11459 {
11460 mSerialPorts[slot]->i_rollback();
11461 serialPorts[slot] = mSerialPorts[slot];
11462 }
11463
11464 if (mData->flModifications & IsModified_ParallelPorts)
11465 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11466 if ( mParallelPorts[slot]
11467 && mParallelPorts[slot]->i_isModified())
11468 {
11469 mParallelPorts[slot]->i_rollback();
11470 parallelPorts[slot] = mParallelPorts[slot];
11471 }
11472
11473 if (aNotify)
11474 {
11475 /* inform the direct session about changes */
11476
11477 ComObjPtr<Machine> that = this;
11478 uint32_t flModifications = mData->flModifications;
11479 alock.release();
11480
11481 if (flModifications & IsModified_SharedFolders)
11482 that->i_onSharedFolderChange();
11483
11484 if (flModifications & IsModified_VRDEServer)
11485 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11486 if (flModifications & IsModified_USB)
11487 that->i_onUSBControllerChange();
11488
11489 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11490 if (networkAdapters[slot])
11491 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11492 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11493 if (serialPorts[slot])
11494 that->i_onSerialPortChange(serialPorts[slot]);
11495 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11496 if (parallelPorts[slot])
11497 that->i_onParallelPortChange(parallelPorts[slot]);
11498
11499 if (flModifications & IsModified_Storage)
11500 that->i_onStorageControllerChange();
11501
11502#if 0
11503 if (flModifications & IsModified_BandwidthControl)
11504 that->onBandwidthControlChange();
11505#endif
11506 }
11507}
11508
11509/**
11510 * Commits all the changes to machine settings.
11511 *
11512 * Note that this operation is supposed to never fail.
11513 *
11514 * @note Locks this object and children for writing.
11515 */
11516void Machine::i_commit()
11517{
11518 AutoCaller autoCaller(this);
11519 AssertComRCReturnVoid(autoCaller.rc());
11520
11521 AutoCaller peerCaller(mPeer);
11522 AssertComRCReturnVoid(peerCaller.rc());
11523
11524 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11525
11526 /*
11527 * use safe commit to ensure Snapshot machines (that share mUserData)
11528 * will still refer to a valid memory location
11529 */
11530 mUserData.commitCopy();
11531
11532 mHWData.commit();
11533
11534 if (mMediaData.isBackedUp())
11535 i_commitMedia(Global::IsOnline(mData->mMachineState));
11536
11537 mBIOSSettings->i_commit();
11538 mVRDEServer->i_commit();
11539 mAudioAdapter->i_commit();
11540 mUSBDeviceFilters->i_commit();
11541 mBandwidthControl->i_commit();
11542
11543 /* Since mNetworkAdapters is a list which might have been changed (resized)
11544 * without using the Backupable<> template we need to handle the copying
11545 * of the list entries manually, including the creation of peers for the
11546 * new objects. */
11547 bool commitNetworkAdapters = false;
11548 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11549 if (mPeer)
11550 {
11551 /* commit everything, even the ones which will go away */
11552 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11553 mNetworkAdapters[slot]->i_commit();
11554 /* copy over the new entries, creating a peer and uninit the original */
11555 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11556 for (size_t slot = 0; slot < newSize; slot++)
11557 {
11558 /* look if this adapter has a peer device */
11559 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11560 if (!peer)
11561 {
11562 /* no peer means the adapter is a newly created one;
11563 * create a peer owning data this data share it with */
11564 peer.createObject();
11565 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11566 }
11567 mPeer->mNetworkAdapters[slot] = peer;
11568 }
11569 /* uninit any no longer needed network adapters */
11570 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11571 mNetworkAdapters[slot]->uninit();
11572 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11573 {
11574 if (mPeer->mNetworkAdapters[slot])
11575 mPeer->mNetworkAdapters[slot]->uninit();
11576 }
11577 /* Keep the original network adapter count until this point, so that
11578 * discarding a chipset type change will not lose settings. */
11579 mNetworkAdapters.resize(newSize);
11580 mPeer->mNetworkAdapters.resize(newSize);
11581 }
11582 else
11583 {
11584 /* we have no peer (our parent is the newly created machine);
11585 * just commit changes to the network adapters */
11586 commitNetworkAdapters = true;
11587 }
11588 if (commitNetworkAdapters)
11589 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11590 mNetworkAdapters[slot]->i_commit();
11591
11592 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11593 mSerialPorts[slot]->i_commit();
11594 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11595 mParallelPorts[slot]->i_commit();
11596
11597 bool commitStorageControllers = false;
11598
11599 if (mStorageControllers.isBackedUp())
11600 {
11601 mStorageControllers.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 StorageControllerList *newList = new StorageControllerList();
11608 StorageControllerList::const_iterator it = mStorageControllers->begin();
11609 while (it != mStorageControllers->end())
11610 {
11611 (*it)->i_commit();
11612
11613 /* look if this controller has a peer device */
11614 ComObjPtr<StorageController> 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->mStorageControllers->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->mStorageControllers->begin();
11635 while (it != mPeer->mStorageControllers->end())
11636 {
11637 (*it)->uninit();
11638 ++it;
11639 }
11640
11641 /* attach new list of controllers to our peer */
11642 mPeer->mStorageControllers.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 commitStorageControllers = true;
11649 }
11650 }
11651 else
11652 {
11653 /* the list of controllers itself is not changed,
11654 * just commit changes to controllers themselves */
11655 commitStorageControllers = true;
11656 }
11657
11658 if (commitStorageControllers)
11659 {
11660 StorageControllerList::const_iterator it = mStorageControllers->begin();
11661 while (it != mStorageControllers->end())
11662 {
11663 (*it)->i_commit();
11664 ++it;
11665 }
11666 }
11667
11668 bool commitUSBControllers = false;
11669
11670 if (mUSBControllers.isBackedUp())
11671 {
11672 mUSBControllers.commit();
11673
11674 if (mPeer)
11675 {
11676 /* Commit all changes to new controllers (this will reshare data with
11677 * peers for those who have peers) */
11678 USBControllerList *newList = new USBControllerList();
11679 USBControllerList::const_iterator it = mUSBControllers->begin();
11680 while (it != mUSBControllers->end())
11681 {
11682 (*it)->i_commit();
11683
11684 /* look if this controller has a peer device */
11685 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11686 if (!peer)
11687 {
11688 /* no peer means the device is a newly created one;
11689 * create a peer owning data this device share it with */
11690 peer.createObject();
11691 peer->init(mPeer, *it, true /* aReshare */);
11692 }
11693 else
11694 {
11695 /* remove peer from the old list */
11696 mPeer->mUSBControllers->remove(peer);
11697 }
11698 /* and add it to the new list */
11699 newList->push_back(peer);
11700
11701 ++it;
11702 }
11703
11704 /* uninit old peer's controllers that are left */
11705 it = mPeer->mUSBControllers->begin();
11706 while (it != mPeer->mUSBControllers->end())
11707 {
11708 (*it)->uninit();
11709 ++it;
11710 }
11711
11712 /* attach new list of controllers to our peer */
11713 mPeer->mUSBControllers.attach(newList);
11714 }
11715 else
11716 {
11717 /* we have no peer (our parent is the newly created machine);
11718 * just commit changes to devices */
11719 commitUSBControllers = true;
11720 }
11721 }
11722 else
11723 {
11724 /* the list of controllers itself is not changed,
11725 * just commit changes to controllers themselves */
11726 commitUSBControllers = true;
11727 }
11728
11729 if (commitUSBControllers)
11730 {
11731 USBControllerList::const_iterator it = mUSBControllers->begin();
11732 while (it != mUSBControllers->end())
11733 {
11734 (*it)->i_commit();
11735 ++it;
11736 }
11737 }
11738
11739 if (i_isSessionMachine())
11740 {
11741 /* attach new data to the primary machine and reshare it */
11742 mPeer->mUserData.attach(mUserData);
11743 mPeer->mHWData.attach(mHWData);
11744 /* mMediaData is reshared by fixupMedia */
11745 // mPeer->mMediaData.attach(mMediaData);
11746 Assert(mPeer->mMediaData.data() == mMediaData.data());
11747 }
11748}
11749
11750/**
11751 * Copies all the hardware data from the given machine.
11752 *
11753 * Currently, only called when the VM is being restored from a snapshot. In
11754 * particular, this implies that the VM is not running during this method's
11755 * call.
11756 *
11757 * @note This method must be called from under this object's lock.
11758 *
11759 * @note This method doesn't call #commit(), so all data remains backed up and
11760 * unsaved.
11761 */
11762void Machine::i_copyFrom(Machine *aThat)
11763{
11764 AssertReturnVoid(!i_isSnapshotMachine());
11765 AssertReturnVoid(aThat->i_isSnapshotMachine());
11766
11767 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11768
11769 mHWData.assignCopy(aThat->mHWData);
11770
11771 // create copies of all shared folders (mHWData after attaching a copy
11772 // contains just references to original objects)
11773 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11774 it != mHWData->mSharedFolders.end();
11775 ++it)
11776 {
11777 ComObjPtr<SharedFolder> folder;
11778 folder.createObject();
11779 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11780 AssertComRC(rc);
11781 *it = folder;
11782 }
11783
11784 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11785 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11786 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11787 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11788 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11789
11790 /* create private copies of all controllers */
11791 mStorageControllers.backup();
11792 mStorageControllers->clear();
11793 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11794 it != aThat->mStorageControllers->end();
11795 ++it)
11796 {
11797 ComObjPtr<StorageController> ctrl;
11798 ctrl.createObject();
11799 ctrl->initCopy(this, *it);
11800 mStorageControllers->push_back(ctrl);
11801 }
11802
11803 /* create private copies of all USB controllers */
11804 mUSBControllers.backup();
11805 mUSBControllers->clear();
11806 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11807 it != aThat->mUSBControllers->end();
11808 ++it)
11809 {
11810 ComObjPtr<USBController> ctrl;
11811 ctrl.createObject();
11812 ctrl->initCopy(this, *it);
11813 mUSBControllers->push_back(ctrl);
11814 }
11815
11816 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11817 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11818 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11819 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11820 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11821 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11822 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11823}
11824
11825/**
11826 * Returns whether the given storage controller is hotplug capable.
11827 *
11828 * @returns true if the controller supports hotplugging
11829 * false otherwise.
11830 * @param enmCtrlType The controller type to check for.
11831 */
11832bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11833{
11834 ComPtr<ISystemProperties> systemProperties;
11835 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11836 if (FAILED(rc))
11837 return false;
11838
11839 BOOL aHotplugCapable = FALSE;
11840 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11841
11842 return RT_BOOL(aHotplugCapable);
11843}
11844
11845#ifdef VBOX_WITH_RESOURCE_USAGE_API
11846
11847void Machine::i_getDiskList(MediaList &list)
11848{
11849 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11850 it != mMediaData->mAttachments.end();
11851 ++it)
11852 {
11853 MediumAttachment* pAttach = *it;
11854 /* just in case */
11855 AssertStmt(pAttach, continue);
11856
11857 AutoCaller localAutoCallerA(pAttach);
11858 if (FAILED(localAutoCallerA.rc())) continue;
11859
11860 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11861
11862 if (pAttach->i_getType() == DeviceType_HardDisk)
11863 list.push_back(pAttach->i_getMedium());
11864 }
11865}
11866
11867void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11868{
11869 AssertReturnVoid(isWriteLockOnCurrentThread());
11870 AssertPtrReturnVoid(aCollector);
11871
11872 pm::CollectorHAL *hal = aCollector->getHAL();
11873 /* Create sub metrics */
11874 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11875 "Percentage of processor time spent in user mode by the VM process.");
11876 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11877 "Percentage of processor time spent in kernel mode by the VM process.");
11878 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11879 "Size of resident portion of VM process in memory.");
11880 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11881 "Actual size of all VM disks combined.");
11882 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11883 "Network receive rate.");
11884 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11885 "Network transmit rate.");
11886 /* Create and register base metrics */
11887 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11888 cpuLoadUser, cpuLoadKernel);
11889 aCollector->registerBaseMetric(cpuLoad);
11890 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11891 ramUsageUsed);
11892 aCollector->registerBaseMetric(ramUsage);
11893 MediaList disks;
11894 i_getDiskList(disks);
11895 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11896 diskUsageUsed);
11897 aCollector->registerBaseMetric(diskUsage);
11898
11899 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11900 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11901 new pm::AggregateAvg()));
11902 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11903 new pm::AggregateMin()));
11904 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11905 new pm::AggregateMax()));
11906 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11907 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11908 new pm::AggregateAvg()));
11909 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11910 new pm::AggregateMin()));
11911 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11912 new pm::AggregateMax()));
11913
11914 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11915 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11916 new pm::AggregateAvg()));
11917 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11918 new pm::AggregateMin()));
11919 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11920 new pm::AggregateMax()));
11921
11922 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11923 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11924 new pm::AggregateAvg()));
11925 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11926 new pm::AggregateMin()));
11927 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11928 new pm::AggregateMax()));
11929
11930
11931 /* Guest metrics collector */
11932 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11933 aCollector->registerGuest(mCollectorGuest);
11934 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11935 this, __PRETTY_FUNCTION__, mCollectorGuest));
11936
11937 /* Create sub metrics */
11938 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11939 "Percentage of processor time spent in user mode as seen by the guest.");
11940 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11941 "Percentage of processor time spent in kernel mode as seen by the guest.");
11942 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11943 "Percentage of processor time spent idling as seen by the guest.");
11944
11945 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11946 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11947 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11948 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11949 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11950 pm::SubMetric *guestMemCache = new pm::SubMetric(
11951 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11952
11953 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
11954 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11955
11956 /* Create and register base metrics */
11957 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11958 machineNetRx, machineNetTx);
11959 aCollector->registerBaseMetric(machineNetRate);
11960
11961 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11962 guestLoadUser, guestLoadKernel, guestLoadIdle);
11963 aCollector->registerBaseMetric(guestCpuLoad);
11964
11965 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11966 guestMemTotal, guestMemFree,
11967 guestMemBalloon, guestMemShared,
11968 guestMemCache, guestPagedTotal);
11969 aCollector->registerBaseMetric(guestCpuMem);
11970
11971 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11972 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11973 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11974 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11975
11976 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11977 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11978 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11979 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11980
11981 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11982 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11983 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11984 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11985
11986 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11987 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11988 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11989 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11990
11991 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11992 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11993 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11994 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11995
11996 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11997 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11998 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11999 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12000
12001 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12002 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12003 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12004 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12005
12006 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12007 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12008 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12009 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12010
12011 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12012 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12013 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12014 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12015
12016 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12017 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12018 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12019 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12020
12021 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12022 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12023 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12024 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12025}
12026
12027void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12028{
12029 AssertReturnVoid(isWriteLockOnCurrentThread());
12030
12031 if (aCollector)
12032 {
12033 aCollector->unregisterMetricsFor(aMachine);
12034 aCollector->unregisterBaseMetricsFor(aMachine);
12035 }
12036}
12037
12038#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12039
12040
12041////////////////////////////////////////////////////////////////////////////////
12042
12043DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12044
12045HRESULT SessionMachine::FinalConstruct()
12046{
12047 LogFlowThisFunc(("\n"));
12048
12049 mClientToken = NULL;
12050
12051 return BaseFinalConstruct();
12052}
12053
12054void SessionMachine::FinalRelease()
12055{
12056 LogFlowThisFunc(("\n"));
12057
12058 Assert(!mClientToken);
12059 /* paranoia, should not hang around any more */
12060 if (mClientToken)
12061 {
12062 delete mClientToken;
12063 mClientToken = NULL;
12064 }
12065
12066 uninit(Uninit::Unexpected);
12067
12068 BaseFinalRelease();
12069}
12070
12071/**
12072 * @note Must be called only by Machine::LockMachine() from its own write lock.
12073 */
12074HRESULT SessionMachine::init(Machine *aMachine)
12075{
12076 LogFlowThisFuncEnter();
12077 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12078
12079 AssertReturn(aMachine, E_INVALIDARG);
12080
12081 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12082
12083 /* Enclose the state transition NotReady->InInit->Ready */
12084 AutoInitSpan autoInitSpan(this);
12085 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12086
12087 HRESULT rc = S_OK;
12088
12089 /* create the machine client token */
12090 try
12091 {
12092 mClientToken = new ClientToken(aMachine, this);
12093 if (!mClientToken->isReady())
12094 {
12095 delete mClientToken;
12096 mClientToken = NULL;
12097 rc = E_FAIL;
12098 }
12099 }
12100 catch (std::bad_alloc &)
12101 {
12102 rc = E_OUTOFMEMORY;
12103 }
12104 if (FAILED(rc))
12105 return rc;
12106
12107 /* memorize the peer Machine */
12108 unconst(mPeer) = aMachine;
12109 /* share the parent pointer */
12110 unconst(mParent) = aMachine->mParent;
12111
12112 /* take the pointers to data to share */
12113 mData.share(aMachine->mData);
12114 mSSData.share(aMachine->mSSData);
12115
12116 mUserData.share(aMachine->mUserData);
12117 mHWData.share(aMachine->mHWData);
12118 mMediaData.share(aMachine->mMediaData);
12119
12120 mStorageControllers.allocate();
12121 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12122 it != aMachine->mStorageControllers->end();
12123 ++it)
12124 {
12125 ComObjPtr<StorageController> ctl;
12126 ctl.createObject();
12127 ctl->init(this, *it);
12128 mStorageControllers->push_back(ctl);
12129 }
12130
12131 mUSBControllers.allocate();
12132 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12133 it != aMachine->mUSBControllers->end();
12134 ++it)
12135 {
12136 ComObjPtr<USBController> ctl;
12137 ctl.createObject();
12138 ctl->init(this, *it);
12139 mUSBControllers->push_back(ctl);
12140 }
12141
12142 unconst(mBIOSSettings).createObject();
12143 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12144 /* create another VRDEServer object that will be mutable */
12145 unconst(mVRDEServer).createObject();
12146 mVRDEServer->init(this, aMachine->mVRDEServer);
12147 /* create another audio adapter object that will be mutable */
12148 unconst(mAudioAdapter).createObject();
12149 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12150 /* create a list of serial ports that will be mutable */
12151 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12152 {
12153 unconst(mSerialPorts[slot]).createObject();
12154 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12155 }
12156 /* create a list of parallel ports that will be mutable */
12157 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12158 {
12159 unconst(mParallelPorts[slot]).createObject();
12160 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12161 }
12162
12163 /* create another USB device filters object that will be mutable */
12164 unconst(mUSBDeviceFilters).createObject();
12165 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12166
12167 /* create a list of network adapters that will be mutable */
12168 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12169 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12170 {
12171 unconst(mNetworkAdapters[slot]).createObject();
12172 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12173 }
12174
12175 /* create another bandwidth control object that will be mutable */
12176 unconst(mBandwidthControl).createObject();
12177 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12178
12179 /* default is to delete saved state on Saved -> PoweredOff transition */
12180 mRemoveSavedState = true;
12181
12182 /* Confirm a successful initialization when it's the case */
12183 autoInitSpan.setSucceeded();
12184
12185 miNATNetworksStarted = 0;
12186
12187 LogFlowThisFuncLeave();
12188 return rc;
12189}
12190
12191/**
12192 * Uninitializes this session object. If the reason is other than
12193 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12194 * or the client watcher code.
12195 *
12196 * @param aReason uninitialization reason
12197 *
12198 * @note Locks mParent + this object for writing.
12199 */
12200void SessionMachine::uninit(Uninit::Reason aReason)
12201{
12202 LogFlowThisFuncEnter();
12203 LogFlowThisFunc(("reason=%d\n", aReason));
12204
12205 /*
12206 * Strongly reference ourselves to prevent this object deletion after
12207 * mData->mSession.mMachine.setNull() below (which can release the last
12208 * reference and call the destructor). Important: this must be done before
12209 * accessing any members (and before AutoUninitSpan that does it as well).
12210 * This self reference will be released as the very last step on return.
12211 */
12212 ComObjPtr<SessionMachine> selfRef = this;
12213
12214 /* Enclose the state transition Ready->InUninit->NotReady */
12215 AutoUninitSpan autoUninitSpan(this);
12216 if (autoUninitSpan.uninitDone())
12217 {
12218 LogFlowThisFunc(("Already uninitialized\n"));
12219 LogFlowThisFuncLeave();
12220 return;
12221 }
12222
12223 if (autoUninitSpan.initFailed())
12224 {
12225 /* We've been called by init() because it's failed. It's not really
12226 * necessary (nor it's safe) to perform the regular uninit sequence
12227 * below, the following is enough.
12228 */
12229 LogFlowThisFunc(("Initialization failed.\n"));
12230 /* destroy the machine client token */
12231 if (mClientToken)
12232 {
12233 delete mClientToken;
12234 mClientToken = NULL;
12235 }
12236 uninitDataAndChildObjects();
12237 mData.free();
12238 unconst(mParent) = NULL;
12239 unconst(mPeer) = NULL;
12240 LogFlowThisFuncLeave();
12241 return;
12242 }
12243
12244 MachineState_T lastState;
12245 {
12246 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12247 lastState = mData->mMachineState;
12248 }
12249 NOREF(lastState);
12250
12251#ifdef VBOX_WITH_USB
12252 // release all captured USB devices, but do this before requesting the locks below
12253 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12254 {
12255 /* Console::captureUSBDevices() is called in the VM process only after
12256 * setting the machine state to Starting or Restoring.
12257 * Console::detachAllUSBDevices() will be called upon successful
12258 * termination. So, we need to release USB devices only if there was
12259 * an abnormal termination of a running VM.
12260 *
12261 * This is identical to SessionMachine::DetachAllUSBDevices except
12262 * for the aAbnormal argument. */
12263 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12264 AssertComRC(rc);
12265 NOREF(rc);
12266
12267 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12268 if (service)
12269 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12270 }
12271#endif /* VBOX_WITH_USB */
12272
12273 // we need to lock this object in uninit() because the lock is shared
12274 // with mPeer (as well as data we modify below). mParent lock is needed
12275 // by several calls to it, and USB needs host lock.
12276 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12277
12278#ifdef VBOX_WITH_RESOURCE_USAGE_API
12279 /*
12280 * It is safe to call Machine::i_unregisterMetrics() here because
12281 * PerformanceCollector::samplerCallback no longer accesses guest methods
12282 * holding the lock.
12283 */
12284 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12285 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12286 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12287 this, __PRETTY_FUNCTION__, mCollectorGuest));
12288 if (mCollectorGuest)
12289 {
12290 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12291 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12292 mCollectorGuest = NULL;
12293 }
12294#endif
12295
12296 if (aReason == Uninit::Abnormal)
12297 {
12298 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12299 Global::IsOnlineOrTransient(lastState)));
12300
12301 /* reset the state to Aborted */
12302 if (mData->mMachineState != MachineState_Aborted)
12303 i_setMachineState(MachineState_Aborted);
12304 }
12305
12306 // any machine settings modified?
12307 if (mData->flModifications)
12308 {
12309 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12310 i_rollback(false /* aNotify */);
12311 }
12312
12313 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12314 || !mConsoleTaskData.mSnapshot);
12315 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12316 {
12317 LogWarningThisFunc(("canceling failed save state request!\n"));
12318 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12319 }
12320 else if (!mConsoleTaskData.mSnapshot.isNull())
12321 {
12322 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12323
12324 /* delete all differencing hard disks created (this will also attach
12325 * their parents back by rolling back mMediaData) */
12326 i_rollbackMedia();
12327
12328 // delete the saved state file (it might have been already created)
12329 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12330 // think it's still in use
12331 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12332 mConsoleTaskData.mSnapshot->uninit();
12333 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12334 }
12335
12336 mData->mSession.mPID = NIL_RTPROCESS;
12337
12338 if (aReason == Uninit::Unexpected)
12339 {
12340 /* Uninitialization didn't come from #checkForDeath(), so tell the
12341 * client watcher thread to update the set of machines that have open
12342 * sessions. */
12343 mParent->i_updateClientWatcher();
12344 }
12345
12346 /* uninitialize all remote controls */
12347 if (mData->mSession.mRemoteControls.size())
12348 {
12349 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12350 mData->mSession.mRemoteControls.size()));
12351
12352 Data::Session::RemoteControlList::iterator it =
12353 mData->mSession.mRemoteControls.begin();
12354 while (it != mData->mSession.mRemoteControls.end())
12355 {
12356 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12357 HRESULT rc = (*it)->Uninitialize();
12358 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12359 if (FAILED(rc))
12360 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12361 ++it;
12362 }
12363 mData->mSession.mRemoteControls.clear();
12364 }
12365
12366 /* Remove all references to the NAT network service. The service will stop
12367 * if all references (also from other VMs) are removed. */
12368 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12369 {
12370 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12371 {
12372 NetworkAttachmentType_T type;
12373 HRESULT hrc;
12374
12375 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12376 if ( SUCCEEDED(hrc)
12377 && type == NetworkAttachmentType_NATNetwork)
12378 {
12379 Bstr name;
12380 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12381 if (SUCCEEDED(hrc))
12382 {
12383 multilock.release();
12384 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12385 mUserData->s.strName.c_str(), name.raw()));
12386 mParent->i_natNetworkRefDec(name.raw());
12387 multilock.acquire();
12388 }
12389 }
12390 }
12391 }
12392
12393 /*
12394 * An expected uninitialization can come only from #checkForDeath().
12395 * Otherwise it means that something's gone really wrong (for example,
12396 * the Session implementation has released the VirtualBox reference
12397 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12398 * etc). However, it's also possible, that the client releases the IPC
12399 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12400 * but the VirtualBox release event comes first to the server process.
12401 * This case is practically possible, so we should not assert on an
12402 * unexpected uninit, just log a warning.
12403 */
12404
12405 if ((aReason == Uninit::Unexpected))
12406 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12407
12408 if (aReason != Uninit::Normal)
12409 {
12410 mData->mSession.mDirectControl.setNull();
12411 }
12412 else
12413 {
12414 /* this must be null here (see #OnSessionEnd()) */
12415 Assert(mData->mSession.mDirectControl.isNull());
12416 Assert(mData->mSession.mState == SessionState_Unlocking);
12417 Assert(!mData->mSession.mProgress.isNull());
12418 }
12419 if (mData->mSession.mProgress)
12420 {
12421 if (aReason == Uninit::Normal)
12422 mData->mSession.mProgress->i_notifyComplete(S_OK);
12423 else
12424 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12425 COM_IIDOF(ISession),
12426 getComponentName(),
12427 tr("The VM session was aborted"));
12428 mData->mSession.mProgress.setNull();
12429 }
12430
12431 /* remove the association between the peer machine and this session machine */
12432 Assert( (SessionMachine*)mData->mSession.mMachine == this
12433 || aReason == Uninit::Unexpected);
12434
12435 /* reset the rest of session data */
12436 mData->mSession.mMachine.setNull();
12437 mData->mSession.mState = SessionState_Unlocked;
12438 mData->mSession.mType.setNull();
12439
12440 /* destroy the machine client token before leaving the exclusive lock */
12441 if (mClientToken)
12442 {
12443 delete mClientToken;
12444 mClientToken = NULL;
12445 }
12446
12447 /* fire an event */
12448 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12449
12450 uninitDataAndChildObjects();
12451
12452 /* free the essential data structure last */
12453 mData.free();
12454
12455 /* release the exclusive lock before setting the below two to NULL */
12456 multilock.release();
12457
12458 unconst(mParent) = NULL;
12459 unconst(mPeer) = NULL;
12460
12461 LogFlowThisFuncLeave();
12462}
12463
12464// util::Lockable interface
12465////////////////////////////////////////////////////////////////////////////////
12466
12467/**
12468 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12469 * with the primary Machine instance (mPeer).
12470 */
12471RWLockHandle *SessionMachine::lockHandle() const
12472{
12473 AssertReturn(mPeer != NULL, NULL);
12474 return mPeer->lockHandle();
12475}
12476
12477// IInternalMachineControl methods
12478////////////////////////////////////////////////////////////////////////////////
12479
12480/**
12481 * Passes collected guest statistics to performance collector object
12482 */
12483STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12484 ULONG aCpuKernel, ULONG aCpuIdle,
12485 ULONG aMemTotal, ULONG aMemFree,
12486 ULONG aMemBalloon, ULONG aMemShared,
12487 ULONG aMemCache, ULONG aPageTotal,
12488 ULONG aAllocVMM, ULONG aFreeVMM,
12489 ULONG aBalloonedVMM, ULONG aSharedVMM,
12490 ULONG aVmNetRx, ULONG aVmNetTx)
12491{
12492#ifdef VBOX_WITH_RESOURCE_USAGE_API
12493 if (mCollectorGuest)
12494 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12495 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12496 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12497 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12498
12499 return S_OK;
12500#else
12501 NOREF(aValidStats);
12502 NOREF(aCpuUser);
12503 NOREF(aCpuKernel);
12504 NOREF(aCpuIdle);
12505 NOREF(aMemTotal);
12506 NOREF(aMemFree);
12507 NOREF(aMemBalloon);
12508 NOREF(aMemShared);
12509 NOREF(aMemCache);
12510 NOREF(aPageTotal);
12511 NOREF(aAllocVMM);
12512 NOREF(aFreeVMM);
12513 NOREF(aBalloonedVMM);
12514 NOREF(aSharedVMM);
12515 NOREF(aVmNetRx);
12516 NOREF(aVmNetTx);
12517 return E_NOTIMPL;
12518#endif
12519}
12520
12521/**
12522 * @note Locks this object for writing.
12523 */
12524STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12525{
12526 AutoCaller autoCaller(this);
12527 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12528
12529 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12530
12531 mRemoveSavedState = aRemove;
12532
12533 return S_OK;
12534}
12535
12536/**
12537 * @note Locks the same as #i_setMachineState() does.
12538 */
12539STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12540{
12541 return i_setMachineState(aMachineState);
12542}
12543
12544/**
12545 * @note Locks this object for writing.
12546 */
12547STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12548{
12549 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12550 AutoCaller autoCaller(this);
12551 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12552
12553 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12554
12555 if (mData->mSession.mState != SessionState_Locked)
12556 return VBOX_E_INVALID_OBJECT_STATE;
12557
12558 if (!mData->mSession.mProgress.isNull())
12559 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12560
12561 /* If we didn't reference the NAT network service yet, add a reference to
12562 * force a start */
12563 if (miNATNetworksStarted < 1)
12564 {
12565 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12566 {
12567 NetworkAttachmentType_T type;
12568 HRESULT hrc;
12569 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12570 if ( SUCCEEDED(hrc)
12571 && type == NetworkAttachmentType_NATNetwork)
12572 {
12573 Bstr name;
12574 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12575 if (SUCCEEDED(hrc))
12576 {
12577 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12578 mUserData->s.strName.c_str(), name.raw()));
12579 mPeer->lockHandle()->unlockWrite();
12580 mParent->i_natNetworkRefInc(name.raw());
12581#ifdef RT_LOCK_STRICT
12582 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12583#else
12584 mPeer->lockHandle()->lockWrite();
12585#endif
12586 }
12587 }
12588 }
12589 miNATNetworksStarted++;
12590 }
12591
12592 LogFlowThisFunc(("returns S_OK.\n"));
12593 return S_OK;
12594}
12595
12596/**
12597 * @note Locks this object for writing.
12598 */
12599STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12600{
12601 AutoCaller autoCaller(this);
12602 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12603
12604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12605
12606 if (mData->mSession.mState != SessionState_Locked)
12607 return VBOX_E_INVALID_OBJECT_STATE;
12608
12609 /* Finalize the LaunchVMProcess progress object. */
12610 if (mData->mSession.mProgress)
12611 {
12612 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12613 mData->mSession.mProgress.setNull();
12614 }
12615
12616 if (SUCCEEDED((HRESULT)iResult))
12617 {
12618#ifdef VBOX_WITH_RESOURCE_USAGE_API
12619 /* The VM has been powered up successfully, so it makes sense
12620 * now to offer the performance metrics for a running machine
12621 * object. Doing it earlier wouldn't be safe. */
12622 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12623 mData->mSession.mPID);
12624#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12625 }
12626
12627 return S_OK;
12628}
12629
12630/**
12631 * @note Locks this object for writing.
12632 */
12633STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12634{
12635 LogFlowThisFuncEnter();
12636
12637 AutoCaller autoCaller(this);
12638 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12639
12640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12641
12642 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12643 E_FAIL);
12644
12645 /* create a progress object to track operation completion */
12646 ComObjPtr<Progress> pProgress;
12647 pProgress.createObject();
12648 pProgress->init(i_getVirtualBox(),
12649 static_cast<IMachine *>(this) /* aInitiator */,
12650 Bstr(tr("Stopping the virtual machine")).raw(),
12651 FALSE /* aCancelable */);
12652
12653 /* fill in the console task data */
12654 mConsoleTaskData.mLastState = mData->mMachineState;
12655 mConsoleTaskData.mProgress = pProgress;
12656
12657 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12658 i_setMachineState(MachineState_Stopping);
12659
12660 pProgress.queryInterfaceTo(aProgress);
12661
12662 return S_OK;
12663}
12664
12665/**
12666 * @note Locks this object for writing.
12667 */
12668STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12669{
12670 LogFlowThisFuncEnter();
12671
12672 AutoCaller autoCaller(this);
12673 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12674
12675 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12676
12677 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12678 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12679 && mConsoleTaskData.mLastState != MachineState_Null,
12680 E_FAIL);
12681
12682 /*
12683 * On failure, set the state to the state we had when BeginPoweringDown()
12684 * was called (this is expected by Console::PowerDown() and the associated
12685 * task). On success the VM process already changed the state to
12686 * MachineState_PoweredOff, so no need to do anything.
12687 */
12688 if (FAILED(iResult))
12689 i_setMachineState(mConsoleTaskData.mLastState);
12690
12691 /* notify the progress object about operation completion */
12692 Assert(mConsoleTaskData.mProgress);
12693 if (SUCCEEDED(iResult))
12694 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12695 else
12696 {
12697 Utf8Str strErrMsg(aErrMsg);
12698 if (strErrMsg.length())
12699 mConsoleTaskData.mProgress->i_notifyComplete(iResult,
12700 COM_IIDOF(ISession),
12701 getComponentName(),
12702 strErrMsg.c_str());
12703 else
12704 mConsoleTaskData.mProgress->i_notifyComplete(iResult);
12705 }
12706
12707 /* clear out the temporary saved state data */
12708 mConsoleTaskData.mLastState = MachineState_Null;
12709 mConsoleTaskData.mProgress.setNull();
12710
12711 LogFlowThisFuncLeave();
12712 return S_OK;
12713}
12714
12715
12716/**
12717 * Goes through the USB filters of the given machine to see if the given
12718 * device matches any filter or not.
12719 *
12720 * @note Locks the same as USBController::hasMatchingFilter() does.
12721 */
12722STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12723 BOOL *aMatched,
12724 ULONG *aMaskedIfs)
12725{
12726 LogFlowThisFunc(("\n"));
12727
12728 AutoCaller autoCaller(this);
12729 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12730
12731#ifdef VBOX_WITH_USB
12732 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aUSBDevice, aMaskedIfs);
12733#else
12734 NOREF(aUSBDevice);
12735 NOREF(aMaskedIfs);
12736 *aMatched = FALSE;
12737#endif
12738
12739 return S_OK;
12740}
12741
12742/**
12743 * @note Locks the same as Host::captureUSBDevice() does.
12744 */
12745STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12746{
12747 LogFlowThisFunc(("\n"));
12748
12749 AutoCaller autoCaller(this);
12750 AssertComRCReturnRC(autoCaller.rc());
12751
12752#ifdef VBOX_WITH_USB
12753 /* if captureDeviceForVM() fails, it must have set extended error info */
12754 clearError();
12755 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12756 if (FAILED(rc)) return rc;
12757
12758 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12759 AssertReturn(service, E_FAIL);
12760 return service->captureDeviceForVM(this, Guid(aId).ref());
12761#else
12762 NOREF(aId);
12763 return E_NOTIMPL;
12764#endif
12765}
12766
12767/**
12768 * @note Locks the same as Host::detachUSBDevice() does.
12769 */
12770STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12771{
12772 LogFlowThisFunc(("\n"));
12773
12774 AutoCaller autoCaller(this);
12775 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12776
12777#ifdef VBOX_WITH_USB
12778 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12779 AssertReturn(service, E_FAIL);
12780 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12781#else
12782 NOREF(aId);
12783 NOREF(aDone);
12784 return E_NOTIMPL;
12785#endif
12786}
12787
12788/**
12789 * Inserts all machine filters to the USB proxy service and then calls
12790 * Host::autoCaptureUSBDevices().
12791 *
12792 * Called by Console from the VM process upon VM startup.
12793 *
12794 * @note Locks what called methods lock.
12795 */
12796STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12797{
12798 LogFlowThisFunc(("\n"));
12799
12800 AutoCaller autoCaller(this);
12801 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12802
12803#ifdef VBOX_WITH_USB
12804 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12805 AssertComRC(rc);
12806 NOREF(rc);
12807
12808 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12809 AssertReturn(service, E_FAIL);
12810 return service->autoCaptureDevicesForVM(this);
12811#else
12812 return S_OK;
12813#endif
12814}
12815
12816/**
12817 * Removes all machine filters from the USB proxy service and then calls
12818 * Host::detachAllUSBDevices().
12819 *
12820 * Called by Console from the VM process upon normal VM termination or by
12821 * SessionMachine::uninit() upon abnormal VM termination (from under the
12822 * Machine/SessionMachine lock).
12823 *
12824 * @note Locks what called methods lock.
12825 */
12826STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12827{
12828 LogFlowThisFunc(("\n"));
12829
12830 AutoCaller autoCaller(this);
12831 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12832
12833#ifdef VBOX_WITH_USB
12834 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12835 AssertComRC(rc);
12836 NOREF(rc);
12837
12838 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12839 AssertReturn(service, E_FAIL);
12840 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12841#else
12842 NOREF(aDone);
12843 return S_OK;
12844#endif
12845}
12846
12847/**
12848 * @note Locks this object for writing.
12849 */
12850STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12851 IProgress **aProgress)
12852{
12853 LogFlowThisFuncEnter();
12854
12855 AssertReturn(aSession, E_INVALIDARG);
12856 AssertReturn(aProgress, E_INVALIDARG);
12857
12858 AutoCaller autoCaller(this);
12859
12860 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12861 /*
12862 * We don't assert below because it might happen that a non-direct session
12863 * informs us it is closed right after we've been uninitialized -- it's ok.
12864 */
12865 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12866
12867 /* get IInternalSessionControl interface */
12868 ComPtr<IInternalSessionControl> control(aSession);
12869
12870 ComAssertRet(!control.isNull(), E_INVALIDARG);
12871
12872 /* Creating a Progress object requires the VirtualBox lock, and
12873 * thus locking it here is required by the lock order rules. */
12874 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12875
12876 if (control == mData->mSession.mDirectControl)
12877 {
12878 ComAssertRet(aProgress, E_POINTER);
12879
12880 /* The direct session is being normally closed by the client process
12881 * ----------------------------------------------------------------- */
12882
12883 /* go to the closing state (essential for all open*Session() calls and
12884 * for #checkForDeath()) */
12885 Assert(mData->mSession.mState == SessionState_Locked);
12886 mData->mSession.mState = SessionState_Unlocking;
12887
12888 /* set direct control to NULL to release the remote instance */
12889 mData->mSession.mDirectControl.setNull();
12890 LogFlowThisFunc(("Direct control is set to NULL\n"));
12891
12892 if (mData->mSession.mProgress)
12893 {
12894 /* finalize the progress, someone might wait if a frontend
12895 * closes the session before powering on the VM. */
12896 mData->mSession.mProgress->notifyComplete(E_FAIL,
12897 COM_IIDOF(ISession),
12898 getComponentName(),
12899 tr("The VM session was closed before any attempt to power it on"));
12900 mData->mSession.mProgress.setNull();
12901 }
12902
12903 /* Create the progress object the client will use to wait until
12904 * #checkForDeath() is called to uninitialize this session object after
12905 * it releases the IPC semaphore.
12906 * Note! Because we're "reusing" mProgress here, this must be a proxy
12907 * object just like for LaunchVMProcess. */
12908 Assert(mData->mSession.mProgress.isNull());
12909 ComObjPtr<ProgressProxy> progress;
12910 progress.createObject();
12911 ComPtr<IUnknown> pPeer(mPeer);
12912 progress->init(mParent, pPeer,
12913 Bstr(tr("Closing session")).raw(),
12914 FALSE /* aCancelable */);
12915 progress.queryInterfaceTo(aProgress);
12916 mData->mSession.mProgress = progress;
12917 }
12918 else
12919 {
12920 /* the remote session is being normally closed */
12921 Data::Session::RemoteControlList::iterator it =
12922 mData->mSession.mRemoteControls.begin();
12923 while (it != mData->mSession.mRemoteControls.end())
12924 {
12925 if (control == *it)
12926 break;
12927 ++it;
12928 }
12929 BOOL found = it != mData->mSession.mRemoteControls.end();
12930 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12931 E_INVALIDARG);
12932 // This MUST be erase(it), not remove(*it) as the latter triggers a
12933 // very nasty use after free due to the place where the value "lives".
12934 mData->mSession.mRemoteControls.erase(it);
12935 }
12936
12937 /* signal the client watcher thread, because the client is going away */
12938 mParent->i_updateClientWatcher();
12939
12940 LogFlowThisFuncLeave();
12941 return S_OK;
12942}
12943
12944/**
12945 * @note Locks this object for writing.
12946 */
12947STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12948{
12949 LogFlowThisFuncEnter();
12950
12951 CheckComArgOutPointerValid(aProgress);
12952 CheckComArgOutPointerValid(aStateFilePath);
12953
12954 AutoCaller autoCaller(this);
12955 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12956
12957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12958
12959 AssertReturn( mData->mMachineState == MachineState_Paused
12960 && mConsoleTaskData.mLastState == MachineState_Null
12961 && mConsoleTaskData.strStateFilePath.isEmpty(),
12962 E_FAIL);
12963
12964 /* create a progress object to track operation completion */
12965 ComObjPtr<Progress> pProgress;
12966 pProgress.createObject();
12967 pProgress->init(i_getVirtualBox(),
12968 static_cast<IMachine *>(this) /* aInitiator */,
12969 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12970 FALSE /* aCancelable */);
12971
12972 Utf8Str strStateFilePath;
12973 /* stateFilePath is null when the machine is not running */
12974 if (mData->mMachineState == MachineState_Paused)
12975 i_composeSavedStateFilename(strStateFilePath);
12976
12977 /* fill in the console task data */
12978 mConsoleTaskData.mLastState = mData->mMachineState;
12979 mConsoleTaskData.strStateFilePath = strStateFilePath;
12980 mConsoleTaskData.mProgress = pProgress;
12981
12982 /* set the state to Saving (this is expected by Console::SaveState()) */
12983 i_setMachineState(MachineState_Saving);
12984
12985 strStateFilePath.cloneTo(aStateFilePath);
12986 pProgress.queryInterfaceTo(aProgress);
12987
12988 return S_OK;
12989}
12990
12991/**
12992 * @note Locks mParent + this object for writing.
12993 */
12994STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12995{
12996 LogFlowThisFunc(("\n"));
12997
12998 AutoCaller autoCaller(this);
12999 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13000
13001 /* endSavingState() need mParent lock */
13002 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13003
13004 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13005 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13006 && mConsoleTaskData.mLastState != MachineState_Null
13007 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13008 E_FAIL);
13009
13010 /*
13011 * On failure, set the state to the state we had when BeginSavingState()
13012 * was called (this is expected by Console::SaveState() and the associated
13013 * task). On success the VM process already changed the state to
13014 * MachineState_Saved, so no need to do anything.
13015 */
13016 if (FAILED(iResult))
13017 i_setMachineState(mConsoleTaskData.mLastState);
13018
13019 return endSavingState(iResult, aErrMsg);
13020}
13021
13022/**
13023 * @note Locks this object for writing.
13024 */
13025STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13026{
13027 LogFlowThisFunc(("\n"));
13028
13029 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13030
13031 AutoCaller autoCaller(this);
13032 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13033
13034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13035
13036 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13037 || mData->mMachineState == MachineState_Teleported
13038 || mData->mMachineState == MachineState_Aborted
13039 , E_FAIL); /** @todo setError. */
13040
13041 Utf8Str stateFilePathFull = aSavedStateFile;
13042 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
13043 if (RT_FAILURE(vrc))
13044 return setError(VBOX_E_FILE_ERROR,
13045 tr("Invalid saved state file path '%ls' (%Rrc)"),
13046 aSavedStateFile,
13047 vrc);
13048
13049 mSSData->strStateFilePath = stateFilePathFull;
13050
13051 /* The below i_setMachineState() will detect the state transition and will
13052 * update the settings file */
13053
13054 return i_setMachineState(MachineState_Saved);
13055}
13056
13057STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13058 ComSafeArrayOut(BSTR, aValues),
13059 ComSafeArrayOut(LONG64, aTimestamps),
13060 ComSafeArrayOut(BSTR, aFlags))
13061{
13062 LogFlowThisFunc(("\n"));
13063
13064#ifdef VBOX_WITH_GUEST_PROPS
13065 using namespace guestProp;
13066
13067 AutoCaller autoCaller(this);
13068 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13069
13070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13071
13072 CheckComArgOutSafeArrayPointerValid(aNames);
13073 CheckComArgOutSafeArrayPointerValid(aValues);
13074 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13075 CheckComArgOutSafeArrayPointerValid(aFlags);
13076
13077 size_t cEntries = mHWData->mGuestProperties.size();
13078 com::SafeArray<BSTR> names(cEntries);
13079 com::SafeArray<BSTR> values(cEntries);
13080 com::SafeArray<LONG64> timestamps(cEntries);
13081 com::SafeArray<BSTR> flags(cEntries);
13082 unsigned i = 0;
13083 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13084 it != mHWData->mGuestProperties.end();
13085 ++it)
13086 {
13087 char szFlags[MAX_FLAGS_LEN + 1];
13088 it->first.cloneTo(&names[i]);
13089 it->second.strValue.cloneTo(&values[i]);
13090 timestamps[i] = it->second.mTimestamp;
13091 /* If it is NULL, keep it NULL. */
13092 if (it->second.mFlags)
13093 {
13094 writeFlags(it->second.mFlags, szFlags);
13095 Bstr(szFlags).cloneTo(&flags[i]);
13096 }
13097 else
13098 flags[i] = NULL;
13099 ++i;
13100 }
13101 names.detachTo(ComSafeArrayOutArg(aNames));
13102 values.detachTo(ComSafeArrayOutArg(aValues));
13103 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13104 flags.detachTo(ComSafeArrayOutArg(aFlags));
13105 return S_OK;
13106#else
13107 ReturnComNotImplemented();
13108#endif
13109}
13110
13111STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13112 IN_BSTR aValue,
13113 LONG64 aTimestamp,
13114 IN_BSTR aFlags)
13115{
13116 LogFlowThisFunc(("\n"));
13117
13118#ifdef VBOX_WITH_GUEST_PROPS
13119 using namespace guestProp;
13120
13121 CheckComArgStrNotEmptyOrNull(aName);
13122 CheckComArgNotNull(aValue);
13123 CheckComArgNotNull(aFlags);
13124
13125 try
13126 {
13127 /*
13128 * Convert input up front.
13129 */
13130 Utf8Str utf8Name(aName);
13131 uint32_t fFlags = NILFLAG;
13132 if (aFlags)
13133 {
13134 Utf8Str utf8Flags(aFlags);
13135 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13136 AssertRCReturn(vrc, E_INVALIDARG);
13137 }
13138
13139 /*
13140 * Now grab the object lock, validate the state and do the update.
13141 */
13142 AutoCaller autoCaller(this);
13143 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13144
13145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13146
13147 switch (mData->mMachineState)
13148 {
13149 case MachineState_Paused:
13150 case MachineState_Running:
13151 case MachineState_Teleporting:
13152 case MachineState_TeleportingPausedVM:
13153 case MachineState_LiveSnapshotting:
13154 case MachineState_DeletingSnapshotOnline:
13155 case MachineState_DeletingSnapshotPaused:
13156 case MachineState_Saving:
13157 case MachineState_Stopping:
13158 break;
13159
13160 default:
13161 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13162 VBOX_E_INVALID_VM_STATE);
13163 }
13164
13165 i_setModified(IsModified_MachineData);
13166 mHWData.backup();
13167
13168 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13169 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13170 if (it != mHWData->mGuestProperties.end())
13171 {
13172 if (!fDelete)
13173 {
13174 it->second.strValue = aValue;
13175 it->second.mTimestamp = aTimestamp;
13176 it->second.mFlags = fFlags;
13177 }
13178 else
13179 mHWData->mGuestProperties.erase(it);
13180
13181 mData->mGuestPropertiesModified = TRUE;
13182 }
13183 else if (!fDelete)
13184 {
13185 HWData::GuestProperty prop;
13186 prop.strValue = aValue;
13187 prop.mTimestamp = aTimestamp;
13188 prop.mFlags = fFlags;
13189
13190 mHWData->mGuestProperties[utf8Name] = prop;
13191 mData->mGuestPropertiesModified = TRUE;
13192 }
13193
13194 /*
13195 * Send a callback notification if appropriate
13196 */
13197 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13198 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13199 RTSTR_MAX,
13200 utf8Name.c_str(),
13201 RTSTR_MAX, NULL)
13202 )
13203 {
13204 alock.release();
13205
13206 mParent->i_onGuestPropertyChange(mData->mUuid,
13207 aName,
13208 aValue,
13209 aFlags);
13210 }
13211 }
13212 catch (...)
13213 {
13214 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13215 }
13216 return S_OK;
13217#else
13218 ReturnComNotImplemented();
13219#endif
13220}
13221
13222STDMETHODIMP SessionMachine::LockMedia()
13223{
13224 AutoCaller autoCaller(this);
13225 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13226
13227 AutoMultiWriteLock2 alock(this->lockHandle(),
13228 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13229
13230 AssertReturn( mData->mMachineState == MachineState_Starting
13231 || mData->mMachineState == MachineState_Restoring
13232 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13233
13234 clearError();
13235 alock.release();
13236 return lockMedia();
13237}
13238
13239STDMETHODIMP SessionMachine::UnlockMedia()
13240{
13241 HRESULT hrc = unlockMedia();
13242 return hrc;
13243}
13244
13245STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13246 IMediumAttachment **aNewAttachment)
13247{
13248 CheckComArgNotNull(aAttachment);
13249 CheckComArgOutPointerValid(aNewAttachment);
13250
13251 AutoCaller autoCaller(this);
13252 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13253
13254 // request the host lock first, since might be calling Host methods for getting host drives;
13255 // next, protect the media tree all the while we're in here, as well as our member variables
13256 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13257 this->lockHandle(),
13258 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13259
13260 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13261
13262 Bstr ctrlName;
13263 LONG lPort;
13264 LONG lDevice;
13265 bool fTempEject;
13266 {
13267 AutoCaller autoAttachCaller(this);
13268 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13269
13270 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13271
13272 /* Need to query the details first, as the IMediumAttachment reference
13273 * might be to the original settings, which we are going to change. */
13274 ctrlName = pAttach->i_getControllerName();
13275 lPort = pAttach->i_getPort();
13276 lDevice = pAttach->i_getDevice();
13277 fTempEject = pAttach->i_getTempEject();
13278 }
13279
13280 if (!fTempEject)
13281 {
13282 /* Remember previously mounted medium. The medium before taking the
13283 * backup is not necessarily the same thing. */
13284 ComObjPtr<Medium> oldmedium;
13285 oldmedium = pAttach->i_getMedium();
13286
13287 i_setModified(IsModified_Storage);
13288 mMediaData.backup();
13289
13290 // The backup operation makes the pAttach reference point to the
13291 // old settings. Re-get the correct reference.
13292 pAttach = i_findAttachment(mMediaData->mAttachments,
13293 ctrlName.raw(),
13294 lPort,
13295 lDevice);
13296
13297 {
13298 AutoCaller autoAttachCaller(this);
13299 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13300
13301 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13302 if (!oldmedium.isNull())
13303 oldmedium->i_removeBackReference(mData->mUuid);
13304
13305 pAttach->i_updateMedium(NULL);
13306 pAttach->i_updateEjected();
13307 }
13308
13309 i_setModified(IsModified_Storage);
13310 }
13311 else
13312 {
13313 {
13314 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13315 pAttach->i_updateEjected();
13316 }
13317 }
13318
13319 pAttach.queryInterfaceTo(aNewAttachment);
13320
13321 return S_OK;
13322}
13323
13324// public methods only for internal purposes
13325/////////////////////////////////////////////////////////////////////////////
13326
13327#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13328/**
13329 * Called from the client watcher thread to check for expected or unexpected
13330 * death of the client process that has a direct session to this machine.
13331 *
13332 * On Win32 and on OS/2, this method is called only when we've got the
13333 * mutex (i.e. the client has either died or terminated normally) so it always
13334 * returns @c true (the client is terminated, the session machine is
13335 * uninitialized).
13336 *
13337 * On other platforms, the method returns @c true if the client process has
13338 * terminated normally or abnormally and the session machine was uninitialized,
13339 * and @c false if the client process is still alive.
13340 *
13341 * @note Locks this object for writing.
13342 */
13343bool SessionMachine::i_checkForDeath()
13344{
13345 Uninit::Reason reason;
13346 bool terminated = false;
13347
13348 /* Enclose autoCaller with a block because calling uninit() from under it
13349 * will deadlock. */
13350 {
13351 AutoCaller autoCaller(this);
13352 if (!autoCaller.isOk())
13353 {
13354 /* return true if not ready, to cause the client watcher to exclude
13355 * the corresponding session from watching */
13356 LogFlowThisFunc(("Already uninitialized!\n"));
13357 return true;
13358 }
13359
13360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13361
13362 /* Determine the reason of death: if the session state is Closing here,
13363 * everything is fine. Otherwise it means that the client did not call
13364 * OnSessionEnd() before it released the IPC semaphore. This may happen
13365 * either because the client process has abnormally terminated, or
13366 * because it simply forgot to call ISession::Close() before exiting. We
13367 * threat the latter also as an abnormal termination (see
13368 * Session::uninit() for details). */
13369 reason = mData->mSession.mState == SessionState_Unlocking ?
13370 Uninit::Normal :
13371 Uninit::Abnormal;
13372
13373 if (mClientToken)
13374 terminated = mClientToken->release();
13375 } /* AutoCaller block */
13376
13377 if (terminated)
13378 uninit(reason);
13379
13380 return terminated;
13381}
13382
13383void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13384{
13385 LogFlowThisFunc(("\n"));
13386
13387 strTokenId.setNull();
13388
13389 AutoCaller autoCaller(this);
13390 AssertComRCReturnVoid(autoCaller.rc());
13391
13392 Assert(mClientToken);
13393 if (mClientToken)
13394 mClientToken->getId(strTokenId);
13395}
13396#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13397IToken *SessionMachine::i_getToken()
13398{
13399 LogFlowThisFunc(("\n"));
13400
13401 AutoCaller autoCaller(this);
13402 AssertComRCReturn(autoCaller.rc(), NULL);
13403
13404 Assert(mClientToken);
13405 if (mClientToken)
13406 return mClientToken->getToken();
13407 else
13408 return NULL;
13409}
13410#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13411
13412Machine::ClientToken *SessionMachine::i_getClientToken()
13413{
13414 LogFlowThisFunc(("\n"));
13415
13416 AutoCaller autoCaller(this);
13417 AssertComRCReturn(autoCaller.rc(), NULL);
13418
13419 return mClientToken;
13420}
13421
13422
13423/**
13424 * @note Locks this object for reading.
13425 */
13426HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13427{
13428 LogFlowThisFunc(("\n"));
13429
13430 AutoCaller autoCaller(this);
13431 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13432
13433 ComPtr<IInternalSessionControl> directControl;
13434 {
13435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13436 directControl = mData->mSession.mDirectControl;
13437 }
13438
13439 /* ignore notifications sent after #OnSessionEnd() is called */
13440 if (!directControl)
13441 return S_OK;
13442
13443 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13444}
13445
13446/**
13447 * @note Locks this object for reading.
13448 */
13449HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13450 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13451 IN_BSTR aGuestIp, LONG aGuestPort)
13452{
13453 LogFlowThisFunc(("\n"));
13454
13455 AutoCaller autoCaller(this);
13456 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13457
13458 ComPtr<IInternalSessionControl> directControl;
13459 {
13460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13461 directControl = mData->mSession.mDirectControl;
13462 }
13463
13464 /* ignore notifications sent after #OnSessionEnd() is called */
13465 if (!directControl)
13466 return S_OK;
13467 /*
13468 * instead acting like callback we ask IVirtualBox deliver corresponding event
13469 */
13470
13471 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13472 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13473 return S_OK;
13474}
13475
13476/**
13477 * @note Locks this object for reading.
13478 */
13479HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13480{
13481 LogFlowThisFunc(("\n"));
13482
13483 AutoCaller autoCaller(this);
13484 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13485
13486 ComPtr<IInternalSessionControl> directControl;
13487 {
13488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13489 directControl = mData->mSession.mDirectControl;
13490 }
13491
13492 /* ignore notifications sent after #OnSessionEnd() is called */
13493 if (!directControl)
13494 return S_OK;
13495
13496 return directControl->OnSerialPortChange(serialPort);
13497}
13498
13499/**
13500 * @note Locks this object for reading.
13501 */
13502HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13503{
13504 LogFlowThisFunc(("\n"));
13505
13506 AutoCaller autoCaller(this);
13507 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13508
13509 ComPtr<IInternalSessionControl> directControl;
13510 {
13511 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13512 directControl = mData->mSession.mDirectControl;
13513 }
13514
13515 /* ignore notifications sent after #OnSessionEnd() is called */
13516 if (!directControl)
13517 return S_OK;
13518
13519 return directControl->OnParallelPortChange(parallelPort);
13520}
13521
13522/**
13523 * @note Locks this object for reading.
13524 */
13525HRESULT SessionMachine::i_onStorageControllerChange()
13526{
13527 LogFlowThisFunc(("\n"));
13528
13529 AutoCaller autoCaller(this);
13530 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13531
13532 ComPtr<IInternalSessionControl> directControl;
13533 {
13534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13535 directControl = mData->mSession.mDirectControl;
13536 }
13537
13538 /* ignore notifications sent after #OnSessionEnd() is called */
13539 if (!directControl)
13540 return S_OK;
13541
13542 return directControl->OnStorageControllerChange();
13543}
13544
13545/**
13546 * @note Locks this object for reading.
13547 */
13548HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13549{
13550 LogFlowThisFunc(("\n"));
13551
13552 AutoCaller autoCaller(this);
13553 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13554
13555 ComPtr<IInternalSessionControl> directControl;
13556 {
13557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13558 directControl = mData->mSession.mDirectControl;
13559 }
13560
13561 /* ignore notifications sent after #OnSessionEnd() is called */
13562 if (!directControl)
13563 return S_OK;
13564
13565 return directControl->OnMediumChange(aAttachment, aForce);
13566}
13567
13568/**
13569 * @note Locks this object for reading.
13570 */
13571HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13572{
13573 LogFlowThisFunc(("\n"));
13574
13575 AutoCaller autoCaller(this);
13576 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13577
13578 ComPtr<IInternalSessionControl> directControl;
13579 {
13580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13581 directControl = mData->mSession.mDirectControl;
13582 }
13583
13584 /* ignore notifications sent after #OnSessionEnd() is called */
13585 if (!directControl)
13586 return S_OK;
13587
13588 return directControl->OnCPUChange(aCPU, aRemove);
13589}
13590
13591HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13592{
13593 LogFlowThisFunc(("\n"));
13594
13595 AutoCaller autoCaller(this);
13596 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13597
13598 ComPtr<IInternalSessionControl> directControl;
13599 {
13600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13601 directControl = mData->mSession.mDirectControl;
13602 }
13603
13604 /* ignore notifications sent after #OnSessionEnd() is called */
13605 if (!directControl)
13606 return S_OK;
13607
13608 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13609}
13610
13611/**
13612 * @note Locks this object for reading.
13613 */
13614HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13615{
13616 LogFlowThisFunc(("\n"));
13617
13618 AutoCaller autoCaller(this);
13619 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13620
13621 ComPtr<IInternalSessionControl> directControl;
13622 {
13623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13624 directControl = mData->mSession.mDirectControl;
13625 }
13626
13627 /* ignore notifications sent after #OnSessionEnd() is called */
13628 if (!directControl)
13629 return S_OK;
13630
13631 return directControl->OnVRDEServerChange(aRestart);
13632}
13633
13634/**
13635 * @note Locks this object for reading.
13636 */
13637HRESULT SessionMachine::i_onVideoCaptureChange()
13638{
13639 LogFlowThisFunc(("\n"));
13640
13641 AutoCaller autoCaller(this);
13642 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13643
13644 ComPtr<IInternalSessionControl> directControl;
13645 {
13646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13647 directControl = mData->mSession.mDirectControl;
13648 }
13649
13650 /* ignore notifications sent after #OnSessionEnd() is called */
13651 if (!directControl)
13652 return S_OK;
13653
13654 return directControl->OnVideoCaptureChange();
13655}
13656
13657/**
13658 * @note Locks this object for reading.
13659 */
13660HRESULT SessionMachine::i_onUSBControllerChange()
13661{
13662 LogFlowThisFunc(("\n"));
13663
13664 AutoCaller autoCaller(this);
13665 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13666
13667 ComPtr<IInternalSessionControl> directControl;
13668 {
13669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13670 directControl = mData->mSession.mDirectControl;
13671 }
13672
13673 /* ignore notifications sent after #OnSessionEnd() is called */
13674 if (!directControl)
13675 return S_OK;
13676
13677 return directControl->OnUSBControllerChange();
13678}
13679
13680/**
13681 * @note Locks this object for reading.
13682 */
13683HRESULT SessionMachine::i_onSharedFolderChange()
13684{
13685 LogFlowThisFunc(("\n"));
13686
13687 AutoCaller autoCaller(this);
13688 AssertComRCReturnRC(autoCaller.rc());
13689
13690 ComPtr<IInternalSessionControl> directControl;
13691 {
13692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13693 directControl = mData->mSession.mDirectControl;
13694 }
13695
13696 /* ignore notifications sent after #OnSessionEnd() is called */
13697 if (!directControl)
13698 return S_OK;
13699
13700 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13701}
13702
13703/**
13704 * @note Locks this object for reading.
13705 */
13706HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13707{
13708 LogFlowThisFunc(("\n"));
13709
13710 AutoCaller autoCaller(this);
13711 AssertComRCReturnRC(autoCaller.rc());
13712
13713 ComPtr<IInternalSessionControl> directControl;
13714 {
13715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13716 directControl = mData->mSession.mDirectControl;
13717 }
13718
13719 /* ignore notifications sent after #OnSessionEnd() is called */
13720 if (!directControl)
13721 return S_OK;
13722
13723 return directControl->OnClipboardModeChange(aClipboardMode);
13724}
13725
13726/**
13727 * @note Locks this object for reading.
13728 */
13729HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13730{
13731 LogFlowThisFunc(("\n"));
13732
13733 AutoCaller autoCaller(this);
13734 AssertComRCReturnRC(autoCaller.rc());
13735
13736 ComPtr<IInternalSessionControl> directControl;
13737 {
13738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13739 directControl = mData->mSession.mDirectControl;
13740 }
13741
13742 /* ignore notifications sent after #OnSessionEnd() is called */
13743 if (!directControl)
13744 return S_OK;
13745
13746 return directControl->OnDnDModeChange(aDnDMode);
13747}
13748
13749/**
13750 * @note Locks this object for reading.
13751 */
13752HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13753{
13754 LogFlowThisFunc(("\n"));
13755
13756 AutoCaller autoCaller(this);
13757 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13758
13759 ComPtr<IInternalSessionControl> directControl;
13760 {
13761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13762 directControl = mData->mSession.mDirectControl;
13763 }
13764
13765 /* ignore notifications sent after #OnSessionEnd() is called */
13766 if (!directControl)
13767 return S_OK;
13768
13769 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13770}
13771
13772/**
13773 * @note Locks this object for reading.
13774 */
13775HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13776{
13777 LogFlowThisFunc(("\n"));
13778
13779 AutoCaller autoCaller(this);
13780 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13781
13782 ComPtr<IInternalSessionControl> directControl;
13783 {
13784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13785 directControl = mData->mSession.mDirectControl;
13786 }
13787
13788 /* ignore notifications sent after #OnSessionEnd() is called */
13789 if (!directControl)
13790 return S_OK;
13791
13792 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13793}
13794
13795/**
13796 * Returns @c true if this machine's USB controller reports it has a matching
13797 * filter for the given USB device and @c false otherwise.
13798 *
13799 * @note locks this object for reading.
13800 */
13801bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13802{
13803 AutoCaller autoCaller(this);
13804 /* silently return if not ready -- this method may be called after the
13805 * direct machine session has been called */
13806 if (!autoCaller.isOk())
13807 return false;
13808
13809#ifdef VBOX_WITH_USB
13810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13811
13812 switch (mData->mMachineState)
13813 {
13814 case MachineState_Starting:
13815 case MachineState_Restoring:
13816 case MachineState_TeleportingIn:
13817 case MachineState_Paused:
13818 case MachineState_Running:
13819 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13820 * elsewhere... */
13821 alock.release();
13822 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13823 default: break;
13824 }
13825#else
13826 NOREF(aDevice);
13827 NOREF(aMaskedIfs);
13828#endif
13829 return false;
13830}
13831
13832/**
13833 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13834 */
13835HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13836 IVirtualBoxErrorInfo *aError,
13837 ULONG aMaskedIfs)
13838{
13839 LogFlowThisFunc(("\n"));
13840
13841 AutoCaller autoCaller(this);
13842
13843 /* This notification may happen after the machine object has been
13844 * uninitialized (the session was closed), so don't assert. */
13845 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13846
13847 ComPtr<IInternalSessionControl> directControl;
13848 {
13849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13850 directControl = mData->mSession.mDirectControl;
13851 }
13852
13853 /* fail on notifications sent after #OnSessionEnd() is called, it is
13854 * expected by the caller */
13855 if (!directControl)
13856 return E_FAIL;
13857
13858 /* No locks should be held at this point. */
13859 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13860 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13861
13862 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13863}
13864
13865/**
13866 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13867 */
13868HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13869 IVirtualBoxErrorInfo *aError)
13870{
13871 LogFlowThisFunc(("\n"));
13872
13873 AutoCaller autoCaller(this);
13874
13875 /* This notification may happen after the machine object has been
13876 * uninitialized (the session was closed), so don't assert. */
13877 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13878
13879 ComPtr<IInternalSessionControl> directControl;
13880 {
13881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13882 directControl = mData->mSession.mDirectControl;
13883 }
13884
13885 /* fail on notifications sent after #OnSessionEnd() is called, it is
13886 * expected by the caller */
13887 if (!directControl)
13888 return E_FAIL;
13889
13890 /* No locks should be held at this point. */
13891 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13892 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13893
13894 return directControl->OnUSBDeviceDetach(aId, aError);
13895}
13896
13897// protected methods
13898/////////////////////////////////////////////////////////////////////////////
13899
13900/**
13901 * Helper method to finalize saving the state.
13902 *
13903 * @note Must be called from under this object's lock.
13904 *
13905 * @param aRc S_OK if the snapshot has been taken successfully
13906 * @param aErrMsg human readable error message for failure
13907 *
13908 * @note Locks mParent + this objects for writing.
13909 */
13910HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13911{
13912 LogFlowThisFuncEnter();
13913
13914 AutoCaller autoCaller(this);
13915 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13916
13917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13918
13919 HRESULT rc = S_OK;
13920
13921 if (SUCCEEDED(aRc))
13922 {
13923 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13924
13925 /* save all VM settings */
13926 rc = i_saveSettings(NULL);
13927 // no need to check whether VirtualBox.xml needs saving also since
13928 // we can't have a name change pending at this point
13929 }
13930 else
13931 {
13932 // delete the saved state file (it might have been already created);
13933 // we need not check whether this is shared with a snapshot here because
13934 // we certainly created this saved state file here anew
13935 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13936 }
13937
13938 /* notify the progress object about operation completion */
13939 Assert(mConsoleTaskData.mProgress);
13940 if (SUCCEEDED(aRc))
13941 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13942 else
13943 {
13944 if (aErrMsg.length())
13945 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13946 COM_IIDOF(ISession),
13947 getComponentName(),
13948 aErrMsg.c_str());
13949 else
13950 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13951 }
13952
13953 /* clear out the temporary saved state data */
13954 mConsoleTaskData.mLastState = MachineState_Null;
13955 mConsoleTaskData.strStateFilePath.setNull();
13956 mConsoleTaskData.mProgress.setNull();
13957
13958 LogFlowThisFuncLeave();
13959 return rc;
13960}
13961
13962/**
13963 * Deletes the given file if it is no longer in use by either the current machine state
13964 * (if the machine is "saved") or any of the machine's snapshots.
13965 *
13966 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13967 * but is different for each SnapshotMachine. When calling this, the order of calling this
13968 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13969 * is therefore critical. I know, it's all rather messy.
13970 *
13971 * @param strStateFile
13972 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
13973 * the test for whether the saved state file is in use.
13974 */
13975void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
13976 Snapshot *pSnapshotToIgnore)
13977{
13978 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13979 if ( (strStateFile.isNotEmpty())
13980 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13981 )
13982 // ... and it must also not be shared with other snapshots
13983 if ( !mData->mFirstSnapshot
13984 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13985 // this checks the SnapshotMachine's state file paths
13986 )
13987 RTFileDelete(strStateFile.c_str());
13988}
13989
13990/**
13991 * Locks the attached media.
13992 *
13993 * All attached hard disks are locked for writing and DVD/floppy are locked for
13994 * reading. Parents of attached hard disks (if any) are locked for reading.
13995 *
13996 * This method also performs accessibility check of all media it locks: if some
13997 * media is inaccessible, the method will return a failure and a bunch of
13998 * extended error info objects per each inaccessible medium.
13999 *
14000 * Note that this method is atomic: if it returns a success, all media are
14001 * locked as described above; on failure no media is locked at all (all
14002 * succeeded individual locks will be undone).
14003 *
14004 * The caller is responsible for doing the necessary state sanity checks.
14005 *
14006 * The locks made by this method must be undone by calling #unlockMedia() when
14007 * no more needed.
14008 */
14009HRESULT SessionMachine::lockMedia()
14010{
14011 AutoCaller autoCaller(this);
14012 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14013
14014 AutoMultiWriteLock2 alock(this->lockHandle(),
14015 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14016
14017 /* bail out if trying to lock things with already set up locking */
14018 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14019
14020 MultiResult mrc(S_OK);
14021
14022 /* Collect locking information for all medium objects attached to the VM. */
14023 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14024 it != mMediaData->mAttachments.end();
14025 ++it)
14026 {
14027 MediumAttachment* pAtt = *it;
14028 DeviceType_T devType = pAtt->i_getType();
14029 Medium *pMedium = pAtt->i_getMedium();
14030
14031 MediumLockList *pMediumLockList(new MediumLockList());
14032 // There can be attachments without a medium (floppy/dvd), and thus
14033 // it's impossible to create a medium lock list. It still makes sense
14034 // to have the empty medium lock list in the map in case a medium is
14035 // attached later.
14036 if (pMedium != NULL)
14037 {
14038 MediumType_T mediumType = pMedium->i_getType();
14039 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14040 || mediumType == MediumType_Shareable;
14041 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14042
14043 alock.release();
14044 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14045 !fIsReadOnlyLock /* fMediumLockWrite */,
14046 NULL,
14047 *pMediumLockList);
14048 alock.acquire();
14049 if (FAILED(mrc))
14050 {
14051 delete pMediumLockList;
14052 mData->mSession.mLockedMedia.Clear();
14053 break;
14054 }
14055 }
14056
14057 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14058 if (FAILED(rc))
14059 {
14060 mData->mSession.mLockedMedia.Clear();
14061 mrc = setError(rc,
14062 tr("Collecting locking information for all attached media failed"));
14063 break;
14064 }
14065 }
14066
14067 if (SUCCEEDED(mrc))
14068 {
14069 /* Now lock all media. If this fails, nothing is locked. */
14070 alock.release();
14071 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14072 alock.acquire();
14073 if (FAILED(rc))
14074 {
14075 mrc = setError(rc,
14076 tr("Locking of attached media failed"));
14077 }
14078 }
14079
14080 return mrc;
14081}
14082
14083/**
14084 * Undoes the locks made by by #lockMedia().
14085 */
14086HRESULT SessionMachine::unlockMedia()
14087{
14088 AutoCaller autoCaller(this);
14089 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14090
14091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14092
14093 /* we may be holding important error info on the current thread;
14094 * preserve it */
14095 ErrorInfoKeeper eik;
14096
14097 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14098 AssertComRC(rc);
14099 return rc;
14100}
14101
14102/**
14103 * Helper to change the machine state (reimplementation).
14104 *
14105 * @note Locks this object for writing.
14106 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14107 * it can cause crashes in random places due to unexpectedly committing
14108 * the current settings. The caller is responsible for that. The call
14109 * to saveStateSettings is fine, because this method does not commit.
14110 */
14111HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14112{
14113 LogFlowThisFuncEnter();
14114 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14115
14116 AutoCaller autoCaller(this);
14117 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14118
14119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14120
14121 MachineState_T oldMachineState = mData->mMachineState;
14122
14123 AssertMsgReturn(oldMachineState != aMachineState,
14124 ("oldMachineState=%s, aMachineState=%s\n",
14125 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14126 E_FAIL);
14127
14128 HRESULT rc = S_OK;
14129
14130 int stsFlags = 0;
14131 bool deleteSavedState = false;
14132
14133 /* detect some state transitions */
14134
14135 if ( ( oldMachineState == MachineState_Saved
14136 && aMachineState == MachineState_Restoring)
14137 || ( ( oldMachineState == MachineState_PoweredOff
14138 || oldMachineState == MachineState_Teleported
14139 || oldMachineState == MachineState_Aborted
14140 )
14141 && ( aMachineState == MachineState_TeleportingIn
14142 || aMachineState == MachineState_Starting
14143 )
14144 )
14145 )
14146 {
14147 /* The EMT thread is about to start */
14148
14149 /* Nothing to do here for now... */
14150
14151 /// @todo NEWMEDIA don't let mDVDDrive and other children
14152 /// change anything when in the Starting/Restoring state
14153 }
14154 else if ( ( oldMachineState == MachineState_Running
14155 || oldMachineState == MachineState_Paused
14156 || oldMachineState == MachineState_Teleporting
14157 || oldMachineState == MachineState_LiveSnapshotting
14158 || oldMachineState == MachineState_Stuck
14159 || oldMachineState == MachineState_Starting
14160 || oldMachineState == MachineState_Stopping
14161 || oldMachineState == MachineState_Saving
14162 || oldMachineState == MachineState_Restoring
14163 || oldMachineState == MachineState_TeleportingPausedVM
14164 || oldMachineState == MachineState_TeleportingIn
14165 )
14166 && ( aMachineState == MachineState_PoweredOff
14167 || aMachineState == MachineState_Saved
14168 || aMachineState == MachineState_Teleported
14169 || aMachineState == MachineState_Aborted
14170 )
14171 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14172 * snapshot */
14173 && ( mConsoleTaskData.mSnapshot.isNull()
14174 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14175 )
14176 )
14177 {
14178 /* The EMT thread has just stopped, unlock attached media. Note that as
14179 * opposed to locking that is done from Console, we do unlocking here
14180 * because the VM process may have aborted before having a chance to
14181 * properly unlock all media it locked. */
14182
14183 unlockMedia();
14184 }
14185
14186 if (oldMachineState == MachineState_Restoring)
14187 {
14188 if (aMachineState != MachineState_Saved)
14189 {
14190 /*
14191 * delete the saved state file once the machine has finished
14192 * restoring from it (note that Console sets the state from
14193 * Restoring to Saved if the VM couldn't restore successfully,
14194 * to give the user an ability to fix an error and retry --
14195 * we keep the saved state file in this case)
14196 */
14197 deleteSavedState = true;
14198 }
14199 }
14200 else if ( oldMachineState == MachineState_Saved
14201 && ( aMachineState == MachineState_PoweredOff
14202 || aMachineState == MachineState_Aborted
14203 || aMachineState == MachineState_Teleported
14204 )
14205 )
14206 {
14207 /*
14208 * delete the saved state after Console::ForgetSavedState() is called
14209 * or if the VM process (owning a direct VM session) crashed while the
14210 * VM was Saved
14211 */
14212
14213 /// @todo (dmik)
14214 // Not sure that deleting the saved state file just because of the
14215 // client death before it attempted to restore the VM is a good
14216 // thing. But when it crashes we need to go to the Aborted state
14217 // which cannot have the saved state file associated... The only
14218 // way to fix this is to make the Aborted condition not a VM state
14219 // but a bool flag: i.e., when a crash occurs, set it to true and
14220 // change the state to PoweredOff or Saved depending on the
14221 // saved state presence.
14222
14223 deleteSavedState = true;
14224 mData->mCurrentStateModified = TRUE;
14225 stsFlags |= SaveSTS_CurStateModified;
14226 }
14227
14228 if ( aMachineState == MachineState_Starting
14229 || aMachineState == MachineState_Restoring
14230 || aMachineState == MachineState_TeleportingIn
14231 )
14232 {
14233 /* set the current state modified flag to indicate that the current
14234 * state is no more identical to the state in the
14235 * current snapshot */
14236 if (!mData->mCurrentSnapshot.isNull())
14237 {
14238 mData->mCurrentStateModified = TRUE;
14239 stsFlags |= SaveSTS_CurStateModified;
14240 }
14241 }
14242
14243 if (deleteSavedState)
14244 {
14245 if (mRemoveSavedState)
14246 {
14247 Assert(!mSSData->strStateFilePath.isEmpty());
14248
14249 // it is safe to delete the saved state file if ...
14250 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14251 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14252 // ... none of the snapshots share the saved state file
14253 )
14254 RTFileDelete(mSSData->strStateFilePath.c_str());
14255 }
14256
14257 mSSData->strStateFilePath.setNull();
14258 stsFlags |= SaveSTS_StateFilePath;
14259 }
14260
14261 /* redirect to the underlying peer machine */
14262 mPeer->i_setMachineState(aMachineState);
14263
14264 if ( aMachineState == MachineState_PoweredOff
14265 || aMachineState == MachineState_Teleported
14266 || aMachineState == MachineState_Aborted
14267 || aMachineState == MachineState_Saved)
14268 {
14269 /* the machine has stopped execution
14270 * (or the saved state file was adopted) */
14271 stsFlags |= SaveSTS_StateTimeStamp;
14272 }
14273
14274 if ( ( oldMachineState == MachineState_PoweredOff
14275 || oldMachineState == MachineState_Aborted
14276 || oldMachineState == MachineState_Teleported
14277 )
14278 && aMachineState == MachineState_Saved)
14279 {
14280 /* the saved state file was adopted */
14281 Assert(!mSSData->strStateFilePath.isEmpty());
14282 stsFlags |= SaveSTS_StateFilePath;
14283 }
14284
14285#ifdef VBOX_WITH_GUEST_PROPS
14286 if ( aMachineState == MachineState_PoweredOff
14287 || aMachineState == MachineState_Aborted
14288 || aMachineState == MachineState_Teleported)
14289 {
14290 /* Make sure any transient guest properties get removed from the
14291 * property store on shutdown. */
14292
14293 HWData::GuestPropertyMap::const_iterator it;
14294 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14295 if (!fNeedsSaving)
14296 for (it = mHWData->mGuestProperties.begin();
14297 it != mHWData->mGuestProperties.end(); ++it)
14298 if ( (it->second.mFlags & guestProp::TRANSIENT)
14299 || (it->second.mFlags & guestProp::TRANSRESET))
14300 {
14301 fNeedsSaving = true;
14302 break;
14303 }
14304 if (fNeedsSaving)
14305 {
14306 mData->mCurrentStateModified = TRUE;
14307 stsFlags |= SaveSTS_CurStateModified;
14308 }
14309 }
14310#endif
14311
14312 rc = i_saveStateSettings(stsFlags);
14313
14314 if ( ( oldMachineState != MachineState_PoweredOff
14315 && oldMachineState != MachineState_Aborted
14316 && oldMachineState != MachineState_Teleported
14317 )
14318 && ( aMachineState == MachineState_PoweredOff
14319 || aMachineState == MachineState_Aborted
14320 || aMachineState == MachineState_Teleported
14321 )
14322 )
14323 {
14324 /* we've been shut down for any reason */
14325 /* no special action so far */
14326 }
14327
14328 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14329 LogFlowThisFuncLeave();
14330 return rc;
14331}
14332
14333/**
14334 * Sends the current machine state value to the VM process.
14335 *
14336 * @note Locks this object for reading, then calls a client process.
14337 */
14338HRESULT SessionMachine::i_updateMachineStateOnClient()
14339{
14340 AutoCaller autoCaller(this);
14341 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14342
14343 ComPtr<IInternalSessionControl> directControl;
14344 {
14345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14346 AssertReturn(!!mData, E_FAIL);
14347 directControl = mData->mSession.mDirectControl;
14348
14349 /* directControl may be already set to NULL here in #OnSessionEnd()
14350 * called too early by the direct session process while there is still
14351 * some operation (like deleting the snapshot) in progress. The client
14352 * process in this case is waiting inside Session::close() for the
14353 * "end session" process object to complete, while #uninit() called by
14354 * #checkForDeath() on the Watcher thread is waiting for the pending
14355 * operation to complete. For now, we accept this inconsistent behavior
14356 * and simply do nothing here. */
14357
14358 if (mData->mSession.mState == SessionState_Unlocking)
14359 return S_OK;
14360
14361 AssertReturn(!directControl.isNull(), E_FAIL);
14362 }
14363
14364 return directControl->UpdateMachineState(mData->mMachineState);
14365}
14366
14367HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14368{
14369 NOREF(aRemove);
14370 ReturnComNotImplemented();
14371}
14372
14373HRESULT Machine::updateState(MachineState_T aState)
14374{
14375 NOREF(aState);
14376 ReturnComNotImplemented();
14377}
14378
14379HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14380{
14381 NOREF(aProgress);
14382 ReturnComNotImplemented();
14383}
14384
14385HRESULT Machine::endPowerUp(LONG aResult)
14386{
14387 NOREF(aResult);
14388 ReturnComNotImplemented();
14389}
14390
14391HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14392{
14393 NOREF(aProgress);
14394 ReturnComNotImplemented();
14395}
14396
14397HRESULT Machine::endPoweringDown(LONG aResult,
14398 const com::Utf8Str &aErrMsg)
14399{
14400 NOREF(aResult);
14401 NOREF(aErrMsg);
14402 ReturnComNotImplemented();
14403}
14404
14405HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14406 BOOL *aMatched,
14407 ULONG *aMaskedInterfaces)
14408{
14409 NOREF(aDevice);
14410 NOREF(aMatched);
14411 NOREF(aMaskedInterfaces);
14412 ReturnComNotImplemented();
14413
14414}
14415
14416HRESULT Machine::captureUSBDevice(const com::Guid &aId)
14417{
14418 NOREF(aId);
14419 ReturnComNotImplemented();
14420}
14421
14422HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14423 BOOL aDone)
14424{
14425 NOREF(aId);
14426 NOREF(aDone);
14427 ReturnComNotImplemented();
14428}
14429
14430HRESULT Machine::autoCaptureUSBDevices()
14431{
14432 ReturnComNotImplemented();
14433}
14434
14435HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14436{
14437 NOREF(aDone);
14438 ReturnComNotImplemented();
14439}
14440
14441HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14442 ComPtr<IProgress> &aProgress)
14443{
14444 NOREF(aSession);
14445 NOREF(aProgress);
14446 ReturnComNotImplemented();
14447}
14448
14449HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14450 com::Utf8Str &aStateFilePath)
14451{
14452 NOREF(aProgress);
14453 NOREF(aStateFilePath);
14454 ReturnComNotImplemented();
14455}
14456
14457HRESULT Machine::endSavingState(LONG aResult,
14458 const com::Utf8Str &aErrMsg)
14459{
14460 NOREF(aResult);
14461 NOREF(aErrMsg);
14462 ReturnComNotImplemented();
14463}
14464
14465HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14466{
14467 NOREF(aSavedStateFile);
14468 ReturnComNotImplemented();
14469}
14470
14471HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14472 const com::Utf8Str &aName,
14473 const com::Utf8Str &aDescription,
14474 const ComPtr<IProgress> &aConsoleProgress,
14475 BOOL aFTakingSnapshotOnline,
14476 com::Utf8Str &aStateFilePath)
14477{
14478 NOREF(aInitiator);
14479 NOREF(aName);
14480 NOREF(aDescription);
14481 NOREF(aConsoleProgress);
14482 NOREF(aFTakingSnapshotOnline);
14483 NOREF(aStateFilePath);
14484 ReturnComNotImplemented();
14485}
14486
14487HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14488{
14489 NOREF(aSuccess);
14490 ReturnComNotImplemented();
14491}
14492
14493HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14494 const com::Guid &aStartId,
14495 const com::Guid &aEndId,
14496 BOOL aDeleteAllChildren,
14497 MachineState_T *aMachineState,
14498 ComPtr<IProgress> &aProgress)
14499{
14500 NOREF(aInitiator);
14501 NOREF(aStartId);
14502 NOREF(aEndId);
14503 NOREF(aDeleteAllChildren);
14504 NOREF(aMachineState);
14505 NOREF(aProgress);
14506 ReturnComNotImplemented();
14507}
14508
14509HRESULT Machine::finishOnlineMergeMedium()
14510{
14511 ReturnComNotImplemented();
14512}
14513
14514HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14515 const ComPtr<ISnapshot> &aSnapshot,
14516 MachineState_T *aMachineState,
14517 ComPtr<IProgress> &aProgress)
14518{
14519 NOREF(aInitiator);
14520 NOREF(aSnapshot);
14521 NOREF(aMachineState);
14522 NOREF(aProgress);
14523 ReturnComNotImplemented();
14524}
14525
14526HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14527 std::vector<com::Utf8Str> &aValues,
14528 std::vector<LONG64> &aTimestamps,
14529 std::vector<com::Utf8Str> &aFlags)
14530{
14531 NOREF(aNames);
14532 NOREF(aValues);
14533 NOREF(aTimestamps);
14534 NOREF(aFlags);
14535 ReturnComNotImplemented();
14536}
14537
14538HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14539 const com::Utf8Str &aValue,
14540 LONG64 aTimestamp,
14541 const com::Utf8Str &aFlags)
14542{
14543 NOREF(aName);
14544 NOREF(aValue);
14545 NOREF(aTimestamp);
14546 NOREF(aFlags);
14547 ReturnComNotImplemented();
14548}
14549
14550HRESULT Machine::lockMedia()
14551{
14552 ReturnComNotImplemented();
14553}
14554
14555HRESULT Machine::unlockMedia()
14556{
14557 ReturnComNotImplemented();
14558}
14559
14560HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14561 ComPtr<IMediumAttachment> &aNewAttachment)
14562{
14563 NOREF(aAttachment);
14564 NOREF(aNewAttachment);
14565 ReturnComNotImplemented();
14566}
14567
14568HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14569 ULONG aCpuUser,
14570 ULONG aCpuKernel,
14571 ULONG aCpuIdle,
14572 ULONG aMemTotal,
14573 ULONG aMemFree,
14574 ULONG aMemBalloon,
14575 ULONG aMemShared,
14576 ULONG aMemCache,
14577 ULONG aPagedTotal,
14578 ULONG aMemAllocTotal,
14579 ULONG aMemFreeTotal,
14580 ULONG aMemBalloonTotal,
14581 ULONG aMemSharedTotal,
14582 ULONG aVmNetRx,
14583 ULONG aVmNetTx)
14584{
14585 NOREF(aValidStats);
14586 NOREF(aCpuUser);
14587 NOREF(aCpuKernel);
14588 NOREF(aCpuIdle);
14589 NOREF(aMemTotal);
14590 NOREF(aMemFree);
14591 NOREF(aMemBalloon);
14592 NOREF(aMemShared);
14593 NOREF(aMemCache);
14594 NOREF(aPagedTotal);
14595 NOREF(aMemAllocTotal);
14596 NOREF(aMemFreeTotal);
14597 NOREF(aMemBalloonTotal);
14598 NOREF(aMemSharedTotal);
14599 NOREF(aVmNetRx);
14600 NOREF(aVmNetTx);
14601 ReturnComNotImplemented();
14602}
Note: See TracBrowser for help on using the repository browser.

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