VirtualBox

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

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

Main: Added paravirt. provider KVM APIs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 501.5 KB
Line 
1/* $Id: MachineImpl.cpp 54911 2015-03-23 17:24:33Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2015 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#ifdef VBOX_WITH_DTRACE_R3_MAIN
90# include "dtrace/VBoxAPI.h"
91#endif
92
93#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
94# define HOSTSUFF_EXE ".exe"
95#else /* !RT_OS_WINDOWS */
96# define HOSTSUFF_EXE ""
97#endif /* !RT_OS_WINDOWS */
98
99// defines / prototypes
100/////////////////////////////////////////////////////////////////////////////
101
102/////////////////////////////////////////////////////////////////////////////
103// Machine::Data structure
104/////////////////////////////////////////////////////////////////////////////
105
106Machine::Data::Data()
107{
108 mRegistered = FALSE;
109 pMachineConfigFile = NULL;
110 /* Contains hints on what has changed when the user is using the VM (config
111 * changes, running the VM, ...). This is used to decide if a config needs
112 * to be written to disk. */
113 flModifications = 0;
114 /* VM modification usually also trigger setting the current state to
115 * "Modified". Although this is not always the case. An e.g. is the VM
116 * initialization phase or when snapshot related data is changed. The
117 * actually behavior is controlled by the following flag. */
118 m_fAllowStateModification = false;
119 mAccessible = FALSE;
120 /* mUuid is initialized in Machine::init() */
121
122 mMachineState = MachineState_PoweredOff;
123 RTTimeNow(&mLastStateChange);
124
125 mMachineStateDeps = 0;
126 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
127 mMachineStateChangePending = 0;
128
129 mCurrentStateModified = TRUE;
130 mGuestPropertiesModified = FALSE;
131
132 mSession.mPID = NIL_RTPROCESS;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mSyntheticCpu = false;
196 mTripleFaultReset = false;
197 mHPETEnabled = false;
198
199 /* default boot order: floppy - DVD - HDD */
200 mBootOrder[0] = DeviceType_Floppy;
201 mBootOrder[1] = DeviceType_DVD;
202 mBootOrder[2] = DeviceType_HardDisk;
203 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
204 mBootOrder[i] = DeviceType_Null;
205
206 mClipboardMode = ClipboardMode_Disabled;
207 mDnDMode = DnDMode_Disabled;
208 mGuestPropertyNotificationPatterns = "";
209
210 mFirmwareType = FirmwareType_BIOS;
211 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
212 mPointingHIDType = PointingHIDType_PS2Mouse;
213 mChipsetType = ChipsetType_PIIX3;
214 mParavirtProvider = ParavirtProvider_Default;
215 mEmulatedUSBCardReaderEnabled = FALSE;
216
217 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
218 mCPUAttached[i] = false;
219
220 mIOCacheEnabled = true;
221 mIOCacheSize = 5; /* 5MB */
222
223 /* Maximum CPU execution cap by default. */
224 mCpuExecutionCap = 100;
225}
226
227Machine::HWData::~HWData()
228{
229}
230
231/////////////////////////////////////////////////////////////////////////////
232// Machine::HDData structure
233/////////////////////////////////////////////////////////////////////////////
234
235Machine::MediaData::MediaData()
236{
237}
238
239Machine::MediaData::~MediaData()
240{
241}
242
243/////////////////////////////////////////////////////////////////////////////
244// Machine class
245/////////////////////////////////////////////////////////////////////////////
246
247// constructor / destructor
248/////////////////////////////////////////////////////////////////////////////
249
250Machine::Machine() :
251#ifdef VBOX_WITH_RESOURCE_USAGE_API
252 mCollectorGuest(NULL),
253#endif
254 mPeer(NULL),
255 mParent(NULL),
256 mSerialPorts(),
257 mParallelPorts(),
258 uRegistryNeedsSaving(0)
259{}
260
261Machine::~Machine()
262{}
263
264HRESULT Machine::FinalConstruct()
265{
266 LogFlowThisFunc(("\n"));
267 return BaseFinalConstruct();
268}
269
270void Machine::FinalRelease()
271{
272 LogFlowThisFunc(("\n"));
273 uninit();
274 BaseFinalRelease();
275}
276
277/**
278 * Initializes a new machine instance; this init() variant creates a new, empty machine.
279 * This gets called from VirtualBox::CreateMachine().
280 *
281 * @param aParent Associated parent object
282 * @param strConfigFile Local file system path to the VM settings file (can
283 * be relative to the VirtualBox config directory).
284 * @param strName name for the machine
285 * @param llGroups list of groups for the machine
286 * @param aOsType OS Type of this machine or NULL.
287 * @param aId UUID for the new machine.
288 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
289 *
290 * @return Success indicator. if not S_OK, the machine object is invalid
291 */
292HRESULT Machine::init(VirtualBox *aParent,
293 const Utf8Str &strConfigFile,
294 const Utf8Str &strName,
295 const StringsList &llGroups,
296 GuestOSType *aOsType,
297 const Guid &aId,
298 bool fForceOverwrite,
299 bool fDirectoryIncludesUUID)
300{
301 LogFlowThisFuncEnter();
302 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
303
304 /* Enclose the state transition NotReady->InInit->Ready */
305 AutoInitSpan autoInitSpan(this);
306 AssertReturn(autoInitSpan.isOk(), E_FAIL);
307
308 HRESULT rc = initImpl(aParent, strConfigFile);
309 if (FAILED(rc)) return rc;
310
311 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
312 if (FAILED(rc)) return rc;
313
314 if (SUCCEEDED(rc))
315 {
316 // create an empty machine config
317 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
318
319 rc = initDataAndChildObjects();
320 }
321
322 if (SUCCEEDED(rc))
323 {
324 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
325 mData->mAccessible = TRUE;
326
327 unconst(mData->mUuid) = aId;
328
329 mUserData->s.strName = strName;
330
331 mUserData->s.llGroups = llGroups;
332
333 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
334 // the "name sync" flag determines whether the machine directory gets renamed along
335 // with the machine file; say so if the settings file name is the same as the
336 // settings file parent directory (machine directory)
337 mUserData->s.fNameSync = i_isInOwnDir();
338
339 // initialize the default snapshots folder
340 rc = COMSETTER(SnapshotFolder)(NULL);
341 AssertComRC(rc);
342
343 if (aOsType)
344 {
345 /* Store OS type */
346 mUserData->s.strOsType = aOsType->i_id();
347
348 /* Apply BIOS defaults */
349 mBIOSSettings->i_applyDefaults(aOsType);
350
351 /* Apply network adapters defaults */
352 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
353 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
354
355 /* Apply serial port defaults */
356 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
357 mSerialPorts[slot]->i_applyDefaults(aOsType);
358
359 /* Let the OS type select 64-bit ness. */
360 mHWData->mLongMode = aOsType->i_is64Bit()
361 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
362 }
363
364 /* At this point the changing of the current state modification
365 * flag is allowed. */
366 i_allowStateModification();
367
368 /* commit all changes made during the initialization */
369 i_commit();
370 }
371
372 /* Confirm a successful initialization when it's the case */
373 if (SUCCEEDED(rc))
374 {
375 if (mData->mAccessible)
376 autoInitSpan.setSucceeded();
377 else
378 autoInitSpan.setLimited();
379 }
380
381 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
382 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
383 mData->mRegistered,
384 mData->mAccessible,
385 rc));
386
387 LogFlowThisFuncLeave();
388
389 return rc;
390}
391
392/**
393 * Initializes a new instance with data from machine XML (formerly Init_Registered).
394 * Gets called in two modes:
395 *
396 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
397 * UUID is specified and we mark the machine as "registered";
398 *
399 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
400 * and the machine remains unregistered until RegisterMachine() is called.
401 *
402 * @param aParent Associated parent object
403 * @param aConfigFile Local file system path to the VM settings file (can
404 * be relative to the VirtualBox config directory).
405 * @param aId UUID of the machine or NULL (see above).
406 *
407 * @return Success indicator. if not S_OK, the machine object is invalid
408 */
409HRESULT Machine::initFromSettings(VirtualBox *aParent,
410 const Utf8Str &strConfigFile,
411 const Guid *aId)
412{
413 LogFlowThisFuncEnter();
414 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
415
416 /* Enclose the state transition NotReady->InInit->Ready */
417 AutoInitSpan autoInitSpan(this);
418 AssertReturn(autoInitSpan.isOk(), E_FAIL);
419
420 HRESULT rc = initImpl(aParent, strConfigFile);
421 if (FAILED(rc)) return rc;
422
423 if (aId)
424 {
425 // loading a registered VM:
426 unconst(mData->mUuid) = *aId;
427 mData->mRegistered = TRUE;
428 // now load the settings from XML:
429 rc = i_registeredInit();
430 // this calls initDataAndChildObjects() and loadSettings()
431 }
432 else
433 {
434 // opening an unregistered VM (VirtualBox::OpenMachine()):
435 rc = initDataAndChildObjects();
436
437 if (SUCCEEDED(rc))
438 {
439 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
440 mData->mAccessible = TRUE;
441
442 try
443 {
444 // load and parse machine XML; this will throw on XML or logic errors
445 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
446
447 // reject VM UUID duplicates, they can happen if someone
448 // tries to register an already known VM config again
449 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
450 true /* fPermitInaccessible */,
451 false /* aDoSetError */,
452 NULL) != VBOX_E_OBJECT_NOT_FOUND)
453 {
454 throw setError(E_FAIL,
455 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
456 mData->m_strConfigFile.c_str());
457 }
458
459 // use UUID from machine config
460 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
461
462 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
463 NULL /* puuidRegistry */);
464 if (FAILED(rc)) throw rc;
465
466 /* At this point the changing of the current state modification
467 * flag is allowed. */
468 i_allowStateModification();
469
470 i_commit();
471 }
472 catch (HRESULT err)
473 {
474 /* we assume that error info is set by the thrower */
475 rc = err;
476 }
477 catch (...)
478 {
479 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
480 }
481 }
482 }
483
484 /* Confirm a successful initialization when it's the case */
485 if (SUCCEEDED(rc))
486 {
487 if (mData->mAccessible)
488 autoInitSpan.setSucceeded();
489 else
490 {
491 autoInitSpan.setLimited();
492
493 // uninit media from this machine's media registry, or else
494 // reloading the settings will fail
495 mParent->i_unregisterMachineMedia(i_getId());
496 }
497 }
498
499 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
500 "rc=%08X\n",
501 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
502 mData->mRegistered, mData->mAccessible, rc));
503
504 LogFlowThisFuncLeave();
505
506 return rc;
507}
508
509/**
510 * Initializes a new instance from a machine config that is already in memory
511 * (import OVF case). Since we are importing, the UUID in the machine
512 * config is ignored and we always generate a fresh one.
513 *
514 * @param strName Name for the new machine; this overrides what is specified in config and is used
515 * for the settings file as well.
516 * @param config Machine configuration loaded and parsed from XML.
517 *
518 * @return Success indicator. if not S_OK, the machine object is invalid
519 */
520HRESULT Machine::init(VirtualBox *aParent,
521 const Utf8Str &strName,
522 const settings::MachineConfigFile &config)
523{
524 LogFlowThisFuncEnter();
525
526 /* Enclose the state transition NotReady->InInit->Ready */
527 AutoInitSpan autoInitSpan(this);
528 AssertReturn(autoInitSpan.isOk(), E_FAIL);
529
530 Utf8Str strConfigFile;
531 aParent->i_getDefaultMachineFolder(strConfigFile);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(RTPATH_DELIMITER);
535 strConfigFile.append(strName);
536 strConfigFile.append(".vbox");
537
538 HRESULT rc = initImpl(aParent, strConfigFile);
539 if (FAILED(rc)) return rc;
540
541 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
542 if (FAILED(rc)) return rc;
543
544 rc = initDataAndChildObjects();
545
546 if (SUCCEEDED(rc))
547 {
548 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
549 mData->mAccessible = TRUE;
550
551 // create empty machine config for instance data
552 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
553
554 // generate fresh UUID, ignore machine config
555 unconst(mData->mUuid).create();
556
557 rc = i_loadMachineDataFromSettings(config,
558 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
559
560 // override VM name as well, it may be different
561 mUserData->s.strName = strName;
562
563 if (SUCCEEDED(rc))
564 {
565 /* At this point the changing of the current state modification
566 * flag is allowed. */
567 i_allowStateModification();
568
569 /* commit all changes made during the initialization */
570 i_commit();
571 }
572 }
573
574 /* Confirm a successful initialization when it's the case */
575 if (SUCCEEDED(rc))
576 {
577 if (mData->mAccessible)
578 autoInitSpan.setSucceeded();
579 else
580 {
581 /* Ignore all errors from unregistering, they would destroy
582- * the more interesting error information we already have,
583- * pinpointing the issue with the VM config. */
584 ErrorInfoKeeper eik;
585
586 autoInitSpan.setLimited();
587
588 // uninit media from this machine's media registry, or else
589 // reloading the settings will fail
590 mParent->i_unregisterMachineMedia(i_getId());
591 }
592 }
593
594 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
595 "rc=%08X\n",
596 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
597 mData->mRegistered, mData->mAccessible, rc));
598
599 LogFlowThisFuncLeave();
600
601 return rc;
602}
603
604/**
605 * Shared code between the various init() implementations.
606 * @param aParent
607 * @return
608 */
609HRESULT Machine::initImpl(VirtualBox *aParent,
610 const Utf8Str &strConfigFile)
611{
612 LogFlowThisFuncEnter();
613
614 AssertReturn(aParent, E_INVALIDARG);
615 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
616
617 HRESULT rc = S_OK;
618
619 /* share the parent weakly */
620 unconst(mParent) = aParent;
621
622 /* allocate the essential machine data structure (the rest will be
623 * allocated later by initDataAndChildObjects() */
624 mData.allocate();
625
626 /* memorize the config file name (as provided) */
627 mData->m_strConfigFile = strConfigFile;
628
629 /* get the full file name */
630 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
631 if (RT_FAILURE(vrc1))
632 return setError(VBOX_E_FILE_ERROR,
633 tr("Invalid machine settings file name '%s' (%Rrc)"),
634 strConfigFile.c_str(),
635 vrc1);
636
637 LogFlowThisFuncLeave();
638
639 return rc;
640}
641
642/**
643 * Tries to create a machine settings file in the path stored in the machine
644 * instance data. Used when a new machine is created to fail gracefully if
645 * the settings file could not be written (e.g. because machine dir is read-only).
646 * @return
647 */
648HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
649{
650 HRESULT rc = S_OK;
651
652 // when we create a new machine, we must be able to create the settings file
653 RTFILE f = NIL_RTFILE;
654 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
655 if ( RT_SUCCESS(vrc)
656 || vrc == VERR_SHARING_VIOLATION
657 )
658 {
659 if (RT_SUCCESS(vrc))
660 RTFileClose(f);
661 if (!fForceOverwrite)
662 rc = setError(VBOX_E_FILE_ERROR,
663 tr("Machine settings file '%s' already exists"),
664 mData->m_strConfigFileFull.c_str());
665 else
666 {
667 /* try to delete the config file, as otherwise the creation
668 * of a new settings file will fail. */
669 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
670 if (RT_FAILURE(vrc2))
671 rc = setError(VBOX_E_FILE_ERROR,
672 tr("Could not delete the existing settings file '%s' (%Rrc)"),
673 mData->m_strConfigFileFull.c_str(), vrc2);
674 }
675 }
676 else if ( vrc != VERR_FILE_NOT_FOUND
677 && vrc != VERR_PATH_NOT_FOUND
678 )
679 rc = setError(VBOX_E_FILE_ERROR,
680 tr("Invalid machine settings file name '%s' (%Rrc)"),
681 mData->m_strConfigFileFull.c_str(),
682 vrc);
683 return rc;
684}
685
686/**
687 * Initializes the registered machine by loading the settings file.
688 * This method is separated from #init() in order to make it possible to
689 * retry the operation after VirtualBox startup instead of refusing to
690 * startup the whole VirtualBox server in case if the settings file of some
691 * registered VM is invalid or inaccessible.
692 *
693 * @note Must be always called from this object's write lock
694 * (unless called from #init() that doesn't need any locking).
695 * @note Locks the mUSBController method for writing.
696 * @note Subclasses must not call this method.
697 */
698HRESULT Machine::i_registeredInit()
699{
700 AssertReturn(!i_isSessionMachine(), E_FAIL);
701 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
702 AssertReturn(mData->mUuid.isValid(), E_FAIL);
703 AssertReturn(!mData->mAccessible, E_FAIL);
704
705 HRESULT rc = initDataAndChildObjects();
706
707 if (SUCCEEDED(rc))
708 {
709 /* Temporarily reset the registered flag in order to let setters
710 * potentially called from loadSettings() succeed (isMutable() used in
711 * all setters will return FALSE for a Machine instance if mRegistered
712 * is TRUE). */
713 mData->mRegistered = FALSE;
714
715 try
716 {
717 // load and parse machine XML; this will throw on XML or logic errors
718 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
719
720 if (mData->mUuid != mData->pMachineConfigFile->uuid)
721 throw setError(E_FAIL,
722 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
723 mData->pMachineConfigFile->uuid.raw(),
724 mData->m_strConfigFileFull.c_str(),
725 mData->mUuid.toString().c_str(),
726 mParent->i_settingsFilePath().c_str());
727
728 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
729 NULL /* const Guid *puuidRegistry */);
730 if (FAILED(rc)) throw rc;
731 }
732 catch (HRESULT err)
733 {
734 /* we assume that error info is set by the thrower */
735 rc = err;
736 }
737 catch (...)
738 {
739 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
740 }
741
742 /* Restore the registered flag (even on failure) */
743 mData->mRegistered = TRUE;
744 }
745
746 if (SUCCEEDED(rc))
747 {
748 /* Set mAccessible to TRUE only if we successfully locked and loaded
749 * the settings file */
750 mData->mAccessible = TRUE;
751
752 /* commit all changes made during loading the settings file */
753 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
754 /// @todo r=klaus for some reason the settings loading logic backs up
755 // the settings, and therefore a commit is needed. Should probably be changed.
756 }
757 else
758 {
759 /* If the machine is registered, then, instead of returning a
760 * failure, we mark it as inaccessible and set the result to
761 * success to give it a try later */
762
763 /* fetch the current error info */
764 mData->mAccessError = com::ErrorInfo();
765 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
766 mData->mUuid.raw(),
767 mData->mAccessError.getText().raw()));
768
769 /* rollback all changes */
770 i_rollback(false /* aNotify */);
771
772 // uninit media from this machine's media registry, or else
773 // reloading the settings will fail
774 mParent->i_unregisterMachineMedia(i_getId());
775
776 /* uninitialize the common part to make sure all data is reset to
777 * default (null) values */
778 uninitDataAndChildObjects();
779
780 rc = S_OK;
781 }
782
783 return rc;
784}
785
786/**
787 * Uninitializes the instance.
788 * Called either from FinalRelease() or by the parent when it gets destroyed.
789 *
790 * @note The caller of this method must make sure that this object
791 * a) doesn't have active callers on the current thread and b) is not locked
792 * by the current thread; otherwise uninit() will hang either a) due to
793 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
794 * a dead-lock caused by this thread waiting for all callers on the other
795 * threads are done but preventing them from doing so by holding a lock.
796 */
797void Machine::uninit()
798{
799 LogFlowThisFuncEnter();
800
801 Assert(!isWriteLockOnCurrentThread());
802
803 Assert(!uRegistryNeedsSaving);
804 if (uRegistryNeedsSaving)
805 {
806 AutoCaller autoCaller(this);
807 if (SUCCEEDED(autoCaller.rc()))
808 {
809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
810 i_saveSettings(NULL, Machine::SaveS_Force);
811 }
812 }
813
814 /* Enclose the state transition Ready->InUninit->NotReady */
815 AutoUninitSpan autoUninitSpan(this);
816 if (autoUninitSpan.uninitDone())
817 return;
818
819 Assert(!i_isSnapshotMachine());
820 Assert(!i_isSessionMachine());
821 Assert(!!mData);
822
823 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
824 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
825
826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
827
828 if (!mData->mSession.mMachine.isNull())
829 {
830 /* Theoretically, this can only happen if the VirtualBox server has been
831 * terminated while there were clients running that owned open direct
832 * sessions. Since in this case we are definitely called by
833 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
834 * won't happen on the client watcher thread (because it does
835 * VirtualBox::addCaller() for the duration of the
836 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
837 * cannot happen until the VirtualBox caller is released). This is
838 * important, because SessionMachine::uninit() cannot correctly operate
839 * after we return from this method (it expects the Machine instance is
840 * still valid). We'll call it ourselves below.
841 */
842 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
843 (SessionMachine*)mData->mSession.mMachine));
844
845 if (Global::IsOnlineOrTransient(mData->mMachineState))
846 {
847 LogWarningThisFunc(("Setting state to Aborted!\n"));
848 /* set machine state using SessionMachine reimplementation */
849 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
850 }
851
852 /*
853 * Uninitialize SessionMachine using public uninit() to indicate
854 * an unexpected uninitialization.
855 */
856 mData->mSession.mMachine->uninit();
857 /* SessionMachine::uninit() must set mSession.mMachine to null */
858 Assert(mData->mSession.mMachine.isNull());
859 }
860
861 // uninit media from this machine's media registry, if they're still there
862 Guid uuidMachine(i_getId());
863
864 /* the lock is no more necessary (SessionMachine is uninitialized) */
865 alock.release();
866
867 /* XXX This will fail with
868 * "cannot be closed because it is still attached to 1 virtual machines"
869 * because at this point we did not call uninitDataAndChildObjects() yet
870 * and therefore also removeBackReference() for all these mediums was not called! */
871
872 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
873 mParent->i_unregisterMachineMedia(uuidMachine);
874
875 // has machine been modified?
876 if (mData->flModifications)
877 {
878 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
879 i_rollback(false /* aNotify */);
880 }
881
882 if (mData->mAccessible)
883 uninitDataAndChildObjects();
884
885 /* free the essential data structure last */
886 mData.free();
887
888 LogFlowThisFuncLeave();
889}
890
891// Wrapped IMachine properties
892/////////////////////////////////////////////////////////////////////////////
893HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
894{
895 /* mParent is constant during life time, no need to lock */
896 ComObjPtr<VirtualBox> pVirtualBox(mParent);
897 aParent = pVirtualBox;
898
899 return S_OK;
900}
901
902
903HRESULT Machine::getAccessible(BOOL *aAccessible)
904{
905 /* In some cases (medium registry related), it is necessary to be able to
906 * go through the list of all machines. Happens when an inaccessible VM
907 * has a sensible medium registry. */
908 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 HRESULT rc = S_OK;
912
913 if (!mData->mAccessible)
914 {
915 /* try to initialize the VM once more if not accessible */
916
917 AutoReinitSpan autoReinitSpan(this);
918 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
919
920#ifdef DEBUG
921 LogFlowThisFunc(("Dumping media backreferences\n"));
922 mParent->i_dumpAllBackRefs();
923#endif
924
925 if (mData->pMachineConfigFile)
926 {
927 // reset the XML file to force loadSettings() (called from registeredInit())
928 // to parse it again; the file might have changed
929 delete mData->pMachineConfigFile;
930 mData->pMachineConfigFile = NULL;
931 }
932
933 rc = i_registeredInit();
934
935 if (SUCCEEDED(rc) && mData->mAccessible)
936 {
937 autoReinitSpan.setSucceeded();
938
939 /* make sure interesting parties will notice the accessibility
940 * state change */
941 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
942 mParent->i_onMachineDataChange(mData->mUuid);
943 }
944 }
945
946 if (SUCCEEDED(rc))
947 *aAccessible = mData->mAccessible;
948
949 LogFlowThisFuncLeave();
950
951 return rc;
952}
953
954HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
955{
956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
957
958 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
959 {
960 /* return shortly */
961 aAccessError = NULL;
962 return S_OK;
963 }
964
965 HRESULT rc = S_OK;
966
967 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
968 rc = errorInfo.createObject();
969 if (SUCCEEDED(rc))
970 {
971 errorInfo->init(mData->mAccessError.getResultCode(),
972 mData->mAccessError.getInterfaceID().ref(),
973 Utf8Str(mData->mAccessError.getComponent()).c_str(),
974 Utf8Str(mData->mAccessError.getText()));
975 aAccessError = errorInfo;
976 }
977
978 return rc;
979}
980
981HRESULT Machine::getName(com::Utf8Str &aName)
982{
983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
984
985 aName = mUserData->s.strName;
986
987 return S_OK;
988}
989
990HRESULT Machine::setName(const com::Utf8Str &aName)
991{
992 // prohibit setting a UUID only as the machine name, or else it can
993 // never be found by findMachine()
994 Guid test(aName);
995
996 if (test.isValid())
997 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
998
999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1000
1001 HRESULT rc = i_checkStateDependency(MutableStateDep);
1002 if (FAILED(rc)) return rc;
1003
1004 i_setModified(IsModified_MachineData);
1005 mUserData.backup();
1006 mUserData->s.strName = aName;
1007
1008 return S_OK;
1009}
1010
1011HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1012{
1013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1014
1015 aDescription = mUserData->s.strDescription;
1016
1017 return S_OK;
1018}
1019
1020HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1021{
1022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 // this can be done in principle in any state as it doesn't affect the VM
1025 // significantly, but play safe by not messing around while complex
1026 // activities are going on
1027 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1028 if (FAILED(rc)) return rc;
1029
1030 i_setModified(IsModified_MachineData);
1031 mUserData.backup();
1032 mUserData->s.strDescription = aDescription;
1033
1034 return S_OK;
1035}
1036
1037HRESULT Machine::getId(com::Guid &aId)
1038{
1039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1040
1041 aId = mData->mUuid;
1042
1043 return S_OK;
1044}
1045
1046HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1047{
1048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1049 aGroups.resize(mUserData->s.llGroups.size());
1050 size_t i = 0;
1051 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1052 it != mUserData->s.llGroups.end(); ++it, ++i)
1053 aGroups[i] = (*it);
1054
1055 return S_OK;
1056}
1057
1058HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1059{
1060 StringsList llGroups;
1061 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1062 if (FAILED(rc))
1063 return rc;
1064
1065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1066
1067 rc = i_checkStateDependency(MutableStateDep);
1068 if (FAILED(rc)) return rc;
1069
1070 i_setModified(IsModified_MachineData);
1071 mUserData.backup();
1072 mUserData->s.llGroups = llGroups;
1073
1074 return S_OK;
1075}
1076
1077HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1078{
1079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1080
1081 aOSTypeId = mUserData->s.strOsType;
1082
1083 return S_OK;
1084}
1085
1086HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1087{
1088 /* look up the object by Id to check it is valid */
1089 ComPtr<IGuestOSType> guestOSType;
1090 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1091 if (FAILED(rc)) return rc;
1092
1093 /* when setting, always use the "etalon" value for consistency -- lookup
1094 * by ID is case-insensitive and the input value may have different case */
1095 Bstr osTypeId;
1096 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1097 if (FAILED(rc)) return rc;
1098
1099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1100
1101 rc = i_checkStateDependency(MutableStateDep);
1102 if (FAILED(rc)) return rc;
1103
1104 i_setModified(IsModified_MachineData);
1105 mUserData.backup();
1106 mUserData->s.strOsType = osTypeId;
1107
1108 return S_OK;
1109}
1110
1111HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1112{
1113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1114
1115 *aFirmwareType = mHWData->mFirmwareType;
1116
1117 return S_OK;
1118}
1119
1120HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1121{
1122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1123
1124 HRESULT rc = i_checkStateDependency(MutableStateDep);
1125 if (FAILED(rc)) return rc;
1126
1127 i_setModified(IsModified_MachineData);
1128 mHWData.backup();
1129 mHWData->mFirmwareType = aFirmwareType;
1130
1131 return S_OK;
1132}
1133
1134HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1135{
1136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1137
1138 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1139
1140 return S_OK;
1141}
1142
1143HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1144{
1145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1146
1147 HRESULT rc = i_checkStateDependency(MutableStateDep);
1148 if (FAILED(rc)) return rc;
1149
1150 i_setModified(IsModified_MachineData);
1151 mHWData.backup();
1152 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1153
1154 return S_OK;
1155}
1156
1157HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1158{
1159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1160
1161 *aPointingHIDType = mHWData->mPointingHIDType;
1162
1163 return S_OK;
1164}
1165
1166HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1167{
1168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1169
1170 HRESULT rc = i_checkStateDependency(MutableStateDep);
1171 if (FAILED(rc)) return rc;
1172
1173 i_setModified(IsModified_MachineData);
1174 mHWData.backup();
1175 mHWData->mPointingHIDType = aPointingHIDType;
1176
1177 return S_OK;
1178}
1179
1180HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1181{
1182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1183
1184 *aChipsetType = mHWData->mChipsetType;
1185
1186 return S_OK;
1187}
1188
1189HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1190{
1191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1192
1193 HRESULT rc = i_checkStateDependency(MutableStateDep);
1194 if (FAILED(rc)) return rc;
1195
1196 if (aChipsetType != mHWData->mChipsetType)
1197 {
1198 i_setModified(IsModified_MachineData);
1199 mHWData.backup();
1200 mHWData->mChipsetType = aChipsetType;
1201
1202 // Resize network adapter array, to be finalized on commit/rollback.
1203 // We must not throw away entries yet, otherwise settings are lost
1204 // without a way to roll back.
1205 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1206 size_t oldCount = mNetworkAdapters.size();
1207 if (newCount > oldCount)
1208 {
1209 mNetworkAdapters.resize(newCount);
1210 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1211 {
1212 unconst(mNetworkAdapters[slot]).createObject();
1213 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1214 }
1215 }
1216 }
1217
1218 return S_OK;
1219}
1220
1221HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1222{
1223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1224
1225 *aParavirtProvider = mHWData->mParavirtProvider;
1226
1227 return S_OK;
1228}
1229
1230HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1231{
1232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1233
1234 HRESULT rc = i_checkStateDependency(MutableStateDep);
1235 if (FAILED(rc)) return rc;
1236
1237 if (aParavirtProvider != mHWData->mParavirtProvider)
1238 {
1239 i_setModified(IsModified_MachineData);
1240 mHWData.backup();
1241 mHWData->mParavirtProvider = aParavirtProvider;
1242 }
1243
1244 return S_OK;
1245}
1246
1247HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1248{
1249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1250
1251 *aParavirtProvider = mHWData->mParavirtProvider;
1252 switch (mHWData->mParavirtProvider)
1253 {
1254 case ParavirtProvider_None:
1255 case ParavirtProvider_HyperV:
1256 case ParavirtProvider_KVM:
1257 case ParavirtProvider_Minimal:
1258 break;
1259
1260 /* Resolve dynamic provider types to the effective types. */
1261 default:
1262 {
1263 ComPtr<IGuestOSType> ptrGuestOSType;
1264 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1265 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1266
1267 Bstr guestTypeFamilyId;
1268 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1269 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1270 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1271
1272 switch (mHWData->mParavirtProvider)
1273 {
1274 case ParavirtProvider_Legacy:
1275 {
1276 if (fOsXGuest)
1277 *aParavirtProvider = ParavirtProvider_Minimal;
1278 else
1279 *aParavirtProvider = ParavirtProvider_None;
1280 break;
1281 }
1282
1283 case ParavirtProvider_Default:
1284 {
1285 if (fOsXGuest)
1286 *aParavirtProvider = ParavirtProvider_Minimal;
1287 else if ( mUserData->s.strOsType == "Windows10"
1288 || mUserData->s.strOsType == "Windows10_64"
1289 || mUserData->s.strOsType == "Windows81"
1290 || mUserData->s.strOsType == "Windows81_64"
1291 || mUserData->s.strOsType == "Windows8"
1292 || mUserData->s.strOsType == "Windows8_64"
1293 || mUserData->s.strOsType == "Windows7"
1294 || mUserData->s.strOsType == "Windows7_64"
1295 || mUserData->s.strOsType == "WindowsVista"
1296 || mUserData->s.strOsType == "WindowsVista_64"
1297 || mUserData->s.strOsType == "Windows2012"
1298 || mUserData->s.strOsType == "Windows2012_64"
1299 || mUserData->s.strOsType == "Windows2008"
1300 || mUserData->s.strOsType == "Windows2008_64")
1301 {
1302 *aParavirtProvider = ParavirtProvider_HyperV;
1303 }
1304 else if ( mUserData->s.strOsType == "Debian" /** @todo add more. */
1305 || mUserData->s.strOsType == "Debian_64"
1306 || mUserData->s.strOsType == "Ubuntu"
1307 || mUserData->s.strOsType == "Ubuntu_64")
1308 {
1309 *aParavirtProvider = ParavirtProvider_KVM;
1310 }
1311 else
1312 *aParavirtProvider = ParavirtProvider_None;
1313 break;
1314 }
1315 }
1316 break;
1317 }
1318 }
1319
1320 Assert( *aParavirtProvider == ParavirtProvider_None
1321 || *aParavirtProvider == ParavirtProvider_Minimal
1322 || *aParavirtProvider == ParavirtProvider_HyperV
1323 || *aParavirtProvider == ParavirtProvider_KVM);
1324 return S_OK;
1325}
1326
1327HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1328{
1329 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1330
1331 aHardwareVersion = mHWData->mHWVersion;
1332
1333 return S_OK;
1334}
1335
1336HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1337{
1338 /* check known version */
1339 Utf8Str hwVersion = aHardwareVersion;
1340 if ( hwVersion.compare("1") != 0
1341 && hwVersion.compare("2") != 0)
1342 return setError(E_INVALIDARG,
1343 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1344
1345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1346
1347 HRESULT rc = i_checkStateDependency(MutableStateDep);
1348 if (FAILED(rc)) return rc;
1349
1350 i_setModified(IsModified_MachineData);
1351 mHWData.backup();
1352 mHWData->mHWVersion = aHardwareVersion;
1353
1354 return S_OK;
1355}
1356
1357HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1358{
1359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1360
1361 if (!mHWData->mHardwareUUID.isZero())
1362 aHardwareUUID = mHWData->mHardwareUUID;
1363 else
1364 aHardwareUUID = mData->mUuid;
1365
1366 return S_OK;
1367}
1368
1369HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1370{
1371 if (!aHardwareUUID.isValid())
1372 return E_INVALIDARG;
1373
1374 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1375
1376 HRESULT rc = i_checkStateDependency(MutableStateDep);
1377 if (FAILED(rc)) return rc;
1378
1379 i_setModified(IsModified_MachineData);
1380 mHWData.backup();
1381 if (aHardwareUUID == mData->mUuid)
1382 mHWData->mHardwareUUID.clear();
1383 else
1384 mHWData->mHardwareUUID = aHardwareUUID;
1385
1386 return S_OK;
1387}
1388
1389HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1390{
1391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1392
1393 *aMemorySize = mHWData->mMemorySize;
1394
1395 return S_OK;
1396}
1397
1398HRESULT Machine::setMemorySize(ULONG aMemorySize)
1399{
1400 /* check RAM limits */
1401 if ( aMemorySize < MM_RAM_MIN_IN_MB
1402 || aMemorySize > MM_RAM_MAX_IN_MB
1403 )
1404 return setError(E_INVALIDARG,
1405 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1406 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1407
1408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 HRESULT rc = i_checkStateDependency(MutableStateDep);
1411 if (FAILED(rc)) return rc;
1412
1413 i_setModified(IsModified_MachineData);
1414 mHWData.backup();
1415 mHWData->mMemorySize = aMemorySize;
1416
1417 return S_OK;
1418}
1419
1420HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1421{
1422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1423
1424 *aCPUCount = mHWData->mCPUCount;
1425
1426 return S_OK;
1427}
1428
1429HRESULT Machine::setCPUCount(ULONG aCPUCount)
1430{
1431 /* check CPU limits */
1432 if ( aCPUCount < SchemaDefs::MinCPUCount
1433 || aCPUCount > SchemaDefs::MaxCPUCount
1434 )
1435 return setError(E_INVALIDARG,
1436 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1437 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1438
1439 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1440
1441 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1442 if (mHWData->mCPUHotPlugEnabled)
1443 {
1444 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1445 {
1446 if (mHWData->mCPUAttached[idx])
1447 return setError(E_INVALIDARG,
1448 tr("There is still a CPU attached to socket %lu."
1449 "Detach the CPU before removing the socket"),
1450 aCPUCount, idx+1);
1451 }
1452 }
1453
1454 HRESULT rc = i_checkStateDependency(MutableStateDep);
1455 if (FAILED(rc)) return rc;
1456
1457 i_setModified(IsModified_MachineData);
1458 mHWData.backup();
1459 mHWData->mCPUCount = aCPUCount;
1460
1461 return S_OK;
1462}
1463
1464HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1465{
1466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1467
1468 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1469
1470 return S_OK;
1471}
1472
1473HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1474{
1475 HRESULT rc = S_OK;
1476
1477 /* check throttle limits */
1478 if ( aCPUExecutionCap < 1
1479 || aCPUExecutionCap > 100
1480 )
1481 return setError(E_INVALIDARG,
1482 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1483 aCPUExecutionCap, 1, 100);
1484
1485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1486
1487 alock.release();
1488 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1489 alock.acquire();
1490 if (FAILED(rc)) return rc;
1491
1492 i_setModified(IsModified_MachineData);
1493 mHWData.backup();
1494 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1495
1496 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1497 if (Global::IsOnline(mData->mMachineState))
1498 i_saveSettings(NULL);
1499
1500 return S_OK;
1501}
1502
1503HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1504{
1505 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1506
1507 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1508
1509 return S_OK;
1510}
1511
1512HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1513{
1514 HRESULT rc = S_OK;
1515
1516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1517
1518 rc = i_checkStateDependency(MutableStateDep);
1519 if (FAILED(rc)) return rc;
1520
1521 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1522 {
1523 if (aCPUHotPlugEnabled)
1524 {
1525 i_setModified(IsModified_MachineData);
1526 mHWData.backup();
1527
1528 /* Add the amount of CPUs currently attached */
1529 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1530 mHWData->mCPUAttached[i] = true;
1531 }
1532 else
1533 {
1534 /*
1535 * We can disable hotplug only if the amount of maximum CPUs is equal
1536 * to the amount of attached CPUs
1537 */
1538 unsigned cCpusAttached = 0;
1539 unsigned iHighestId = 0;
1540
1541 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1542 {
1543 if (mHWData->mCPUAttached[i])
1544 {
1545 cCpusAttached++;
1546 iHighestId = i;
1547 }
1548 }
1549
1550 if ( (cCpusAttached != mHWData->mCPUCount)
1551 || (iHighestId >= mHWData->mCPUCount))
1552 return setError(E_INVALIDARG,
1553 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1554
1555 i_setModified(IsModified_MachineData);
1556 mHWData.backup();
1557 }
1558 }
1559
1560 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1561
1562 return rc;
1563}
1564
1565HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1566{
1567#ifdef VBOX_WITH_USB_CARDREADER
1568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1569
1570 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1571
1572 return S_OK;
1573#else
1574 NOREF(aEmulatedUSBCardReaderEnabled);
1575 return E_NOTIMPL;
1576#endif
1577}
1578
1579HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1580{
1581#ifdef VBOX_WITH_USB_CARDREADER
1582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1583
1584 HRESULT rc = i_checkStateDependency(MutableStateDep);
1585 if (FAILED(rc)) return rc;
1586
1587 i_setModified(IsModified_MachineData);
1588 mHWData.backup();
1589 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1590
1591 return S_OK;
1592#else
1593 NOREF(aEmulatedUSBCardReaderEnabled);
1594 return E_NOTIMPL;
1595#endif
1596}
1597
1598HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1599{
1600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1601
1602 *aHPETEnabled = mHWData->mHPETEnabled;
1603
1604 return S_OK;
1605}
1606
1607HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1608{
1609 HRESULT rc = S_OK;
1610
1611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1612
1613 rc = i_checkStateDependency(MutableStateDep);
1614 if (FAILED(rc)) return rc;
1615
1616 i_setModified(IsModified_MachineData);
1617 mHWData.backup();
1618
1619 mHWData->mHPETEnabled = aHPETEnabled;
1620
1621 return rc;
1622}
1623
1624HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1625{
1626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1627
1628 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1629 return S_OK;
1630}
1631
1632HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1633{
1634 HRESULT rc = S_OK;
1635
1636 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1637
1638 i_setModified(IsModified_MachineData);
1639 mHWData.backup();
1640 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1641
1642 alock.release();
1643 rc = i_onVideoCaptureChange();
1644 alock.acquire();
1645 if (FAILED(rc))
1646 {
1647 /*
1648 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1649 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1650 * determine if it should start or stop capturing. Therefore we need to manually
1651 * undo change.
1652 */
1653 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1654 return rc;
1655 }
1656
1657 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1658 if (Global::IsOnline(mData->mMachineState))
1659 i_saveSettings(NULL);
1660
1661 return rc;
1662}
1663
1664HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1665{
1666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1667 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1668 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1669 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1670 return S_OK;
1671}
1672
1673HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1674{
1675 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1676 bool fChanged = false;
1677
1678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1679
1680 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1681 {
1682 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1683 {
1684 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1685 fChanged = true;
1686 }
1687 }
1688 if (fChanged)
1689 {
1690 alock.release();
1691 HRESULT rc = i_onVideoCaptureChange();
1692 alock.acquire();
1693 if (FAILED(rc)) return rc;
1694 i_setModified(IsModified_MachineData);
1695
1696 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1697 if (Global::IsOnline(mData->mMachineState))
1698 i_saveSettings(NULL);
1699 }
1700
1701 return S_OK;
1702}
1703
1704HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1705{
1706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1707 if (mHWData->mVideoCaptureFile.isEmpty())
1708 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1709 else
1710 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1711 return S_OK;
1712}
1713
1714HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1715{
1716 Utf8Str strFile(aVideoCaptureFile);
1717 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1718
1719 if ( Global::IsOnline(mData->mMachineState)
1720 && mHWData->mVideoCaptureEnabled)
1721 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1722
1723 if (!RTPathStartsWithRoot(strFile.c_str()))
1724 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1725
1726 if (!strFile.isEmpty())
1727 {
1728 Utf8Str defaultFile;
1729 i_getDefaultVideoCaptureFile(defaultFile);
1730 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1731 strFile.setNull();
1732 }
1733
1734 i_setModified(IsModified_MachineData);
1735 mHWData.backup();
1736 mHWData->mVideoCaptureFile = strFile;
1737
1738 return S_OK;
1739}
1740
1741HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1742{
1743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1744 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1745 return S_OK;
1746}
1747
1748HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1749{
1750 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1751
1752 if ( Global::IsOnline(mData->mMachineState)
1753 && mHWData->mVideoCaptureEnabled)
1754 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1755
1756 i_setModified(IsModified_MachineData);
1757 mHWData.backup();
1758 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1759
1760 return S_OK;
1761}
1762
1763HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1764{
1765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1766 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1767 return S_OK;
1768}
1769
1770HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1771{
1772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1773
1774 if ( Global::IsOnline(mData->mMachineState)
1775 && mHWData->mVideoCaptureEnabled)
1776 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1777
1778 i_setModified(IsModified_MachineData);
1779 mHWData.backup();
1780 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1781
1782 return S_OK;
1783}
1784
1785HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1786{
1787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1788 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1789 return S_OK;
1790}
1791
1792HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1793{
1794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1795
1796 if ( Global::IsOnline(mData->mMachineState)
1797 && mHWData->mVideoCaptureEnabled)
1798 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1799
1800 i_setModified(IsModified_MachineData);
1801 mHWData.backup();
1802 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1803
1804 return S_OK;
1805}
1806
1807HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1808{
1809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1810 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1811 return S_OK;
1812}
1813
1814HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1815{
1816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1817
1818 if ( Global::IsOnline(mData->mMachineState)
1819 && mHWData->mVideoCaptureEnabled)
1820 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1821
1822 i_setModified(IsModified_MachineData);
1823 mHWData.backup();
1824 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1825
1826 return S_OK;
1827}
1828
1829HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1830{
1831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1832 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1833 return S_OK;
1834}
1835
1836HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1837{
1838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1839
1840 if ( Global::IsOnline(mData->mMachineState)
1841 && mHWData->mVideoCaptureEnabled)
1842 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1843
1844 i_setModified(IsModified_MachineData);
1845 mHWData.backup();
1846 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1847
1848 return S_OK;
1849}
1850
1851HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1852{
1853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1854 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1855 return S_OK;
1856}
1857
1858HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1859{
1860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 if ( Global::IsOnline(mData->mMachineState)
1863 && mHWData->mVideoCaptureEnabled)
1864 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1865
1866 i_setModified(IsModified_MachineData);
1867 mHWData.backup();
1868 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1869
1870 return S_OK;
1871}
1872
1873HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1874{
1875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1876
1877 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1878 return S_OK;
1879}
1880
1881HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1882{
1883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1884
1885 if ( Global::IsOnline(mData->mMachineState)
1886 && mHWData->mVideoCaptureEnabled)
1887 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1888
1889 i_setModified(IsModified_MachineData);
1890 mHWData.backup();
1891 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1892
1893 return S_OK;
1894}
1895
1896HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1897{
1898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1899
1900 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1901
1902 return S_OK;
1903}
1904
1905HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1906{
1907 switch (aGraphicsControllerType)
1908 {
1909 case GraphicsControllerType_Null:
1910 case GraphicsControllerType_VBoxVGA:
1911#ifdef VBOX_WITH_VMSVGA
1912 case GraphicsControllerType_VMSVGA:
1913#endif
1914 break;
1915 default:
1916 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1917 }
1918
1919 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1920
1921 HRESULT rc = i_checkStateDependency(MutableStateDep);
1922 if (FAILED(rc)) return rc;
1923
1924 i_setModified(IsModified_MachineData);
1925 mHWData.backup();
1926 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1927
1928 return S_OK;
1929}
1930
1931HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1932{
1933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1934
1935 *aVRAMSize = mHWData->mVRAMSize;
1936
1937 return S_OK;
1938}
1939
1940HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1941{
1942 /* check VRAM limits */
1943 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1944 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1945 return setError(E_INVALIDARG,
1946 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1947 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1948
1949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1950
1951 HRESULT rc = i_checkStateDependency(MutableStateDep);
1952 if (FAILED(rc)) return rc;
1953
1954 i_setModified(IsModified_MachineData);
1955 mHWData.backup();
1956 mHWData->mVRAMSize = aVRAMSize;
1957
1958 return S_OK;
1959}
1960
1961/** @todo this method should not be public */
1962HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1963{
1964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1965
1966 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1967
1968 return S_OK;
1969}
1970
1971/**
1972 * Set the memory balloon size.
1973 *
1974 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1975 * we have to make sure that we never call IGuest from here.
1976 */
1977HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1978{
1979 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1980#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1981 /* check limits */
1982 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1983 return setError(E_INVALIDARG,
1984 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1985 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1986
1987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1988
1989 i_setModified(IsModified_MachineData);
1990 mHWData.backup();
1991 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1992
1993 return S_OK;
1994#else
1995 NOREF(aMemoryBalloonSize);
1996 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1997#endif
1998}
1999
2000HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2001{
2002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2003
2004 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2005 return S_OK;
2006}
2007
2008HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2009{
2010#ifdef VBOX_WITH_PAGE_SHARING
2011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2012
2013 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2014 i_setModified(IsModified_MachineData);
2015 mHWData.backup();
2016 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2017 return S_OK;
2018#else
2019 NOREF(aPageFusionEnabled);
2020 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2021#endif
2022}
2023
2024HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2025{
2026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2027
2028 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2029
2030 return S_OK;
2031}
2032
2033HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2034{
2035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2036
2037 HRESULT rc = i_checkStateDependency(MutableStateDep);
2038 if (FAILED(rc)) return rc;
2039
2040 /** @todo check validity! */
2041
2042 i_setModified(IsModified_MachineData);
2043 mHWData.backup();
2044 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2045
2046 return S_OK;
2047}
2048
2049
2050HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2051{
2052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2053
2054 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2055
2056 return S_OK;
2057}
2058
2059HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2060{
2061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2062
2063 HRESULT rc = i_checkStateDependency(MutableStateDep);
2064 if (FAILED(rc)) return rc;
2065
2066 /** @todo check validity! */
2067 i_setModified(IsModified_MachineData);
2068 mHWData.backup();
2069 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2070
2071 return S_OK;
2072}
2073
2074HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2075{
2076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2077
2078 *aMonitorCount = mHWData->mMonitorCount;
2079
2080 return S_OK;
2081}
2082
2083HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2084{
2085 /* make sure monitor count is a sensible number */
2086 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2087 return setError(E_INVALIDARG,
2088 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2089 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2090
2091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2092
2093 HRESULT rc = i_checkStateDependency(MutableStateDep);
2094 if (FAILED(rc)) return rc;
2095
2096 i_setModified(IsModified_MachineData);
2097 mHWData.backup();
2098 mHWData->mMonitorCount = aMonitorCount;
2099
2100 return S_OK;
2101}
2102
2103HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2104{
2105 /* mBIOSSettings is constant during life time, no need to lock */
2106 aBIOSSettings = mBIOSSettings;
2107
2108 return S_OK;
2109}
2110
2111HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2112{
2113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2114
2115 switch (aProperty)
2116 {
2117 case CPUPropertyType_PAE:
2118 *aValue = mHWData->mPAEEnabled;
2119 break;
2120
2121 case CPUPropertyType_Synthetic:
2122 *aValue = mHWData->mSyntheticCpu;
2123 break;
2124
2125 case CPUPropertyType_LongMode:
2126 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2127 *aValue = TRUE;
2128 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2129 *aValue = FALSE;
2130#if HC_ARCH_BITS == 64
2131 else
2132 *aValue = TRUE;
2133#else
2134 else
2135 {
2136 *aValue = FALSE;
2137
2138 ComPtr<IGuestOSType> ptrGuestOSType;
2139 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2140 if (SUCCEEDED(hrc2))
2141 {
2142 BOOL fIs64Bit = FALSE;
2143 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2144 if (SUCCEEDED(hrc2) && fIs64Bit)
2145 {
2146 ComObjPtr<Host> ptrHost = mParent->i_host();
2147 alock.release();
2148
2149 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2150 if (FAILED(hrc2))
2151 *aValue = FALSE;
2152 }
2153 }
2154 }
2155#endif
2156 break;
2157
2158 case CPUPropertyType_TripleFaultReset:
2159 *aValue = mHWData->mTripleFaultReset;
2160 break;
2161
2162 default:
2163 return E_INVALIDARG;
2164 }
2165 return S_OK;
2166}
2167
2168HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2169{
2170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2171
2172 HRESULT rc = i_checkStateDependency(MutableStateDep);
2173 if (FAILED(rc)) return rc;
2174
2175 switch (aProperty)
2176 {
2177 case CPUPropertyType_PAE:
2178 i_setModified(IsModified_MachineData);
2179 mHWData.backup();
2180 mHWData->mPAEEnabled = !!aValue;
2181 break;
2182
2183 case CPUPropertyType_Synthetic:
2184 i_setModified(IsModified_MachineData);
2185 mHWData.backup();
2186 mHWData->mSyntheticCpu = !!aValue;
2187 break;
2188
2189 case CPUPropertyType_LongMode:
2190 i_setModified(IsModified_MachineData);
2191 mHWData.backup();
2192 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2193 break;
2194
2195 case CPUPropertyType_TripleFaultReset:
2196 i_setModified(IsModified_MachineData);
2197 mHWData.backup();
2198 mHWData->mTripleFaultReset = !!aValue;
2199 break;
2200
2201 default:
2202 return E_INVALIDARG;
2203 }
2204 return S_OK;
2205}
2206
2207HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2208{
2209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2210
2211 switch(aId)
2212 {
2213 case 0x0:
2214 case 0x1:
2215 case 0x2:
2216 case 0x3:
2217 case 0x4:
2218 case 0x5:
2219 case 0x6:
2220 case 0x7:
2221 case 0x8:
2222 case 0x9:
2223 case 0xA:
2224 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2225 return E_INVALIDARG;
2226
2227 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2228 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2229 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2230 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2231 break;
2232
2233 case 0x80000000:
2234 case 0x80000001:
2235 case 0x80000002:
2236 case 0x80000003:
2237 case 0x80000004:
2238 case 0x80000005:
2239 case 0x80000006:
2240 case 0x80000007:
2241 case 0x80000008:
2242 case 0x80000009:
2243 case 0x8000000A:
2244 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2245 return E_INVALIDARG;
2246
2247 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2248 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2249 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2250 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2251 break;
2252
2253 default:
2254 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2255 }
2256 return S_OK;
2257}
2258
2259
2260HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2261{
2262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2263
2264 HRESULT rc = i_checkStateDependency(MutableStateDep);
2265 if (FAILED(rc)) return rc;
2266
2267 switch(aId)
2268 {
2269 case 0x0:
2270 case 0x1:
2271 case 0x2:
2272 case 0x3:
2273 case 0x4:
2274 case 0x5:
2275 case 0x6:
2276 case 0x7:
2277 case 0x8:
2278 case 0x9:
2279 case 0xA:
2280 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2281 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2282 i_setModified(IsModified_MachineData);
2283 mHWData.backup();
2284 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2285 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2286 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2287 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2288 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2289 break;
2290
2291 case 0x80000000:
2292 case 0x80000001:
2293 case 0x80000002:
2294 case 0x80000003:
2295 case 0x80000004:
2296 case 0x80000005:
2297 case 0x80000006:
2298 case 0x80000007:
2299 case 0x80000008:
2300 case 0x80000009:
2301 case 0x8000000A:
2302 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2303 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2304 i_setModified(IsModified_MachineData);
2305 mHWData.backup();
2306 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2307 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2308 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2309 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2310 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2311 break;
2312
2313 default:
2314 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2315 }
2316 return S_OK;
2317}
2318
2319HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2320{
2321 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2322
2323 HRESULT rc = i_checkStateDependency(MutableStateDep);
2324 if (FAILED(rc)) return rc;
2325
2326 switch(aId)
2327 {
2328 case 0x0:
2329 case 0x1:
2330 case 0x2:
2331 case 0x3:
2332 case 0x4:
2333 case 0x5:
2334 case 0x6:
2335 case 0x7:
2336 case 0x8:
2337 case 0x9:
2338 case 0xA:
2339 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2340 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2341 i_setModified(IsModified_MachineData);
2342 mHWData.backup();
2343 /* Invalidate leaf. */
2344 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2345 break;
2346
2347 case 0x80000000:
2348 case 0x80000001:
2349 case 0x80000002:
2350 case 0x80000003:
2351 case 0x80000004:
2352 case 0x80000005:
2353 case 0x80000006:
2354 case 0x80000007:
2355 case 0x80000008:
2356 case 0x80000009:
2357 case 0x8000000A:
2358 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2359 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2360 i_setModified(IsModified_MachineData);
2361 mHWData.backup();
2362 /* Invalidate leaf. */
2363 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2364 break;
2365
2366 default:
2367 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2368 }
2369 return S_OK;
2370}
2371
2372HRESULT Machine::removeAllCPUIDLeaves()
2373{
2374 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2375
2376 HRESULT rc = i_checkStateDependency(MutableStateDep);
2377 if (FAILED(rc)) return rc;
2378
2379 i_setModified(IsModified_MachineData);
2380 mHWData.backup();
2381
2382 /* Invalidate all standard leafs. */
2383 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2384 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2385
2386 /* Invalidate all extended leafs. */
2387 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2388 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2389
2390 return S_OK;
2391}
2392HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2393{
2394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2395
2396 switch(aProperty)
2397 {
2398 case HWVirtExPropertyType_Enabled:
2399 *aValue = mHWData->mHWVirtExEnabled;
2400 break;
2401
2402 case HWVirtExPropertyType_VPID:
2403 *aValue = mHWData->mHWVirtExVPIDEnabled;
2404 break;
2405
2406 case HWVirtExPropertyType_NestedPaging:
2407 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2408 break;
2409
2410 case HWVirtExPropertyType_UnrestrictedExecution:
2411 *aValue = mHWData->mHWVirtExUXEnabled;
2412 break;
2413
2414 case HWVirtExPropertyType_LargePages:
2415 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2416#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2417 *aValue = FALSE;
2418#endif
2419 break;
2420
2421 case HWVirtExPropertyType_Force:
2422 *aValue = mHWData->mHWVirtExForceEnabled;
2423 break;
2424
2425 default:
2426 return E_INVALIDARG;
2427 }
2428 return S_OK;
2429}
2430
2431HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2432{
2433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2434
2435 HRESULT rc = i_checkStateDependency(MutableStateDep);
2436 if (FAILED(rc)) return rc;
2437
2438 switch(aProperty)
2439 {
2440 case HWVirtExPropertyType_Enabled:
2441 i_setModified(IsModified_MachineData);
2442 mHWData.backup();
2443 mHWData->mHWVirtExEnabled = !!aValue;
2444 break;
2445
2446 case HWVirtExPropertyType_VPID:
2447 i_setModified(IsModified_MachineData);
2448 mHWData.backup();
2449 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2450 break;
2451
2452 case HWVirtExPropertyType_NestedPaging:
2453 i_setModified(IsModified_MachineData);
2454 mHWData.backup();
2455 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2456 break;
2457
2458 case HWVirtExPropertyType_UnrestrictedExecution:
2459 i_setModified(IsModified_MachineData);
2460 mHWData.backup();
2461 mHWData->mHWVirtExUXEnabled = !!aValue;
2462 break;
2463
2464 case HWVirtExPropertyType_LargePages:
2465 i_setModified(IsModified_MachineData);
2466 mHWData.backup();
2467 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2468 break;
2469
2470 case HWVirtExPropertyType_Force:
2471 i_setModified(IsModified_MachineData);
2472 mHWData.backup();
2473 mHWData->mHWVirtExForceEnabled = !!aValue;
2474 break;
2475
2476 default:
2477 return E_INVALIDARG;
2478 }
2479
2480 return S_OK;
2481}
2482
2483HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2484{
2485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2486
2487 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2488
2489 return S_OK;
2490}
2491
2492HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2493{
2494 /* @todo (r=dmik):
2495 * 1. Allow to change the name of the snapshot folder containing snapshots
2496 * 2. Rename the folder on disk instead of just changing the property
2497 * value (to be smart and not to leave garbage). Note that it cannot be
2498 * done here because the change may be rolled back. Thus, the right
2499 * place is #saveSettings().
2500 */
2501
2502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2503
2504 HRESULT rc = i_checkStateDependency(MutableStateDep);
2505 if (FAILED(rc)) return rc;
2506
2507 if (!mData->mCurrentSnapshot.isNull())
2508 return setError(E_FAIL,
2509 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2510
2511 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2512
2513 if (strSnapshotFolder.isEmpty())
2514 strSnapshotFolder = "Snapshots";
2515 int vrc = i_calculateFullPath(strSnapshotFolder,
2516 strSnapshotFolder);
2517 if (RT_FAILURE(vrc))
2518 return setError(E_FAIL,
2519 tr("Invalid snapshot folder '%s' (%Rrc)"),
2520 strSnapshotFolder.c_str(), vrc);
2521
2522 i_setModified(IsModified_MachineData);
2523 mUserData.backup();
2524
2525 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2526
2527 return S_OK;
2528}
2529
2530HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2531{
2532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2533
2534 aMediumAttachments.resize(mMediaData->mAttachments.size());
2535 size_t i = 0;
2536 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2537 it != mMediaData->mAttachments.end(); ++it, ++i)
2538 aMediumAttachments[i] = *it;
2539
2540 return S_OK;
2541}
2542
2543HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2544{
2545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2546
2547 Assert(!!mVRDEServer);
2548
2549 aVRDEServer = mVRDEServer;
2550
2551 return S_OK;
2552}
2553
2554HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2555{
2556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2557
2558 aAudioAdapter = mAudioAdapter;
2559
2560 return S_OK;
2561}
2562
2563HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2564{
2565#ifdef VBOX_WITH_VUSB
2566 clearError();
2567 MultiResult rc(S_OK);
2568
2569# ifdef VBOX_WITH_USB
2570 rc = mParent->i_host()->i_checkUSBProxyService();
2571 if (FAILED(rc)) return rc;
2572# endif
2573
2574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2575
2576 USBControllerList data = *mUSBControllers.data();
2577 aUSBControllers.resize(data.size());
2578 size_t i = 0;
2579 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2580 aUSBControllers[i] = *it;
2581
2582 return S_OK;
2583#else
2584 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2585 * extended error info to indicate that USB is simply not available
2586 * (w/o treating it as a failure), for example, as in OSE */
2587 NOREF(aUSBControllers);
2588 ReturnComNotImplemented();
2589#endif /* VBOX_WITH_VUSB */
2590}
2591
2592HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2593{
2594#ifdef VBOX_WITH_VUSB
2595 clearError();
2596 MultiResult rc(S_OK);
2597
2598# ifdef VBOX_WITH_USB
2599 rc = mParent->i_host()->i_checkUSBProxyService();
2600 if (FAILED(rc)) return rc;
2601# endif
2602
2603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2604
2605 aUSBDeviceFilters = mUSBDeviceFilters;
2606 return rc;
2607#else
2608 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2609 * extended error info to indicate that USB is simply not available
2610 * (w/o treating it as a failure), for example, as in OSE */
2611 NOREF(aUSBDeviceFilters);
2612 ReturnComNotImplemented();
2613#endif /* VBOX_WITH_VUSB */
2614}
2615
2616HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2617{
2618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2619
2620 aSettingsFilePath = mData->m_strConfigFileFull;
2621
2622 return S_OK;
2623}
2624
2625HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2626{
2627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2628
2629 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2630 if (FAILED(rc)) return rc;
2631
2632 if (!mData->pMachineConfigFile->fileExists())
2633 // this is a new machine, and no config file exists yet:
2634 *aSettingsModified = TRUE;
2635 else
2636 *aSettingsModified = (mData->flModifications != 0);
2637
2638 return S_OK;
2639}
2640
2641HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2642{
2643
2644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2645
2646 *aSessionState = mData->mSession.mState;
2647
2648 return S_OK;
2649}
2650
2651HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2652{
2653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2654
2655 aSessionType = mData->mSession.mType;
2656
2657 return S_OK;
2658}
2659
2660HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2661{
2662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2663
2664 *aSessionPID = mData->mSession.mPID;
2665
2666 return S_OK;
2667}
2668
2669HRESULT Machine::getState(MachineState_T *aState)
2670{
2671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2672
2673 *aState = mData->mMachineState;
2674
2675 return S_OK;
2676}
2677
2678HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2679{
2680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2681
2682 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2683
2684 return S_OK;
2685}
2686
2687HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2688{
2689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2690
2691 aStateFilePath = mSSData->strStateFilePath;
2692
2693 return S_OK;
2694}
2695
2696HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2697{
2698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2699
2700 i_getLogFolder(aLogFolder);
2701
2702 return S_OK;
2703}
2704
2705HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2706{
2707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2708
2709 aCurrentSnapshot = mData->mCurrentSnapshot;
2710
2711 return S_OK;
2712}
2713
2714HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2715{
2716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2717
2718 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2719 ? 0
2720 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2721
2722 return S_OK;
2723}
2724
2725HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2726{
2727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2728
2729 /* Note: for machines with no snapshots, we always return FALSE
2730 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2731 * reasons :) */
2732
2733 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2734 ? FALSE
2735 : mData->mCurrentStateModified;
2736
2737 return S_OK;
2738}
2739
2740HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2741{
2742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2743
2744 aSharedFolders.resize(mHWData->mSharedFolders.size());
2745 size_t i = 0;
2746 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2747 it != mHWData->mSharedFolders.end(); ++i, ++it)
2748 aSharedFolders[i] = *it;
2749
2750 return S_OK;
2751}
2752
2753HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2754{
2755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2756
2757 *aClipboardMode = mHWData->mClipboardMode;
2758
2759 return S_OK;
2760}
2761
2762HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2763{
2764 HRESULT rc = S_OK;
2765
2766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2767
2768 alock.release();
2769 rc = i_onClipboardModeChange(aClipboardMode);
2770 alock.acquire();
2771 if (FAILED(rc)) return rc;
2772
2773 i_setModified(IsModified_MachineData);
2774 mHWData.backup();
2775 mHWData->mClipboardMode = aClipboardMode;
2776
2777 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2778 if (Global::IsOnline(mData->mMachineState))
2779 i_saveSettings(NULL);
2780
2781 return S_OK;
2782}
2783
2784HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2785{
2786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2787
2788 *aDnDMode = mHWData->mDnDMode;
2789
2790 return S_OK;
2791}
2792
2793HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2794{
2795 HRESULT rc = S_OK;
2796
2797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2798
2799 alock.release();
2800 rc = i_onDnDModeChange(aDnDMode);
2801
2802 alock.acquire();
2803 if (FAILED(rc)) return rc;
2804
2805 i_setModified(IsModified_MachineData);
2806 mHWData.backup();
2807 mHWData->mDnDMode = aDnDMode;
2808
2809 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2810 if (Global::IsOnline(mData->mMachineState))
2811 i_saveSettings(NULL);
2812
2813 return S_OK;
2814}
2815
2816HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2817{
2818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2819
2820 try
2821 {
2822 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2823 }
2824 catch (...)
2825 {
2826 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2827 }
2828
2829 return S_OK;
2830}
2831
2832HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2833{
2834 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2835
2836 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2837 if (FAILED(rc)) return rc;
2838
2839 i_setModified(IsModified_MachineData);
2840 mHWData.backup();
2841 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2842 return rc;
2843}
2844
2845HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2846{
2847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2848 StorageControllerList data = *mStorageControllers.data();
2849 size_t i = 0;
2850 aStorageControllers.resize(data.size());
2851 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2852 aStorageControllers[i] = *it;
2853 return S_OK;
2854}
2855
2856HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2857{
2858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2859
2860 *aEnabled = mUserData->s.fTeleporterEnabled;
2861
2862 return S_OK;
2863}
2864
2865HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2866{
2867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2868
2869 /* Only allow it to be set to true when PoweredOff or Aborted.
2870 (Clearing it is always permitted.) */
2871 if ( aTeleporterEnabled
2872 && mData->mRegistered
2873 && ( !i_isSessionMachine()
2874 || ( mData->mMachineState != MachineState_PoweredOff
2875 && mData->mMachineState != MachineState_Teleported
2876 && mData->mMachineState != MachineState_Aborted
2877 )
2878 )
2879 )
2880 return setError(VBOX_E_INVALID_VM_STATE,
2881 tr("The machine is not powered off (state is %s)"),
2882 Global::stringifyMachineState(mData->mMachineState));
2883
2884 i_setModified(IsModified_MachineData);
2885 mUserData.backup();
2886 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2887
2888 return S_OK;
2889}
2890
2891HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2892{
2893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2894
2895 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2896
2897 return S_OK;
2898}
2899
2900HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2901{
2902 if (aTeleporterPort >= _64K)
2903 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2904
2905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2906
2907 HRESULT rc = i_checkStateDependency(MutableStateDep);
2908 if (FAILED(rc)) return rc;
2909
2910 i_setModified(IsModified_MachineData);
2911 mUserData.backup();
2912 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2913
2914 return S_OK;
2915}
2916
2917HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2918{
2919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2920
2921 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2922
2923 return S_OK;
2924}
2925
2926HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2927{
2928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2929
2930 HRESULT rc = i_checkStateDependency(MutableStateDep);
2931 if (FAILED(rc)) return rc;
2932
2933 i_setModified(IsModified_MachineData);
2934 mUserData.backup();
2935 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2936
2937 return S_OK;
2938}
2939
2940HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2941{
2942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2943 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2944
2945 return S_OK;
2946}
2947
2948HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2949{
2950 /*
2951 * Hash the password first.
2952 */
2953 com::Utf8Str aT = aTeleporterPassword;
2954
2955 if (!aT.isEmpty())
2956 {
2957 if (VBoxIsPasswordHashed(&aT))
2958 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2959 VBoxHashPassword(&aT);
2960 }
2961
2962 /*
2963 * Do the update.
2964 */
2965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2966 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2967 if (SUCCEEDED(hrc))
2968 {
2969 i_setModified(IsModified_MachineData);
2970 mUserData.backup();
2971 mUserData->s.strTeleporterPassword = aT;
2972 }
2973
2974 return hrc;
2975}
2976
2977HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2978{
2979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2980
2981 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2982 return S_OK;
2983}
2984
2985HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2986{
2987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2988
2989 /* @todo deal with running state change. */
2990 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2991 if (FAILED(rc)) return rc;
2992
2993 i_setModified(IsModified_MachineData);
2994 mUserData.backup();
2995 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2996 return S_OK;
2997}
2998
2999HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3000{
3001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3002
3003 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3004 return S_OK;
3005}
3006
3007HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3008{
3009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 /* @todo deal with running state change. */
3012 HRESULT rc = i_checkStateDependency(MutableStateDep);
3013 if (FAILED(rc)) return rc;
3014
3015 i_setModified(IsModified_MachineData);
3016 mUserData.backup();
3017 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3018 return S_OK;
3019}
3020
3021HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3022{
3023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3024
3025 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3026 return S_OK;
3027}
3028
3029HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3030{
3031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3032
3033 /* @todo deal with running state change. */
3034 HRESULT rc = i_checkStateDependency(MutableStateDep);
3035 if (FAILED(rc)) return rc;
3036
3037 i_setModified(IsModified_MachineData);
3038 mUserData.backup();
3039 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3040 return S_OK;
3041}
3042
3043HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3044{
3045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3046
3047 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3048
3049 return S_OK;
3050}
3051
3052HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3053{
3054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3055
3056 /* @todo deal with running state change. */
3057 HRESULT rc = i_checkStateDependency(MutableStateDep);
3058 if (FAILED(rc)) return rc;
3059
3060 i_setModified(IsModified_MachineData);
3061 mUserData.backup();
3062 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3063
3064 return S_OK;
3065}
3066
3067HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3068{
3069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3070
3071 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3072 return S_OK;
3073}
3074
3075HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3076{
3077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3078
3079 /* @todo deal with running state change. */
3080 HRESULT rc = i_checkStateDependency(MutableStateDep);
3081 if (FAILED(rc)) return rc;
3082
3083 i_setModified(IsModified_MachineData);
3084 mUserData.backup();
3085 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3086 return S_OK;
3087}
3088
3089HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3090{
3091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3092
3093 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3094
3095 return S_OK;
3096}
3097
3098HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3099{
3100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3101
3102 /* Only allow it to be set to true when PoweredOff or Aborted.
3103 (Clearing it is always permitted.) */
3104 if ( aRTCUseUTC
3105 && mData->mRegistered
3106 && ( !i_isSessionMachine()
3107 || ( mData->mMachineState != MachineState_PoweredOff
3108 && mData->mMachineState != MachineState_Teleported
3109 && mData->mMachineState != MachineState_Aborted
3110 )
3111 )
3112 )
3113 return setError(VBOX_E_INVALID_VM_STATE,
3114 tr("The machine is not powered off (state is %s)"),
3115 Global::stringifyMachineState(mData->mMachineState));
3116
3117 i_setModified(IsModified_MachineData);
3118 mUserData.backup();
3119 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3120
3121 return S_OK;
3122}
3123
3124HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3125{
3126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3127
3128 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3129
3130 return S_OK;
3131}
3132
3133HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3134{
3135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3136
3137 HRESULT rc = i_checkStateDependency(MutableStateDep);
3138 if (FAILED(rc)) return rc;
3139
3140 i_setModified(IsModified_MachineData);
3141 mHWData.backup();
3142 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3143
3144 return S_OK;
3145}
3146
3147HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3148{
3149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3150
3151 *aIOCacheSize = mHWData->mIOCacheSize;
3152
3153 return S_OK;
3154}
3155
3156HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3157{
3158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3159
3160 HRESULT rc = i_checkStateDependency(MutableStateDep);
3161 if (FAILED(rc)) return rc;
3162
3163 i_setModified(IsModified_MachineData);
3164 mHWData.backup();
3165 mHWData->mIOCacheSize = aIOCacheSize;
3166
3167 return S_OK;
3168}
3169
3170
3171/**
3172 * @note Locks objects!
3173 */
3174HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3175 LockType_T aLockType)
3176
3177{
3178 /* check the session state */
3179 SessionState_T state;
3180 HRESULT rc = aSession->COMGETTER(State)(&state);
3181 if (FAILED(rc)) return rc;
3182
3183 if (state != SessionState_Unlocked)
3184 return setError(VBOX_E_INVALID_OBJECT_STATE,
3185 tr("The given session is busy"));
3186
3187 // get the client's IInternalSessionControl interface
3188 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3189 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3190 E_INVALIDARG);
3191
3192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3193
3194 if (!mData->mRegistered)
3195 return setError(E_UNEXPECTED,
3196 tr("The machine '%s' is not registered"),
3197 mUserData->s.strName.c_str());
3198
3199 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3200
3201 SessionState_T oldState = mData->mSession.mState;
3202 /* Hack: in case the session is closing and there is a progress object
3203 * which allows waiting for the session to be closed, take the opportunity
3204 * and do a limited wait (max. 1 second). This helps a lot when the system
3205 * is busy and thus session closing can take a little while. */
3206 if ( mData->mSession.mState == SessionState_Unlocking
3207 && mData->mSession.mProgress)
3208 {
3209 alock.release();
3210 mData->mSession.mProgress->WaitForCompletion(1000);
3211 alock.acquire();
3212 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3213 }
3214
3215 // try again now
3216 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3217 // (i.e. session machine exists)
3218 && (aLockType == LockType_Shared) // caller wants a shared link to the
3219 // existing session that holds the write lock:
3220 )
3221 {
3222 // OK, share the session... we are now dealing with three processes:
3223 // 1) VBoxSVC (where this code runs);
3224 // 2) process C: the caller's client process (who wants a shared session);
3225 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3226
3227 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3228 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3229 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3230 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3231 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3232
3233 /*
3234 * Release the lock before calling the client process. It's safe here
3235 * since the only thing to do after we get the lock again is to add
3236 * the remote control to the list (which doesn't directly influence
3237 * anything).
3238 */
3239 alock.release();
3240
3241 // get the console of the session holding the write lock (this is a remote call)
3242 ComPtr<IConsole> pConsoleW;
3243 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3244 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3245 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3246 if (FAILED(rc))
3247 // the failure may occur w/o any error info (from RPC), so provide one
3248 return setError(VBOX_E_VM_ERROR,
3249 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3250
3251 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3252
3253 // share the session machine and W's console with the caller's session
3254 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3255 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3256 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3257
3258 if (FAILED(rc))
3259 // the failure may occur w/o any error info (from RPC), so provide one
3260 return setError(VBOX_E_VM_ERROR,
3261 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3262 alock.acquire();
3263
3264 // need to revalidate the state after acquiring the lock again
3265 if (mData->mSession.mState != SessionState_Locked)
3266 {
3267 pSessionControl->Uninitialize();
3268 return setError(VBOX_E_INVALID_SESSION_STATE,
3269 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3270 mUserData->s.strName.c_str());
3271 }
3272
3273 // add the caller's session to the list
3274 mData->mSession.mRemoteControls.push_back(pSessionControl);
3275 }
3276 else if ( mData->mSession.mState == SessionState_Locked
3277 || mData->mSession.mState == SessionState_Unlocking
3278 )
3279 {
3280 // sharing not permitted, or machine still unlocking:
3281 return setError(VBOX_E_INVALID_OBJECT_STATE,
3282 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3283 mUserData->s.strName.c_str());
3284 }
3285 else
3286 {
3287 // machine is not locked: then write-lock the machine (create the session machine)
3288
3289 // must not be busy
3290 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3291
3292 // get the caller's session PID
3293 RTPROCESS pid = NIL_RTPROCESS;
3294 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3295 pSessionControl->GetPID((ULONG*)&pid);
3296 Assert(pid != NIL_RTPROCESS);
3297
3298 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3299
3300 if (fLaunchingVMProcess)
3301 {
3302 if (mData->mSession.mPID == NIL_RTPROCESS)
3303 {
3304 // two or more clients racing for a lock, the one which set the
3305 // session state to Spawning will win, the others will get an
3306 // error as we can't decide here if waiting a little would help
3307 // (only for shared locks this would avoid an error)
3308 return setError(VBOX_E_INVALID_OBJECT_STATE,
3309 tr("The machine '%s' already has a lock request pending"),
3310 mUserData->s.strName.c_str());
3311 }
3312
3313 // this machine is awaiting for a spawning session to be opened:
3314 // then the calling process must be the one that got started by
3315 // LaunchVMProcess()
3316
3317 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3318 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3319
3320#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3321 /* Hardened windows builds spawns three processes when a VM is
3322 launched, the 3rd one is the one that will end up here. */
3323 RTPROCESS ppid;
3324 int rc = RTProcQueryParent(pid, &ppid);
3325 if (RT_SUCCESS(rc))
3326 rc = RTProcQueryParent(ppid, &ppid);
3327 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3328 || rc == VERR_ACCESS_DENIED)
3329 {
3330 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3331 mData->mSession.mPID = pid;
3332 }
3333#endif
3334
3335 if (mData->mSession.mPID != pid)
3336 return setError(E_ACCESSDENIED,
3337 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3338 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3339 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3340 }
3341
3342 // create the mutable SessionMachine from the current machine
3343 ComObjPtr<SessionMachine> sessionMachine;
3344 sessionMachine.createObject();
3345 rc = sessionMachine->init(this);
3346 AssertComRC(rc);
3347
3348 /* NOTE: doing return from this function after this point but
3349 * before the end is forbidden since it may call SessionMachine::uninit()
3350 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3351 * lock while still holding the Machine lock in alock so that a deadlock
3352 * is possible due to the wrong lock order. */
3353
3354 if (SUCCEEDED(rc))
3355 {
3356 /*
3357 * Set the session state to Spawning to protect against subsequent
3358 * attempts to open a session and to unregister the machine after
3359 * we release the lock.
3360 */
3361 SessionState_T origState = mData->mSession.mState;
3362 mData->mSession.mState = SessionState_Spawning;
3363
3364#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3365 /* Get the client token ID to be passed to the client process */
3366 Utf8Str strTokenId;
3367 sessionMachine->i_getTokenId(strTokenId);
3368 Assert(!strTokenId.isEmpty());
3369#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3370 /* Get the client token to be passed to the client process */
3371 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3372 /* The token is now "owned" by pToken, fix refcount */
3373 if (!pToken.isNull())
3374 pToken->Release();
3375#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3376
3377 /*
3378 * Release the lock before calling the client process -- it will call
3379 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3380 * because the state is Spawning, so that LaunchVMProcess() and
3381 * LockMachine() calls will fail. This method, called before we
3382 * acquire the lock again, will fail because of the wrong PID.
3383 *
3384 * Note that mData->mSession.mRemoteControls accessed outside
3385 * the lock may not be modified when state is Spawning, so it's safe.
3386 */
3387 alock.release();
3388
3389 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3390#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3391 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3392#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3393 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3394 /* Now the token is owned by the client process. */
3395 pToken.setNull();
3396#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3397 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3398
3399 /* The failure may occur w/o any error info (from RPC), so provide one */
3400 if (FAILED(rc))
3401 setError(VBOX_E_VM_ERROR,
3402 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3403
3404 if ( SUCCEEDED(rc)
3405 && fLaunchingVMProcess
3406 )
3407 {
3408 /* complete the remote session initialization */
3409
3410 /* get the console from the direct session */
3411 ComPtr<IConsole> console;
3412 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3413 ComAssertComRC(rc);
3414
3415 if (SUCCEEDED(rc) && !console)
3416 {
3417 ComAssert(!!console);
3418 rc = E_FAIL;
3419 }
3420
3421 /* assign machine & console to the remote session */
3422 if (SUCCEEDED(rc))
3423 {
3424 /*
3425 * after LaunchVMProcess(), the first and the only
3426 * entry in remoteControls is that remote session
3427 */
3428 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3429 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3430 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3431
3432 /* The failure may occur w/o any error info (from RPC), so provide one */
3433 if (FAILED(rc))
3434 setError(VBOX_E_VM_ERROR,
3435 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3436 }
3437
3438 if (FAILED(rc))
3439 pSessionControl->Uninitialize();
3440 }
3441
3442 /* acquire the lock again */
3443 alock.acquire();
3444
3445 /* Restore the session state */
3446 mData->mSession.mState = origState;
3447 }
3448
3449 // finalize spawning anyway (this is why we don't return on errors above)
3450 if (fLaunchingVMProcess)
3451 {
3452 /* Note that the progress object is finalized later */
3453 /** @todo Consider checking mData->mSession.mProgress for cancellation
3454 * around here. */
3455
3456 /* We don't reset mSession.mPID here because it is necessary for
3457 * SessionMachine::uninit() to reap the child process later. */
3458
3459 if (FAILED(rc))
3460 {
3461 /* Close the remote session, remove the remote control from the list
3462 * and reset session state to Closed (@note keep the code in sync
3463 * with the relevant part in checkForSpawnFailure()). */
3464
3465 Assert(mData->mSession.mRemoteControls.size() == 1);
3466 if (mData->mSession.mRemoteControls.size() == 1)
3467 {
3468 ErrorInfoKeeper eik;
3469 mData->mSession.mRemoteControls.front()->Uninitialize();
3470 }
3471
3472 mData->mSession.mRemoteControls.clear();
3473 mData->mSession.mState = SessionState_Unlocked;
3474 }
3475 }
3476 else
3477 {
3478 /* memorize PID of the directly opened session */
3479 if (SUCCEEDED(rc))
3480 mData->mSession.mPID = pid;
3481 }
3482
3483 if (SUCCEEDED(rc))
3484 {
3485 /* memorize the direct session control and cache IUnknown for it */
3486 mData->mSession.mDirectControl = pSessionControl;
3487 mData->mSession.mState = SessionState_Locked;
3488 /* associate the SessionMachine with this Machine */
3489 mData->mSession.mMachine = sessionMachine;
3490
3491 /* request an IUnknown pointer early from the remote party for later
3492 * identity checks (it will be internally cached within mDirectControl
3493 * at least on XPCOM) */
3494 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3495 NOREF(unk);
3496 }
3497
3498 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3499 * would break the lock order */
3500 alock.release();
3501
3502 /* uninitialize the created session machine on failure */
3503 if (FAILED(rc))
3504 sessionMachine->uninit();
3505
3506 }
3507
3508 if (SUCCEEDED(rc))
3509 {
3510 /*
3511 * tell the client watcher thread to update the set of
3512 * machines that have open sessions
3513 */
3514 mParent->i_updateClientWatcher();
3515
3516 if (oldState != SessionState_Locked)
3517 /* fire an event */
3518 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3519 }
3520
3521 return rc;
3522}
3523
3524/**
3525 * @note Locks objects!
3526 */
3527HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3528 const com::Utf8Str &aType,
3529 const com::Utf8Str &aEnvironment,
3530 ComPtr<IProgress> &aProgress)
3531{
3532 Utf8Str strFrontend(aType);
3533 /* "emergencystop" doesn't need the session, so skip the checks/interface
3534 * retrieval. This code doesn't quite fit in here, but introducing a
3535 * special API method would be even more effort, and would require explicit
3536 * support by every API client. It's better to hide the feature a bit. */
3537 if (strFrontend != "emergencystop")
3538 CheckComArgNotNull(aSession);
3539
3540 HRESULT rc = S_OK;
3541 if (strFrontend.isEmpty())
3542 {
3543 Bstr bstrFrontend;
3544 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3545 if (FAILED(rc))
3546 return rc;
3547 strFrontend = bstrFrontend;
3548 if (strFrontend.isEmpty())
3549 {
3550 ComPtr<ISystemProperties> systemProperties;
3551 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3552 if (FAILED(rc))
3553 return rc;
3554 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3555 if (FAILED(rc))
3556 return rc;
3557 strFrontend = bstrFrontend;
3558 }
3559 /* paranoia - emergencystop is not a valid default */
3560 if (strFrontend == "emergencystop")
3561 strFrontend = Utf8Str::Empty;
3562 }
3563 /* default frontend: Qt GUI */
3564 if (strFrontend.isEmpty())
3565 strFrontend = "GUI/Qt";
3566
3567 if (strFrontend != "emergencystop")
3568 {
3569 /* check the session state */
3570 SessionState_T state;
3571 rc = aSession->COMGETTER(State)(&state);
3572 if (FAILED(rc))
3573 return rc;
3574
3575 if (state != SessionState_Unlocked)
3576 return setError(VBOX_E_INVALID_OBJECT_STATE,
3577 tr("The given session is busy"));
3578
3579 /* get the IInternalSessionControl interface */
3580 ComPtr<IInternalSessionControl> control(aSession);
3581 ComAssertMsgRet(!control.isNull(),
3582 ("No IInternalSessionControl interface"),
3583 E_INVALIDARG);
3584
3585 /* get the teleporter enable state for the progress object init. */
3586 BOOL fTeleporterEnabled;
3587 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3588 if (FAILED(rc))
3589 return rc;
3590
3591 /* create a progress object */
3592 ComObjPtr<ProgressProxy> progress;
3593 progress.createObject();
3594 rc = progress->init(mParent,
3595 static_cast<IMachine*>(this),
3596 Bstr(tr("Starting VM")).raw(),
3597 TRUE /* aCancelable */,
3598 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3599 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3600 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3601 2 /* uFirstOperationWeight */,
3602 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3603
3604 if (SUCCEEDED(rc))
3605 {
3606 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3607 if (SUCCEEDED(rc))
3608 {
3609 aProgress = progress;
3610
3611 /* signal the client watcher thread */
3612 mParent->i_updateClientWatcher();
3613
3614 /* fire an event */
3615 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3616 }
3617 }
3618 }
3619 else
3620 {
3621 /* no progress object - either instant success or failure */
3622 aProgress = NULL;
3623
3624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3625
3626 if (mData->mSession.mState != SessionState_Locked)
3627 return setError(VBOX_E_INVALID_OBJECT_STATE,
3628 tr("The machine '%s' is not locked by a session"),
3629 mUserData->s.strName.c_str());
3630
3631 /* must have a VM process associated - do not kill normal API clients
3632 * with an open session */
3633 if (!Global::IsOnline(mData->mMachineState))
3634 return setError(VBOX_E_INVALID_OBJECT_STATE,
3635 tr("The machine '%s' does not have a VM process"),
3636 mUserData->s.strName.c_str());
3637
3638 /* forcibly terminate the VM process */
3639 if (mData->mSession.mPID != NIL_RTPROCESS)
3640 RTProcTerminate(mData->mSession.mPID);
3641
3642 /* signal the client watcher thread, as most likely the client has
3643 * been terminated */
3644 mParent->i_updateClientWatcher();
3645 }
3646
3647 return rc;
3648}
3649
3650HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3651{
3652 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3653 return setError(E_INVALIDARG,
3654 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3655 aPosition, SchemaDefs::MaxBootPosition);
3656
3657 if (aDevice == DeviceType_USB)
3658 return setError(E_NOTIMPL,
3659 tr("Booting from USB device is currently not supported"));
3660
3661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3662
3663 HRESULT rc = i_checkStateDependency(MutableStateDep);
3664 if (FAILED(rc)) return rc;
3665
3666 i_setModified(IsModified_MachineData);
3667 mHWData.backup();
3668 mHWData->mBootOrder[aPosition - 1] = aDevice;
3669
3670 return S_OK;
3671}
3672
3673HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3674{
3675 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3676 return setError(E_INVALIDARG,
3677 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3678 aPosition, SchemaDefs::MaxBootPosition);
3679
3680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3681
3682 *aDevice = mHWData->mBootOrder[aPosition - 1];
3683
3684 return S_OK;
3685}
3686
3687HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3688 LONG aControllerPort,
3689 LONG aDevice,
3690 DeviceType_T aType,
3691 const ComPtr<IMedium> &aMedium)
3692{
3693 IMedium *aM = aMedium;
3694 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3695 aName.c_str(), aControllerPort, aDevice, aType, aM));
3696
3697 // request the host lock first, since might be calling Host methods for getting host drives;
3698 // next, protect the media tree all the while we're in here, as well as our member variables
3699 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3700 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3701
3702 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3703 if (FAILED(rc)) return rc;
3704
3705 /// @todo NEWMEDIA implicit machine registration
3706 if (!mData->mRegistered)
3707 return setError(VBOX_E_INVALID_OBJECT_STATE,
3708 tr("Cannot attach storage devices to an unregistered machine"));
3709
3710 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3711
3712 /* Check for an existing controller. */
3713 ComObjPtr<StorageController> ctl;
3714 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3715 if (FAILED(rc)) return rc;
3716
3717 StorageControllerType_T ctrlType;
3718 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3719 if (FAILED(rc))
3720 return setError(E_FAIL,
3721 tr("Could not get type of controller '%s'"),
3722 aName.c_str());
3723
3724 bool fSilent = false;
3725 Utf8Str strReconfig;
3726
3727 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3728 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3729 if ( mData->mMachineState == MachineState_Paused
3730 && strReconfig == "1")
3731 fSilent = true;
3732
3733 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3734 bool fHotplug = false;
3735 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3736 fHotplug = true;
3737
3738 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3739 return setError(VBOX_E_INVALID_VM_STATE,
3740 tr("Controller '%s' does not support hotplugging"),
3741 aName.c_str());
3742
3743 // check that the port and device are not out of range
3744 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3745 if (FAILED(rc)) return rc;
3746
3747 /* check if the device slot is already busy */
3748 MediumAttachment *pAttachTemp;
3749 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3750 Bstr(aName).raw(),
3751 aControllerPort,
3752 aDevice)))
3753 {
3754 Medium *pMedium = pAttachTemp->i_getMedium();
3755 if (pMedium)
3756 {
3757 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3758 return setError(VBOX_E_OBJECT_IN_USE,
3759 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3760 pMedium->i_getLocationFull().c_str(),
3761 aControllerPort,
3762 aDevice,
3763 aName.c_str());
3764 }
3765 else
3766 return setError(VBOX_E_OBJECT_IN_USE,
3767 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3768 aControllerPort, aDevice, aName.c_str());
3769 }
3770
3771 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3772 if (aMedium && medium.isNull())
3773 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3774
3775 AutoCaller mediumCaller(medium);
3776 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3777
3778 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3779
3780 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3781 && !medium.isNull()
3782 )
3783 return setError(VBOX_E_OBJECT_IN_USE,
3784 tr("Medium '%s' is already attached to this virtual machine"),
3785 medium->i_getLocationFull().c_str());
3786
3787 if (!medium.isNull())
3788 {
3789 MediumType_T mtype = medium->i_getType();
3790 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3791 // For DVDs it's not written to the config file, so needs no global config
3792 // version bump. For floppies it's a new attribute "type", which is ignored
3793 // by older VirtualBox version, so needs no global config version bump either.
3794 // For hard disks this type is not accepted.
3795 if (mtype == MediumType_MultiAttach)
3796 {
3797 // This type is new with VirtualBox 4.0 and therefore requires settings
3798 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3799 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3800 // two reasons: The medium type is a property of the media registry tree, which
3801 // can reside in the global config file (for pre-4.0 media); we would therefore
3802 // possibly need to bump the global config version. We don't want to do that though
3803 // because that might make downgrading to pre-4.0 impossible.
3804 // As a result, we can only use these two new types if the medium is NOT in the
3805 // global registry:
3806 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3807 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3808 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3809 )
3810 return setError(VBOX_E_INVALID_OBJECT_STATE,
3811 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3812 "to machines that were created with VirtualBox 4.0 or later"),
3813 medium->i_getLocationFull().c_str());
3814 }
3815 }
3816
3817 bool fIndirect = false;
3818 if (!medium.isNull())
3819 fIndirect = medium->i_isReadOnly();
3820 bool associate = true;
3821
3822 do
3823 {
3824 if ( aType == DeviceType_HardDisk
3825 && mMediaData.isBackedUp())
3826 {
3827 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3828
3829 /* check if the medium was attached to the VM before we started
3830 * changing attachments in which case the attachment just needs to
3831 * be restored */
3832 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3833 {
3834 AssertReturn(!fIndirect, E_FAIL);
3835
3836 /* see if it's the same bus/channel/device */
3837 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3838 {
3839 /* the simplest case: restore the whole attachment
3840 * and return, nothing else to do */
3841 mMediaData->mAttachments.push_back(pAttachTemp);
3842
3843 /* Reattach the medium to the VM. */
3844 if (fHotplug || fSilent)
3845 {
3846 mediumLock.release();
3847 treeLock.release();
3848 alock.release();
3849
3850 MediumLockList *pMediumLockList(new MediumLockList());
3851
3852 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3853 true /* fMediumLockWrite */,
3854 false /* fMediumLockWriteAll */,
3855 NULL,
3856 *pMediumLockList);
3857 alock.acquire();
3858 if (FAILED(rc))
3859 delete pMediumLockList;
3860 else
3861 {
3862 mData->mSession.mLockedMedia.Unlock();
3863 alock.release();
3864 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3865 mData->mSession.mLockedMedia.Lock();
3866 alock.acquire();
3867 }
3868 alock.release();
3869
3870 if (SUCCEEDED(rc))
3871 {
3872 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3873 /* Remove lock list in case of error. */
3874 if (FAILED(rc))
3875 {
3876 mData->mSession.mLockedMedia.Unlock();
3877 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3878 mData->mSession.mLockedMedia.Lock();
3879 }
3880 }
3881 }
3882
3883 return S_OK;
3884 }
3885
3886 /* bus/channel/device differ; we need a new attachment object,
3887 * but don't try to associate it again */
3888 associate = false;
3889 break;
3890 }
3891 }
3892
3893 /* go further only if the attachment is to be indirect */
3894 if (!fIndirect)
3895 break;
3896
3897 /* perform the so called smart attachment logic for indirect
3898 * attachments. Note that smart attachment is only applicable to base
3899 * hard disks. */
3900
3901 if (medium->i_getParent().isNull())
3902 {
3903 /* first, investigate the backup copy of the current hard disk
3904 * attachments to make it possible to re-attach existing diffs to
3905 * another device slot w/o losing their contents */
3906 if (mMediaData.isBackedUp())
3907 {
3908 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3909
3910 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3911 uint32_t foundLevel = 0;
3912
3913 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3914 {
3915 uint32_t level = 0;
3916 MediumAttachment *pAttach = *it;
3917 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3918 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3919 if (pMedium.isNull())
3920 continue;
3921
3922 if (pMedium->i_getBase(&level) == medium)
3923 {
3924 /* skip the hard disk if its currently attached (we
3925 * cannot attach the same hard disk twice) */
3926 if (i_findAttachment(mMediaData->mAttachments,
3927 pMedium))
3928 continue;
3929
3930 /* matched device, channel and bus (i.e. attached to the
3931 * same place) will win and immediately stop the search;
3932 * otherwise the attachment that has the youngest
3933 * descendant of medium will be used
3934 */
3935 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3936 {
3937 /* the simplest case: restore the whole attachment
3938 * and return, nothing else to do */
3939 mMediaData->mAttachments.push_back(*it);
3940
3941 /* Reattach the medium to the VM. */
3942 if (fHotplug || fSilent)
3943 {
3944 mediumLock.release();
3945 treeLock.release();
3946 alock.release();
3947
3948 MediumLockList *pMediumLockList(new MediumLockList());
3949
3950 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3951 true /* fMediumLockWrite */,
3952 false /* fMediumLockWriteAll */,
3953 NULL,
3954 *pMediumLockList);
3955 alock.acquire();
3956 if (FAILED(rc))
3957 delete pMediumLockList;
3958 else
3959 {
3960 mData->mSession.mLockedMedia.Unlock();
3961 alock.release();
3962 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3963 mData->mSession.mLockedMedia.Lock();
3964 alock.acquire();
3965 }
3966 alock.release();
3967
3968 if (SUCCEEDED(rc))
3969 {
3970 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3971 /* Remove lock list in case of error. */
3972 if (FAILED(rc))
3973 {
3974 mData->mSession.mLockedMedia.Unlock();
3975 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3976 mData->mSession.mLockedMedia.Lock();
3977 }
3978 }
3979 }
3980
3981 return S_OK;
3982 }
3983 else if ( foundIt == oldAtts.end()
3984 || level > foundLevel /* prefer younger */
3985 )
3986 {
3987 foundIt = it;
3988 foundLevel = level;
3989 }
3990 }
3991 }
3992
3993 if (foundIt != oldAtts.end())
3994 {
3995 /* use the previously attached hard disk */
3996 medium = (*foundIt)->i_getMedium();
3997 mediumCaller.attach(medium);
3998 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3999 mediumLock.attach(medium);
4000 /* not implicit, doesn't require association with this VM */
4001 fIndirect = false;
4002 associate = false;
4003 /* go right to the MediumAttachment creation */
4004 break;
4005 }
4006 }
4007
4008 /* must give up the medium lock and medium tree lock as below we
4009 * go over snapshots, which needs a lock with higher lock order. */
4010 mediumLock.release();
4011 treeLock.release();
4012
4013 /* then, search through snapshots for the best diff in the given
4014 * hard disk's chain to base the new diff on */
4015
4016 ComObjPtr<Medium> base;
4017 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4018 while (snap)
4019 {
4020 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4021
4022 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4023
4024 MediumAttachment *pAttachFound = NULL;
4025 uint32_t foundLevel = 0;
4026
4027 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4028 {
4029 MediumAttachment *pAttach = *it;
4030 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4031 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4032 if (pMedium.isNull())
4033 continue;
4034
4035 uint32_t level = 0;
4036 if (pMedium->i_getBase(&level) == medium)
4037 {
4038 /* matched device, channel and bus (i.e. attached to the
4039 * same place) will win and immediately stop the search;
4040 * otherwise the attachment that has the youngest
4041 * descendant of medium will be used
4042 */
4043 if ( pAttach->i_getDevice() == aDevice
4044 && pAttach->i_getPort() == aControllerPort
4045 && pAttach->i_getControllerName() == aName
4046 )
4047 {
4048 pAttachFound = pAttach;
4049 break;
4050 }
4051 else if ( !pAttachFound
4052 || level > foundLevel /* prefer younger */
4053 )
4054 {
4055 pAttachFound = pAttach;
4056 foundLevel = level;
4057 }
4058 }
4059 }
4060
4061 if (pAttachFound)
4062 {
4063 base = pAttachFound->i_getMedium();
4064 break;
4065 }
4066
4067 snap = snap->i_getParent();
4068 }
4069
4070 /* re-lock medium tree and the medium, as we need it below */
4071 treeLock.acquire();
4072 mediumLock.acquire();
4073
4074 /* found a suitable diff, use it as a base */
4075 if (!base.isNull())
4076 {
4077 medium = base;
4078 mediumCaller.attach(medium);
4079 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4080 mediumLock.attach(medium);
4081 }
4082 }
4083
4084 Utf8Str strFullSnapshotFolder;
4085 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4086
4087 ComObjPtr<Medium> diff;
4088 diff.createObject();
4089 // store this diff in the same registry as the parent
4090 Guid uuidRegistryParent;
4091 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4092 {
4093 // parent image has no registry: this can happen if we're attaching a new immutable
4094 // image that has not yet been attached (medium then points to the base and we're
4095 // creating the diff image for the immutable, and the parent is not yet registered);
4096 // put the parent in the machine registry then
4097 mediumLock.release();
4098 treeLock.release();
4099 alock.release();
4100 i_addMediumToRegistry(medium);
4101 alock.acquire();
4102 treeLock.acquire();
4103 mediumLock.acquire();
4104 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4105 }
4106 rc = diff->init(mParent,
4107 medium->i_getPreferredDiffFormat(),
4108 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4109 uuidRegistryParent,
4110 DeviceType_HardDisk);
4111 if (FAILED(rc)) return rc;
4112
4113 /* Apply the normal locking logic to the entire chain. */
4114 MediumLockList *pMediumLockList(new MediumLockList());
4115 mediumLock.release();
4116 treeLock.release();
4117 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4118 true /* fMediumLockWrite */,
4119 false /* fMediumLockWriteAll */,
4120 medium,
4121 *pMediumLockList);
4122 treeLock.acquire();
4123 mediumLock.acquire();
4124 if (SUCCEEDED(rc))
4125 {
4126 mediumLock.release();
4127 treeLock.release();
4128 rc = pMediumLockList->Lock();
4129 treeLock.acquire();
4130 mediumLock.acquire();
4131 if (FAILED(rc))
4132 setError(rc,
4133 tr("Could not lock medium when creating diff '%s'"),
4134 diff->i_getLocationFull().c_str());
4135 else
4136 {
4137 /* will release the lock before the potentially lengthy
4138 * operation, so protect with the special state */
4139 MachineState_T oldState = mData->mMachineState;
4140 i_setMachineState(MachineState_SettingUp);
4141
4142 mediumLock.release();
4143 treeLock.release();
4144 alock.release();
4145
4146 rc = medium->i_createDiffStorage(diff,
4147 MediumVariant_Standard,
4148 pMediumLockList,
4149 NULL /* aProgress */,
4150 true /* aWait */);
4151
4152 alock.acquire();
4153 treeLock.acquire();
4154 mediumLock.acquire();
4155
4156 i_setMachineState(oldState);
4157 }
4158 }
4159
4160 /* Unlock the media and free the associated memory. */
4161 delete pMediumLockList;
4162
4163 if (FAILED(rc)) return rc;
4164
4165 /* use the created diff for the actual attachment */
4166 medium = diff;
4167 mediumCaller.attach(medium);
4168 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4169 mediumLock.attach(medium);
4170 }
4171 while (0);
4172
4173 ComObjPtr<MediumAttachment> attachment;
4174 attachment.createObject();
4175 rc = attachment->init(this,
4176 medium,
4177 aName,
4178 aControllerPort,
4179 aDevice,
4180 aType,
4181 fIndirect,
4182 false /* fPassthrough */,
4183 false /* fTempEject */,
4184 false /* fNonRotational */,
4185 false /* fDiscard */,
4186 fHotplug /* fHotPluggable */,
4187 Utf8Str::Empty);
4188 if (FAILED(rc)) return rc;
4189
4190 if (associate && !medium.isNull())
4191 {
4192 // as the last step, associate the medium to the VM
4193 rc = medium->i_addBackReference(mData->mUuid);
4194 // here we can fail because of Deleting, or being in process of creating a Diff
4195 if (FAILED(rc)) return rc;
4196
4197 mediumLock.release();
4198 treeLock.release();
4199 alock.release();
4200 i_addMediumToRegistry(medium);
4201 alock.acquire();
4202 treeLock.acquire();
4203 mediumLock.acquire();
4204 }
4205
4206 /* success: finally remember the attachment */
4207 i_setModified(IsModified_Storage);
4208 mMediaData.backup();
4209 mMediaData->mAttachments.push_back(attachment);
4210
4211 mediumLock.release();
4212 treeLock.release();
4213 alock.release();
4214
4215 if (fHotplug || fSilent)
4216 {
4217 if (!medium.isNull())
4218 {
4219 MediumLockList *pMediumLockList(new MediumLockList());
4220
4221 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4222 true /* fMediumLockWrite */,
4223 false /* fMediumLockWriteAll */,
4224 NULL,
4225 *pMediumLockList);
4226 alock.acquire();
4227 if (FAILED(rc))
4228 delete pMediumLockList;
4229 else
4230 {
4231 mData->mSession.mLockedMedia.Unlock();
4232 alock.release();
4233 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4234 mData->mSession.mLockedMedia.Lock();
4235 alock.acquire();
4236 }
4237 alock.release();
4238 }
4239
4240 if (SUCCEEDED(rc))
4241 {
4242 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4243 /* Remove lock list in case of error. */
4244 if (FAILED(rc))
4245 {
4246 mData->mSession.mLockedMedia.Unlock();
4247 mData->mSession.mLockedMedia.Remove(attachment);
4248 mData->mSession.mLockedMedia.Lock();
4249 }
4250 }
4251 }
4252
4253 mParent->i_saveModifiedRegistries();
4254
4255 return rc;
4256}
4257
4258HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4259 LONG aDevice)
4260{
4261 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4262 aName.c_str(), aControllerPort, aDevice));
4263
4264 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4265
4266 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4267 if (FAILED(rc)) return rc;
4268
4269 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4270
4271 /* Check for an existing controller. */
4272 ComObjPtr<StorageController> ctl;
4273 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4274 if (FAILED(rc)) return rc;
4275
4276 StorageControllerType_T ctrlType;
4277 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4278 if (FAILED(rc))
4279 return setError(E_FAIL,
4280 tr("Could not get type of controller '%s'"),
4281 aName.c_str());
4282
4283 bool fSilent = false;
4284 Utf8Str strReconfig;
4285
4286 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4287 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4288 if ( mData->mMachineState == MachineState_Paused
4289 && strReconfig == "1")
4290 fSilent = true;
4291
4292 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4293 bool fHotplug = false;
4294 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4295 fHotplug = true;
4296
4297 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4298 return setError(VBOX_E_INVALID_VM_STATE,
4299 tr("Controller '%s' does not support hotplugging"),
4300 aName.c_str());
4301
4302 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4303 Bstr(aName).raw(),
4304 aControllerPort,
4305 aDevice);
4306 if (!pAttach)
4307 return setError(VBOX_E_OBJECT_NOT_FOUND,
4308 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4309 aDevice, aControllerPort, aName.c_str());
4310
4311 if (fHotplug && !pAttach->i_getHotPluggable())
4312 return setError(VBOX_E_NOT_SUPPORTED,
4313 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4314 aDevice, aControllerPort, aName.c_str());
4315
4316 /*
4317 * The VM has to detach the device before we delete any implicit diffs.
4318 * If this fails we can roll back without loosing data.
4319 */
4320 if (fHotplug || fSilent)
4321 {
4322 alock.release();
4323 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4324 alock.acquire();
4325 }
4326 if (FAILED(rc)) return rc;
4327
4328 /* If we are here everything went well and we can delete the implicit now. */
4329 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4330
4331 alock.release();
4332
4333 mParent->i_saveModifiedRegistries();
4334
4335 return rc;
4336}
4337
4338HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4339 LONG aDevice, BOOL aPassthrough)
4340{
4341 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4342 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4343
4344 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4345
4346 HRESULT rc = i_checkStateDependency(MutableStateDep);
4347 if (FAILED(rc)) return rc;
4348
4349 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4350
4351 if (Global::IsOnlineOrTransient(mData->mMachineState))
4352 return setError(VBOX_E_INVALID_VM_STATE,
4353 tr("Invalid machine state: %s"),
4354 Global::stringifyMachineState(mData->mMachineState));
4355
4356 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4357 Bstr(aName).raw(),
4358 aControllerPort,
4359 aDevice);
4360 if (!pAttach)
4361 return setError(VBOX_E_OBJECT_NOT_FOUND,
4362 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4363 aDevice, aControllerPort, aName.c_str());
4364
4365
4366 i_setModified(IsModified_Storage);
4367 mMediaData.backup();
4368
4369 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4370
4371 if (pAttach->i_getType() != DeviceType_DVD)
4372 return setError(E_INVALIDARG,
4373 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4374 aDevice, aControllerPort, aName.c_str());
4375 pAttach->i_updatePassthrough(!!aPassthrough);
4376
4377 return S_OK;
4378}
4379
4380HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4381 LONG aDevice, BOOL aTemporaryEject)
4382{
4383
4384 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4385 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4386
4387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4388
4389 HRESULT rc = i_checkStateDependency(MutableStateDep);
4390 if (FAILED(rc)) return rc;
4391
4392 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4393 Bstr(aName).raw(),
4394 aControllerPort,
4395 aDevice);
4396 if (!pAttach)
4397 return setError(VBOX_E_OBJECT_NOT_FOUND,
4398 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4399 aDevice, aControllerPort, aName.c_str());
4400
4401
4402 i_setModified(IsModified_Storage);
4403 mMediaData.backup();
4404
4405 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4406
4407 if (pAttach->i_getType() != DeviceType_DVD)
4408 return setError(E_INVALIDARG,
4409 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4410 aDevice, aControllerPort, aName.c_str());
4411 pAttach->i_updateTempEject(!!aTemporaryEject);
4412
4413 return S_OK;
4414}
4415
4416HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4417 LONG aDevice, BOOL aNonRotational)
4418{
4419
4420 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4421 aName.c_str(), aControllerPort, aDevice, aNonRotational));
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
4445 i_setModified(IsModified_Storage);
4446 mMediaData.backup();
4447
4448 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4449
4450 if (pAttach->i_getType() != DeviceType_HardDisk)
4451 return setError(E_INVALIDARG,
4452 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"),
4453 aDevice, aControllerPort, aName.c_str());
4454 pAttach->i_updateNonRotational(!!aNonRotational);
4455
4456 return S_OK;
4457}
4458
4459HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4460 LONG aDevice, BOOL aDiscard)
4461{
4462
4463 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4464 aName.c_str(), aControllerPort, aDevice, aDiscard));
4465
4466 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4467
4468 HRESULT rc = i_checkStateDependency(MutableStateDep);
4469 if (FAILED(rc)) return rc;
4470
4471 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4472
4473 if (Global::IsOnlineOrTransient(mData->mMachineState))
4474 return setError(VBOX_E_INVALID_VM_STATE,
4475 tr("Invalid machine state: %s"),
4476 Global::stringifyMachineState(mData->mMachineState));
4477
4478 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4479 Bstr(aName).raw(),
4480 aControllerPort,
4481 aDevice);
4482 if (!pAttach)
4483 return setError(VBOX_E_OBJECT_NOT_FOUND,
4484 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4485 aDevice, aControllerPort, aName.c_str());
4486
4487
4488 i_setModified(IsModified_Storage);
4489 mMediaData.backup();
4490
4491 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4492
4493 if (pAttach->i_getType() != DeviceType_HardDisk)
4494 return setError(E_INVALIDARG,
4495 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"),
4496 aDevice, aControllerPort, aName.c_str());
4497 pAttach->i_updateDiscard(!!aDiscard);
4498
4499 return S_OK;
4500}
4501
4502HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4503 LONG aDevice, BOOL aHotPluggable)
4504{
4505 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4506 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4507
4508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4509
4510 HRESULT rc = i_checkStateDependency(MutableStateDep);
4511 if (FAILED(rc)) return rc;
4512
4513 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4514
4515 if (Global::IsOnlineOrTransient(mData->mMachineState))
4516 return setError(VBOX_E_INVALID_VM_STATE,
4517 tr("Invalid machine state: %s"),
4518 Global::stringifyMachineState(mData->mMachineState));
4519
4520 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4521 Bstr(aName).raw(),
4522 aControllerPort,
4523 aDevice);
4524 if (!pAttach)
4525 return setError(VBOX_E_OBJECT_NOT_FOUND,
4526 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4527 aDevice, aControllerPort, aName.c_str());
4528
4529 /* Check for an existing controller. */
4530 ComObjPtr<StorageController> ctl;
4531 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4532 if (FAILED(rc)) return rc;
4533
4534 StorageControllerType_T ctrlType;
4535 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4536 if (FAILED(rc))
4537 return setError(E_FAIL,
4538 tr("Could not get type of controller '%s'"),
4539 aName.c_str());
4540
4541 if (!i_isControllerHotplugCapable(ctrlType))
4542 return setError(VBOX_E_NOT_SUPPORTED,
4543 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4544 aName.c_str());
4545
4546 i_setModified(IsModified_Storage);
4547 mMediaData.backup();
4548
4549 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4550
4551 if (pAttach->i_getType() == DeviceType_Floppy)
4552 return setError(E_INVALIDARG,
4553 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"),
4554 aDevice, aControllerPort, aName.c_str());
4555 pAttach->i_updateHotPluggable(!!aHotPluggable);
4556
4557 return S_OK;
4558}
4559
4560HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4561 LONG aDevice)
4562{
4563 int rc = S_OK;
4564 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4565 aName.c_str(), aControllerPort, aDevice));
4566
4567 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4568
4569 return rc;
4570}
4571
4572HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4573 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4574{
4575 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4576 aName.c_str(), aControllerPort, aDevice));
4577
4578 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4579
4580 HRESULT rc = i_checkStateDependency(MutableStateDep);
4581 if (FAILED(rc)) return rc;
4582
4583 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4584
4585 if (Global::IsOnlineOrTransient(mData->mMachineState))
4586 return setError(VBOX_E_INVALID_VM_STATE,
4587 tr("Invalid machine state: %s"),
4588 Global::stringifyMachineState(mData->mMachineState));
4589
4590 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4591 Bstr(aName).raw(),
4592 aControllerPort,
4593 aDevice);
4594 if (!pAttach)
4595 return setError(VBOX_E_OBJECT_NOT_FOUND,
4596 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4597 aDevice, aControllerPort, aName.c_str());
4598
4599
4600 i_setModified(IsModified_Storage);
4601 mMediaData.backup();
4602
4603 IBandwidthGroup *iB = aBandwidthGroup;
4604 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4605 if (aBandwidthGroup && group.isNull())
4606 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4607
4608 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4609
4610 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4611 if (strBandwidthGroupOld.isNotEmpty())
4612 {
4613 /* Get the bandwidth group object and release it - this must not fail. */
4614 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4615 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4616 Assert(SUCCEEDED(rc));
4617
4618 pBandwidthGroupOld->i_release();
4619 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4620 }
4621
4622 if (!group.isNull())
4623 {
4624 group->i_reference();
4625 pAttach->i_updateBandwidthGroup(group->i_getName());
4626 }
4627
4628 return S_OK;
4629}
4630
4631HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4632 LONG aControllerPort,
4633 LONG aDevice,
4634 DeviceType_T aType)
4635{
4636 HRESULT rc = S_OK;
4637
4638 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4639 aName.c_str(), aControllerPort, aDevice, aType));
4640
4641 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4642
4643 return rc;
4644}
4645
4646
4647HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4648 LONG aControllerPort,
4649 LONG aDevice,
4650 BOOL aForce)
4651{
4652 int rc = S_OK;
4653 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4654 aName.c_str(), aControllerPort, aForce));
4655
4656 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4657
4658 return rc;
4659}
4660
4661HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4662 LONG aControllerPort,
4663 LONG aDevice,
4664 const ComPtr<IMedium> &aMedium,
4665 BOOL aForce)
4666{
4667 int rc = S_OK;
4668 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4669 aName.c_str(), aControllerPort, aDevice, aForce));
4670
4671 // request the host lock first, since might be calling Host methods for getting host drives;
4672 // next, protect the media tree all the while we're in here, as well as our member variables
4673 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4674 this->lockHandle(),
4675 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4676
4677 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4678 Bstr(aName).raw(),
4679 aControllerPort,
4680 aDevice);
4681 if (pAttach.isNull())
4682 return setError(VBOX_E_OBJECT_NOT_FOUND,
4683 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4684 aDevice, aControllerPort, aName.c_str());
4685
4686 /* Remember previously mounted medium. The medium before taking the
4687 * backup is not necessarily the same thing. */
4688 ComObjPtr<Medium> oldmedium;
4689 oldmedium = pAttach->i_getMedium();
4690
4691 IMedium *iM = aMedium;
4692 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4693 if (aMedium && pMedium.isNull())
4694 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4695
4696 AutoCaller mediumCaller(pMedium);
4697 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4698
4699 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4700 if (pMedium)
4701 {
4702 DeviceType_T mediumType = pAttach->i_getType();
4703 switch (mediumType)
4704 {
4705 case DeviceType_DVD:
4706 case DeviceType_Floppy:
4707 break;
4708
4709 default:
4710 return setError(VBOX_E_INVALID_OBJECT_STATE,
4711 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4712 aControllerPort,
4713 aDevice,
4714 aName.c_str());
4715 }
4716 }
4717
4718 i_setModified(IsModified_Storage);
4719 mMediaData.backup();
4720
4721 {
4722 // The backup operation makes the pAttach reference point to the
4723 // old settings. Re-get the correct reference.
4724 pAttach = i_findAttachment(mMediaData->mAttachments,
4725 Bstr(aName).raw(),
4726 aControllerPort,
4727 aDevice);
4728 if (!oldmedium.isNull())
4729 oldmedium->i_removeBackReference(mData->mUuid);
4730 if (!pMedium.isNull())
4731 {
4732 pMedium->i_addBackReference(mData->mUuid);
4733
4734 mediumLock.release();
4735 multiLock.release();
4736 i_addMediumToRegistry(pMedium);
4737 multiLock.acquire();
4738 mediumLock.acquire();
4739 }
4740
4741 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4742 pAttach->i_updateMedium(pMedium);
4743 }
4744
4745 i_setModified(IsModified_Storage);
4746
4747 mediumLock.release();
4748 multiLock.release();
4749 rc = i_onMediumChange(pAttach, aForce);
4750 multiLock.acquire();
4751 mediumLock.acquire();
4752
4753 /* On error roll back this change only. */
4754 if (FAILED(rc))
4755 {
4756 if (!pMedium.isNull())
4757 pMedium->i_removeBackReference(mData->mUuid);
4758 pAttach = i_findAttachment(mMediaData->mAttachments,
4759 Bstr(aName).raw(),
4760 aControllerPort,
4761 aDevice);
4762 /* If the attachment is gone in the meantime, bail out. */
4763 if (pAttach.isNull())
4764 return rc;
4765 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4766 if (!oldmedium.isNull())
4767 oldmedium->i_addBackReference(mData->mUuid);
4768 pAttach->i_updateMedium(oldmedium);
4769 }
4770
4771 mediumLock.release();
4772 multiLock.release();
4773
4774 mParent->i_saveModifiedRegistries();
4775
4776 return rc;
4777}
4778HRESULT Machine::getMedium(const com::Utf8Str &aName,
4779 LONG aControllerPort,
4780 LONG aDevice,
4781 ComPtr<IMedium> &aMedium)
4782{
4783 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4784 aName.c_str(), aControllerPort, aDevice));
4785
4786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4787
4788 aMedium = NULL;
4789
4790 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4791 Bstr(aName).raw(),
4792 aControllerPort,
4793 aDevice);
4794 if (pAttach.isNull())
4795 return setError(VBOX_E_OBJECT_NOT_FOUND,
4796 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4797 aDevice, aControllerPort, aName.c_str());
4798
4799 aMedium = pAttach->i_getMedium();
4800
4801 return S_OK;
4802}
4803
4804HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4805{
4806
4807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4808
4809 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4810
4811 return S_OK;
4812}
4813
4814HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4815{
4816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4817
4818 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4819
4820 return S_OK;
4821}
4822
4823HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4824{
4825 /* Do not assert if slot is out of range, just return the advertised
4826 status. testdriver/vbox.py triggers this in logVmInfo. */
4827 if (aSlot >= mNetworkAdapters.size())
4828 return setError(E_INVALIDARG,
4829 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4830 aSlot, mNetworkAdapters.size());
4831
4832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4833
4834 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4835
4836 return S_OK;
4837}
4838
4839HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4840{
4841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4842
4843 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4844 size_t i = 0;
4845 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4846 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4847 ++it, ++i)
4848 aKeys[i] = it->first;
4849
4850 return S_OK;
4851}
4852
4853 /**
4854 * @note Locks this object for reading.
4855 */
4856HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4857 com::Utf8Str &aValue)
4858{
4859 /* start with nothing found */
4860 aValue = "";
4861
4862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4863
4864 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4865 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4866 // found:
4867 aValue = it->second; // source is a Utf8Str
4868
4869 /* return the result to caller (may be empty) */
4870 return S_OK;
4871}
4872
4873 /**
4874 * @note Locks mParent for writing + this object for writing.
4875 */
4876HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4877{
4878 Utf8Str strOldValue; // empty
4879
4880 // locking note: we only hold the read lock briefly to look up the old value,
4881 // then release it and call the onExtraCanChange callbacks. There is a small
4882 // chance of a race insofar as the callback might be called twice if two callers
4883 // change the same key at the same time, but that's a much better solution
4884 // than the deadlock we had here before. The actual changing of the extradata
4885 // is then performed under the write lock and race-free.
4886
4887 // look up the old value first; if nothing has changed then we need not do anything
4888 {
4889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4890
4891 // For snapshots don't even think about allowing changes, extradata
4892 // is global for a machine, so there is nothing snapshot specific.
4893 if (i_isSnapshotMachine())
4894 return setError(VBOX_E_INVALID_VM_STATE,
4895 tr("Cannot set extradata for a snapshot"));
4896
4897 // check if the right IMachine instance is used
4898 if (!i_isSessionMachine())
4899 return setError(VBOX_E_INVALID_VM_STATE,
4900 tr("Cannot set extradata for an immutable machine"));
4901
4902 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4903 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4904 strOldValue = it->second;
4905 }
4906
4907 bool fChanged;
4908 if ((fChanged = (strOldValue != aValue)))
4909 {
4910 // ask for permission from all listeners outside the locks;
4911 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4912 // lock to copy the list of callbacks to invoke
4913 Bstr error;
4914 Bstr bstrValue(aValue);
4915
4916 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4917 {
4918 const char *sep = error.isEmpty() ? "" : ": ";
4919 CBSTR err = error.raw();
4920 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4921 sep, err));
4922 return setError(E_ACCESSDENIED,
4923 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4924 aKey.c_str(),
4925 aValue.c_str(),
4926 sep,
4927 err);
4928 }
4929
4930 // data is changing and change not vetoed: then write it out under the lock
4931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4932
4933 if (aValue.isEmpty())
4934 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4935 else
4936 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4937 // creates a new key if needed
4938
4939 bool fNeedsGlobalSaveSettings = false;
4940 // This saving of settings is tricky: there is no "old state" for the
4941 // extradata items at all (unlike all other settings), so the old/new
4942 // settings comparison would give a wrong result!
4943 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4944
4945 if (fNeedsGlobalSaveSettings)
4946 {
4947 // save the global settings; for that we should hold only the VirtualBox lock
4948 alock.release();
4949 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4950 mParent->i_saveSettings();
4951 }
4952 }
4953
4954 // fire notification outside the lock
4955 if (fChanged)
4956 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4957
4958 return S_OK;
4959}
4960
4961HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4962{
4963 aProgress = NULL;
4964 NOREF(aSettingsFilePath);
4965 ReturnComNotImplemented();
4966}
4967
4968HRESULT Machine::saveSettings()
4969{
4970 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4971
4972 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4973 if (FAILED(rc)) return rc;
4974
4975 /* the settings file path may never be null */
4976 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4977
4978 /* save all VM data excluding snapshots */
4979 bool fNeedsGlobalSaveSettings = false;
4980 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4981 mlock.release();
4982
4983 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4984 {
4985 // save the global settings; for that we should hold only the VirtualBox lock
4986 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4987 rc = mParent->i_saveSettings();
4988 }
4989
4990 return rc;
4991}
4992
4993
4994HRESULT Machine::discardSettings()
4995{
4996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4997
4998 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4999 if (FAILED(rc)) return rc;
5000
5001 /*
5002 * during this rollback, the session will be notified if data has
5003 * been actually changed
5004 */
5005 i_rollback(true /* aNotify */);
5006
5007 return S_OK;
5008}
5009
5010/** @note Locks objects! */
5011HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
5012 std::vector<ComPtr<IMedium> > &aMedia)
5013{
5014 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5015 AutoLimitedCaller autoCaller(this);
5016 AssertComRCReturnRC(autoCaller.rc());
5017
5018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5019
5020 Guid id(i_getId());
5021
5022 if (mData->mSession.mState != SessionState_Unlocked)
5023 return setError(VBOX_E_INVALID_OBJECT_STATE,
5024 tr("Cannot unregister the machine '%s' while it is locked"),
5025 mUserData->s.strName.c_str());
5026
5027 // wait for state dependents to drop to zero
5028 i_ensureNoStateDependencies();
5029
5030 if (!mData->mAccessible)
5031 {
5032 // inaccessible maschines can only be unregistered; uninitialize ourselves
5033 // here because currently there may be no unregistered that are inaccessible
5034 // (this state combination is not supported). Note releasing the caller and
5035 // leaving the lock before calling uninit()
5036 alock.release();
5037 autoCaller.release();
5038
5039 uninit();
5040
5041 mParent->i_unregisterMachine(this, id);
5042 // calls VirtualBox::i_saveSettings()
5043
5044 return S_OK;
5045 }
5046
5047 HRESULT rc = S_OK;
5048
5049 // discard saved state
5050 if (mData->mMachineState == MachineState_Saved)
5051 {
5052 // add the saved state file to the list of files the caller should delete
5053 Assert(!mSSData->strStateFilePath.isEmpty());
5054 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5055
5056 mSSData->strStateFilePath.setNull();
5057
5058 // unconditionally set the machine state to powered off, we now
5059 // know no session has locked the machine
5060 mData->mMachineState = MachineState_PoweredOff;
5061 }
5062
5063 size_t cSnapshots = 0;
5064 if (mData->mFirstSnapshot)
5065 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5066 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5067 // fail now before we start detaching media
5068 return setError(VBOX_E_INVALID_OBJECT_STATE,
5069 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5070 mUserData->s.strName.c_str(), cSnapshots);
5071
5072 // This list collects the medium objects from all medium attachments
5073 // which we will detach from the machine and its snapshots, in a specific
5074 // order which allows for closing all media without getting "media in use"
5075 // errors, simply by going through the list from the front to the back:
5076 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5077 // and must be closed before the parent media from the snapshots, or closing the parents
5078 // will fail because they still have children);
5079 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5080 // the root ("first") snapshot of the machine.
5081 MediaList llMedia;
5082
5083 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5084 && mMediaData->mAttachments.size()
5085 )
5086 {
5087 // we have media attachments: detach them all and add the Medium objects to our list
5088 if (aCleanupMode != CleanupMode_UnregisterOnly)
5089 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5090 else
5091 return setError(VBOX_E_INVALID_OBJECT_STATE,
5092 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5093 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5094 }
5095
5096 if (cSnapshots)
5097 {
5098 // autoCleanup must be true here, or we would have failed above
5099
5100 // add the media from the medium attachments of the snapshots to llMedia
5101 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5102 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5103 // into the children first
5104
5105 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5106 MachineState_T oldState = mData->mMachineState;
5107 mData->mMachineState = MachineState_DeletingSnapshot;
5108
5109 // make a copy of the first snapshot so the refcount does not drop to 0
5110 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5111 // because of the AutoCaller voodoo)
5112 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5113
5114 // GO!
5115 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5116
5117 mData->mMachineState = oldState;
5118 }
5119
5120 if (FAILED(rc))
5121 {
5122 i_rollbackMedia();
5123 return rc;
5124 }
5125
5126 // commit all the media changes made above
5127 i_commitMedia();
5128
5129 mData->mRegistered = false;
5130
5131 // machine lock no longer needed
5132 alock.release();
5133
5134 // return media to caller
5135 size_t i = 0;
5136 aMedia.resize(llMedia.size());
5137 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5138 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5139
5140 mParent->i_unregisterMachine(this, id);
5141 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5142
5143 return S_OK;
5144}
5145
5146struct Machine::DeleteTask
5147{
5148 ComObjPtr<Machine> pMachine;
5149 RTCList<ComPtr<IMedium> > llMediums;
5150 StringsList llFilesToDelete;
5151 ComObjPtr<Progress> pProgress;
5152};
5153
5154HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5155{
5156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5157
5158 HRESULT rc = i_checkStateDependency(MutableStateDep);
5159 if (FAILED(rc)) return rc;
5160
5161 if (mData->mRegistered)
5162 return setError(VBOX_E_INVALID_VM_STATE,
5163 tr("Cannot delete settings of a registered machine"));
5164
5165 DeleteTask *pTask = new DeleteTask;
5166 pTask->pMachine = this;
5167
5168 // collect files to delete
5169 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5170
5171 for (size_t i = 0; i < aMedia.size(); ++i)
5172 {
5173 IMedium *pIMedium(aMedia[i]);
5174 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5175 if (pMedium.isNull())
5176 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5177 SafeArray<BSTR> ids;
5178 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5179 if (FAILED(rc)) return rc;
5180 /* At this point the medium should not have any back references
5181 * anymore. If it has it is attached to another VM and *must* not
5182 * deleted. */
5183 if (ids.size() < 1)
5184 pTask->llMediums.append(pMedium);
5185 }
5186 if (mData->pMachineConfigFile->fileExists())
5187 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5188
5189 pTask->pProgress.createObject();
5190 pTask->pProgress->init(i_getVirtualBox(),
5191 static_cast<IMachine*>(this) /* aInitiator */,
5192 Bstr(tr("Deleting files")).raw(),
5193 true /* fCancellable */,
5194 (ULONG)(pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1), // cOperations
5195 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5196
5197 int vrc = RTThreadCreate(NULL,
5198 Machine::deleteThread,
5199 (void*)pTask,
5200 0,
5201 RTTHREADTYPE_MAIN_WORKER,
5202 0,
5203 "MachineDelete");
5204
5205 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5206
5207 if (RT_FAILURE(vrc))
5208 {
5209 delete pTask;
5210 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5211 }
5212
5213 LogFlowFuncLeave();
5214
5215 return S_OK;
5216}
5217
5218/**
5219 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5220 * calls Machine::deleteTaskWorker() on the actual machine object.
5221 * @param Thread
5222 * @param pvUser
5223 * @return
5224 */
5225/*static*/
5226DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5227{
5228 LogFlowFuncEnter();
5229
5230 DeleteTask *pTask = (DeleteTask*)pvUser;
5231 Assert(pTask);
5232 Assert(pTask->pMachine);
5233 Assert(pTask->pProgress);
5234
5235 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5236 pTask->pProgress->i_notifyComplete(rc);
5237
5238 delete pTask;
5239
5240 LogFlowFuncLeave();
5241
5242 NOREF(Thread);
5243
5244 return VINF_SUCCESS;
5245}
5246
5247/**
5248 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5249 * @param task
5250 * @return
5251 */
5252HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5253{
5254 AutoCaller autoCaller(this);
5255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5256
5257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5258
5259 HRESULT rc = S_OK;
5260
5261 try
5262 {
5263 ULONG uLogHistoryCount = 3;
5264 ComPtr<ISystemProperties> systemProperties;
5265 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5266 if (FAILED(rc)) throw rc;
5267
5268 if (!systemProperties.isNull())
5269 {
5270 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5271 if (FAILED(rc)) throw rc;
5272 }
5273
5274 MachineState_T oldState = mData->mMachineState;
5275 i_setMachineState(MachineState_SettingUp);
5276 alock.release();
5277 for (size_t i = 0; i < task.llMediums.size(); ++i)
5278 {
5279 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5280 {
5281 AutoCaller mac(pMedium);
5282 if (FAILED(mac.rc())) throw mac.rc();
5283 Utf8Str strLocation = pMedium->i_getLocationFull();
5284 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5285 if (FAILED(rc)) throw rc;
5286 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5287 }
5288 if (pMedium->i_isMediumFormatFile())
5289 {
5290 ComPtr<IProgress> pProgress2;
5291 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5292 if (FAILED(rc)) throw rc;
5293 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5294 if (FAILED(rc)) throw rc;
5295 /* Check the result of the asynchronous process. */
5296 LONG iRc;
5297 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5298 if (FAILED(rc)) throw rc;
5299 /* If the thread of the progress object has an error, then
5300 * retrieve the error info from there, or it'll be lost. */
5301 if (FAILED(iRc))
5302 throw setError(ProgressErrorInfo(pProgress2));
5303 }
5304
5305 /* Close the medium, deliberately without checking the return
5306 * code, and without leaving any trace in the error info, as
5307 * a failure here is a very minor issue, which shouldn't happen
5308 * as above we even managed to delete the medium. */
5309 {
5310 ErrorInfoKeeper eik;
5311 pMedium->Close();
5312 }
5313 }
5314 i_setMachineState(oldState);
5315 alock.acquire();
5316
5317 // delete the files pushed on the task list by Machine::Delete()
5318 // (this includes saved states of the machine and snapshots and
5319 // medium storage files from the IMedium list passed in, and the
5320 // machine XML file)
5321 StringsList::const_iterator it = task.llFilesToDelete.begin();
5322 while (it != task.llFilesToDelete.end())
5323 {
5324 const Utf8Str &strFile = *it;
5325 LogFunc(("Deleting file %s\n", strFile.c_str()));
5326 int vrc = RTFileDelete(strFile.c_str());
5327 if (RT_FAILURE(vrc))
5328 throw setError(VBOX_E_IPRT_ERROR,
5329 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5330
5331 ++it;
5332 if (it == task.llFilesToDelete.end())
5333 {
5334 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5335 if (FAILED(rc)) throw rc;
5336 break;
5337 }
5338
5339 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5340 if (FAILED(rc)) throw rc;
5341 }
5342
5343 /* delete the settings only when the file actually exists */
5344 if (mData->pMachineConfigFile->fileExists())
5345 {
5346 /* Delete any backup or uncommitted XML files. Ignore failures.
5347 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5348 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5349 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5350 RTFileDelete(otherXml.c_str());
5351 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5352 RTFileDelete(otherXml.c_str());
5353
5354 /* delete the Logs folder, nothing important should be left
5355 * there (we don't check for errors because the user might have
5356 * some private files there that we don't want to delete) */
5357 Utf8Str logFolder;
5358 getLogFolder(logFolder);
5359 Assert(logFolder.length());
5360 if (RTDirExists(logFolder.c_str()))
5361 {
5362 /* Delete all VBox.log[.N] files from the Logs folder
5363 * (this must be in sync with the rotation logic in
5364 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5365 * files that may have been created by the GUI. */
5366 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5367 logFolder.c_str(), RTPATH_DELIMITER);
5368 RTFileDelete(log.c_str());
5369 log = Utf8StrFmt("%s%cVBox.png",
5370 logFolder.c_str(), RTPATH_DELIMITER);
5371 RTFileDelete(log.c_str());
5372 for (int i = uLogHistoryCount; i > 0; i--)
5373 {
5374 log = Utf8StrFmt("%s%cVBox.log.%d",
5375 logFolder.c_str(), RTPATH_DELIMITER, i);
5376 RTFileDelete(log.c_str());
5377 log = Utf8StrFmt("%s%cVBox.png.%d",
5378 logFolder.c_str(), RTPATH_DELIMITER, i);
5379 RTFileDelete(log.c_str());
5380 }
5381#if defined(RT_OS_WINDOWS)
5382 log = Utf8StrFmt("%s%cVBoxStartup.log",
5383 logFolder.c_str(), RTPATH_DELIMITER);
5384 RTFileDelete(log.c_str());
5385#endif
5386
5387 RTDirRemove(logFolder.c_str());
5388 }
5389
5390 /* delete the Snapshots folder, nothing important should be left
5391 * there (we don't check for errors because the user might have
5392 * some private files there that we don't want to delete) */
5393 Utf8Str strFullSnapshotFolder;
5394 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5395 Assert(!strFullSnapshotFolder.isEmpty());
5396 if (RTDirExists(strFullSnapshotFolder.c_str()))
5397 RTDirRemove(strFullSnapshotFolder.c_str());
5398
5399 // delete the directory that contains the settings file, but only
5400 // if it matches the VM name
5401 Utf8Str settingsDir;
5402 if (i_isInOwnDir(&settingsDir))
5403 RTDirRemove(settingsDir.c_str());
5404 }
5405
5406 alock.release();
5407
5408 mParent->i_saveModifiedRegistries();
5409 }
5410 catch (HRESULT aRC) { rc = aRC; }
5411
5412 return rc;
5413}
5414
5415HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5416{
5417 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5418
5419 ComObjPtr<Snapshot> pSnapshot;
5420 HRESULT rc;
5421
5422 if (aNameOrId.isEmpty())
5423 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5424 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5425 else
5426 {
5427 Guid uuid(aNameOrId);
5428 if (uuid.isValid())
5429 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5430 else
5431 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5432 }
5433 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5434
5435 return rc;
5436}
5437
5438HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5439{
5440 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5441
5442 HRESULT rc = i_checkStateDependency(MutableStateDep);
5443 if (FAILED(rc)) return rc;
5444
5445 ComObjPtr<SharedFolder> sharedFolder;
5446 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5447 if (SUCCEEDED(rc))
5448 return setError(VBOX_E_OBJECT_IN_USE,
5449 tr("Shared folder named '%s' already exists"),
5450 aName.c_str());
5451
5452 sharedFolder.createObject();
5453 rc = sharedFolder->init(i_getMachine(),
5454 aName,
5455 aHostPath,
5456 !!aWritable,
5457 !!aAutomount,
5458 true /* fFailOnError */);
5459 if (FAILED(rc)) return rc;
5460
5461 i_setModified(IsModified_SharedFolders);
5462 mHWData.backup();
5463 mHWData->mSharedFolders.push_back(sharedFolder);
5464
5465 /* inform the direct session if any */
5466 alock.release();
5467 i_onSharedFolderChange();
5468
5469 return S_OK;
5470}
5471
5472HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5473{
5474 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5475
5476 HRESULT rc = i_checkStateDependency(MutableStateDep);
5477 if (FAILED(rc)) return rc;
5478
5479 ComObjPtr<SharedFolder> sharedFolder;
5480 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5481 if (FAILED(rc)) return rc;
5482
5483 i_setModified(IsModified_SharedFolders);
5484 mHWData.backup();
5485 mHWData->mSharedFolders.remove(sharedFolder);
5486
5487 /* inform the direct session if any */
5488 alock.release();
5489 i_onSharedFolderChange();
5490
5491 return S_OK;
5492}
5493
5494HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5495{
5496 /* start with No */
5497 *aCanShow = FALSE;
5498
5499 ComPtr<IInternalSessionControl> directControl;
5500 {
5501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5502
5503 if (mData->mSession.mState != SessionState_Locked)
5504 return setError(VBOX_E_INVALID_VM_STATE,
5505 tr("Machine is not locked for session (session state: %s)"),
5506 Global::stringifySessionState(mData->mSession.mState));
5507
5508 directControl = mData->mSession.mDirectControl;
5509 }
5510
5511 /* ignore calls made after #OnSessionEnd() is called */
5512 if (!directControl)
5513 return S_OK;
5514
5515 LONG64 dummy;
5516 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5517}
5518
5519HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5520{
5521 ComPtr<IInternalSessionControl> directControl;
5522 {
5523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5524
5525 if (mData->mSession.mState != SessionState_Locked)
5526 return setError(E_FAIL,
5527 tr("Machine is not locked for session (session state: %s)"),
5528 Global::stringifySessionState(mData->mSession.mState));
5529
5530 directControl = mData->mSession.mDirectControl;
5531 }
5532
5533 /* ignore calls made after #OnSessionEnd() is called */
5534 if (!directControl)
5535 return S_OK;
5536
5537 BOOL dummy;
5538 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5539}
5540
5541#ifdef VBOX_WITH_GUEST_PROPS
5542/**
5543 * Look up a guest property in VBoxSVC's internal structures.
5544 */
5545HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5546 com::Utf8Str &aValue,
5547 LONG64 *aTimestamp,
5548 com::Utf8Str &aFlags) const
5549{
5550 using namespace guestProp;
5551
5552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5553 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5554
5555 if (it != mHWData->mGuestProperties.end())
5556 {
5557 char szFlags[MAX_FLAGS_LEN + 1];
5558 aValue = it->second.strValue;
5559 *aTimestamp = it->second.mTimestamp;
5560 writeFlags(it->second.mFlags, szFlags);
5561 aFlags = Utf8Str(szFlags);
5562 }
5563
5564 return S_OK;
5565}
5566
5567/**
5568 * Query the VM that a guest property belongs to for the property.
5569 * @returns E_ACCESSDENIED if the VM process is not available or not
5570 * currently handling queries and the lookup should then be done in
5571 * VBoxSVC.
5572 */
5573HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5574 com::Utf8Str &aValue,
5575 LONG64 *aTimestamp,
5576 com::Utf8Str &aFlags) const
5577{
5578 HRESULT rc = S_OK;
5579 BSTR bValue = NULL;
5580 BSTR bFlags = NULL;
5581
5582 ComPtr<IInternalSessionControl> directControl;
5583 directControl = mData->mSession.mDirectControl;
5584
5585 /* fail if we were called after #OnSessionEnd() is called. This is a
5586 * silly race condition. */
5587
5588 /** @todo This code is bothering API clients (like python script clients) with
5589 * the AccessGuestProperty call, creating unncessary IPC. Need to
5590 * have a way of figuring out which kind of direct session it is... */
5591 if (!directControl)
5592 rc = E_ACCESSDENIED;
5593 else
5594 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5595 0 /* accessMode */,
5596 &bValue, aTimestamp, &bFlags);
5597
5598 aValue = bValue;
5599 aFlags = bFlags;
5600
5601 return rc;
5602}
5603#endif // VBOX_WITH_GUEST_PROPS
5604
5605HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5606 com::Utf8Str &aValue,
5607 LONG64 *aTimestamp,
5608 com::Utf8Str &aFlags)
5609{
5610#ifndef VBOX_WITH_GUEST_PROPS
5611 ReturnComNotImplemented();
5612#else // VBOX_WITH_GUEST_PROPS
5613
5614 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5615
5616 if (rc == E_ACCESSDENIED)
5617 /* The VM is not running or the service is not (yet) accessible */
5618 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5619 return rc;
5620#endif // VBOX_WITH_GUEST_PROPS
5621}
5622
5623HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5624{
5625 LONG64 dummyTimestamp;
5626 com::Utf8Str dummyFlags;
5627 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5628 return rc;
5629
5630}
5631HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5632{
5633 com::Utf8Str dummyFlags;
5634 com::Utf8Str dummyValue;
5635 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5636 return rc;
5637}
5638
5639#ifdef VBOX_WITH_GUEST_PROPS
5640/**
5641 * Set a guest property in VBoxSVC's internal structures.
5642 */
5643HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5644 const com::Utf8Str &aFlags, bool fDelete)
5645{
5646 using namespace guestProp;
5647
5648 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5649 HRESULT rc = S_OK;
5650
5651 rc = i_checkStateDependency(MutableOrSavedStateDep);
5652 if (FAILED(rc)) return rc;
5653
5654 try
5655 {
5656 uint32_t fFlags = NILFLAG;
5657 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5658 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5659
5660 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5661 if (it == mHWData->mGuestProperties.end())
5662 {
5663 if (!fDelete)
5664 {
5665 i_setModified(IsModified_MachineData);
5666 mHWData.backupEx();
5667
5668 RTTIMESPEC time;
5669 HWData::GuestProperty prop;
5670 prop.strValue = Bstr(aValue).raw();
5671 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5672 prop.mFlags = fFlags;
5673 mHWData->mGuestProperties[aName] = prop;
5674 }
5675 }
5676 else
5677 {
5678 if (it->second.mFlags & (RDONLYHOST))
5679 {
5680 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5681 }
5682 else
5683 {
5684 i_setModified(IsModified_MachineData);
5685 mHWData.backupEx();
5686
5687 /* The backupEx() operation invalidates our iterator,
5688 * so get a new one. */
5689 it = mHWData->mGuestProperties.find(aName);
5690 Assert(it != mHWData->mGuestProperties.end());
5691
5692 if (!fDelete)
5693 {
5694 RTTIMESPEC time;
5695 it->second.strValue = aValue;
5696 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5697 it->second.mFlags = fFlags;
5698 }
5699 else
5700 mHWData->mGuestProperties.erase(it);
5701 }
5702 }
5703
5704 if ( SUCCEEDED(rc)
5705 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5706 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5707 RTSTR_MAX,
5708 aName.c_str(),
5709 RTSTR_MAX,
5710 NULL)
5711 )
5712 )
5713 {
5714 alock.release();
5715
5716 mParent->i_onGuestPropertyChange(mData->mUuid,
5717 Bstr(aName).raw(),
5718 Bstr(aValue).raw(),
5719 Bstr(aFlags).raw());
5720 }
5721 }
5722 catch (std::bad_alloc &)
5723 {
5724 rc = E_OUTOFMEMORY;
5725 }
5726
5727 return rc;
5728}
5729
5730/**
5731 * Set a property on the VM that that property belongs to.
5732 * @returns E_ACCESSDENIED if the VM process is not available or not
5733 * currently handling queries and the setting should then be done in
5734 * VBoxSVC.
5735 */
5736HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5737 const com::Utf8Str &aFlags, bool fDelete)
5738{
5739 HRESULT rc;
5740
5741 try
5742 {
5743 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5744
5745 BSTR dummy = NULL; /* will not be changed (setter) */
5746 LONG64 dummy64;
5747 if (!directControl)
5748 rc = E_ACCESSDENIED;
5749 else
5750 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5751 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5752 fDelete? 2: 1 /* accessMode */,
5753 &dummy, &dummy64, &dummy);
5754 }
5755 catch (std::bad_alloc &)
5756 {
5757 rc = E_OUTOFMEMORY;
5758 }
5759
5760 return rc;
5761}
5762#endif // VBOX_WITH_GUEST_PROPS
5763
5764HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5765 const com::Utf8Str &aFlags)
5766{
5767#ifndef VBOX_WITH_GUEST_PROPS
5768 ReturnComNotImplemented();
5769#else // VBOX_WITH_GUEST_PROPS
5770 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5771 if (rc == E_ACCESSDENIED)
5772 /* The VM is not running or the service is not (yet) accessible */
5773 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5774 return rc;
5775#endif // VBOX_WITH_GUEST_PROPS
5776}
5777
5778HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5779{
5780 return setGuestProperty(aProperty, aValue, "");
5781}
5782
5783HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5784{
5785#ifndef VBOX_WITH_GUEST_PROPS
5786 ReturnComNotImplemented();
5787#else // VBOX_WITH_GUEST_PROPS
5788 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5789 if (rc == E_ACCESSDENIED)
5790 /* The VM is not running or the service is not (yet) accessible */
5791 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5792 return rc;
5793#endif // VBOX_WITH_GUEST_PROPS
5794}
5795
5796#ifdef VBOX_WITH_GUEST_PROPS
5797/**
5798 * Enumerate the guest properties in VBoxSVC's internal structures.
5799 */
5800HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5801 std::vector<com::Utf8Str> &aNames,
5802 std::vector<com::Utf8Str> &aValues,
5803 std::vector<LONG64> &aTimestamps,
5804 std::vector<com::Utf8Str> &aFlags)
5805{
5806 using namespace guestProp;
5807
5808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5809 Utf8Str strPatterns(aPatterns);
5810
5811 HWData::GuestPropertyMap propMap;
5812
5813 /*
5814 * Look for matching patterns and build up a list.
5815 */
5816 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5817 while (it != mHWData->mGuestProperties.end())
5818 {
5819 if ( strPatterns.isEmpty()
5820 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5821 RTSTR_MAX,
5822 it->first.c_str(),
5823 RTSTR_MAX,
5824 NULL)
5825 )
5826 propMap.insert(*it);
5827 it++;
5828 }
5829
5830 alock.release();
5831
5832 /*
5833 * And build up the arrays for returning the property information.
5834 */
5835 size_t cEntries = propMap.size();
5836
5837 aNames.resize(cEntries);
5838 aValues.resize(cEntries);
5839 aTimestamps.resize(cEntries);
5840 aFlags.resize(cEntries);
5841
5842 char szFlags[MAX_FLAGS_LEN + 1];
5843 size_t i= 0;
5844 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5845 {
5846 aNames[i] = it->first;
5847 aValues[i] = it->second.strValue;
5848 aTimestamps[i] = it->second.mTimestamp;
5849 writeFlags(it->second.mFlags, szFlags);
5850 aFlags[i] = Utf8Str(szFlags);
5851 }
5852
5853 return S_OK;
5854}
5855
5856/**
5857 * Enumerate the properties managed by a VM.
5858 * @returns E_ACCESSDENIED if the VM process is not available or not
5859 * currently handling queries and the setting should then be done in
5860 * VBoxSVC.
5861 */
5862HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5863 std::vector<com::Utf8Str> &aNames,
5864 std::vector<com::Utf8Str> &aValues,
5865 std::vector<LONG64> &aTimestamps,
5866 std::vector<com::Utf8Str> &aFlags)
5867{
5868 HRESULT rc;
5869 ComPtr<IInternalSessionControl> directControl;
5870 directControl = mData->mSession.mDirectControl;
5871
5872
5873 com::SafeArray<BSTR> bNames;
5874 com::SafeArray<BSTR> bValues;
5875 com::SafeArray<LONG64> bTimestamps;
5876 com::SafeArray<BSTR> bFlags;
5877
5878 if (!directControl)
5879 rc = E_ACCESSDENIED;
5880 else
5881 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5882 ComSafeArrayAsOutParam(bNames),
5883 ComSafeArrayAsOutParam(bValues),
5884 ComSafeArrayAsOutParam(bTimestamps),
5885 ComSafeArrayAsOutParam(bFlags));
5886 size_t i;
5887 aNames.resize(bNames.size());
5888 for (i = 0; i < bNames.size(); ++i)
5889 aNames[i] = Utf8Str(bNames[i]);
5890 aValues.resize(bValues.size());
5891 for (i = 0; i < bValues.size(); ++i)
5892 aValues[i] = Utf8Str(bValues[i]);
5893 aTimestamps.resize(bTimestamps.size());
5894 for (i = 0; i < bTimestamps.size(); ++i)
5895 aTimestamps[i] = bTimestamps[i];
5896 aFlags.resize(bFlags.size());
5897 for (i = 0; i < bFlags.size(); ++i)
5898 aFlags[i] = Utf8Str(bFlags[i]);
5899
5900 return rc;
5901}
5902#endif // VBOX_WITH_GUEST_PROPS
5903HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5904 std::vector<com::Utf8Str> &aNames,
5905 std::vector<com::Utf8Str> &aValues,
5906 std::vector<LONG64> &aTimestamps,
5907 std::vector<com::Utf8Str> &aFlags)
5908{
5909#ifndef VBOX_WITH_GUEST_PROPS
5910 ReturnComNotImplemented();
5911#else // VBOX_WITH_GUEST_PROPS
5912
5913 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5914
5915 if (rc == E_ACCESSDENIED)
5916 /* The VM is not running or the service is not (yet) accessible */
5917 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5918 return rc;
5919#endif // VBOX_WITH_GUEST_PROPS
5920}
5921
5922HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5923 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5924{
5925 MediaData::AttachmentList atts;
5926
5927 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5928 if (FAILED(rc)) return rc;
5929
5930 size_t i = 0;
5931 aMediumAttachments.resize(atts.size());
5932 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5933 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5934
5935 return S_OK;
5936}
5937
5938HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5939 LONG aControllerPort,
5940 LONG aDevice,
5941 ComPtr<IMediumAttachment> &aAttachment)
5942{
5943 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5944 aName.c_str(), aControllerPort, aDevice));
5945
5946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5947
5948 aAttachment = NULL;
5949
5950 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5951 Bstr(aName).raw(),
5952 aControllerPort,
5953 aDevice);
5954 if (pAttach.isNull())
5955 return setError(VBOX_E_OBJECT_NOT_FOUND,
5956 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5957 aDevice, aControllerPort, aName.c_str());
5958
5959 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5960
5961 return S_OK;
5962}
5963
5964
5965HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5966 StorageBus_T aConnectionType,
5967 ComPtr<IStorageController> &aController)
5968{
5969 if ( (aConnectionType <= StorageBus_Null)
5970 || (aConnectionType > StorageBus_USB))
5971 return setError(E_INVALIDARG,
5972 tr("Invalid connection type: %d"),
5973 aConnectionType);
5974
5975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5976
5977 HRESULT rc = i_checkStateDependency(MutableStateDep);
5978 if (FAILED(rc)) return rc;
5979
5980 /* try to find one with the name first. */
5981 ComObjPtr<StorageController> ctrl;
5982
5983 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5984 if (SUCCEEDED(rc))
5985 return setError(VBOX_E_OBJECT_IN_USE,
5986 tr("Storage controller named '%s' already exists"),
5987 aName.c_str());
5988
5989 ctrl.createObject();
5990
5991 /* get a new instance number for the storage controller */
5992 ULONG ulInstance = 0;
5993 bool fBootable = true;
5994 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5995 it != mStorageControllers->end();
5996 ++it)
5997 {
5998 if ((*it)->i_getStorageBus() == aConnectionType)
5999 {
6000 ULONG ulCurInst = (*it)->i_getInstance();
6001
6002 if (ulCurInst >= ulInstance)
6003 ulInstance = ulCurInst + 1;
6004
6005 /* Only one controller of each type can be marked as bootable. */
6006 if ((*it)->i_getBootable())
6007 fBootable = false;
6008 }
6009 }
6010
6011 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6012 if (FAILED(rc)) return rc;
6013
6014 i_setModified(IsModified_Storage);
6015 mStorageControllers.backup();
6016 mStorageControllers->push_back(ctrl);
6017
6018 ctrl.queryInterfaceTo(aController.asOutParam());
6019
6020 /* inform the direct session if any */
6021 alock.release();
6022 i_onStorageControllerChange();
6023
6024 return S_OK;
6025}
6026
6027HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6028 ComPtr<IStorageController> &aStorageController)
6029{
6030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6031
6032 ComObjPtr<StorageController> ctrl;
6033
6034 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6035 if (SUCCEEDED(rc))
6036 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6037
6038 return rc;
6039}
6040
6041HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6042 ComPtr<IStorageController> &aStorageController)
6043{
6044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6045
6046 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6047 it != mStorageControllers->end();
6048 ++it)
6049 {
6050 if ((*it)->i_getInstance() == aInstance)
6051 {
6052 (*it).queryInterfaceTo(aStorageController.asOutParam());
6053 return S_OK;
6054 }
6055 }
6056
6057 return setError(VBOX_E_OBJECT_NOT_FOUND,
6058 tr("Could not find a storage controller with instance number '%lu'"),
6059 aInstance);
6060}
6061
6062HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6063{
6064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6065
6066 HRESULT rc = i_checkStateDependency(MutableStateDep);
6067 if (FAILED(rc)) return rc;
6068
6069 ComObjPtr<StorageController> ctrl;
6070
6071 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6072 if (SUCCEEDED(rc))
6073 {
6074 /* Ensure that only one controller of each type is marked as bootable. */
6075 if (aBootable == TRUE)
6076 {
6077 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6078 it != mStorageControllers->end();
6079 ++it)
6080 {
6081 ComObjPtr<StorageController> aCtrl = (*it);
6082
6083 if ( (aCtrl->i_getName() != aName)
6084 && aCtrl->i_getBootable() == TRUE
6085 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6086 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6087 {
6088 aCtrl->i_setBootable(FALSE);
6089 break;
6090 }
6091 }
6092 }
6093
6094 if (SUCCEEDED(rc))
6095 {
6096 ctrl->i_setBootable(aBootable);
6097 i_setModified(IsModified_Storage);
6098 }
6099 }
6100
6101 if (SUCCEEDED(rc))
6102 {
6103 /* inform the direct session if any */
6104 alock.release();
6105 i_onStorageControllerChange();
6106 }
6107
6108 return rc;
6109}
6110
6111HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6112{
6113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6114
6115 HRESULT rc = i_checkStateDependency(MutableStateDep);
6116 if (FAILED(rc)) return rc;
6117
6118 ComObjPtr<StorageController> ctrl;
6119 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6120 if (FAILED(rc)) return rc;
6121
6122 {
6123 /* find all attached devices to the appropriate storage controller and detach them all */
6124 // make a temporary list because detachDevice invalidates iterators into
6125 // mMediaData->mAttachments
6126 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6127
6128 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6129 it != llAttachments2.end();
6130 ++it)
6131 {
6132 MediumAttachment *pAttachTemp = *it;
6133
6134 AutoCaller localAutoCaller(pAttachTemp);
6135 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6136
6137 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6138
6139 if (pAttachTemp->i_getControllerName() == aName)
6140 {
6141 rc = i_detachDevice(pAttachTemp, alock, NULL);
6142 if (FAILED(rc)) return rc;
6143 }
6144 }
6145 }
6146
6147 /* We can remove it now. */
6148 i_setModified(IsModified_Storage);
6149 mStorageControllers.backup();
6150
6151 ctrl->i_unshare();
6152
6153 mStorageControllers->remove(ctrl);
6154
6155 /* inform the direct session if any */
6156 alock.release();
6157 i_onStorageControllerChange();
6158
6159 return S_OK;
6160}
6161
6162HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6163 ComPtr<IUSBController> &aController)
6164{
6165 if ( (aType <= USBControllerType_Null)
6166 || (aType >= USBControllerType_Last))
6167 return setError(E_INVALIDARG,
6168 tr("Invalid USB controller type: %d"),
6169 aType);
6170
6171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6172
6173 HRESULT rc = i_checkStateDependency(MutableStateDep);
6174 if (FAILED(rc)) return rc;
6175
6176 /* try to find one with the same type first. */
6177 ComObjPtr<USBController> ctrl;
6178
6179 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6180 if (SUCCEEDED(rc))
6181 return setError(VBOX_E_OBJECT_IN_USE,
6182 tr("USB controller named '%s' already exists"),
6183 aName.c_str());
6184
6185 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6186 ULONG maxInstances;
6187 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6188 if (FAILED(rc))
6189 return rc;
6190
6191 ULONG cInstances = i_getUSBControllerCountByType(aType);
6192 if (cInstances >= maxInstances)
6193 return setError(E_INVALIDARG,
6194 tr("Too many USB controllers of this type"));
6195
6196 ctrl.createObject();
6197
6198 rc = ctrl->init(this, aName, aType);
6199 if (FAILED(rc)) return rc;
6200
6201 i_setModified(IsModified_USB);
6202 mUSBControllers.backup();
6203 mUSBControllers->push_back(ctrl);
6204
6205 ctrl.queryInterfaceTo(aController.asOutParam());
6206
6207 /* inform the direct session if any */
6208 alock.release();
6209 i_onUSBControllerChange();
6210
6211 return S_OK;
6212}
6213
6214HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6215{
6216 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6217
6218 ComObjPtr<USBController> ctrl;
6219
6220 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6221 if (SUCCEEDED(rc))
6222 ctrl.queryInterfaceTo(aController.asOutParam());
6223
6224 return rc;
6225}
6226
6227HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6228 ULONG *aControllers)
6229{
6230 if ( (aType <= USBControllerType_Null)
6231 || (aType >= USBControllerType_Last))
6232 return setError(E_INVALIDARG,
6233 tr("Invalid USB controller type: %d"),
6234 aType);
6235
6236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6237
6238 ComObjPtr<USBController> ctrl;
6239
6240 *aControllers = i_getUSBControllerCountByType(aType);
6241
6242 return S_OK;
6243}
6244
6245HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6246{
6247
6248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6249
6250 HRESULT rc = i_checkStateDependency(MutableStateDep);
6251 if (FAILED(rc)) return rc;
6252
6253 ComObjPtr<USBController> ctrl;
6254 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6255 if (FAILED(rc)) return rc;
6256
6257 i_setModified(IsModified_USB);
6258 mUSBControllers.backup();
6259
6260 ctrl->i_unshare();
6261
6262 mUSBControllers->remove(ctrl);
6263
6264 /* inform the direct session if any */
6265 alock.release();
6266 i_onUSBControllerChange();
6267
6268 return S_OK;
6269}
6270
6271HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6272 ULONG *aOriginX,
6273 ULONG *aOriginY,
6274 ULONG *aWidth,
6275 ULONG *aHeight,
6276 BOOL *aEnabled)
6277{
6278 uint32_t u32OriginX= 0;
6279 uint32_t u32OriginY= 0;
6280 uint32_t u32Width = 0;
6281 uint32_t u32Height = 0;
6282 uint16_t u16Flags = 0;
6283
6284 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6285 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6286 if (RT_FAILURE(vrc))
6287 {
6288#ifdef RT_OS_WINDOWS
6289 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6290 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6291 * So just assign fEnable to TRUE again.
6292 * The right fix would be to change GUI API wrappers to make sure that parameters
6293 * are changed only if API succeeds.
6294 */
6295 *aEnabled = TRUE;
6296#endif
6297 return setError(VBOX_E_IPRT_ERROR,
6298 tr("Saved guest size is not available (%Rrc)"),
6299 vrc);
6300 }
6301
6302 *aOriginX = u32OriginX;
6303 *aOriginY = u32OriginY;
6304 *aWidth = u32Width;
6305 *aHeight = u32Height;
6306 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6307
6308 return S_OK;
6309}
6310
6311HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6312{
6313 if (aScreenId != 0)
6314 return E_NOTIMPL;
6315
6316 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6317
6318 uint8_t *pu8Data = NULL;
6319 uint32_t cbData = 0;
6320 uint32_t u32Width = 0;
6321 uint32_t u32Height = 0;
6322
6323 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6324
6325 if (RT_FAILURE(vrc))
6326 return setError(VBOX_E_IPRT_ERROR,
6327 tr("Saved screenshot data is not available (%Rrc)"),
6328 vrc);
6329
6330 *aSize = cbData;
6331 *aWidth = u32Width;
6332 *aHeight = u32Height;
6333
6334 freeSavedDisplayScreenshot(pu8Data);
6335
6336 return S_OK;
6337}
6338
6339HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6340 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6341{
6342 if (aScreenId != 0)
6343 return E_NOTIMPL;
6344
6345 if ( aBitmapFormat != BitmapFormat_BGR0
6346 && aBitmapFormat != BitmapFormat_BGRA
6347 && aBitmapFormat != BitmapFormat_RGBA
6348 && aBitmapFormat != BitmapFormat_PNG)
6349 return setError(E_NOTIMPL,
6350 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6351
6352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6353
6354 uint8_t *pu8Data = NULL;
6355 uint32_t cbData = 0;
6356 uint32_t u32Width = 0;
6357 uint32_t u32Height = 0;
6358
6359 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6360
6361 if (RT_FAILURE(vrc))
6362 return setError(VBOX_E_IPRT_ERROR,
6363 tr("Saved thumbnail data is not available (%Rrc)"),
6364 vrc);
6365
6366 HRESULT hr = S_OK;
6367
6368 *aWidth = u32Width;
6369 *aHeight = u32Height;
6370
6371 if (cbData > 0)
6372 {
6373 /* Convert pixels to the format expected by the API caller. */
6374 if (aBitmapFormat == BitmapFormat_BGR0)
6375 {
6376 /* [0] B, [1] G, [2] R, [3] 0. */
6377 aData.resize(cbData);
6378 memcpy(&aData.front(), pu8Data, cbData);
6379 }
6380 else if (aBitmapFormat == BitmapFormat_BGRA)
6381 {
6382 /* [0] B, [1] G, [2] R, [3] A. */
6383 aData.resize(cbData);
6384 for (uint32_t i = 0; i < cbData; i += 4)
6385 {
6386 aData[i] = pu8Data[i];
6387 aData[i + 1] = pu8Data[i + 1];
6388 aData[i + 2] = pu8Data[i + 2];
6389 aData[i + 3] = 0xff;
6390 }
6391 }
6392 else if (aBitmapFormat == BitmapFormat_RGBA)
6393 {
6394 /* [0] R, [1] G, [2] B, [3] A. */
6395 aData.resize(cbData);
6396 for (uint32_t i = 0; i < cbData; i += 4)
6397 {
6398 aData[i] = pu8Data[i + 2];
6399 aData[i + 1] = pu8Data[i + 1];
6400 aData[i + 2] = pu8Data[i];
6401 aData[i + 3] = 0xff;
6402 }
6403 }
6404 else if (aBitmapFormat == BitmapFormat_PNG)
6405 {
6406 uint8_t *pu8PNG = NULL;
6407 uint32_t cbPNG = 0;
6408 uint32_t cxPNG = 0;
6409 uint32_t cyPNG = 0;
6410
6411 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6412
6413 if (RT_SUCCESS(vrc))
6414 {
6415 aData.resize(cbPNG);
6416 if (cbPNG)
6417 memcpy(&aData.front(), pu8PNG, cbPNG);
6418 }
6419 else
6420 hr = setError(VBOX_E_IPRT_ERROR,
6421 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6422 vrc);
6423
6424 RTMemFree(pu8PNG);
6425 }
6426 }
6427
6428 freeSavedDisplayScreenshot(pu8Data);
6429
6430 return hr;
6431}
6432
6433HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6434{
6435 if (aScreenId != 0)
6436 return E_NOTIMPL;
6437
6438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6439
6440 uint8_t *pu8Data = NULL;
6441 uint32_t cbData = 0;
6442 uint32_t u32Width = 0;
6443 uint32_t u32Height = 0;
6444
6445 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6446
6447 if (RT_FAILURE(vrc))
6448 return setError(VBOX_E_IPRT_ERROR,
6449 tr("Saved screenshot data is not available (%Rrc)"),
6450 vrc);
6451
6452 *aSize = cbData;
6453 *aWidth = u32Width;
6454 *aHeight = u32Height;
6455
6456 freeSavedDisplayScreenshot(pu8Data);
6457
6458 return S_OK;
6459}
6460
6461HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6462{
6463 if (aScreenId != 0)
6464 return E_NOTIMPL;
6465
6466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6467
6468 uint8_t *pu8Data = NULL;
6469 uint32_t cbData = 0;
6470 uint32_t u32Width = 0;
6471 uint32_t u32Height = 0;
6472
6473 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6474
6475 if (RT_FAILURE(vrc))
6476 return setError(VBOX_E_IPRT_ERROR,
6477 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6478 vrc);
6479
6480 *aWidth = u32Width;
6481 *aHeight = u32Height;
6482
6483 aData.resize(cbData);
6484 if (cbData)
6485 memcpy(&aData.front(), pu8Data, cbData);
6486
6487 freeSavedDisplayScreenshot(pu8Data);
6488
6489 return S_OK;
6490}
6491
6492HRESULT Machine::hotPlugCPU(ULONG aCpu)
6493{
6494 HRESULT rc = S_OK;
6495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6496
6497 if (!mHWData->mCPUHotPlugEnabled)
6498 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6499
6500 if (aCpu >= mHWData->mCPUCount)
6501 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6502
6503 if (mHWData->mCPUAttached[aCpu])
6504 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6505
6506 alock.release();
6507 rc = i_onCPUChange(aCpu, false);
6508 alock.acquire();
6509 if (FAILED(rc)) return rc;
6510
6511 i_setModified(IsModified_MachineData);
6512 mHWData.backup();
6513 mHWData->mCPUAttached[aCpu] = true;
6514
6515 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6516 if (Global::IsOnline(mData->mMachineState))
6517 i_saveSettings(NULL);
6518
6519 return S_OK;
6520}
6521
6522HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6523{
6524 HRESULT rc = S_OK;
6525
6526 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6527
6528 if (!mHWData->mCPUHotPlugEnabled)
6529 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6530
6531 if (aCpu >= SchemaDefs::MaxCPUCount)
6532 return setError(E_INVALIDARG,
6533 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6534 SchemaDefs::MaxCPUCount);
6535
6536 if (!mHWData->mCPUAttached[aCpu])
6537 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6538
6539 /* CPU 0 can't be detached */
6540 if (aCpu == 0)
6541 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6542
6543 alock.release();
6544 rc = i_onCPUChange(aCpu, true);
6545 alock.acquire();
6546 if (FAILED(rc)) return rc;
6547
6548 i_setModified(IsModified_MachineData);
6549 mHWData.backup();
6550 mHWData->mCPUAttached[aCpu] = false;
6551
6552 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6553 if (Global::IsOnline(mData->mMachineState))
6554 i_saveSettings(NULL);
6555
6556 return S_OK;
6557}
6558
6559HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6560{
6561 *aAttached = false;
6562
6563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6564
6565 /* If hotplug is enabled the CPU is always enabled. */
6566 if (!mHWData->mCPUHotPlugEnabled)
6567 {
6568 if (aCpu < mHWData->mCPUCount)
6569 *aAttached = true;
6570 }
6571 else
6572 {
6573 if (aCpu < SchemaDefs::MaxCPUCount)
6574 *aAttached = mHWData->mCPUAttached[aCpu];
6575 }
6576
6577 return S_OK;
6578}
6579
6580HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6581{
6582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6583
6584 Utf8Str log = i_queryLogFilename(aIdx);
6585 if (!RTFileExists(log.c_str()))
6586 log.setNull();
6587 aFilename = log;
6588
6589 return S_OK;
6590}
6591
6592HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6593{
6594 if (aSize < 0)
6595 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6596
6597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6598
6599 HRESULT rc = S_OK;
6600 Utf8Str log = i_queryLogFilename(aIdx);
6601
6602 /* do not unnecessarily hold the lock while doing something which does
6603 * not need the lock and potentially takes a long time. */
6604 alock.release();
6605
6606 /* Limit the chunk size to 32K for now, as that gives better performance
6607 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6608 * One byte expands to approx. 25 bytes of breathtaking XML. */
6609 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6610 aData.resize(cbData);
6611
6612 RTFILE LogFile;
6613 int vrc = RTFileOpen(&LogFile, log.c_str(),
6614 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6615 if (RT_SUCCESS(vrc))
6616 {
6617 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6618 if (RT_SUCCESS(vrc))
6619 aData.resize(cbData);
6620 else
6621 rc = setError(VBOX_E_IPRT_ERROR,
6622 tr("Could not read log file '%s' (%Rrc)"),
6623 log.c_str(), vrc);
6624 RTFileClose(LogFile);
6625 }
6626 else
6627 rc = setError(VBOX_E_IPRT_ERROR,
6628 tr("Could not open log file '%s' (%Rrc)"),
6629 log.c_str(), vrc);
6630
6631 if (FAILED(rc))
6632 aData.resize(0);
6633
6634 return rc;
6635}
6636
6637
6638/**
6639 * Currently this method doesn't attach device to the running VM,
6640 * just makes sure it's plugged on next VM start.
6641 */
6642HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6643{
6644 // lock scope
6645 {
6646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6647
6648 HRESULT rc = i_checkStateDependency(MutableStateDep);
6649 if (FAILED(rc)) return rc;
6650
6651 ChipsetType_T aChipset = ChipsetType_PIIX3;
6652 COMGETTER(ChipsetType)(&aChipset);
6653
6654 if (aChipset != ChipsetType_ICH9)
6655 {
6656 return setError(E_INVALIDARG,
6657 tr("Host PCI attachment only supported with ICH9 chipset"));
6658 }
6659
6660 // check if device with this host PCI address already attached
6661 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6662 it != mHWData->mPCIDeviceAssignments.end();
6663 ++it)
6664 {
6665 LONG iHostAddress = -1;
6666 ComPtr<PCIDeviceAttachment> pAttach;
6667 pAttach = *it;
6668 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6669 if (iHostAddress == aHostAddress)
6670 return setError(E_INVALIDARG,
6671 tr("Device with host PCI address already attached to this VM"));
6672 }
6673
6674 ComObjPtr<PCIDeviceAttachment> pda;
6675 char name[32];
6676
6677 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6678 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6679 Bstr bname(name);
6680 pda.createObject();
6681 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6682 i_setModified(IsModified_MachineData);
6683 mHWData.backup();
6684 mHWData->mPCIDeviceAssignments.push_back(pda);
6685 }
6686
6687 return S_OK;
6688}
6689
6690/**
6691 * Currently this method doesn't detach device from the running VM,
6692 * just makes sure it's not plugged on next VM start.
6693 */
6694HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6695{
6696 ComObjPtr<PCIDeviceAttachment> pAttach;
6697 bool fRemoved = false;
6698 HRESULT rc;
6699
6700 // lock scope
6701 {
6702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6703
6704 rc = i_checkStateDependency(MutableStateDep);
6705 if (FAILED(rc)) return rc;
6706
6707 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6708 it != mHWData->mPCIDeviceAssignments.end();
6709 ++it)
6710 {
6711 LONG iHostAddress = -1;
6712 pAttach = *it;
6713 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6714 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6715 {
6716 i_setModified(IsModified_MachineData);
6717 mHWData.backup();
6718 mHWData->mPCIDeviceAssignments.remove(pAttach);
6719 fRemoved = true;
6720 break;
6721 }
6722 }
6723 }
6724
6725
6726 /* Fire event outside of the lock */
6727 if (fRemoved)
6728 {
6729 Assert(!pAttach.isNull());
6730 ComPtr<IEventSource> es;
6731 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6732 Assert(SUCCEEDED(rc));
6733 Bstr mid;
6734 rc = this->COMGETTER(Id)(mid.asOutParam());
6735 Assert(SUCCEEDED(rc));
6736 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6737 }
6738
6739 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6740 tr("No host PCI device %08x attached"),
6741 aHostAddress
6742 );
6743}
6744
6745HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6746{
6747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6748
6749 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6750
6751 size_t i = 0;
6752 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6753 it != mHWData->mPCIDeviceAssignments.end();
6754 ++i, ++it)
6755 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6756
6757 return S_OK;
6758}
6759
6760HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6761{
6762 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6763
6764 return S_OK;
6765}
6766
6767HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6768{
6769 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6770
6771 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6772
6773 return S_OK;
6774}
6775
6776HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6777{
6778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6779 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6780 if (SUCCEEDED(hrc))
6781 {
6782 hrc = mHWData.backupEx();
6783 if (SUCCEEDED(hrc))
6784 {
6785 i_setModified(IsModified_MachineData);
6786 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6787 }
6788 }
6789 return hrc;
6790}
6791
6792HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6793{
6794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6795 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6796 return S_OK;
6797}
6798
6799HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6800{
6801 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6802 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6803 if (SUCCEEDED(hrc))
6804 {
6805 hrc = mHWData.backupEx();
6806 if (SUCCEEDED(hrc))
6807 {
6808 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6809 if (SUCCEEDED(hrc))
6810 i_setModified(IsModified_MachineData);
6811 }
6812 }
6813 return hrc;
6814}
6815
6816HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6817{
6818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6819
6820 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6821
6822 return S_OK;
6823}
6824
6825HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6826{
6827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6828 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6829 if (SUCCEEDED(hrc))
6830 {
6831 hrc = mHWData.backupEx();
6832 if (SUCCEEDED(hrc))
6833 {
6834 i_setModified(IsModified_MachineData);
6835 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6836 }
6837 }
6838 return hrc;
6839}
6840
6841HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6842{
6843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6844
6845 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6846
6847 return S_OK;
6848}
6849
6850HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6851{
6852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6853
6854 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6855 if ( SUCCEEDED(hrc)
6856 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6857 {
6858 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6859 int vrc;
6860
6861 if (aAutostartEnabled)
6862 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6863 else
6864 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6865
6866 if (RT_SUCCESS(vrc))
6867 {
6868 hrc = mHWData.backupEx();
6869 if (SUCCEEDED(hrc))
6870 {
6871 i_setModified(IsModified_MachineData);
6872 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6873 }
6874 }
6875 else if (vrc == VERR_NOT_SUPPORTED)
6876 hrc = setError(VBOX_E_NOT_SUPPORTED,
6877 tr("The VM autostart feature is not supported on this platform"));
6878 else if (vrc == VERR_PATH_NOT_FOUND)
6879 hrc = setError(E_FAIL,
6880 tr("The path to the autostart database is not set"));
6881 else
6882 hrc = setError(E_UNEXPECTED,
6883 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6884 aAutostartEnabled ? "Adding" : "Removing",
6885 mUserData->s.strName.c_str(), vrc);
6886 }
6887 return hrc;
6888}
6889
6890HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6891{
6892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6893
6894 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6895
6896 return S_OK;
6897}
6898
6899HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6900{
6901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6902 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6903 if (SUCCEEDED(hrc))
6904 {
6905 hrc = mHWData.backupEx();
6906 if (SUCCEEDED(hrc))
6907 {
6908 i_setModified(IsModified_MachineData);
6909 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6910 }
6911 }
6912 return hrc;
6913}
6914
6915HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6916{
6917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6918
6919 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6920
6921 return S_OK;
6922}
6923
6924HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6925{
6926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6927 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6928 if ( SUCCEEDED(hrc)
6929 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6930 {
6931 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6932 int vrc;
6933
6934 if (aAutostopType != AutostopType_Disabled)
6935 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6936 else
6937 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6938
6939 if (RT_SUCCESS(vrc))
6940 {
6941 hrc = mHWData.backupEx();
6942 if (SUCCEEDED(hrc))
6943 {
6944 i_setModified(IsModified_MachineData);
6945 mHWData->mAutostart.enmAutostopType = aAutostopType;
6946 }
6947 }
6948 else if (vrc == VERR_NOT_SUPPORTED)
6949 hrc = setError(VBOX_E_NOT_SUPPORTED,
6950 tr("The VM autostop feature is not supported on this platform"));
6951 else if (vrc == VERR_PATH_NOT_FOUND)
6952 hrc = setError(E_FAIL,
6953 tr("The path to the autostart database is not set"));
6954 else
6955 hrc = setError(E_UNEXPECTED,
6956 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6957 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6958 mUserData->s.strName.c_str(), vrc);
6959 }
6960 return hrc;
6961}
6962
6963HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6964{
6965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6966
6967 aDefaultFrontend = mHWData->mDefaultFrontend;
6968
6969 return S_OK;
6970}
6971
6972HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6973{
6974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6975 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6976 if (SUCCEEDED(hrc))
6977 {
6978 hrc = mHWData.backupEx();
6979 if (SUCCEEDED(hrc))
6980 {
6981 i_setModified(IsModified_MachineData);
6982 mHWData->mDefaultFrontend = aDefaultFrontend;
6983 }
6984 }
6985 return hrc;
6986}
6987
6988HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6989{
6990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6991 size_t cbIcon = mUserData->mIcon.size();
6992 aIcon.resize(cbIcon);
6993 if (cbIcon)
6994 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
6995 return S_OK;
6996}
6997
6998HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6999{
7000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7001 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7002 if (SUCCEEDED(hrc))
7003 {
7004 i_setModified(IsModified_MachineData);
7005 mUserData.backup();
7006 size_t cbIcon = aIcon.size();
7007 mUserData->mIcon.resize(cbIcon);
7008 if (cbIcon)
7009 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7010 }
7011 return hrc;
7012}
7013
7014HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7015{
7016#ifdef VBOX_WITH_USB
7017 *aUSBProxyAvailable = true;
7018#else
7019 *aUSBProxyAvailable = false;
7020#endif
7021 return S_OK;
7022}
7023
7024HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7025 ComPtr<IProgress> &aProgress)
7026{
7027 ComObjPtr<Progress> pP;
7028 Progress *ppP = pP;
7029 IProgress *iP = static_cast<IProgress *>(ppP);
7030 IProgress **pProgress = &iP;
7031
7032 IMachine *pTarget = aTarget;
7033
7034 /* Convert the options. */
7035 RTCList<CloneOptions_T> optList;
7036 if (aOptions.size())
7037 for (size_t i = 0; i < aOptions.size(); ++i)
7038 optList.append(aOptions[i]);
7039
7040 if (optList.contains(CloneOptions_Link))
7041 {
7042 if (!i_isSnapshotMachine())
7043 return setError(E_INVALIDARG,
7044 tr("Linked clone can only be created from a snapshot"));
7045 if (aMode != CloneMode_MachineState)
7046 return setError(E_INVALIDARG,
7047 tr("Linked clone can only be created for a single machine state"));
7048 }
7049 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7050
7051 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7052
7053 HRESULT rc = pWorker->start(pProgress);
7054
7055 pP = static_cast<Progress *>(*pProgress);
7056 pP.queryInterfaceTo(aProgress.asOutParam());
7057
7058 return rc;
7059
7060}
7061
7062// public methods for internal purposes
7063/////////////////////////////////////////////////////////////////////////////
7064
7065/**
7066 * Adds the given IsModified_* flag to the dirty flags of the machine.
7067 * This must be called either during i_loadSettings or under the machine write lock.
7068 * @param fl
7069 */
7070void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7071{
7072 mData->flModifications |= fl;
7073 if (fAllowStateModification && i_isStateModificationAllowed())
7074 mData->mCurrentStateModified = true;
7075}
7076
7077/**
7078 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7079 * care of the write locking.
7080 *
7081 * @param fModifications The flag to add.
7082 */
7083void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7084{
7085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7086 i_setModified(fModification, fAllowStateModification);
7087}
7088
7089/**
7090 * Saves the registry entry of this machine to the given configuration node.
7091 *
7092 * @param aEntryNode Node to save the registry entry to.
7093 *
7094 * @note locks this object for reading.
7095 */
7096HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7097{
7098 AutoLimitedCaller autoCaller(this);
7099 AssertComRCReturnRC(autoCaller.rc());
7100
7101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7102
7103 data.uuid = mData->mUuid;
7104 data.strSettingsFile = mData->m_strConfigFile;
7105
7106 return S_OK;
7107}
7108
7109/**
7110 * Calculates the absolute path of the given path taking the directory of the
7111 * machine settings file as the current directory.
7112 *
7113 * @param aPath Path to calculate the absolute path for.
7114 * @param aResult Where to put the result (used only on success, can be the
7115 * same Utf8Str instance as passed in @a aPath).
7116 * @return IPRT result.
7117 *
7118 * @note Locks this object for reading.
7119 */
7120int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7121{
7122 AutoCaller autoCaller(this);
7123 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7124
7125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7126
7127 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7128
7129 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7130
7131 strSettingsDir.stripFilename();
7132 char folder[RTPATH_MAX];
7133 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7134 if (RT_SUCCESS(vrc))
7135 aResult = folder;
7136
7137 return vrc;
7138}
7139
7140/**
7141 * Copies strSource to strTarget, making it relative to the machine folder
7142 * if it is a subdirectory thereof, or simply copying it otherwise.
7143 *
7144 * @param strSource Path to evaluate and copy.
7145 * @param strTarget Buffer to receive target path.
7146 *
7147 * @note Locks this object for reading.
7148 */
7149void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7150 Utf8Str &strTarget)
7151{
7152 AutoCaller autoCaller(this);
7153 AssertComRCReturn(autoCaller.rc(), (void)0);
7154
7155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7156
7157 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7158 // use strTarget as a temporary buffer to hold the machine settings dir
7159 strTarget = mData->m_strConfigFileFull;
7160 strTarget.stripFilename();
7161 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7162 {
7163 // is relative: then append what's left
7164 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7165 // for empty paths (only possible for subdirs) use "." to avoid
7166 // triggering default settings for not present config attributes.
7167 if (strTarget.isEmpty())
7168 strTarget = ".";
7169 }
7170 else
7171 // is not relative: then overwrite
7172 strTarget = strSource;
7173}
7174
7175/**
7176 * Returns the full path to the machine's log folder in the
7177 * \a aLogFolder argument.
7178 */
7179void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7180{
7181 AutoCaller autoCaller(this);
7182 AssertComRCReturnVoid(autoCaller.rc());
7183
7184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7185
7186 char szTmp[RTPATH_MAX];
7187 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7188 if (RT_SUCCESS(vrc))
7189 {
7190 if (szTmp[0] && !mUserData.isNull())
7191 {
7192 char szTmp2[RTPATH_MAX];
7193 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7194 if (RT_SUCCESS(vrc))
7195 aLogFolder = BstrFmt("%s%c%s",
7196 szTmp2,
7197 RTPATH_DELIMITER,
7198 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7199 }
7200 else
7201 vrc = VERR_PATH_IS_RELATIVE;
7202 }
7203
7204 if (RT_FAILURE(vrc))
7205 {
7206 // fallback if VBOX_USER_LOGHOME is not set or invalid
7207 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7208 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7209 aLogFolder.append(RTPATH_DELIMITER);
7210 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7211 }
7212}
7213
7214/**
7215 * Returns the full path to the machine's log file for an given index.
7216 */
7217Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7218 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7219{
7220 Utf8Str logFolder;
7221 getLogFolder(logFolder);
7222 Assert(logFolder.length());
7223 Utf8Str log;
7224 if (idx == 0)
7225 log = Utf8StrFmt("%s%cVBox.log",
7226 logFolder.c_str(), RTPATH_DELIMITER);
7227 else
7228 log = Utf8StrFmt("%s%cVBox.log.%d",
7229 logFolder.c_str(), RTPATH_DELIMITER, idx);
7230 return log;
7231}
7232
7233/**
7234 * Returns the full path to the machine's (hardened) startup log file.
7235 */
7236Utf8Str Machine::i_getStartupLogFilename(void)
7237{
7238 Utf8Str strFilename;
7239 getLogFolder(strFilename);
7240 Assert(strFilename.length());
7241 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7242 return strFilename;
7243}
7244
7245
7246/**
7247 * Composes a unique saved state filename based on the current system time. The filename is
7248 * granular to the second so this will work so long as no more than one snapshot is taken on
7249 * a machine per second.
7250 *
7251 * Before version 4.1, we used this formula for saved state files:
7252 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7253 * which no longer works because saved state files can now be shared between the saved state of the
7254 * "saved" machine and an online snapshot, and the following would cause problems:
7255 * 1) save machine
7256 * 2) create online snapshot from that machine state --> reusing saved state file
7257 * 3) save machine again --> filename would be reused, breaking the online snapshot
7258 *
7259 * So instead we now use a timestamp.
7260 *
7261 * @param str
7262 */
7263
7264void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7265{
7266 AutoCaller autoCaller(this);
7267 AssertComRCReturnVoid(autoCaller.rc());
7268
7269 {
7270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7271 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7272 }
7273
7274 RTTIMESPEC ts;
7275 RTTimeNow(&ts);
7276 RTTIME time;
7277 RTTimeExplode(&time, &ts);
7278
7279 strStateFilePath += RTPATH_DELIMITER;
7280 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7281 time.i32Year, time.u8Month, time.u8MonthDay,
7282 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7283}
7284
7285/**
7286 * Returns the full path to the default video capture file.
7287 */
7288void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7289{
7290 AutoCaller autoCaller(this);
7291 AssertComRCReturnVoid(autoCaller.rc());
7292
7293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7294
7295 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7296 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7297 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7298}
7299
7300/**
7301 * Returns whether at least one USB controller is present for the VM.
7302 */
7303bool Machine::i_isUSBControllerPresent()
7304{
7305 AutoCaller autoCaller(this);
7306 AssertComRCReturn(autoCaller.rc(), false);
7307
7308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7309
7310 return (mUSBControllers->size() > 0);
7311}
7312
7313/**
7314 * @note Locks this object for writing, calls the client process
7315 * (inside the lock).
7316 */
7317HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7318 const Utf8Str &strFrontend,
7319 const Utf8Str &strEnvironment,
7320 ProgressProxy *aProgress)
7321{
7322 LogFlowThisFuncEnter();
7323
7324 AssertReturn(aControl, E_FAIL);
7325 AssertReturn(aProgress, E_FAIL);
7326 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7327
7328 AutoCaller autoCaller(this);
7329 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7330
7331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7332
7333 if (!mData->mRegistered)
7334 return setError(E_UNEXPECTED,
7335 tr("The machine '%s' is not registered"),
7336 mUserData->s.strName.c_str());
7337
7338 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7339
7340 if ( mData->mSession.mState == SessionState_Locked
7341 || mData->mSession.mState == SessionState_Spawning
7342 || mData->mSession.mState == SessionState_Unlocking)
7343 return setError(VBOX_E_INVALID_OBJECT_STATE,
7344 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7345 mUserData->s.strName.c_str());
7346
7347 /* may not be busy */
7348 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7349
7350 /* get the path to the executable */
7351 char szPath[RTPATH_MAX];
7352 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7353 size_t cchBufLeft = strlen(szPath);
7354 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7355 szPath[cchBufLeft] = 0;
7356 char *pszNamePart = szPath + cchBufLeft;
7357 cchBufLeft = sizeof(szPath) - cchBufLeft;
7358
7359 int vrc = VINF_SUCCESS;
7360 RTPROCESS pid = NIL_RTPROCESS;
7361
7362 RTENV env = RTENV_DEFAULT;
7363
7364 if (!strEnvironment.isEmpty())
7365 {
7366 char *newEnvStr = NULL;
7367
7368 do
7369 {
7370 /* clone the current environment */
7371 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7372 AssertRCBreakStmt(vrc2, vrc = vrc2);
7373
7374 newEnvStr = RTStrDup(strEnvironment.c_str());
7375 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7376
7377 /* put new variables to the environment
7378 * (ignore empty variable names here since RTEnv API
7379 * intentionally doesn't do that) */
7380 char *var = newEnvStr;
7381 for (char *p = newEnvStr; *p; ++p)
7382 {
7383 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7384 {
7385 *p = '\0';
7386 if (*var)
7387 {
7388 char *val = strchr(var, '=');
7389 if (val)
7390 {
7391 *val++ = '\0';
7392 vrc2 = RTEnvSetEx(env, var, val);
7393 }
7394 else
7395 vrc2 = RTEnvUnsetEx(env, var);
7396 if (RT_FAILURE(vrc2))
7397 break;
7398 }
7399 var = p + 1;
7400 }
7401 }
7402 if (RT_SUCCESS(vrc2) && *var)
7403 vrc2 = RTEnvPutEx(env, var);
7404
7405 AssertRCBreakStmt(vrc2, vrc = vrc2);
7406 }
7407 while (0);
7408
7409 if (newEnvStr != NULL)
7410 RTStrFree(newEnvStr);
7411 }
7412
7413 /* Hardened startup logging */
7414#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7415 Utf8Str strSupStartLogArg("--sup-startup-log=");
7416 {
7417 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7418 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7419 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7420 {
7421 Utf8Str strStartupLogDir = strStartupLogFile;
7422 strStartupLogDir.stripFilename();
7423 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7424 file without stripping the file. */
7425 }
7426 strSupStartLogArg.append(strStartupLogFile);
7427 }
7428 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7429#else
7430 const char *pszSupStartupLogArg = NULL;
7431#endif
7432
7433
7434#ifdef VBOX_WITH_QTGUI
7435 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7436 {
7437# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7438 /* Modify the base path so that we don't need to use ".." below. */
7439 RTPathStripTrailingSlash(szPath);
7440 RTPathStripFilename(szPath);
7441 cchBufLeft = strlen(szPath);
7442 pszNamePart = szPath + cchBufLeft;
7443 cchBufLeft = sizeof(szPath) - cchBufLeft;
7444
7445# define OSX_APP_NAME "VirtualBoxVM"
7446# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7447
7448 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7449 if ( strAppOverride.contains(".")
7450 || strAppOverride.contains("/")
7451 || strAppOverride.contains("\\")
7452 || strAppOverride.contains(":"))
7453 strAppOverride.setNull();
7454 Utf8Str strAppPath;
7455 if (!strAppOverride.isEmpty())
7456 {
7457 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7458 Utf8Str strFullPath(szPath);
7459 strFullPath.append(strAppPath);
7460 /* there is a race, but people using this deserve the failure */
7461 if (!RTFileExists(strFullPath.c_str()))
7462 strAppOverride.setNull();
7463 }
7464 if (strAppOverride.isEmpty())
7465 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7466 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7467 strcpy(pszNamePart, strAppPath.c_str());
7468# else
7469 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7470 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7471 strcpy(pszNamePart, s_szVirtualBox_exe);
7472# endif
7473
7474 Utf8Str idStr = mData->mUuid.toString();
7475 const char *apszArgs[] =
7476 {
7477 szPath,
7478 "--comment", mUserData->s.strName.c_str(),
7479 "--startvm", idStr.c_str(),
7480 "--no-startvm-errormsgbox",
7481 pszSupStartupLogArg,
7482 NULL
7483 };
7484 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7485 }
7486#else /* !VBOX_WITH_QTGUI */
7487 if (0)
7488 ;
7489#endif /* VBOX_WITH_QTGUI */
7490
7491 else
7492
7493#ifdef VBOX_WITH_VBOXSDL
7494 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7495 {
7496 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7497 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7498 strcpy(pszNamePart, s_szVBoxSDL_exe);
7499
7500 Utf8Str idStr = mData->mUuid.toString();
7501 const char *apszArgs[] =
7502 {
7503 szPath,
7504 "--comment", mUserData->s.strName.c_str(),
7505 "--startvm", idStr.c_str(),
7506 pszSupStartupLogArg,
7507 NULL
7508 };
7509 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7510 }
7511#else /* !VBOX_WITH_VBOXSDL */
7512 if (0)
7513 ;
7514#endif /* !VBOX_WITH_VBOXSDL */
7515
7516 else
7517
7518#ifdef VBOX_WITH_HEADLESS
7519 if ( strFrontend == "headless"
7520 || strFrontend == "capture"
7521 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7522 )
7523 {
7524 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7525 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7526 * and a VM works even if the server has not been installed.
7527 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7528 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7529 * differently in 4.0 and 3.x.
7530 */
7531 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7532 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7533 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7534
7535 Utf8Str idStr = mData->mUuid.toString();
7536 const char *apszArgs[] =
7537 {
7538 szPath,
7539 "--comment", mUserData->s.strName.c_str(),
7540 "--startvm", idStr.c_str(),
7541 "--vrde", "config",
7542 0, /* For "--capture". */
7543 0, /* For "--sup-startup-log". */
7544 0
7545 };
7546 unsigned iArg = 7;
7547 if (strFrontend == "capture")
7548 apszArgs[iArg++] = "--capture";
7549 apszArgs[iArg++] = pszSupStartupLogArg;
7550
7551# ifdef RT_OS_WINDOWS
7552 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7553# else
7554 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7555# endif
7556 }
7557#else /* !VBOX_WITH_HEADLESS */
7558 if (0)
7559 ;
7560#endif /* !VBOX_WITH_HEADLESS */
7561 else
7562 {
7563 RTEnvDestroy(env);
7564 return setError(E_INVALIDARG,
7565 tr("Invalid frontend name: '%s'"),
7566 strFrontend.c_str());
7567 }
7568
7569 RTEnvDestroy(env);
7570
7571 if (RT_FAILURE(vrc))
7572 return setError(VBOX_E_IPRT_ERROR,
7573 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7574 mUserData->s.strName.c_str(), vrc);
7575
7576 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7577
7578 /*
7579 * Note that we don't release the lock here before calling the client,
7580 * because it doesn't need to call us back if called with a NULL argument.
7581 * Releasing the lock here is dangerous because we didn't prepare the
7582 * launch data yet, but the client we've just started may happen to be
7583 * too fast and call LockMachine() that will fail (because of PID, etc.),
7584 * so that the Machine will never get out of the Spawning session state.
7585 */
7586
7587 /* inform the session that it will be a remote one */
7588 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7589#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7590 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7591#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7592 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7593#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7594 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7595
7596 if (FAILED(rc))
7597 {
7598 /* restore the session state */
7599 mData->mSession.mState = SessionState_Unlocked;
7600 alock.release();
7601 mParent->i_addProcessToReap(pid);
7602 /* The failure may occur w/o any error info (from RPC), so provide one */
7603 return setError(VBOX_E_VM_ERROR,
7604 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7605 }
7606
7607 /* attach launch data to the machine */
7608 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7609 mData->mSession.mRemoteControls.push_back(aControl);
7610 mData->mSession.mProgress = aProgress;
7611 mData->mSession.mPID = pid;
7612 mData->mSession.mState = SessionState_Spawning;
7613 mData->mSession.mType = strFrontend;
7614
7615 alock.release();
7616 mParent->i_addProcessToReap(pid);
7617
7618 LogFlowThisFuncLeave();
7619 return S_OK;
7620}
7621
7622/**
7623 * Returns @c true if the given session machine instance has an open direct
7624 * session (and optionally also for direct sessions which are closing) and
7625 * returns the session control machine instance if so.
7626 *
7627 * Note that when the method returns @c false, the arguments remain unchanged.
7628 *
7629 * @param aMachine Session machine object.
7630 * @param aControl Direct session control object (optional).
7631 * @param aAllowClosing If true then additionally a session which is currently
7632 * being closed will also be allowed.
7633 *
7634 * @note locks this object for reading.
7635 */
7636bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7637 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7638 bool aAllowClosing /*= false*/)
7639{
7640 AutoLimitedCaller autoCaller(this);
7641 AssertComRCReturn(autoCaller.rc(), false);
7642
7643 /* just return false for inaccessible machines */
7644 if (getObjectState().getState() != ObjectState::Ready)
7645 return false;
7646
7647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7648
7649 if ( mData->mSession.mState == SessionState_Locked
7650 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7651 )
7652 {
7653 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7654
7655 aMachine = mData->mSession.mMachine;
7656
7657 if (aControl != NULL)
7658 *aControl = mData->mSession.mDirectControl;
7659
7660 return true;
7661 }
7662
7663 return false;
7664}
7665
7666/**
7667 * Returns @c true if the given machine has an spawning direct session.
7668 *
7669 * @note locks this object for reading.
7670 */
7671bool Machine::i_isSessionSpawning()
7672{
7673 AutoLimitedCaller autoCaller(this);
7674 AssertComRCReturn(autoCaller.rc(), false);
7675
7676 /* just return false for inaccessible machines */
7677 if (getObjectState().getState() != ObjectState::Ready)
7678 return false;
7679
7680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7681
7682 if (mData->mSession.mState == SessionState_Spawning)
7683 return true;
7684
7685 return false;
7686}
7687
7688/**
7689 * Called from the client watcher thread to check for unexpected client process
7690 * death during Session_Spawning state (e.g. before it successfully opened a
7691 * direct session).
7692 *
7693 * On Win32 and on OS/2, this method is called only when we've got the
7694 * direct client's process termination notification, so it always returns @c
7695 * true.
7696 *
7697 * On other platforms, this method returns @c true if the client process is
7698 * terminated and @c false if it's still alive.
7699 *
7700 * @note Locks this object for writing.
7701 */
7702bool Machine::i_checkForSpawnFailure()
7703{
7704 AutoCaller autoCaller(this);
7705 if (!autoCaller.isOk())
7706 {
7707 /* nothing to do */
7708 LogFlowThisFunc(("Already uninitialized!\n"));
7709 return true;
7710 }
7711
7712 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7713
7714 if (mData->mSession.mState != SessionState_Spawning)
7715 {
7716 /* nothing to do */
7717 LogFlowThisFunc(("Not spawning any more!\n"));
7718 return true;
7719 }
7720
7721 HRESULT rc = S_OK;
7722
7723 /* PID not yet initialized, skip check. */
7724 if (mData->mSession.mPID == NIL_RTPROCESS)
7725 return false;
7726
7727 RTPROCSTATUS status;
7728 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7729
7730 if (vrc != VERR_PROCESS_RUNNING)
7731 {
7732 Utf8Str strExtraInfo;
7733
7734#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7735 /* If the startup logfile exists and is of non-zero length, tell the
7736 user to look there for more details to encourage them to attach it
7737 when reporting startup issues. */
7738 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7739 uint64_t cbStartupLogFile = 0;
7740 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7741 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7742 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7743#endif
7744
7745 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7746 rc = setError(E_FAIL,
7747 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7748 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7749 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7750 rc = setError(E_FAIL,
7751 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7752 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7753 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7754 rc = setError(E_FAIL,
7755 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7756 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7757 else
7758 rc = setError(E_FAIL,
7759 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7760 i_getName().c_str(), vrc, strExtraInfo.c_str());
7761 }
7762
7763 if (FAILED(rc))
7764 {
7765 /* Close the remote session, remove the remote control from the list
7766 * and reset session state to Closed (@note keep the code in sync with
7767 * the relevant part in LockMachine()). */
7768
7769 Assert(mData->mSession.mRemoteControls.size() == 1);
7770 if (mData->mSession.mRemoteControls.size() == 1)
7771 {
7772 ErrorInfoKeeper eik;
7773 mData->mSession.mRemoteControls.front()->Uninitialize();
7774 }
7775
7776 mData->mSession.mRemoteControls.clear();
7777 mData->mSession.mState = SessionState_Unlocked;
7778
7779 /* finalize the progress after setting the state */
7780 if (!mData->mSession.mProgress.isNull())
7781 {
7782 mData->mSession.mProgress->notifyComplete(rc);
7783 mData->mSession.mProgress.setNull();
7784 }
7785
7786 mData->mSession.mPID = NIL_RTPROCESS;
7787
7788 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7789 return true;
7790 }
7791
7792 return false;
7793}
7794
7795/**
7796 * Checks whether the machine can be registered. If so, commits and saves
7797 * all settings.
7798 *
7799 * @note Must be called from mParent's write lock. Locks this object and
7800 * children for writing.
7801 */
7802HRESULT Machine::i_prepareRegister()
7803{
7804 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7805
7806 AutoLimitedCaller autoCaller(this);
7807 AssertComRCReturnRC(autoCaller.rc());
7808
7809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7810
7811 /* wait for state dependents to drop to zero */
7812 i_ensureNoStateDependencies();
7813
7814 if (!mData->mAccessible)
7815 return setError(VBOX_E_INVALID_OBJECT_STATE,
7816 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7817 mUserData->s.strName.c_str(),
7818 mData->mUuid.toString().c_str());
7819
7820 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7821
7822 if (mData->mRegistered)
7823 return setError(VBOX_E_INVALID_OBJECT_STATE,
7824 tr("The machine '%s' with UUID {%s} is already registered"),
7825 mUserData->s.strName.c_str(),
7826 mData->mUuid.toString().c_str());
7827
7828 HRESULT rc = S_OK;
7829
7830 // Ensure the settings are saved. If we are going to be registered and
7831 // no config file exists yet, create it by calling i_saveSettings() too.
7832 if ( (mData->flModifications)
7833 || (!mData->pMachineConfigFile->fileExists())
7834 )
7835 {
7836 rc = i_saveSettings(NULL);
7837 // no need to check whether VirtualBox.xml needs saving too since
7838 // we can't have a machine XML file rename pending
7839 if (FAILED(rc)) return rc;
7840 }
7841
7842 /* more config checking goes here */
7843
7844 if (SUCCEEDED(rc))
7845 {
7846 /* we may have had implicit modifications we want to fix on success */
7847 i_commit();
7848
7849 mData->mRegistered = true;
7850 }
7851 else
7852 {
7853 /* we may have had implicit modifications we want to cancel on failure*/
7854 i_rollback(false /* aNotify */);
7855 }
7856
7857 return rc;
7858}
7859
7860/**
7861 * Increases the number of objects dependent on the machine state or on the
7862 * registered state. Guarantees that these two states will not change at least
7863 * until #releaseStateDependency() is called.
7864 *
7865 * Depending on the @a aDepType value, additional state checks may be made.
7866 * These checks will set extended error info on failure. See
7867 * #checkStateDependency() for more info.
7868 *
7869 * If this method returns a failure, the dependency is not added and the caller
7870 * is not allowed to rely on any particular machine state or registration state
7871 * value and may return the failed result code to the upper level.
7872 *
7873 * @param aDepType Dependency type to add.
7874 * @param aState Current machine state (NULL if not interested).
7875 * @param aRegistered Current registered state (NULL if not interested).
7876 *
7877 * @note Locks this object for writing.
7878 */
7879HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7880 MachineState_T *aState /* = NULL */,
7881 BOOL *aRegistered /* = NULL */)
7882{
7883 AutoCaller autoCaller(this);
7884 AssertComRCReturnRC(autoCaller.rc());
7885
7886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7887
7888 HRESULT rc = i_checkStateDependency(aDepType);
7889 if (FAILED(rc)) return rc;
7890
7891 {
7892 if (mData->mMachineStateChangePending != 0)
7893 {
7894 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7895 * drop to zero so don't add more. It may make sense to wait a bit
7896 * and retry before reporting an error (since the pending state
7897 * transition should be really quick) but let's just assert for
7898 * now to see if it ever happens on practice. */
7899
7900 AssertFailed();
7901
7902 return setError(E_ACCESSDENIED,
7903 tr("Machine state change is in progress. Please retry the operation later."));
7904 }
7905
7906 ++mData->mMachineStateDeps;
7907 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7908 }
7909
7910 if (aState)
7911 *aState = mData->mMachineState;
7912 if (aRegistered)
7913 *aRegistered = mData->mRegistered;
7914
7915 return S_OK;
7916}
7917
7918/**
7919 * Decreases the number of objects dependent on the machine state.
7920 * Must always complete the #addStateDependency() call after the state
7921 * dependency is no more necessary.
7922 */
7923void Machine::i_releaseStateDependency()
7924{
7925 AutoCaller autoCaller(this);
7926 AssertComRCReturnVoid(autoCaller.rc());
7927
7928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7929
7930 /* releaseStateDependency() w/o addStateDependency()? */
7931 AssertReturnVoid(mData->mMachineStateDeps != 0);
7932 -- mData->mMachineStateDeps;
7933
7934 if (mData->mMachineStateDeps == 0)
7935 {
7936 /* inform i_ensureNoStateDependencies() that there are no more deps */
7937 if (mData->mMachineStateChangePending != 0)
7938 {
7939 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7940 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7941 }
7942 }
7943}
7944
7945Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7946{
7947 /* start with nothing found */
7948 Utf8Str strResult("");
7949
7950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7951
7952 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7953 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7954 // found:
7955 strResult = it->second; // source is a Utf8Str
7956
7957 return strResult;
7958}
7959
7960// protected methods
7961/////////////////////////////////////////////////////////////////////////////
7962
7963/**
7964 * Performs machine state checks based on the @a aDepType value. If a check
7965 * fails, this method will set extended error info, otherwise it will return
7966 * S_OK. It is supposed, that on failure, the caller will immediately return
7967 * the return value of this method to the upper level.
7968 *
7969 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7970 *
7971 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7972 * current state of this machine object allows to change settings of the
7973 * machine (i.e. the machine is not registered, or registered but not running
7974 * and not saved). It is useful to call this method from Machine setters
7975 * before performing any change.
7976 *
7977 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7978 * as for MutableStateDep except that if the machine is saved, S_OK is also
7979 * returned. This is useful in setters which allow changing machine
7980 * properties when it is in the saved state.
7981 *
7982 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
7983 * if the current state of this machine object allows to change runtime
7984 * changeable settings of the machine (i.e. the machine is not registered, or
7985 * registered but either running or not running and not saved). It is useful
7986 * to call this method from Machine setters before performing any changes to
7987 * runtime changeable settings.
7988 *
7989 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
7990 * the same as for MutableOrRunningStateDep except that if the machine is
7991 * saved, S_OK is also returned. This is useful in setters which allow
7992 * changing runtime and saved state changeable machine properties.
7993 *
7994 * @param aDepType Dependency type to check.
7995 *
7996 * @note Non Machine based classes should use #addStateDependency() and
7997 * #releaseStateDependency() methods or the smart AutoStateDependency
7998 * template.
7999 *
8000 * @note This method must be called from under this object's read or write
8001 * lock.
8002 */
8003HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8004{
8005 switch (aDepType)
8006 {
8007 case AnyStateDep:
8008 {
8009 break;
8010 }
8011 case MutableStateDep:
8012 {
8013 if ( mData->mRegistered
8014 && ( !i_isSessionMachine()
8015 || ( mData->mMachineState != MachineState_Aborted
8016 && mData->mMachineState != MachineState_Teleported
8017 && mData->mMachineState != MachineState_PoweredOff
8018 )
8019 )
8020 )
8021 return setError(VBOX_E_INVALID_VM_STATE,
8022 tr("The machine is not mutable (state is %s)"),
8023 Global::stringifyMachineState(mData->mMachineState));
8024 break;
8025 }
8026 case MutableOrSavedStateDep:
8027 {
8028 if ( mData->mRegistered
8029 && ( !i_isSessionMachine()
8030 || ( mData->mMachineState != MachineState_Aborted
8031 && mData->mMachineState != MachineState_Teleported
8032 && mData->mMachineState != MachineState_Saved
8033 && mData->mMachineState != MachineState_PoweredOff
8034 )
8035 )
8036 )
8037 return setError(VBOX_E_INVALID_VM_STATE,
8038 tr("The machine is not mutable (state is %s)"),
8039 Global::stringifyMachineState(mData->mMachineState));
8040 break;
8041 }
8042 case MutableOrRunningStateDep:
8043 {
8044 if ( mData->mRegistered
8045 && ( !i_isSessionMachine()
8046 || ( mData->mMachineState != MachineState_Aborted
8047 && mData->mMachineState != MachineState_Teleported
8048 && mData->mMachineState != MachineState_PoweredOff
8049 && !Global::IsOnline(mData->mMachineState)
8050 )
8051 )
8052 )
8053 return setError(VBOX_E_INVALID_VM_STATE,
8054 tr("The machine is not mutable (state is %s)"),
8055 Global::stringifyMachineState(mData->mMachineState));
8056 break;
8057 }
8058 case MutableOrSavedOrRunningStateDep:
8059 {
8060 if ( mData->mRegistered
8061 && ( !i_isSessionMachine()
8062 || ( mData->mMachineState != MachineState_Aborted
8063 && mData->mMachineState != MachineState_Teleported
8064 && mData->mMachineState != MachineState_Saved
8065 && mData->mMachineState != MachineState_PoweredOff
8066 && !Global::IsOnline(mData->mMachineState)
8067 )
8068 )
8069 )
8070 return setError(VBOX_E_INVALID_VM_STATE,
8071 tr("The machine is not mutable (state is %s)"),
8072 Global::stringifyMachineState(mData->mMachineState));
8073 break;
8074 }
8075 }
8076
8077 return S_OK;
8078}
8079
8080/**
8081 * Helper to initialize all associated child objects and allocate data
8082 * structures.
8083 *
8084 * This method must be called as a part of the object's initialization procedure
8085 * (usually done in the #init() method).
8086 *
8087 * @note Must be called only from #init() or from #registeredInit().
8088 */
8089HRESULT Machine::initDataAndChildObjects()
8090{
8091 AutoCaller autoCaller(this);
8092 AssertComRCReturnRC(autoCaller.rc());
8093 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8094 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8095
8096 AssertReturn(!mData->mAccessible, E_FAIL);
8097
8098 /* allocate data structures */
8099 mSSData.allocate();
8100 mUserData.allocate();
8101 mHWData.allocate();
8102 mMediaData.allocate();
8103 mStorageControllers.allocate();
8104 mUSBControllers.allocate();
8105
8106 /* initialize mOSTypeId */
8107 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8108
8109 /* create associated BIOS settings object */
8110 unconst(mBIOSSettings).createObject();
8111 mBIOSSettings->init(this);
8112
8113 /* create an associated VRDE object (default is disabled) */
8114 unconst(mVRDEServer).createObject();
8115 mVRDEServer->init(this);
8116
8117 /* create associated serial port objects */
8118 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8119 {
8120 unconst(mSerialPorts[slot]).createObject();
8121 mSerialPorts[slot]->init(this, slot);
8122 }
8123
8124 /* create associated parallel port objects */
8125 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8126 {
8127 unconst(mParallelPorts[slot]).createObject();
8128 mParallelPorts[slot]->init(this, slot);
8129 }
8130
8131 /* create the audio adapter object (always present, default is disabled) */
8132 unconst(mAudioAdapter).createObject();
8133 mAudioAdapter->init(this);
8134
8135 /* create the USB device filters object (always present) */
8136 unconst(mUSBDeviceFilters).createObject();
8137 mUSBDeviceFilters->init(this);
8138
8139 /* create associated network adapter objects */
8140 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8141 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8142 {
8143 unconst(mNetworkAdapters[slot]).createObject();
8144 mNetworkAdapters[slot]->init(this, slot);
8145 }
8146
8147 /* create the bandwidth control */
8148 unconst(mBandwidthControl).createObject();
8149 mBandwidthControl->init(this);
8150
8151 return S_OK;
8152}
8153
8154/**
8155 * Helper to uninitialize all associated child objects and to free all data
8156 * structures.
8157 *
8158 * This method must be called as a part of the object's uninitialization
8159 * procedure (usually done in the #uninit() method).
8160 *
8161 * @note Must be called only from #uninit() or from #registeredInit().
8162 */
8163void Machine::uninitDataAndChildObjects()
8164{
8165 AutoCaller autoCaller(this);
8166 AssertComRCReturnVoid(autoCaller.rc());
8167 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8168 || getObjectState().getState() == ObjectState::Limited);
8169
8170 /* tell all our other child objects we've been uninitialized */
8171 if (mBandwidthControl)
8172 {
8173 mBandwidthControl->uninit();
8174 unconst(mBandwidthControl).setNull();
8175 }
8176
8177 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8178 {
8179 if (mNetworkAdapters[slot])
8180 {
8181 mNetworkAdapters[slot]->uninit();
8182 unconst(mNetworkAdapters[slot]).setNull();
8183 }
8184 }
8185
8186 if (mUSBDeviceFilters)
8187 {
8188 mUSBDeviceFilters->uninit();
8189 unconst(mUSBDeviceFilters).setNull();
8190 }
8191
8192 if (mAudioAdapter)
8193 {
8194 mAudioAdapter->uninit();
8195 unconst(mAudioAdapter).setNull();
8196 }
8197
8198 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8199 {
8200 if (mParallelPorts[slot])
8201 {
8202 mParallelPorts[slot]->uninit();
8203 unconst(mParallelPorts[slot]).setNull();
8204 }
8205 }
8206
8207 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8208 {
8209 if (mSerialPorts[slot])
8210 {
8211 mSerialPorts[slot]->uninit();
8212 unconst(mSerialPorts[slot]).setNull();
8213 }
8214 }
8215
8216 if (mVRDEServer)
8217 {
8218 mVRDEServer->uninit();
8219 unconst(mVRDEServer).setNull();
8220 }
8221
8222 if (mBIOSSettings)
8223 {
8224 mBIOSSettings->uninit();
8225 unconst(mBIOSSettings).setNull();
8226 }
8227
8228 /* Deassociate media (only when a real Machine or a SnapshotMachine
8229 * instance is uninitialized; SessionMachine instances refer to real
8230 * Machine media). This is necessary for a clean re-initialization of
8231 * the VM after successfully re-checking the accessibility state. Note
8232 * that in case of normal Machine or SnapshotMachine uninitialization (as
8233 * a result of unregistering or deleting the snapshot), outdated media
8234 * attachments will already be uninitialized and deleted, so this
8235 * code will not affect them. */
8236 if ( !!mMediaData
8237 && (!i_isSessionMachine())
8238 )
8239 {
8240 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8241 it != mMediaData->mAttachments.end();
8242 ++it)
8243 {
8244 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8245 if (pMedium.isNull())
8246 continue;
8247 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8248 AssertComRC(rc);
8249 }
8250 }
8251
8252 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8253 {
8254 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8255 if (mData->mFirstSnapshot)
8256 {
8257 // snapshots tree is protected by machine write lock; strictly
8258 // this isn't necessary here since we're deleting the entire
8259 // machine, but otherwise we assert in Snapshot::uninit()
8260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8261 mData->mFirstSnapshot->uninit();
8262 mData->mFirstSnapshot.setNull();
8263 }
8264
8265 mData->mCurrentSnapshot.setNull();
8266 }
8267
8268 /* free data structures (the essential mData structure is not freed here
8269 * since it may be still in use) */
8270 mMediaData.free();
8271 mStorageControllers.free();
8272 mUSBControllers.free();
8273 mHWData.free();
8274 mUserData.free();
8275 mSSData.free();
8276}
8277
8278/**
8279 * Returns a pointer to the Machine object for this machine that acts like a
8280 * parent for complex machine data objects such as shared folders, etc.
8281 *
8282 * For primary Machine objects and for SnapshotMachine objects, returns this
8283 * object's pointer itself. For SessionMachine objects, returns the peer
8284 * (primary) machine pointer.
8285 */
8286Machine* Machine::i_getMachine()
8287{
8288 if (i_isSessionMachine())
8289 return (Machine*)mPeer;
8290 return this;
8291}
8292
8293/**
8294 * Makes sure that there are no machine state dependents. If necessary, waits
8295 * for the number of dependents to drop to zero.
8296 *
8297 * Make sure this method is called from under this object's write lock to
8298 * guarantee that no new dependents may be added when this method returns
8299 * control to the caller.
8300 *
8301 * @note Locks this object for writing. The lock will be released while waiting
8302 * (if necessary).
8303 *
8304 * @warning To be used only in methods that change the machine state!
8305 */
8306void Machine::i_ensureNoStateDependencies()
8307{
8308 AssertReturnVoid(isWriteLockOnCurrentThread());
8309
8310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8311
8312 /* Wait for all state dependents if necessary */
8313 if (mData->mMachineStateDeps != 0)
8314 {
8315 /* lazy semaphore creation */
8316 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8317 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8318
8319 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8320 mData->mMachineStateDeps));
8321
8322 ++mData->mMachineStateChangePending;
8323
8324 /* reset the semaphore before waiting, the last dependent will signal
8325 * it */
8326 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8327
8328 alock.release();
8329
8330 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8331
8332 alock.acquire();
8333
8334 -- mData->mMachineStateChangePending;
8335 }
8336}
8337
8338/**
8339 * Changes the machine state and informs callbacks.
8340 *
8341 * This method is not intended to fail so it either returns S_OK or asserts (and
8342 * returns a failure).
8343 *
8344 * @note Locks this object for writing.
8345 */
8346HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8347{
8348 LogFlowThisFuncEnter();
8349 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8350
8351 AutoCaller autoCaller(this);
8352 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8353
8354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8355
8356 /* wait for state dependents to drop to zero */
8357 i_ensureNoStateDependencies();
8358
8359 MachineState_T const enmOldState = mData->mMachineState;
8360 if (enmOldState != aMachineState)
8361 {
8362 mData->mMachineState = aMachineState;
8363 RTTimeNow(&mData->mLastStateChange);
8364
8365#ifdef VBOX_WITH_DTRACE_R3_MAIN
8366 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8367#endif
8368 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8369 }
8370
8371 LogFlowThisFuncLeave();
8372 return S_OK;
8373}
8374
8375/**
8376 * Searches for a shared folder with the given logical name
8377 * in the collection of shared folders.
8378 *
8379 * @param aName logical name of the shared folder
8380 * @param aSharedFolder where to return the found object
8381 * @param aSetError whether to set the error info if the folder is
8382 * not found
8383 * @return
8384 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8385 *
8386 * @note
8387 * must be called from under the object's lock!
8388 */
8389HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8390 ComObjPtr<SharedFolder> &aSharedFolder,
8391 bool aSetError /* = false */)
8392{
8393 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8394 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8395 it != mHWData->mSharedFolders.end();
8396 ++it)
8397 {
8398 SharedFolder *pSF = *it;
8399 AutoCaller autoCaller(pSF);
8400 if (pSF->i_getName() == aName)
8401 {
8402 aSharedFolder = pSF;
8403 rc = S_OK;
8404 break;
8405 }
8406 }
8407
8408 if (aSetError && FAILED(rc))
8409 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8410
8411 return rc;
8412}
8413
8414/**
8415 * Initializes all machine instance data from the given settings structures
8416 * from XML. The exception is the machine UUID which needs special handling
8417 * depending on the caller's use case, so the caller needs to set that herself.
8418 *
8419 * This gets called in several contexts during machine initialization:
8420 *
8421 * -- When machine XML exists on disk already and needs to be loaded into memory,
8422 * for example, from registeredInit() to load all registered machines on
8423 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8424 * attached to the machine should be part of some media registry already.
8425 *
8426 * -- During OVF import, when a machine config has been constructed from an
8427 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8428 * ensure that the media listed as attachments in the config (which have
8429 * been imported from the OVF) receive the correct registry ID.
8430 *
8431 * -- During VM cloning.
8432 *
8433 * @param config Machine settings from XML.
8434 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8435 * for each attached medium in the config.
8436 * @return
8437 */
8438HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8439 const Guid *puuidRegistry)
8440{
8441 // copy name, description, OS type, teleporter, UTC etc.
8442 mUserData->s = config.machineUserData;
8443
8444 // Decode the Icon overide data from config userdata and set onto Machine.
8445 #define DECODE_STR_MAX _1M
8446 const char* pszStr = config.machineUserData.ovIcon.c_str();
8447 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8448 if (cbOut > DECODE_STR_MAX)
8449 return setError(E_FAIL,
8450 tr("Icon Data too long.'%d' > '%d'"),
8451 cbOut,
8452 DECODE_STR_MAX);
8453 mUserData->mIcon.resize(cbOut);
8454 int vrc = VINF_SUCCESS;
8455 if (cbOut)
8456 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8457 if (RT_FAILURE(vrc))
8458 {
8459 mUserData->mIcon.resize(0);
8460 return setError(E_FAIL,
8461 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8462 pszStr,
8463 vrc);
8464 }
8465
8466 // look up the object by Id to check it is valid
8467 ComPtr<IGuestOSType> guestOSType;
8468 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8469 guestOSType.asOutParam());
8470 if (FAILED(rc)) return rc;
8471
8472 // stateFile (optional)
8473 if (config.strStateFile.isEmpty())
8474 mSSData->strStateFilePath.setNull();
8475 else
8476 {
8477 Utf8Str stateFilePathFull(config.strStateFile);
8478 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8479 if (RT_FAILURE(vrc))
8480 return setError(E_FAIL,
8481 tr("Invalid saved state file path '%s' (%Rrc)"),
8482 config.strStateFile.c_str(),
8483 vrc);
8484 mSSData->strStateFilePath = stateFilePathFull;
8485 }
8486
8487 // snapshot folder needs special processing so set it again
8488 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8489 if (FAILED(rc)) return rc;
8490
8491 /* Copy the extra data items (Not in any case config is already the same as
8492 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8493 * make sure the extra data map is copied). */
8494 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8495
8496 /* currentStateModified (optional, default is true) */
8497 mData->mCurrentStateModified = config.fCurrentStateModified;
8498
8499 mData->mLastStateChange = config.timeLastStateChange;
8500
8501 /*
8502 * note: all mUserData members must be assigned prior this point because
8503 * we need to commit changes in order to let mUserData be shared by all
8504 * snapshot machine instances.
8505 */
8506 mUserData.commitCopy();
8507
8508 // machine registry, if present (must be loaded before snapshots)
8509 if (config.canHaveOwnMediaRegistry())
8510 {
8511 // determine machine folder
8512 Utf8Str strMachineFolder = i_getSettingsFileFull();
8513 strMachineFolder.stripFilename();
8514 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8515 config.mediaRegistry,
8516 strMachineFolder);
8517 if (FAILED(rc)) return rc;
8518 }
8519
8520 /* Snapshot node (optional) */
8521 size_t cRootSnapshots;
8522 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8523 {
8524 // there must be only one root snapshot
8525 Assert(cRootSnapshots == 1);
8526
8527 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8528
8529 rc = i_loadSnapshot(snap,
8530 config.uuidCurrentSnapshot,
8531 NULL); // no parent == first snapshot
8532 if (FAILED(rc)) return rc;
8533 }
8534
8535 // hardware data
8536 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8537 if (FAILED(rc)) return rc;
8538
8539 // load storage controllers
8540 rc = i_loadStorageControllers(config.storageMachine,
8541 puuidRegistry,
8542 NULL /* puuidSnapshot */);
8543 if (FAILED(rc)) return rc;
8544
8545 /*
8546 * NOTE: the assignment below must be the last thing to do,
8547 * otherwise it will be not possible to change the settings
8548 * somewhere in the code above because all setters will be
8549 * blocked by i_checkStateDependency(MutableStateDep).
8550 */
8551
8552 /* set the machine state to Aborted or Saved when appropriate */
8553 if (config.fAborted)
8554 {
8555 mSSData->strStateFilePath.setNull();
8556
8557 /* no need to use i_setMachineState() during init() */
8558 mData->mMachineState = MachineState_Aborted;
8559 }
8560 else if (!mSSData->strStateFilePath.isEmpty())
8561 {
8562 /* no need to use i_setMachineState() during init() */
8563 mData->mMachineState = MachineState_Saved;
8564 }
8565
8566 // after loading settings, we are no longer different from the XML on disk
8567 mData->flModifications = 0;
8568
8569 return S_OK;
8570}
8571
8572/**
8573 * Recursively loads all snapshots starting from the given.
8574 *
8575 * @param aNode <Snapshot> node.
8576 * @param aCurSnapshotId Current snapshot ID from the settings file.
8577 * @param aParentSnapshot Parent snapshot.
8578 */
8579HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8580 const Guid &aCurSnapshotId,
8581 Snapshot *aParentSnapshot)
8582{
8583 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8584 AssertReturn(!i_isSessionMachine(), E_FAIL);
8585
8586 HRESULT rc = S_OK;
8587
8588 Utf8Str strStateFile;
8589 if (!data.strStateFile.isEmpty())
8590 {
8591 /* optional */
8592 strStateFile = data.strStateFile;
8593 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8594 if (RT_FAILURE(vrc))
8595 return setError(E_FAIL,
8596 tr("Invalid saved state file path '%s' (%Rrc)"),
8597 strStateFile.c_str(),
8598 vrc);
8599 }
8600
8601 /* create a snapshot machine object */
8602 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8603 pSnapshotMachine.createObject();
8604 rc = pSnapshotMachine->initFromSettings(this,
8605 data.hardware,
8606 &data.debugging,
8607 &data.autostart,
8608 data.storage,
8609 data.uuid.ref(),
8610 strStateFile);
8611 if (FAILED(rc)) return rc;
8612
8613 /* create a snapshot object */
8614 ComObjPtr<Snapshot> pSnapshot;
8615 pSnapshot.createObject();
8616 /* initialize the snapshot */
8617 rc = pSnapshot->init(mParent, // VirtualBox object
8618 data.uuid,
8619 data.strName,
8620 data.strDescription,
8621 data.timestamp,
8622 pSnapshotMachine,
8623 aParentSnapshot);
8624 if (FAILED(rc)) return rc;
8625
8626 /* memorize the first snapshot if necessary */
8627 if (!mData->mFirstSnapshot)
8628 mData->mFirstSnapshot = pSnapshot;
8629
8630 /* memorize the current snapshot when appropriate */
8631 if ( !mData->mCurrentSnapshot
8632 && pSnapshot->i_getId() == aCurSnapshotId
8633 )
8634 mData->mCurrentSnapshot = pSnapshot;
8635
8636 // now create the children
8637 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8638 it != data.llChildSnapshots.end();
8639 ++it)
8640 {
8641 const settings::Snapshot &childData = *it;
8642 // recurse
8643 rc = i_loadSnapshot(childData,
8644 aCurSnapshotId,
8645 pSnapshot); // parent = the one we created above
8646 if (FAILED(rc)) return rc;
8647 }
8648
8649 return rc;
8650}
8651
8652/**
8653 * Loads settings into mHWData.
8654 *
8655 * @param data Reference to the hardware settings.
8656 * @param pDbg Pointer to the debugging settings.
8657 * @param pAutostart Pointer to the autostart settings.
8658 */
8659HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8660 const settings::Autostart *pAutostart)
8661{
8662 AssertReturn(!i_isSessionMachine(), E_FAIL);
8663
8664 HRESULT rc = S_OK;
8665
8666 try
8667 {
8668 /* The hardware version attribute (optional). */
8669 mHWData->mHWVersion = data.strVersion;
8670 mHWData->mHardwareUUID = data.uuid;
8671
8672 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8673 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8674 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8675 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8676 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8677 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8678 mHWData->mPAEEnabled = data.fPAE;
8679 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8680 mHWData->mLongMode = data.enmLongMode;
8681 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8682 mHWData->mCPUCount = data.cCPUs;
8683 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8684 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8685
8686 // cpu
8687 if (mHWData->mCPUHotPlugEnabled)
8688 {
8689 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8690 it != data.llCpus.end();
8691 ++it)
8692 {
8693 const settings::Cpu &cpu = *it;
8694
8695 mHWData->mCPUAttached[cpu.ulId] = true;
8696 }
8697 }
8698
8699 // cpuid leafs
8700 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8701 it != data.llCpuIdLeafs.end();
8702 ++it)
8703 {
8704 const settings::CpuIdLeaf &leaf = *it;
8705
8706 switch (leaf.ulId)
8707 {
8708 case 0x0:
8709 case 0x1:
8710 case 0x2:
8711 case 0x3:
8712 case 0x4:
8713 case 0x5:
8714 case 0x6:
8715 case 0x7:
8716 case 0x8:
8717 case 0x9:
8718 case 0xA:
8719 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8720 break;
8721
8722 case 0x80000000:
8723 case 0x80000001:
8724 case 0x80000002:
8725 case 0x80000003:
8726 case 0x80000004:
8727 case 0x80000005:
8728 case 0x80000006:
8729 case 0x80000007:
8730 case 0x80000008:
8731 case 0x80000009:
8732 case 0x8000000A:
8733 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8734 break;
8735
8736 default:
8737 /* just ignore */
8738 break;
8739 }
8740 }
8741
8742 mHWData->mMemorySize = data.ulMemorySizeMB;
8743 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8744
8745 // boot order
8746 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8747 {
8748 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8749 if (it == data.mapBootOrder.end())
8750 mHWData->mBootOrder[i] = DeviceType_Null;
8751 else
8752 mHWData->mBootOrder[i] = it->second;
8753 }
8754
8755 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8756 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8757 mHWData->mMonitorCount = data.cMonitors;
8758 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8759 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8760 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8761 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8762 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8763 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8764 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8765 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8766 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8767 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8768 if (!data.strVideoCaptureFile.isEmpty())
8769 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8770 else
8771 mHWData->mVideoCaptureFile.setNull();
8772 mHWData->mFirmwareType = data.firmwareType;
8773 mHWData->mPointingHIDType = data.pointingHIDType;
8774 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8775 mHWData->mChipsetType = data.chipsetType;
8776 mHWData->mParavirtProvider = data.paravirtProvider;
8777 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8778 mHWData->mHPETEnabled = data.fHPETEnabled;
8779
8780 /* VRDEServer */
8781 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8782 if (FAILED(rc)) return rc;
8783
8784 /* BIOS */
8785 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8786 if (FAILED(rc)) return rc;
8787
8788 // Bandwidth control (must come before network adapters)
8789 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8790 if (FAILED(rc)) return rc;
8791
8792 /* Shared folders */
8793 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8794 it != data.usbSettings.llUSBControllers.end();
8795 ++it)
8796 {
8797 const settings::USBController &settingsCtrl = *it;
8798 ComObjPtr<USBController> newCtrl;
8799
8800 newCtrl.createObject();
8801 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8802 mUSBControllers->push_back(newCtrl);
8803 }
8804
8805 /* USB device filters */
8806 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8807 if (FAILED(rc)) return rc;
8808
8809 // network adapters
8810 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8811 size_t oldCount = mNetworkAdapters.size();
8812 if (newCount > oldCount)
8813 {
8814 mNetworkAdapters.resize(newCount);
8815 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8816 {
8817 unconst(mNetworkAdapters[slot]).createObject();
8818 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8819 }
8820 }
8821 else if (newCount < oldCount)
8822 mNetworkAdapters.resize(newCount);
8823 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8824 it != data.llNetworkAdapters.end();
8825 ++it)
8826 {
8827 const settings::NetworkAdapter &nic = *it;
8828
8829 /* slot unicity is guaranteed by XML Schema */
8830 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8831 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8832 if (FAILED(rc)) return rc;
8833 }
8834
8835 // serial ports
8836 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8837 it != data.llSerialPorts.end();
8838 ++it)
8839 {
8840 const settings::SerialPort &s = *it;
8841
8842 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8843 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8844 if (FAILED(rc)) return rc;
8845 }
8846
8847 // parallel ports (optional)
8848 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8849 it != data.llParallelPorts.end();
8850 ++it)
8851 {
8852 const settings::ParallelPort &p = *it;
8853
8854 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8855 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8856 if (FAILED(rc)) return rc;
8857 }
8858
8859 /* AudioAdapter */
8860 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8861 if (FAILED(rc)) return rc;
8862
8863 /* Shared folders */
8864 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8865 it != data.llSharedFolders.end();
8866 ++it)
8867 {
8868 const settings::SharedFolder &sf = *it;
8869
8870 ComObjPtr<SharedFolder> sharedFolder;
8871 /* Check for double entries. Not allowed! */
8872 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8873 if (SUCCEEDED(rc))
8874 return setError(VBOX_E_OBJECT_IN_USE,
8875 tr("Shared folder named '%s' already exists"),
8876 sf.strName.c_str());
8877
8878 /* Create the new shared folder. Don't break on error. This will be
8879 * reported when the machine starts. */
8880 sharedFolder.createObject();
8881 rc = sharedFolder->init(i_getMachine(),
8882 sf.strName,
8883 sf.strHostPath,
8884 RT_BOOL(sf.fWritable),
8885 RT_BOOL(sf.fAutoMount),
8886 false /* fFailOnError */);
8887 if (FAILED(rc)) return rc;
8888 mHWData->mSharedFolders.push_back(sharedFolder);
8889 }
8890
8891 // Clipboard
8892 mHWData->mClipboardMode = data.clipboardMode;
8893
8894 // drag'n'drop
8895 mHWData->mDnDMode = data.dndMode;
8896
8897 // guest settings
8898 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8899
8900 // IO settings
8901 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8902 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8903
8904 // Host PCI devices
8905 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8906 it != data.pciAttachments.end();
8907 ++it)
8908 {
8909 const settings::HostPCIDeviceAttachment &hpda = *it;
8910 ComObjPtr<PCIDeviceAttachment> pda;
8911
8912 pda.createObject();
8913 pda->i_loadSettings(this, hpda);
8914 mHWData->mPCIDeviceAssignments.push_back(pda);
8915 }
8916
8917 /*
8918 * (The following isn't really real hardware, but it lives in HWData
8919 * for reasons of convenience.)
8920 */
8921
8922#ifdef VBOX_WITH_GUEST_PROPS
8923 /* Guest properties (optional) */
8924 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8925 it != data.llGuestProperties.end();
8926 ++it)
8927 {
8928 const settings::GuestProperty &prop = *it;
8929 uint32_t fFlags = guestProp::NILFLAG;
8930 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8931 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8932 mHWData->mGuestProperties[prop.strName] = property;
8933 }
8934
8935 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8936#endif /* VBOX_WITH_GUEST_PROPS defined */
8937
8938 rc = i_loadDebugging(pDbg);
8939 if (FAILED(rc))
8940 return rc;
8941
8942 mHWData->mAutostart = *pAutostart;
8943
8944 /* default frontend */
8945 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8946 }
8947 catch(std::bad_alloc &)
8948 {
8949 return E_OUTOFMEMORY;
8950 }
8951
8952 AssertComRC(rc);
8953 return rc;
8954}
8955
8956/**
8957 * Called from Machine::loadHardware() to load the debugging settings of the
8958 * machine.
8959 *
8960 * @param pDbg Pointer to the settings.
8961 */
8962HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8963{
8964 mHWData->mDebugging = *pDbg;
8965 /* no more processing currently required, this will probably change. */
8966 return S_OK;
8967}
8968
8969/**
8970 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8971 *
8972 * @param data
8973 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8974 * @param puuidSnapshot
8975 * @return
8976 */
8977HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8978 const Guid *puuidRegistry,
8979 const Guid *puuidSnapshot)
8980{
8981 AssertReturn(!i_isSessionMachine(), E_FAIL);
8982
8983 HRESULT rc = S_OK;
8984
8985 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8986 it != data.llStorageControllers.end();
8987 ++it)
8988 {
8989 const settings::StorageController &ctlData = *it;
8990
8991 ComObjPtr<StorageController> pCtl;
8992 /* Try to find one with the name first. */
8993 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8994 if (SUCCEEDED(rc))
8995 return setError(VBOX_E_OBJECT_IN_USE,
8996 tr("Storage controller named '%s' already exists"),
8997 ctlData.strName.c_str());
8998
8999 pCtl.createObject();
9000 rc = pCtl->init(this,
9001 ctlData.strName,
9002 ctlData.storageBus,
9003 ctlData.ulInstance,
9004 ctlData.fBootable);
9005 if (FAILED(rc)) return rc;
9006
9007 mStorageControllers->push_back(pCtl);
9008
9009 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9010 if (FAILED(rc)) return rc;
9011
9012 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9013 if (FAILED(rc)) return rc;
9014
9015 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9016 if (FAILED(rc)) return rc;
9017
9018 /* Set IDE emulation settings (only for AHCI controller). */
9019 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9020 {
9021 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9022 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9023 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9024 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9025 )
9026 return rc;
9027 }
9028
9029 /* Load the attached devices now. */
9030 rc = i_loadStorageDevices(pCtl,
9031 ctlData,
9032 puuidRegistry,
9033 puuidSnapshot);
9034 if (FAILED(rc)) return rc;
9035 }
9036
9037 return S_OK;
9038}
9039
9040/**
9041 * Called from i_loadStorageControllers for a controller's devices.
9042 *
9043 * @param aStorageController
9044 * @param data
9045 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9046 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9047 * @return
9048 */
9049HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9050 const settings::StorageController &data,
9051 const Guid *puuidRegistry,
9052 const Guid *puuidSnapshot)
9053{
9054 HRESULT rc = S_OK;
9055
9056 /* paranoia: detect duplicate attachments */
9057 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9058 it != data.llAttachedDevices.end();
9059 ++it)
9060 {
9061 const settings::AttachedDevice &ad = *it;
9062
9063 for (settings::AttachedDevicesList::const_iterator it2 = it;
9064 it2 != data.llAttachedDevices.end();
9065 ++it2)
9066 {
9067 if (it == it2)
9068 continue;
9069
9070 const settings::AttachedDevice &ad2 = *it2;
9071
9072 if ( ad.lPort == ad2.lPort
9073 && ad.lDevice == ad2.lDevice)
9074 {
9075 return setError(E_FAIL,
9076 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9077 aStorageController->i_getName().c_str(),
9078 ad.lPort,
9079 ad.lDevice,
9080 mUserData->s.strName.c_str());
9081 }
9082 }
9083 }
9084
9085 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9086 it != data.llAttachedDevices.end();
9087 ++it)
9088 {
9089 const settings::AttachedDevice &dev = *it;
9090 ComObjPtr<Medium> medium;
9091
9092 switch (dev.deviceType)
9093 {
9094 case DeviceType_Floppy:
9095 case DeviceType_DVD:
9096 if (dev.strHostDriveSrc.isNotEmpty())
9097 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9098 false /* fRefresh */, medium);
9099 else
9100 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9101 dev.uuid,
9102 false /* fRefresh */,
9103 false /* aSetError */,
9104 medium);
9105 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9106 // This is not an error. The host drive or UUID might have vanished, so just go
9107 // ahead without this removeable medium attachment
9108 rc = S_OK;
9109 break;
9110
9111 case DeviceType_HardDisk:
9112 {
9113 /* find a hard disk by UUID */
9114 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9115 if (FAILED(rc))
9116 {
9117 if (i_isSnapshotMachine())
9118 {
9119 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9120 // so the user knows that the bad disk is in a snapshot somewhere
9121 com::ErrorInfo info;
9122 return setError(E_FAIL,
9123 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9124 puuidSnapshot->raw(),
9125 info.getText().raw());
9126 }
9127 else
9128 return rc;
9129 }
9130
9131 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9132
9133 if (medium->i_getType() == MediumType_Immutable)
9134 {
9135 if (i_isSnapshotMachine())
9136 return setError(E_FAIL,
9137 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9138 "of the virtual machine '%s' ('%s')"),
9139 medium->i_getLocationFull().c_str(),
9140 dev.uuid.raw(),
9141 puuidSnapshot->raw(),
9142 mUserData->s.strName.c_str(),
9143 mData->m_strConfigFileFull.c_str());
9144
9145 return setError(E_FAIL,
9146 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9147 medium->i_getLocationFull().c_str(),
9148 dev.uuid.raw(),
9149 mUserData->s.strName.c_str(),
9150 mData->m_strConfigFileFull.c_str());
9151 }
9152
9153 if (medium->i_getType() == MediumType_MultiAttach)
9154 {
9155 if (i_isSnapshotMachine())
9156 return setError(E_FAIL,
9157 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9158 "of the virtual machine '%s' ('%s')"),
9159 medium->i_getLocationFull().c_str(),
9160 dev.uuid.raw(),
9161 puuidSnapshot->raw(),
9162 mUserData->s.strName.c_str(),
9163 mData->m_strConfigFileFull.c_str());
9164
9165 return setError(E_FAIL,
9166 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9167 medium->i_getLocationFull().c_str(),
9168 dev.uuid.raw(),
9169 mUserData->s.strName.c_str(),
9170 mData->m_strConfigFileFull.c_str());
9171 }
9172
9173 if ( !i_isSnapshotMachine()
9174 && medium->i_getChildren().size() != 0
9175 )
9176 return setError(E_FAIL,
9177 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9178 "because it has %d differencing child hard disks"),
9179 medium->i_getLocationFull().c_str(),
9180 dev.uuid.raw(),
9181 mUserData->s.strName.c_str(),
9182 mData->m_strConfigFileFull.c_str(),
9183 medium->i_getChildren().size());
9184
9185 if (i_findAttachment(mMediaData->mAttachments,
9186 medium))
9187 return setError(E_FAIL,
9188 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9189 medium->i_getLocationFull().c_str(),
9190 dev.uuid.raw(),
9191 mUserData->s.strName.c_str(),
9192 mData->m_strConfigFileFull.c_str());
9193
9194 break;
9195 }
9196
9197 default:
9198 return setError(E_FAIL,
9199 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9200 medium->i_getLocationFull().c_str(),
9201 mUserData->s.strName.c_str(),
9202 mData->m_strConfigFileFull.c_str());
9203 }
9204
9205 if (FAILED(rc))
9206 break;
9207
9208 /* Bandwidth groups are loaded at this point. */
9209 ComObjPtr<BandwidthGroup> pBwGroup;
9210
9211 if (!dev.strBwGroup.isEmpty())
9212 {
9213 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9214 if (FAILED(rc))
9215 return setError(E_FAIL,
9216 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9217 medium->i_getLocationFull().c_str(),
9218 dev.strBwGroup.c_str(),
9219 mUserData->s.strName.c_str(),
9220 mData->m_strConfigFileFull.c_str());
9221 pBwGroup->i_reference();
9222 }
9223
9224 const Bstr controllerName = aStorageController->i_getName();
9225 ComObjPtr<MediumAttachment> pAttachment;
9226 pAttachment.createObject();
9227 rc = pAttachment->init(this,
9228 medium,
9229 controllerName,
9230 dev.lPort,
9231 dev.lDevice,
9232 dev.deviceType,
9233 false,
9234 dev.fPassThrough,
9235 dev.fTempEject,
9236 dev.fNonRotational,
9237 dev.fDiscard,
9238 dev.fHotPluggable,
9239 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9240 if (FAILED(rc)) break;
9241
9242 /* associate the medium with this machine and snapshot */
9243 if (!medium.isNull())
9244 {
9245 AutoCaller medCaller(medium);
9246 if (FAILED(medCaller.rc())) return medCaller.rc();
9247 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9248
9249 if (i_isSnapshotMachine())
9250 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9251 else
9252 rc = medium->i_addBackReference(mData->mUuid);
9253 /* If the medium->addBackReference fails it sets an appropriate
9254 * error message, so no need to do any guesswork here. */
9255
9256 if (puuidRegistry)
9257 // caller wants registry ID to be set on all attached media (OVF import case)
9258 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9259 }
9260
9261 if (FAILED(rc))
9262 break;
9263
9264 /* back up mMediaData to let registeredInit() properly rollback on failure
9265 * (= limited accessibility) */
9266 i_setModified(IsModified_Storage);
9267 mMediaData.backup();
9268 mMediaData->mAttachments.push_back(pAttachment);
9269 }
9270
9271 return rc;
9272}
9273
9274/**
9275 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9276 *
9277 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9278 * @param aSnapshot where to return the found snapshot
9279 * @param aSetError true to set extended error info on failure
9280 */
9281HRESULT Machine::i_findSnapshotById(const Guid &aId,
9282 ComObjPtr<Snapshot> &aSnapshot,
9283 bool aSetError /* = false */)
9284{
9285 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9286
9287 if (!mData->mFirstSnapshot)
9288 {
9289 if (aSetError)
9290 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9291 return E_FAIL;
9292 }
9293
9294 if (aId.isZero())
9295 aSnapshot = mData->mFirstSnapshot;
9296 else
9297 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9298
9299 if (!aSnapshot)
9300 {
9301 if (aSetError)
9302 return setError(E_FAIL,
9303 tr("Could not find a snapshot with UUID {%s}"),
9304 aId.toString().c_str());
9305 return E_FAIL;
9306 }
9307
9308 return S_OK;
9309}
9310
9311/**
9312 * Returns the snapshot with the given name or fails of no such snapshot.
9313 *
9314 * @param aName snapshot name to find
9315 * @param aSnapshot where to return the found snapshot
9316 * @param aSetError true to set extended error info on failure
9317 */
9318HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9319 ComObjPtr<Snapshot> &aSnapshot,
9320 bool aSetError /* = false */)
9321{
9322 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9323
9324 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9325
9326 if (!mData->mFirstSnapshot)
9327 {
9328 if (aSetError)
9329 return setError(VBOX_E_OBJECT_NOT_FOUND,
9330 tr("This machine does not have any snapshots"));
9331 return VBOX_E_OBJECT_NOT_FOUND;
9332 }
9333
9334 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9335
9336 if (!aSnapshot)
9337 {
9338 if (aSetError)
9339 return setError(VBOX_E_OBJECT_NOT_FOUND,
9340 tr("Could not find a snapshot named '%s'"), strName.c_str());
9341 return VBOX_E_OBJECT_NOT_FOUND;
9342 }
9343
9344 return S_OK;
9345}
9346
9347/**
9348 * Returns a storage controller object with the given name.
9349 *
9350 * @param aName storage controller name to find
9351 * @param aStorageController where to return the found storage controller
9352 * @param aSetError true to set extended error info on failure
9353 */
9354HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9355 ComObjPtr<StorageController> &aStorageController,
9356 bool aSetError /* = false */)
9357{
9358 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9359
9360 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9361 it != mStorageControllers->end();
9362 ++it)
9363 {
9364 if ((*it)->i_getName() == aName)
9365 {
9366 aStorageController = (*it);
9367 return S_OK;
9368 }
9369 }
9370
9371 if (aSetError)
9372 return setError(VBOX_E_OBJECT_NOT_FOUND,
9373 tr("Could not find a storage controller named '%s'"),
9374 aName.c_str());
9375 return VBOX_E_OBJECT_NOT_FOUND;
9376}
9377
9378/**
9379 * Returns a USB controller object with the given name.
9380 *
9381 * @param aName USB controller name to find
9382 * @param aUSBController where to return the found USB controller
9383 * @param aSetError true to set extended error info on failure
9384 */
9385HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9386 ComObjPtr<USBController> &aUSBController,
9387 bool aSetError /* = false */)
9388{
9389 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9390
9391 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9392 it != mUSBControllers->end();
9393 ++it)
9394 {
9395 if ((*it)->i_getName() == aName)
9396 {
9397 aUSBController = (*it);
9398 return S_OK;
9399 }
9400 }
9401
9402 if (aSetError)
9403 return setError(VBOX_E_OBJECT_NOT_FOUND,
9404 tr("Could not find a storage controller named '%s'"),
9405 aName.c_str());
9406 return VBOX_E_OBJECT_NOT_FOUND;
9407}
9408
9409/**
9410 * Returns the number of USB controller instance of the given type.
9411 *
9412 * @param enmType USB controller type.
9413 */
9414ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9415{
9416 ULONG cCtrls = 0;
9417
9418 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9419 it != mUSBControllers->end();
9420 ++it)
9421 {
9422 if ((*it)->i_getControllerType() == enmType)
9423 cCtrls++;
9424 }
9425
9426 return cCtrls;
9427}
9428
9429HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9430 MediaData::AttachmentList &atts)
9431{
9432 AutoCaller autoCaller(this);
9433 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9434
9435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9436
9437 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9438 it != mMediaData->mAttachments.end();
9439 ++it)
9440 {
9441 const ComObjPtr<MediumAttachment> &pAtt = *it;
9442 // should never happen, but deal with NULL pointers in the list.
9443 AssertStmt(!pAtt.isNull(), continue);
9444
9445 // getControllerName() needs caller+read lock
9446 AutoCaller autoAttCaller(pAtt);
9447 if (FAILED(autoAttCaller.rc()))
9448 {
9449 atts.clear();
9450 return autoAttCaller.rc();
9451 }
9452 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9453
9454 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9455 atts.push_back(pAtt);
9456 }
9457
9458 return S_OK;
9459}
9460
9461
9462/**
9463 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9464 * file if the machine name was changed and about creating a new settings file
9465 * if this is a new machine.
9466 *
9467 * @note Must be never called directly but only from #saveSettings().
9468 */
9469HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9470{
9471 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9472
9473 HRESULT rc = S_OK;
9474
9475 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9476
9477 /// @todo need to handle primary group change, too
9478
9479 /* attempt to rename the settings file if machine name is changed */
9480 if ( mUserData->s.fNameSync
9481 && mUserData.isBackedUp()
9482 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9483 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9484 )
9485 {
9486 bool dirRenamed = false;
9487 bool fileRenamed = false;
9488
9489 Utf8Str configFile, newConfigFile;
9490 Utf8Str configFilePrev, newConfigFilePrev;
9491 Utf8Str configDir, newConfigDir;
9492
9493 do
9494 {
9495 int vrc = VINF_SUCCESS;
9496
9497 Utf8Str name = mUserData.backedUpData()->s.strName;
9498 Utf8Str newName = mUserData->s.strName;
9499 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9500 if (group == "/")
9501 group.setNull();
9502 Utf8Str newGroup = mUserData->s.llGroups.front();
9503 if (newGroup == "/")
9504 newGroup.setNull();
9505
9506 configFile = mData->m_strConfigFileFull;
9507
9508 /* first, rename the directory if it matches the group and machine name */
9509 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9510 group.c_str(), RTPATH_DELIMITER, name.c_str());
9511 /** @todo hack, make somehow use of ComposeMachineFilename */
9512 if (mUserData->s.fDirectoryIncludesUUID)
9513 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9514 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9515 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9516 /** @todo hack, make somehow use of ComposeMachineFilename */
9517 if (mUserData->s.fDirectoryIncludesUUID)
9518 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9519 configDir = configFile;
9520 configDir.stripFilename();
9521 newConfigDir = configDir;
9522 if ( configDir.length() >= groupPlusName.length()
9523 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9524 groupPlusName.c_str()))
9525 {
9526 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9527 Utf8Str newConfigBaseDir(newConfigDir);
9528 newConfigDir.append(newGroupPlusName);
9529 /* consistency: use \ if appropriate on the platform */
9530 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9531 /* new dir and old dir cannot be equal here because of 'if'
9532 * above and because name != newName */
9533 Assert(configDir != newConfigDir);
9534 if (!fSettingsFileIsNew)
9535 {
9536 /* perform real rename only if the machine is not new */
9537 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9538 if ( vrc == VERR_FILE_NOT_FOUND
9539 || vrc == VERR_PATH_NOT_FOUND)
9540 {
9541 /* create the parent directory, then retry renaming */
9542 Utf8Str parent(newConfigDir);
9543 parent.stripFilename();
9544 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9545 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9546 }
9547 if (RT_FAILURE(vrc))
9548 {
9549 rc = setError(E_FAIL,
9550 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9551 configDir.c_str(),
9552 newConfigDir.c_str(),
9553 vrc);
9554 break;
9555 }
9556 /* delete subdirectories which are no longer needed */
9557 Utf8Str dir(configDir);
9558 dir.stripFilename();
9559 while (dir != newConfigBaseDir && dir != ".")
9560 {
9561 vrc = RTDirRemove(dir.c_str());
9562 if (RT_FAILURE(vrc))
9563 break;
9564 dir.stripFilename();
9565 }
9566 dirRenamed = true;
9567 }
9568 }
9569
9570 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9571 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9572
9573 /* then try to rename the settings file itself */
9574 if (newConfigFile != configFile)
9575 {
9576 /* get the path to old settings file in renamed directory */
9577 configFile = Utf8StrFmt("%s%c%s",
9578 newConfigDir.c_str(),
9579 RTPATH_DELIMITER,
9580 RTPathFilename(configFile.c_str()));
9581 if (!fSettingsFileIsNew)
9582 {
9583 /* perform real rename only if the machine is not new */
9584 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9585 if (RT_FAILURE(vrc))
9586 {
9587 rc = setError(E_FAIL,
9588 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9589 configFile.c_str(),
9590 newConfigFile.c_str(),
9591 vrc);
9592 break;
9593 }
9594 fileRenamed = true;
9595 configFilePrev = configFile;
9596 configFilePrev += "-prev";
9597 newConfigFilePrev = newConfigFile;
9598 newConfigFilePrev += "-prev";
9599 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9600 }
9601 }
9602
9603 // update m_strConfigFileFull amd mConfigFile
9604 mData->m_strConfigFileFull = newConfigFile;
9605 // compute the relative path too
9606 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9607
9608 // store the old and new so that VirtualBox::i_saveSettings() can update
9609 // the media registry
9610 if ( mData->mRegistered
9611 && (configDir != newConfigDir || configFile != newConfigFile))
9612 {
9613 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9614
9615 if (pfNeedsGlobalSaveSettings)
9616 *pfNeedsGlobalSaveSettings = true;
9617 }
9618
9619 // in the saved state file path, replace the old directory with the new directory
9620 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9621 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9622
9623 // and do the same thing for the saved state file paths of all the online snapshots
9624 if (mData->mFirstSnapshot)
9625 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9626 newConfigDir.c_str());
9627 }
9628 while (0);
9629
9630 if (FAILED(rc))
9631 {
9632 /* silently try to rename everything back */
9633 if (fileRenamed)
9634 {
9635 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9636 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9637 }
9638 if (dirRenamed)
9639 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9640 }
9641
9642 if (FAILED(rc)) return rc;
9643 }
9644
9645 if (fSettingsFileIsNew)
9646 {
9647 /* create a virgin config file */
9648 int vrc = VINF_SUCCESS;
9649
9650 /* ensure the settings directory exists */
9651 Utf8Str path(mData->m_strConfigFileFull);
9652 path.stripFilename();
9653 if (!RTDirExists(path.c_str()))
9654 {
9655 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9656 if (RT_FAILURE(vrc))
9657 {
9658 return setError(E_FAIL,
9659 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9660 path.c_str(),
9661 vrc);
9662 }
9663 }
9664
9665 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9666 path = Utf8Str(mData->m_strConfigFileFull);
9667 RTFILE f = NIL_RTFILE;
9668 vrc = RTFileOpen(&f, path.c_str(),
9669 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9670 if (RT_FAILURE(vrc))
9671 return setError(E_FAIL,
9672 tr("Could not create the settings file '%s' (%Rrc)"),
9673 path.c_str(),
9674 vrc);
9675 RTFileClose(f);
9676 }
9677
9678 return rc;
9679}
9680
9681/**
9682 * Saves and commits machine data, user data and hardware data.
9683 *
9684 * Note that on failure, the data remains uncommitted.
9685 *
9686 * @a aFlags may combine the following flags:
9687 *
9688 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9689 * Used when saving settings after an operation that makes them 100%
9690 * correspond to the settings from the current snapshot.
9691 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9692 * #isReallyModified() returns false. This is necessary for cases when we
9693 * change machine data directly, not through the backup()/commit() mechanism.
9694 * - SaveS_Force: settings will be saved without doing a deep compare of the
9695 * settings structures. This is used when this is called because snapshots
9696 * have changed to avoid the overhead of the deep compare.
9697 *
9698 * @note Must be called from under this object's write lock. Locks children for
9699 * writing.
9700 *
9701 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9702 * initialized to false and that will be set to true by this function if
9703 * the caller must invoke VirtualBox::i_saveSettings() because the global
9704 * settings have changed. This will happen if a machine rename has been
9705 * saved and the global machine and media registries will therefore need
9706 * updating.
9707 */
9708HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9709 int aFlags /*= 0*/)
9710{
9711 LogFlowThisFuncEnter();
9712
9713 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9714
9715 /* make sure child objects are unable to modify the settings while we are
9716 * saving them */
9717 i_ensureNoStateDependencies();
9718
9719 AssertReturn(!i_isSnapshotMachine(),
9720 E_FAIL);
9721
9722 HRESULT rc = S_OK;
9723 bool fNeedsWrite = false;
9724
9725 /* First, prepare to save settings. It will care about renaming the
9726 * settings directory and file if the machine name was changed and about
9727 * creating a new settings file if this is a new machine. */
9728 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9729 if (FAILED(rc)) return rc;
9730
9731 // keep a pointer to the current settings structures
9732 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9733 settings::MachineConfigFile *pNewConfig = NULL;
9734
9735 try
9736 {
9737 // make a fresh one to have everyone write stuff into
9738 pNewConfig = new settings::MachineConfigFile(NULL);
9739 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9740
9741 // now go and copy all the settings data from COM to the settings structures
9742 // (this calles i_saveSettings() on all the COM objects in the machine)
9743 i_copyMachineDataToSettings(*pNewConfig);
9744
9745 if (aFlags & SaveS_ResetCurStateModified)
9746 {
9747 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9748 mData->mCurrentStateModified = FALSE;
9749 fNeedsWrite = true; // always, no need to compare
9750 }
9751 else if (aFlags & SaveS_Force)
9752 {
9753 fNeedsWrite = true; // always, no need to compare
9754 }
9755 else
9756 {
9757 if (!mData->mCurrentStateModified)
9758 {
9759 // do a deep compare of the settings that we just saved with the settings
9760 // previously stored in the config file; this invokes MachineConfigFile::operator==
9761 // which does a deep compare of all the settings, which is expensive but less expensive
9762 // than writing out XML in vain
9763 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9764
9765 // could still be modified if any settings changed
9766 mData->mCurrentStateModified = fAnySettingsChanged;
9767
9768 fNeedsWrite = fAnySettingsChanged;
9769 }
9770 else
9771 fNeedsWrite = true;
9772 }
9773
9774 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9775
9776 if (fNeedsWrite)
9777 // now spit it all out!
9778 pNewConfig->write(mData->m_strConfigFileFull);
9779
9780 mData->pMachineConfigFile = pNewConfig;
9781 delete pOldConfig;
9782 i_commit();
9783
9784 // after saving settings, we are no longer different from the XML on disk
9785 mData->flModifications = 0;
9786 }
9787 catch (HRESULT err)
9788 {
9789 // we assume that error info is set by the thrower
9790 rc = err;
9791
9792 // restore old config
9793 delete pNewConfig;
9794 mData->pMachineConfigFile = pOldConfig;
9795 }
9796 catch (...)
9797 {
9798 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9799 }
9800
9801 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9802 {
9803 /* Fire the data change event, even on failure (since we've already
9804 * committed all data). This is done only for SessionMachines because
9805 * mutable Machine instances are always not registered (i.e. private
9806 * to the client process that creates them) and thus don't need to
9807 * inform callbacks. */
9808 if (i_isSessionMachine())
9809 mParent->i_onMachineDataChange(mData->mUuid);
9810 }
9811
9812 LogFlowThisFunc(("rc=%08X\n", rc));
9813 LogFlowThisFuncLeave();
9814 return rc;
9815}
9816
9817/**
9818 * Implementation for saving the machine settings into the given
9819 * settings::MachineConfigFile instance. This copies machine extradata
9820 * from the previous machine config file in the instance data, if any.
9821 *
9822 * This gets called from two locations:
9823 *
9824 * -- Machine::i_saveSettings(), during the regular XML writing;
9825 *
9826 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9827 * exported to OVF and we write the VirtualBox proprietary XML
9828 * into a <vbox:Machine> tag.
9829 *
9830 * This routine fills all the fields in there, including snapshots, *except*
9831 * for the following:
9832 *
9833 * -- fCurrentStateModified. There is some special logic associated with that.
9834 *
9835 * The caller can then call MachineConfigFile::write() or do something else
9836 * with it.
9837 *
9838 * Caller must hold the machine lock!
9839 *
9840 * This throws XML errors and HRESULT, so the caller must have a catch block!
9841 */
9842void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9843{
9844 // deep copy extradata
9845 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9846
9847 config.uuid = mData->mUuid;
9848
9849 // copy name, description, OS type, teleport, UTC etc.
9850 config.machineUserData = mUserData->s;
9851
9852 // Encode the Icon Override data from Machine and store on config userdata.
9853 std::vector<BYTE> iconByte;
9854 getIcon(iconByte);
9855 ssize_t cbData = iconByte.size();
9856 if (cbData > 0)
9857 {
9858 ssize_t cchOut = RTBase64EncodedLength(cbData);
9859 Utf8Str strIconData;
9860 strIconData.reserve(cchOut+1);
9861 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9862 strIconData.mutableRaw(), strIconData.capacity(),
9863 NULL);
9864 if (RT_FAILURE(vrc))
9865 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9866 strIconData.jolt();
9867 config.machineUserData.ovIcon = strIconData;
9868 }
9869 else
9870 config.machineUserData.ovIcon.setNull();
9871
9872 if ( mData->mMachineState == MachineState_Saved
9873 || mData->mMachineState == MachineState_Restoring
9874 // when deleting a snapshot we may or may not have a saved state in the current state,
9875 // so let's not assert here please
9876 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9877 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9878 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9879 && (!mSSData->strStateFilePath.isEmpty())
9880 )
9881 )
9882 {
9883 Assert(!mSSData->strStateFilePath.isEmpty());
9884 /* try to make the file name relative to the settings file dir */
9885 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9886 }
9887 else
9888 {
9889 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9890 config.strStateFile.setNull();
9891 }
9892
9893 if (mData->mCurrentSnapshot)
9894 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9895 else
9896 config.uuidCurrentSnapshot.clear();
9897
9898 config.timeLastStateChange = mData->mLastStateChange;
9899 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9900 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9901
9902 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9903 if (FAILED(rc)) throw rc;
9904
9905 rc = i_saveStorageControllers(config.storageMachine);
9906 if (FAILED(rc)) throw rc;
9907
9908 // save machine's media registry if this is VirtualBox 4.0 or later
9909 if (config.canHaveOwnMediaRegistry())
9910 {
9911 // determine machine folder
9912 Utf8Str strMachineFolder = i_getSettingsFileFull();
9913 strMachineFolder.stripFilename();
9914 mParent->i_saveMediaRegistry(config.mediaRegistry,
9915 i_getId(), // only media with registry ID == machine UUID
9916 strMachineFolder);
9917 // this throws HRESULT
9918 }
9919
9920 // save snapshots
9921 rc = i_saveAllSnapshots(config);
9922 if (FAILED(rc)) throw rc;
9923}
9924
9925/**
9926 * Saves all snapshots of the machine into the given machine config file. Called
9927 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9928 * @param config
9929 * @return
9930 */
9931HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9932{
9933 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9934
9935 HRESULT rc = S_OK;
9936
9937 try
9938 {
9939 config.llFirstSnapshot.clear();
9940
9941 if (mData->mFirstSnapshot)
9942 {
9943 settings::Snapshot snapNew;
9944 config.llFirstSnapshot.push_back(snapNew);
9945
9946 // get reference to the fresh copy of the snapshot on the list and
9947 // work on that copy directly to avoid excessive copying later
9948 settings::Snapshot &snap = config.llFirstSnapshot.front();
9949
9950 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9951 if (FAILED(rc)) throw rc;
9952 }
9953
9954// if (mType == IsSessionMachine)
9955// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9956
9957 }
9958 catch (HRESULT err)
9959 {
9960 /* we assume that error info is set by the thrower */
9961 rc = err;
9962 }
9963 catch (...)
9964 {
9965 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9966 }
9967
9968 return rc;
9969}
9970
9971/**
9972 * Saves the VM hardware configuration. It is assumed that the
9973 * given node is empty.
9974 *
9975 * @param data Reference to the settings object for the hardware config.
9976 * @param pDbg Pointer to the settings object for the debugging config
9977 * which happens to live in mHWData.
9978 * @param pAutostart Pointer to the settings object for the autostart config
9979 * which happens to live in mHWData.
9980 */
9981HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9982 settings::Autostart *pAutostart)
9983{
9984 HRESULT rc = S_OK;
9985
9986 try
9987 {
9988 /* The hardware version attribute (optional).
9989 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9990 if ( mHWData->mHWVersion == "1"
9991 && mSSData->strStateFilePath.isEmpty()
9992 )
9993 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
9994 other point needs to be found where this can be done. */
9995
9996 data.strVersion = mHWData->mHWVersion;
9997 data.uuid = mHWData->mHardwareUUID;
9998
9999 // CPU
10000 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10001 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10002 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10003 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10004 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10005 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10006 data.fPAE = !!mHWData->mPAEEnabled;
10007 data.enmLongMode = mHWData->mLongMode;
10008 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10009 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10010
10011 /* Standard and Extended CPUID leafs. */
10012 data.llCpuIdLeafs.clear();
10013 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10014 {
10015 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10016 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10017 }
10018 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10019 {
10020 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10021 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10022 }
10023
10024 data.cCPUs = mHWData->mCPUCount;
10025 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10026 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10027
10028 data.llCpus.clear();
10029 if (data.fCpuHotPlug)
10030 {
10031 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10032 {
10033 if (mHWData->mCPUAttached[idx])
10034 {
10035 settings::Cpu cpu;
10036 cpu.ulId = idx;
10037 data.llCpus.push_back(cpu);
10038 }
10039 }
10040 }
10041
10042 // memory
10043 data.ulMemorySizeMB = mHWData->mMemorySize;
10044 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10045
10046 // firmware
10047 data.firmwareType = mHWData->mFirmwareType;
10048
10049 // HID
10050 data.pointingHIDType = mHWData->mPointingHIDType;
10051 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10052
10053 // chipset
10054 data.chipsetType = mHWData->mChipsetType;
10055
10056 // paravirt
10057 data.paravirtProvider = mHWData->mParavirtProvider;
10058
10059
10060 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10061
10062 // HPET
10063 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10064
10065 // boot order
10066 data.mapBootOrder.clear();
10067 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10068 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10069
10070 // display
10071 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10072 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10073 data.cMonitors = mHWData->mMonitorCount;
10074 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10075 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10076 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10077 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10078 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10079 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10080 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10081 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10082 {
10083 if (mHWData->maVideoCaptureScreens[i])
10084 ASMBitSet(&data.u64VideoCaptureScreens, i);
10085 else
10086 ASMBitClear(&data.u64VideoCaptureScreens, i);
10087 }
10088 /* store relative video capture file if possible */
10089 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10090
10091 /* VRDEServer settings (optional) */
10092 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10093 if (FAILED(rc)) throw rc;
10094
10095 /* BIOS (required) */
10096 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10097 if (FAILED(rc)) throw rc;
10098
10099 /* USB Controller (required) */
10100 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10101 {
10102 ComObjPtr<USBController> ctrl = *it;
10103 settings::USBController settingsCtrl;
10104
10105 settingsCtrl.strName = ctrl->i_getName();
10106 settingsCtrl.enmType = ctrl->i_getControllerType();
10107
10108 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10109 }
10110
10111 /* USB device filters (required) */
10112 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10113 if (FAILED(rc)) throw rc;
10114
10115 /* Network adapters (required) */
10116 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10117 data.llNetworkAdapters.clear();
10118 /* Write out only the nominal number of network adapters for this
10119 * chipset type. Since Machine::commit() hasn't been called there
10120 * may be extra NIC settings in the vector. */
10121 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10122 {
10123 settings::NetworkAdapter nic;
10124 nic.ulSlot = (uint32_t)slot;
10125 /* paranoia check... must not be NULL, but must not crash either. */
10126 if (mNetworkAdapters[slot])
10127 {
10128 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10129 if (FAILED(rc)) throw rc;
10130
10131 data.llNetworkAdapters.push_back(nic);
10132 }
10133 }
10134
10135 /* Serial ports */
10136 data.llSerialPorts.clear();
10137 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10138 {
10139 settings::SerialPort s;
10140 s.ulSlot = slot;
10141 rc = mSerialPorts[slot]->i_saveSettings(s);
10142 if (FAILED(rc)) return rc;
10143
10144 data.llSerialPorts.push_back(s);
10145 }
10146
10147 /* Parallel ports */
10148 data.llParallelPorts.clear();
10149 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10150 {
10151 settings::ParallelPort p;
10152 p.ulSlot = slot;
10153 rc = mParallelPorts[slot]->i_saveSettings(p);
10154 if (FAILED(rc)) return rc;
10155
10156 data.llParallelPorts.push_back(p);
10157 }
10158
10159 /* Audio adapter */
10160 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10161 if (FAILED(rc)) return rc;
10162
10163 /* Shared folders */
10164 data.llSharedFolders.clear();
10165 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10166 it != mHWData->mSharedFolders.end();
10167 ++it)
10168 {
10169 SharedFolder *pSF = *it;
10170 AutoCaller sfCaller(pSF);
10171 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10172 settings::SharedFolder sf;
10173 sf.strName = pSF->i_getName();
10174 sf.strHostPath = pSF->i_getHostPath();
10175 sf.fWritable = !!pSF->i_isWritable();
10176 sf.fAutoMount = !!pSF->i_isAutoMounted();
10177
10178 data.llSharedFolders.push_back(sf);
10179 }
10180
10181 // clipboard
10182 data.clipboardMode = mHWData->mClipboardMode;
10183
10184 // drag'n'drop
10185 data.dndMode = mHWData->mDnDMode;
10186
10187 /* Guest */
10188 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10189
10190 // IO settings
10191 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10192 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10193
10194 /* BandwidthControl (required) */
10195 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10196 if (FAILED(rc)) throw rc;
10197
10198 /* Host PCI devices */
10199 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10200 it != mHWData->mPCIDeviceAssignments.end();
10201 ++it)
10202 {
10203 ComObjPtr<PCIDeviceAttachment> pda = *it;
10204 settings::HostPCIDeviceAttachment hpda;
10205
10206 rc = pda->i_saveSettings(hpda);
10207 if (FAILED(rc)) throw rc;
10208
10209 data.pciAttachments.push_back(hpda);
10210 }
10211
10212
10213 // guest properties
10214 data.llGuestProperties.clear();
10215#ifdef VBOX_WITH_GUEST_PROPS
10216 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10217 it != mHWData->mGuestProperties.end();
10218 ++it)
10219 {
10220 HWData::GuestProperty property = it->second;
10221
10222 /* Remove transient guest properties at shutdown unless we
10223 * are saving state */
10224 if ( ( mData->mMachineState == MachineState_PoweredOff
10225 || mData->mMachineState == MachineState_Aborted
10226 || mData->mMachineState == MachineState_Teleported)
10227 && ( property.mFlags & guestProp::TRANSIENT
10228 || property.mFlags & guestProp::TRANSRESET))
10229 continue;
10230 settings::GuestProperty prop;
10231 prop.strName = it->first;
10232 prop.strValue = property.strValue;
10233 prop.timestamp = property.mTimestamp;
10234 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10235 guestProp::writeFlags(property.mFlags, szFlags);
10236 prop.strFlags = szFlags;
10237
10238 data.llGuestProperties.push_back(prop);
10239 }
10240
10241 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10242 /* I presume this doesn't require a backup(). */
10243 mData->mGuestPropertiesModified = FALSE;
10244#endif /* VBOX_WITH_GUEST_PROPS defined */
10245
10246 *pDbg = mHWData->mDebugging;
10247 *pAutostart = mHWData->mAutostart;
10248
10249 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10250 }
10251 catch(std::bad_alloc &)
10252 {
10253 return E_OUTOFMEMORY;
10254 }
10255
10256 AssertComRC(rc);
10257 return rc;
10258}
10259
10260/**
10261 * Saves the storage controller configuration.
10262 *
10263 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10264 */
10265HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10266{
10267 data.llStorageControllers.clear();
10268
10269 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10270 it != mStorageControllers->end();
10271 ++it)
10272 {
10273 HRESULT rc;
10274 ComObjPtr<StorageController> pCtl = *it;
10275
10276 settings::StorageController ctl;
10277 ctl.strName = pCtl->i_getName();
10278 ctl.controllerType = pCtl->i_getControllerType();
10279 ctl.storageBus = pCtl->i_getStorageBus();
10280 ctl.ulInstance = pCtl->i_getInstance();
10281 ctl.fBootable = pCtl->i_getBootable();
10282
10283 /* Save the port count. */
10284 ULONG portCount;
10285 rc = pCtl->COMGETTER(PortCount)(&portCount);
10286 ComAssertComRCRet(rc, rc);
10287 ctl.ulPortCount = portCount;
10288
10289 /* Save fUseHostIOCache */
10290 BOOL fUseHostIOCache;
10291 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10292 ComAssertComRCRet(rc, rc);
10293 ctl.fUseHostIOCache = !!fUseHostIOCache;
10294
10295 /* Save IDE emulation settings. */
10296 if (ctl.controllerType == StorageControllerType_IntelAhci)
10297 {
10298 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10299 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10300 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10301 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10302 )
10303 ComAssertComRCRet(rc, rc);
10304 }
10305
10306 /* save the devices now. */
10307 rc = i_saveStorageDevices(pCtl, ctl);
10308 ComAssertComRCRet(rc, rc);
10309
10310 data.llStorageControllers.push_back(ctl);
10311 }
10312
10313 return S_OK;
10314}
10315
10316/**
10317 * Saves the hard disk configuration.
10318 */
10319HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10320 settings::StorageController &data)
10321{
10322 MediaData::AttachmentList atts;
10323
10324 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10325 if (FAILED(rc)) return rc;
10326
10327 data.llAttachedDevices.clear();
10328 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10329 it != atts.end();
10330 ++it)
10331 {
10332 settings::AttachedDevice dev;
10333 IMediumAttachment *iA = *it;
10334 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10335 Medium *pMedium = pAttach->i_getMedium();
10336
10337 dev.deviceType = pAttach->i_getType();
10338 dev.lPort = pAttach->i_getPort();
10339 dev.lDevice = pAttach->i_getDevice();
10340 dev.fPassThrough = pAttach->i_getPassthrough();
10341 dev.fHotPluggable = pAttach->i_getHotPluggable();
10342 if (pMedium)
10343 {
10344 if (pMedium->i_isHostDrive())
10345 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10346 else
10347 dev.uuid = pMedium->i_getId();
10348 dev.fTempEject = pAttach->i_getTempEject();
10349 dev.fNonRotational = pAttach->i_getNonRotational();
10350 dev.fDiscard = pAttach->i_getDiscard();
10351 }
10352
10353 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10354
10355 data.llAttachedDevices.push_back(dev);
10356 }
10357
10358 return S_OK;
10359}
10360
10361/**
10362 * Saves machine state settings as defined by aFlags
10363 * (SaveSTS_* values).
10364 *
10365 * @param aFlags Combination of SaveSTS_* flags.
10366 *
10367 * @note Locks objects for writing.
10368 */
10369HRESULT Machine::i_saveStateSettings(int aFlags)
10370{
10371 if (aFlags == 0)
10372 return S_OK;
10373
10374 AutoCaller autoCaller(this);
10375 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10376
10377 /* This object's write lock is also necessary to serialize file access
10378 * (prevent concurrent reads and writes) */
10379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10380
10381 HRESULT rc = S_OK;
10382
10383 Assert(mData->pMachineConfigFile);
10384
10385 try
10386 {
10387 if (aFlags & SaveSTS_CurStateModified)
10388 mData->pMachineConfigFile->fCurrentStateModified = true;
10389
10390 if (aFlags & SaveSTS_StateFilePath)
10391 {
10392 if (!mSSData->strStateFilePath.isEmpty())
10393 /* try to make the file name relative to the settings file dir */
10394 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10395 else
10396 mData->pMachineConfigFile->strStateFile.setNull();
10397 }
10398
10399 if (aFlags & SaveSTS_StateTimeStamp)
10400 {
10401 Assert( mData->mMachineState != MachineState_Aborted
10402 || mSSData->strStateFilePath.isEmpty());
10403
10404 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10405
10406 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10407//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10408 }
10409
10410 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10411 }
10412 catch (...)
10413 {
10414 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10415 }
10416
10417 return rc;
10418}
10419
10420/**
10421 * Ensures that the given medium is added to a media registry. If this machine
10422 * was created with 4.0 or later, then the machine registry is used. Otherwise
10423 * the global VirtualBox media registry is used.
10424 *
10425 * Caller must NOT hold machine lock, media tree or any medium locks!
10426 *
10427 * @param pMedium
10428 */
10429void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10430{
10431 /* Paranoia checks: do not hold machine or media tree locks. */
10432 AssertReturnVoid(!isWriteLockOnCurrentThread());
10433 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10434
10435 ComObjPtr<Medium> pBase;
10436 {
10437 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10438 pBase = pMedium->i_getBase();
10439 }
10440
10441 /* Paranoia checks: do not hold medium locks. */
10442 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10443 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10444
10445 // decide which medium registry to use now that the medium is attached:
10446 Guid uuid;
10447 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10448 // machine XML is VirtualBox 4.0 or higher:
10449 uuid = i_getId(); // machine UUID
10450 else
10451 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10452
10453 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10454 mParent->i_markRegistryModified(uuid);
10455
10456 /* For more complex hard disk structures it can happen that the base
10457 * medium isn't yet associated with any medium registry. Do that now. */
10458 if (pMedium != pBase)
10459 {
10460 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10461 mParent->i_markRegistryModified(uuid);
10462 }
10463}
10464
10465/**
10466 * Creates differencing hard disks for all normal hard disks attached to this
10467 * machine and a new set of attachments to refer to created disks.
10468 *
10469 * Used when taking a snapshot or when deleting the current state. Gets called
10470 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10471 *
10472 * This method assumes that mMediaData contains the original hard disk attachments
10473 * it needs to create diffs for. On success, these attachments will be replaced
10474 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10475 * called to delete created diffs which will also rollback mMediaData and restore
10476 * whatever was backed up before calling this method.
10477 *
10478 * Attachments with non-normal hard disks are left as is.
10479 *
10480 * If @a aOnline is @c false then the original hard disks that require implicit
10481 * diffs will be locked for reading. Otherwise it is assumed that they are
10482 * already locked for writing (when the VM was started). Note that in the latter
10483 * case it is responsibility of the caller to lock the newly created diffs for
10484 * writing if this method succeeds.
10485 *
10486 * @param aProgress Progress object to run (must contain at least as
10487 * many operations left as the number of hard disks
10488 * attached).
10489 * @param aOnline Whether the VM was online prior to this operation.
10490 *
10491 * @note The progress object is not marked as completed, neither on success nor
10492 * on failure. This is a responsibility of the caller.
10493 *
10494 * @note Locks this object and the media tree for writing.
10495 */
10496HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10497 ULONG aWeight,
10498 bool aOnline)
10499{
10500 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10501
10502 AutoCaller autoCaller(this);
10503 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10504
10505 AutoMultiWriteLock2 alock(this->lockHandle(),
10506 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10507
10508 /* must be in a protective state because we release the lock below */
10509 AssertReturn( mData->mMachineState == MachineState_Saving
10510 || mData->mMachineState == MachineState_LiveSnapshotting
10511 || mData->mMachineState == MachineState_RestoringSnapshot
10512 || mData->mMachineState == MachineState_DeletingSnapshot
10513 , E_FAIL);
10514
10515 HRESULT rc = S_OK;
10516
10517 // use appropriate locked media map (online or offline)
10518 MediumLockListMap lockedMediaOffline;
10519 MediumLockListMap *lockedMediaMap;
10520 if (aOnline)
10521 lockedMediaMap = &mData->mSession.mLockedMedia;
10522 else
10523 lockedMediaMap = &lockedMediaOffline;
10524
10525 try
10526 {
10527 if (!aOnline)
10528 {
10529 /* lock all attached hard disks early to detect "in use"
10530 * situations before creating actual diffs */
10531 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10532 it != mMediaData->mAttachments.end();
10533 ++it)
10534 {
10535 MediumAttachment* pAtt = *it;
10536 if (pAtt->i_getType() == DeviceType_HardDisk)
10537 {
10538 Medium* pMedium = pAtt->i_getMedium();
10539 Assert(pMedium);
10540
10541 MediumLockList *pMediumLockList(new MediumLockList());
10542 alock.release();
10543 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10544 false /* fMediumLockWrite */,
10545 false /* fMediumLockWriteAll */,
10546 NULL,
10547 *pMediumLockList);
10548 alock.acquire();
10549 if (FAILED(rc))
10550 {
10551 delete pMediumLockList;
10552 throw rc;
10553 }
10554 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10555 if (FAILED(rc))
10556 {
10557 throw setError(rc,
10558 tr("Collecting locking information for all attached media failed"));
10559 }
10560 }
10561 }
10562
10563 /* Now lock all media. If this fails, nothing is locked. */
10564 alock.release();
10565 rc = lockedMediaMap->Lock();
10566 alock.acquire();
10567 if (FAILED(rc))
10568 {
10569 throw setError(rc,
10570 tr("Locking of attached media failed"));
10571 }
10572 }
10573
10574 /* remember the current list (note that we don't use backup() since
10575 * mMediaData may be already backed up) */
10576 MediaData::AttachmentList atts = mMediaData->mAttachments;
10577
10578 /* start from scratch */
10579 mMediaData->mAttachments.clear();
10580
10581 /* go through remembered attachments and create diffs for normal hard
10582 * disks and attach them */
10583 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10584 it != atts.end();
10585 ++it)
10586 {
10587 MediumAttachment* pAtt = *it;
10588
10589 DeviceType_T devType = pAtt->i_getType();
10590 Medium* pMedium = pAtt->i_getMedium();
10591
10592 if ( devType != DeviceType_HardDisk
10593 || pMedium == NULL
10594 || pMedium->i_getType() != MediumType_Normal)
10595 {
10596 /* copy the attachment as is */
10597
10598 /** @todo the progress object created in Console::TakeSnaphot
10599 * only expects operations for hard disks. Later other
10600 * device types need to show up in the progress as well. */
10601 if (devType == DeviceType_HardDisk)
10602 {
10603 if (pMedium == NULL)
10604 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10605 aWeight); // weight
10606 else
10607 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10608 pMedium->i_getBase()->i_getName().c_str()).raw(),
10609 aWeight); // weight
10610 }
10611
10612 mMediaData->mAttachments.push_back(pAtt);
10613 continue;
10614 }
10615
10616 /* need a diff */
10617 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10618 pMedium->i_getBase()->i_getName().c_str()).raw(),
10619 aWeight); // weight
10620
10621 Utf8Str strFullSnapshotFolder;
10622 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10623
10624 ComObjPtr<Medium> diff;
10625 diff.createObject();
10626 // store the diff in the same registry as the parent
10627 // (this cannot fail here because we can't create implicit diffs for
10628 // unregistered images)
10629 Guid uuidRegistryParent;
10630 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10631 Assert(fInRegistry); NOREF(fInRegistry);
10632 rc = diff->init(mParent,
10633 pMedium->i_getPreferredDiffFormat(),
10634 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10635 uuidRegistryParent,
10636 DeviceType_HardDisk);
10637 if (FAILED(rc)) throw rc;
10638
10639 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10640 * the push_back? Looks like we're going to release medium with the
10641 * wrong kind of lock (general issue with if we fail anywhere at all)
10642 * and an orphaned VDI in the snapshots folder. */
10643
10644 /* update the appropriate lock list */
10645 MediumLockList *pMediumLockList;
10646 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10647 AssertComRCThrowRC(rc);
10648 if (aOnline)
10649 {
10650 alock.release();
10651 /* The currently attached medium will be read-only, change
10652 * the lock type to read. */
10653 rc = pMediumLockList->Update(pMedium, false);
10654 alock.acquire();
10655 AssertComRCThrowRC(rc);
10656 }
10657
10658 /* release the locks before the potentially lengthy operation */
10659 alock.release();
10660 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10661 pMediumLockList,
10662 NULL /* aProgress */,
10663 true /* aWait */);
10664 alock.acquire();
10665 if (FAILED(rc)) throw rc;
10666
10667 /* actual lock list update is done in Medium::commitMedia */
10668
10669 rc = diff->i_addBackReference(mData->mUuid);
10670 AssertComRCThrowRC(rc);
10671
10672 /* add a new attachment */
10673 ComObjPtr<MediumAttachment> attachment;
10674 attachment.createObject();
10675 rc = attachment->init(this,
10676 diff,
10677 pAtt->i_getControllerName(),
10678 pAtt->i_getPort(),
10679 pAtt->i_getDevice(),
10680 DeviceType_HardDisk,
10681 true /* aImplicit */,
10682 false /* aPassthrough */,
10683 false /* aTempEject */,
10684 pAtt->i_getNonRotational(),
10685 pAtt->i_getDiscard(),
10686 pAtt->i_getHotPluggable(),
10687 pAtt->i_getBandwidthGroup());
10688 if (FAILED(rc)) throw rc;
10689
10690 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10691 AssertComRCThrowRC(rc);
10692 mMediaData->mAttachments.push_back(attachment);
10693 }
10694 }
10695 catch (HRESULT aRC) { rc = aRC; }
10696
10697 /* unlock all hard disks we locked when there is no VM */
10698 if (!aOnline)
10699 {
10700 ErrorInfoKeeper eik;
10701
10702 HRESULT rc1 = lockedMediaMap->Clear();
10703 AssertComRC(rc1);
10704 }
10705
10706 return rc;
10707}
10708
10709/**
10710 * Deletes implicit differencing hard disks created either by
10711 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10712 *
10713 * Note that to delete hard disks created by #AttachDevice() this method is
10714 * called from #fixupMedia() when the changes are rolled back.
10715 *
10716 * @note Locks this object and the media tree for writing.
10717 */
10718HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10719{
10720 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10721
10722 AutoCaller autoCaller(this);
10723 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10724
10725 AutoMultiWriteLock2 alock(this->lockHandle(),
10726 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10727
10728 /* We absolutely must have backed up state. */
10729 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10730
10731 /* Check if there are any implicitly created diff images. */
10732 bool fImplicitDiffs = false;
10733 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10734 it != mMediaData->mAttachments.end();
10735 ++it)
10736 {
10737 const ComObjPtr<MediumAttachment> &pAtt = *it;
10738 if (pAtt->i_isImplicit())
10739 {
10740 fImplicitDiffs = true;
10741 break;
10742 }
10743 }
10744 /* If there is nothing to do, leave early. This saves lots of image locking
10745 * effort. It also avoids a MachineStateChanged event without real reason.
10746 * This is important e.g. when loading a VM config, because there should be
10747 * no events. Otherwise API clients can become thoroughly confused for
10748 * inaccessible VMs (the code for loading VM configs uses this method for
10749 * cleanup if the config makes no sense), as they take such events as an
10750 * indication that the VM is alive, and they would force the VM config to
10751 * be reread, leading to an endless loop. */
10752 if (!fImplicitDiffs)
10753 return S_OK;
10754
10755 HRESULT rc = S_OK;
10756 MachineState_T oldState = mData->mMachineState;
10757
10758 /* will release the lock before the potentially lengthy operation,
10759 * so protect with the special state (unless already protected) */
10760 if ( oldState != MachineState_Saving
10761 && oldState != MachineState_LiveSnapshotting
10762 && oldState != MachineState_RestoringSnapshot
10763 && oldState != MachineState_DeletingSnapshot
10764 && oldState != MachineState_DeletingSnapshotOnline
10765 && oldState != MachineState_DeletingSnapshotPaused
10766 )
10767 i_setMachineState(MachineState_SettingUp);
10768
10769 // use appropriate locked media map (online or offline)
10770 MediumLockListMap lockedMediaOffline;
10771 MediumLockListMap *lockedMediaMap;
10772 if (aOnline)
10773 lockedMediaMap = &mData->mSession.mLockedMedia;
10774 else
10775 lockedMediaMap = &lockedMediaOffline;
10776
10777 try
10778 {
10779 if (!aOnline)
10780 {
10781 /* lock all attached hard disks early to detect "in use"
10782 * situations before deleting actual diffs */
10783 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10784 it != mMediaData->mAttachments.end();
10785 ++it)
10786 {
10787 MediumAttachment* pAtt = *it;
10788 if (pAtt->i_getType() == DeviceType_HardDisk)
10789 {
10790 Medium* pMedium = pAtt->i_getMedium();
10791 Assert(pMedium);
10792
10793 MediumLockList *pMediumLockList(new MediumLockList());
10794 alock.release();
10795 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10796 false /* fMediumLockWrite */,
10797 false /* fMediumLockWriteAll */,
10798 NULL,
10799 *pMediumLockList);
10800 alock.acquire();
10801
10802 if (FAILED(rc))
10803 {
10804 delete pMediumLockList;
10805 throw rc;
10806 }
10807
10808 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10809 if (FAILED(rc))
10810 throw rc;
10811 }
10812 }
10813
10814 if (FAILED(rc))
10815 throw rc;
10816 } // end of offline
10817
10818 /* Lock lists are now up to date and include implicitly created media */
10819
10820 /* Go through remembered attachments and delete all implicitly created
10821 * diffs and fix up the attachment information */
10822 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10823 MediaData::AttachmentList implicitAtts;
10824 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10825 it != mMediaData->mAttachments.end();
10826 ++it)
10827 {
10828 ComObjPtr<MediumAttachment> pAtt = *it;
10829 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10830 if (pMedium.isNull())
10831 continue;
10832
10833 // Implicit attachments go on the list for deletion and back references are removed.
10834 if (pAtt->i_isImplicit())
10835 {
10836 /* Deassociate and mark for deletion */
10837 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10838 rc = pMedium->i_removeBackReference(mData->mUuid);
10839 if (FAILED(rc))
10840 throw rc;
10841 implicitAtts.push_back(pAtt);
10842 continue;
10843 }
10844
10845 /* Was this medium attached before? */
10846 if (!i_findAttachment(oldAtts, pMedium))
10847 {
10848 /* no: de-associate */
10849 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10850 rc = pMedium->i_removeBackReference(mData->mUuid);
10851 if (FAILED(rc))
10852 throw rc;
10853 continue;
10854 }
10855 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10856 }
10857
10858 /* If there are implicit attachments to delete, throw away the lock
10859 * map contents (which will unlock all media) since the medium
10860 * attachments will be rolled back. Below we need to completely
10861 * recreate the lock map anyway since it is infinitely complex to
10862 * do this incrementally (would need reconstructing each attachment
10863 * change, which would be extremely hairy). */
10864 if (implicitAtts.size() != 0)
10865 {
10866 ErrorInfoKeeper eik;
10867
10868 HRESULT rc1 = lockedMediaMap->Clear();
10869 AssertComRC(rc1);
10870 }
10871
10872 /* rollback hard disk changes */
10873 mMediaData.rollback();
10874
10875 MultiResult mrc(S_OK);
10876
10877 // Delete unused implicit diffs.
10878 if (implicitAtts.size() != 0)
10879 {
10880 alock.release();
10881
10882 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10883 {
10884 // Remove medium associated with this attachment.
10885 ComObjPtr<MediumAttachment> pAtt = *it;
10886 Assert(pAtt);
10887 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10888 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10889 Assert(pMedium);
10890
10891 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10892 // continue on delete failure, just collect error messages
10893 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10894 pMedium->i_getLocationFull().c_str() ));
10895 mrc = rc;
10896 }
10897
10898 alock.acquire();
10899
10900 /* if there is a VM recreate media lock map as mentioned above,
10901 * otherwise it is a waste of time and we leave things unlocked */
10902 if (aOnline)
10903 {
10904 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10905 /* must never be NULL, but better safe than sorry */
10906 if (!pMachine.isNull())
10907 {
10908 alock.release();
10909 rc = mData->mSession.mMachine->i_lockMedia();
10910 alock.acquire();
10911 if (FAILED(rc))
10912 throw rc;
10913 }
10914 }
10915 }
10916 }
10917 catch (HRESULT aRC) {rc = aRC;}
10918
10919 if (mData->mMachineState == MachineState_SettingUp)
10920 i_setMachineState(oldState);
10921
10922 /* unlock all hard disks we locked when there is no VM */
10923 if (!aOnline)
10924 {
10925 ErrorInfoKeeper eik;
10926
10927 HRESULT rc1 = lockedMediaMap->Clear();
10928 AssertComRC(rc1);
10929 }
10930
10931 return rc;
10932}
10933
10934
10935/**
10936 * Looks through the given list of media attachments for one with the given parameters
10937 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10938 * can be searched as well if needed.
10939 *
10940 * @param list
10941 * @param aControllerName
10942 * @param aControllerPort
10943 * @param aDevice
10944 * @return
10945 */
10946MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10947 IN_BSTR aControllerName,
10948 LONG aControllerPort,
10949 LONG aDevice)
10950{
10951 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10952 {
10953 MediumAttachment *pAttach = *it;
10954 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10955 return pAttach;
10956 }
10957
10958 return NULL;
10959}
10960
10961/**
10962 * Looks through the given list of media attachments for one with the given parameters
10963 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10964 * can be searched as well if needed.
10965 *
10966 * @param list
10967 * @param aControllerName
10968 * @param aControllerPort
10969 * @param aDevice
10970 * @return
10971 */
10972MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10973 ComObjPtr<Medium> pMedium)
10974{
10975 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10976 {
10977 MediumAttachment *pAttach = *it;
10978 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
10979 if (pMediumThis == pMedium)
10980 return pAttach;
10981 }
10982
10983 return NULL;
10984}
10985
10986/**
10987 * Looks through the given list of media attachments for one with the given parameters
10988 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10989 * can be searched as well if needed.
10990 *
10991 * @param list
10992 * @param aControllerName
10993 * @param aControllerPort
10994 * @param aDevice
10995 * @return
10996 */
10997MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10998 Guid &id)
10999{
11000 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11001 {
11002 MediumAttachment *pAttach = *it;
11003 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11004 if (pMediumThis->i_getId() == id)
11005 return pAttach;
11006 }
11007
11008 return NULL;
11009}
11010
11011/**
11012 * Main implementation for Machine::DetachDevice. This also gets called
11013 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11014 *
11015 * @param pAttach Medium attachment to detach.
11016 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11017 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11018 * SnapshotMachine, and this must be its snapshot.
11019 * @return
11020 */
11021HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11022 AutoWriteLock &writeLock,
11023 Snapshot *pSnapshot)
11024{
11025 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11026 DeviceType_T mediumType = pAttach->i_getType();
11027
11028 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11029
11030 if (pAttach->i_isImplicit())
11031 {
11032 /* attempt to implicitly delete the implicitly created diff */
11033
11034 /// @todo move the implicit flag from MediumAttachment to Medium
11035 /// and forbid any hard disk operation when it is implicit. Or maybe
11036 /// a special media state for it to make it even more simple.
11037
11038 Assert(mMediaData.isBackedUp());
11039
11040 /* will release the lock before the potentially lengthy operation, so
11041 * protect with the special state */
11042 MachineState_T oldState = mData->mMachineState;
11043 i_setMachineState(MachineState_SettingUp);
11044
11045 writeLock.release();
11046
11047 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11048 true /*aWait*/);
11049
11050 writeLock.acquire();
11051
11052 i_setMachineState(oldState);
11053
11054 if (FAILED(rc)) return rc;
11055 }
11056
11057 i_setModified(IsModified_Storage);
11058 mMediaData.backup();
11059 mMediaData->mAttachments.remove(pAttach);
11060
11061 if (!oldmedium.isNull())
11062 {
11063 // if this is from a snapshot, do not defer detachment to commitMedia()
11064 if (pSnapshot)
11065 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11066 // else if non-hard disk media, do not defer detachment to commitMedia() either
11067 else if (mediumType != DeviceType_HardDisk)
11068 oldmedium->i_removeBackReference(mData->mUuid);
11069 }
11070
11071 return S_OK;
11072}
11073
11074/**
11075 * Goes thru all media of the given list and
11076 *
11077 * 1) calls i_detachDevice() on each of them for this machine and
11078 * 2) adds all Medium objects found in the process to the given list,
11079 * depending on cleanupMode.
11080 *
11081 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11082 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11083 * media to the list.
11084 *
11085 * This gets called from Machine::Unregister, both for the actual Machine and
11086 * the SnapshotMachine objects that might be found in the snapshots.
11087 *
11088 * Requires caller and locking. The machine lock must be passed in because it
11089 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11090 *
11091 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11092 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11093 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11094 * Full, then all media get added;
11095 * otherwise no media get added.
11096 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11097 * @return
11098 */
11099HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11100 Snapshot *pSnapshot,
11101 CleanupMode_T cleanupMode,
11102 MediaList &llMedia)
11103{
11104 Assert(isWriteLockOnCurrentThread());
11105
11106 HRESULT rc;
11107
11108 // make a temporary list because i_detachDevice invalidates iterators into
11109 // mMediaData->mAttachments
11110 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11111
11112 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11113 {
11114 ComObjPtr<MediumAttachment> &pAttach = *it;
11115 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11116
11117 if (!pMedium.isNull())
11118 {
11119 AutoCaller mac(pMedium);
11120 if (FAILED(mac.rc())) return mac.rc();
11121 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11122 DeviceType_T devType = pMedium->i_getDeviceType();
11123 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11124 && devType == DeviceType_HardDisk)
11125 || (cleanupMode == CleanupMode_Full)
11126 )
11127 {
11128 llMedia.push_back(pMedium);
11129 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11130 /* Not allowed to keep this lock as below we need the parent
11131 * medium lock, and the lock order is parent to child. */
11132 lock.release();
11133 /*
11134 * Search for medias which are not attached to any machine, but
11135 * in the chain to an attached disk. Mediums are only consided
11136 * if they are:
11137 * - have only one child
11138 * - no references to any machines
11139 * - are of normal medium type
11140 */
11141 while (!pParent.isNull())
11142 {
11143 AutoCaller mac1(pParent);
11144 if (FAILED(mac1.rc())) return mac1.rc();
11145 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11146 if (pParent->i_getChildren().size() == 1)
11147 {
11148 if ( pParent->i_getMachineBackRefCount() == 0
11149 && pParent->i_getType() == MediumType_Normal
11150 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11151 llMedia.push_back(pParent);
11152 }
11153 else
11154 break;
11155 pParent = pParent->i_getParent();
11156 }
11157 }
11158 }
11159
11160 // real machine: then we need to use the proper method
11161 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11162
11163 if (FAILED(rc))
11164 return rc;
11165 }
11166
11167 return S_OK;
11168}
11169
11170/**
11171 * Perform deferred hard disk detachments.
11172 *
11173 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11174 * backed up).
11175 *
11176 * If @a aOnline is @c true then this method will also unlock the old hard disks
11177 * for which the new implicit diffs were created and will lock these new diffs for
11178 * writing.
11179 *
11180 * @param aOnline Whether the VM was online prior to this operation.
11181 *
11182 * @note Locks this object for writing!
11183 */
11184void Machine::i_commitMedia(bool aOnline /*= false*/)
11185{
11186 AutoCaller autoCaller(this);
11187 AssertComRCReturnVoid(autoCaller.rc());
11188
11189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11190
11191 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11192
11193 HRESULT rc = S_OK;
11194
11195 /* no attach/detach operations -- nothing to do */
11196 if (!mMediaData.isBackedUp())
11197 return;
11198
11199 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11200 bool fMediaNeedsLocking = false;
11201
11202 /* enumerate new attachments */
11203 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11204 it != mMediaData->mAttachments.end();
11205 ++it)
11206 {
11207 MediumAttachment *pAttach = *it;
11208
11209 pAttach->i_commit();
11210
11211 Medium* pMedium = pAttach->i_getMedium();
11212 bool fImplicit = pAttach->i_isImplicit();
11213
11214 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11215 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11216 fImplicit));
11217
11218 /** @todo convert all this Machine-based voodoo to MediumAttachment
11219 * based commit logic. */
11220 if (fImplicit)
11221 {
11222 /* convert implicit attachment to normal */
11223 pAttach->i_setImplicit(false);
11224
11225 if ( aOnline
11226 && pMedium
11227 && pAttach->i_getType() == DeviceType_HardDisk
11228 )
11229 {
11230 /* update the appropriate lock list */
11231 MediumLockList *pMediumLockList;
11232 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11233 AssertComRC(rc);
11234 if (pMediumLockList)
11235 {
11236 /* unlock if there's a need to change the locking */
11237 if (!fMediaNeedsLocking)
11238 {
11239 rc = mData->mSession.mLockedMedia.Unlock();
11240 AssertComRC(rc);
11241 fMediaNeedsLocking = true;
11242 }
11243 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11244 AssertComRC(rc);
11245 rc = pMediumLockList->Append(pMedium, true);
11246 AssertComRC(rc);
11247 }
11248 }
11249
11250 continue;
11251 }
11252
11253 if (pMedium)
11254 {
11255 /* was this medium attached before? */
11256 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11257 {
11258 MediumAttachment *pOldAttach = *oldIt;
11259 if (pOldAttach->i_getMedium() == pMedium)
11260 {
11261 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11262
11263 /* yes: remove from old to avoid de-association */
11264 oldAtts.erase(oldIt);
11265 break;
11266 }
11267 }
11268 }
11269 }
11270
11271 /* enumerate remaining old attachments and de-associate from the
11272 * current machine state */
11273 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11274 {
11275 MediumAttachment *pAttach = *it;
11276 Medium* pMedium = pAttach->i_getMedium();
11277
11278 /* Detach only hard disks, since DVD/floppy media is detached
11279 * instantly in MountMedium. */
11280 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11281 {
11282 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11283
11284 /* now de-associate from the current machine state */
11285 rc = pMedium->i_removeBackReference(mData->mUuid);
11286 AssertComRC(rc);
11287
11288 if (aOnline)
11289 {
11290 /* unlock since medium is not used anymore */
11291 MediumLockList *pMediumLockList;
11292 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11293 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11294 {
11295 /* this happens for online snapshots, there the attachment
11296 * is changing, but only to a diff image created under
11297 * the old one, so there is no separate lock list */
11298 Assert(!pMediumLockList);
11299 }
11300 else
11301 {
11302 AssertComRC(rc);
11303 if (pMediumLockList)
11304 {
11305 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11306 AssertComRC(rc);
11307 }
11308 }
11309 }
11310 }
11311 }
11312
11313 /* take media locks again so that the locking state is consistent */
11314 if (fMediaNeedsLocking)
11315 {
11316 Assert(aOnline);
11317 rc = mData->mSession.mLockedMedia.Lock();
11318 AssertComRC(rc);
11319 }
11320
11321 /* commit the hard disk changes */
11322 mMediaData.commit();
11323
11324 if (i_isSessionMachine())
11325 {
11326 /*
11327 * Update the parent machine to point to the new owner.
11328 * This is necessary because the stored parent will point to the
11329 * session machine otherwise and cause crashes or errors later
11330 * when the session machine gets invalid.
11331 */
11332 /** @todo Change the MediumAttachment class to behave like any other
11333 * class in this regard by creating peer MediumAttachment
11334 * objects for session machines and share the data with the peer
11335 * machine.
11336 */
11337 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11338 it != mMediaData->mAttachments.end();
11339 ++it)
11340 (*it)->i_updateParentMachine(mPeer);
11341
11342 /* attach new data to the primary machine and reshare it */
11343 mPeer->mMediaData.attach(mMediaData);
11344 }
11345
11346 return;
11347}
11348
11349/**
11350 * Perform deferred deletion of implicitly created diffs.
11351 *
11352 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11353 * backed up).
11354 *
11355 * @note Locks this object for writing!
11356 */
11357void Machine::i_rollbackMedia()
11358{
11359 AutoCaller autoCaller(this);
11360 AssertComRCReturnVoid(autoCaller.rc());
11361
11362 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11363 LogFlowThisFunc(("Entering rollbackMedia\n"));
11364
11365 HRESULT rc = S_OK;
11366
11367 /* no attach/detach operations -- nothing to do */
11368 if (!mMediaData.isBackedUp())
11369 return;
11370
11371 /* enumerate new attachments */
11372 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11373 it != mMediaData->mAttachments.end();
11374 ++it)
11375 {
11376 MediumAttachment *pAttach = *it;
11377 /* Fix up the backrefs for DVD/floppy media. */
11378 if (pAttach->i_getType() != DeviceType_HardDisk)
11379 {
11380 Medium* pMedium = pAttach->i_getMedium();
11381 if (pMedium)
11382 {
11383 rc = pMedium->i_removeBackReference(mData->mUuid);
11384 AssertComRC(rc);
11385 }
11386 }
11387
11388 (*it)->i_rollback();
11389
11390 pAttach = *it;
11391 /* Fix up the backrefs for DVD/floppy media. */
11392 if (pAttach->i_getType() != DeviceType_HardDisk)
11393 {
11394 Medium* pMedium = pAttach->i_getMedium();
11395 if (pMedium)
11396 {
11397 rc = pMedium->i_addBackReference(mData->mUuid);
11398 AssertComRC(rc);
11399 }
11400 }
11401 }
11402
11403 /** @todo convert all this Machine-based voodoo to MediumAttachment
11404 * based rollback logic. */
11405 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11406
11407 return;
11408}
11409
11410/**
11411 * Returns true if the settings file is located in the directory named exactly
11412 * as the machine; this means, among other things, that the machine directory
11413 * should be auto-renamed.
11414 *
11415 * @param aSettingsDir if not NULL, the full machine settings file directory
11416 * name will be assigned there.
11417 *
11418 * @note Doesn't lock anything.
11419 * @note Not thread safe (must be called from this object's lock).
11420 */
11421bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11422{
11423 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11424 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11425 if (aSettingsDir)
11426 *aSettingsDir = strMachineDirName;
11427 strMachineDirName.stripPath(); // vmname
11428 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11429 strConfigFileOnly.stripPath() // vmname.vbox
11430 .stripSuffix(); // vmname
11431 /** @todo hack, make somehow use of ComposeMachineFilename */
11432 if (mUserData->s.fDirectoryIncludesUUID)
11433 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11434
11435 AssertReturn(!strMachineDirName.isEmpty(), false);
11436 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11437
11438 return strMachineDirName == strConfigFileOnly;
11439}
11440
11441/**
11442 * Discards all changes to machine settings.
11443 *
11444 * @param aNotify Whether to notify the direct session about changes or not.
11445 *
11446 * @note Locks objects for writing!
11447 */
11448void Machine::i_rollback(bool aNotify)
11449{
11450 AutoCaller autoCaller(this);
11451 AssertComRCReturn(autoCaller.rc(), (void)0);
11452
11453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11454
11455 if (!mStorageControllers.isNull())
11456 {
11457 if (mStorageControllers.isBackedUp())
11458 {
11459 /* unitialize all new devices (absent in the backed up list). */
11460 StorageControllerList::const_iterator it = mStorageControllers->begin();
11461 StorageControllerList *backedList = mStorageControllers.backedUpData();
11462 while (it != mStorageControllers->end())
11463 {
11464 if ( std::find(backedList->begin(), backedList->end(), *it)
11465 == backedList->end()
11466 )
11467 {
11468 (*it)->uninit();
11469 }
11470 ++it;
11471 }
11472
11473 /* restore the list */
11474 mStorageControllers.rollback();
11475 }
11476
11477 /* rollback any changes to devices after restoring the list */
11478 if (mData->flModifications & IsModified_Storage)
11479 {
11480 StorageControllerList::const_iterator it = mStorageControllers->begin();
11481 while (it != mStorageControllers->end())
11482 {
11483 (*it)->i_rollback();
11484 ++it;
11485 }
11486 }
11487 }
11488
11489 if (!mUSBControllers.isNull())
11490 {
11491 if (mUSBControllers.isBackedUp())
11492 {
11493 /* unitialize all new devices (absent in the backed up list). */
11494 USBControllerList::const_iterator it = mUSBControllers->begin();
11495 USBControllerList *backedList = mUSBControllers.backedUpData();
11496 while (it != mUSBControllers->end())
11497 {
11498 if ( std::find(backedList->begin(), backedList->end(), *it)
11499 == backedList->end()
11500 )
11501 {
11502 (*it)->uninit();
11503 }
11504 ++it;
11505 }
11506
11507 /* restore the list */
11508 mUSBControllers.rollback();
11509 }
11510
11511 /* rollback any changes to devices after restoring the list */
11512 if (mData->flModifications & IsModified_USB)
11513 {
11514 USBControllerList::const_iterator it = mUSBControllers->begin();
11515 while (it != mUSBControllers->end())
11516 {
11517 (*it)->i_rollback();
11518 ++it;
11519 }
11520 }
11521 }
11522
11523 mUserData.rollback();
11524
11525 mHWData.rollback();
11526
11527 if (mData->flModifications & IsModified_Storage)
11528 i_rollbackMedia();
11529
11530 if (mBIOSSettings)
11531 mBIOSSettings->i_rollback();
11532
11533 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11534 mVRDEServer->i_rollback();
11535
11536 if (mAudioAdapter)
11537 mAudioAdapter->i_rollback();
11538
11539 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11540 mUSBDeviceFilters->i_rollback();
11541
11542 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11543 mBandwidthControl->i_rollback();
11544
11545 if (!mHWData.isNull())
11546 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11547 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11548 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11549 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11550
11551 if (mData->flModifications & IsModified_NetworkAdapters)
11552 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11553 if ( mNetworkAdapters[slot]
11554 && mNetworkAdapters[slot]->i_isModified())
11555 {
11556 mNetworkAdapters[slot]->i_rollback();
11557 networkAdapters[slot] = mNetworkAdapters[slot];
11558 }
11559
11560 if (mData->flModifications & IsModified_SerialPorts)
11561 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11562 if ( mSerialPorts[slot]
11563 && mSerialPorts[slot]->i_isModified())
11564 {
11565 mSerialPorts[slot]->i_rollback();
11566 serialPorts[slot] = mSerialPorts[slot];
11567 }
11568
11569 if (mData->flModifications & IsModified_ParallelPorts)
11570 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11571 if ( mParallelPorts[slot]
11572 && mParallelPorts[slot]->i_isModified())
11573 {
11574 mParallelPorts[slot]->i_rollback();
11575 parallelPorts[slot] = mParallelPorts[slot];
11576 }
11577
11578 if (aNotify)
11579 {
11580 /* inform the direct session about changes */
11581
11582 ComObjPtr<Machine> that = this;
11583 uint32_t flModifications = mData->flModifications;
11584 alock.release();
11585
11586 if (flModifications & IsModified_SharedFolders)
11587 that->i_onSharedFolderChange();
11588
11589 if (flModifications & IsModified_VRDEServer)
11590 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11591 if (flModifications & IsModified_USB)
11592 that->i_onUSBControllerChange();
11593
11594 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11595 if (networkAdapters[slot])
11596 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11597 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11598 if (serialPorts[slot])
11599 that->i_onSerialPortChange(serialPorts[slot]);
11600 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11601 if (parallelPorts[slot])
11602 that->i_onParallelPortChange(parallelPorts[slot]);
11603
11604 if (flModifications & IsModified_Storage)
11605 that->i_onStorageControllerChange();
11606
11607#if 0
11608 if (flModifications & IsModified_BandwidthControl)
11609 that->onBandwidthControlChange();
11610#endif
11611 }
11612}
11613
11614/**
11615 * Commits all the changes to machine settings.
11616 *
11617 * Note that this operation is supposed to never fail.
11618 *
11619 * @note Locks this object and children for writing.
11620 */
11621void Machine::i_commit()
11622{
11623 AutoCaller autoCaller(this);
11624 AssertComRCReturnVoid(autoCaller.rc());
11625
11626 AutoCaller peerCaller(mPeer);
11627 AssertComRCReturnVoid(peerCaller.rc());
11628
11629 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11630
11631 /*
11632 * use safe commit to ensure Snapshot machines (that share mUserData)
11633 * will still refer to a valid memory location
11634 */
11635 mUserData.commitCopy();
11636
11637 mHWData.commit();
11638
11639 if (mMediaData.isBackedUp())
11640 i_commitMedia(Global::IsOnline(mData->mMachineState));
11641
11642 mBIOSSettings->i_commit();
11643 mVRDEServer->i_commit();
11644 mAudioAdapter->i_commit();
11645 mUSBDeviceFilters->i_commit();
11646 mBandwidthControl->i_commit();
11647
11648 /* Since mNetworkAdapters is a list which might have been changed (resized)
11649 * without using the Backupable<> template we need to handle the copying
11650 * of the list entries manually, including the creation of peers for the
11651 * new objects. */
11652 bool commitNetworkAdapters = false;
11653 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11654 if (mPeer)
11655 {
11656 /* commit everything, even the ones which will go away */
11657 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11658 mNetworkAdapters[slot]->i_commit();
11659 /* copy over the new entries, creating a peer and uninit the original */
11660 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11661 for (size_t slot = 0; slot < newSize; slot++)
11662 {
11663 /* look if this adapter has a peer device */
11664 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11665 if (!peer)
11666 {
11667 /* no peer means the adapter is a newly created one;
11668 * create a peer owning data this data share it with */
11669 peer.createObject();
11670 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11671 }
11672 mPeer->mNetworkAdapters[slot] = peer;
11673 }
11674 /* uninit any no longer needed network adapters */
11675 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11676 mNetworkAdapters[slot]->uninit();
11677 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11678 {
11679 if (mPeer->mNetworkAdapters[slot])
11680 mPeer->mNetworkAdapters[slot]->uninit();
11681 }
11682 /* Keep the original network adapter count until this point, so that
11683 * discarding a chipset type change will not lose settings. */
11684 mNetworkAdapters.resize(newSize);
11685 mPeer->mNetworkAdapters.resize(newSize);
11686 }
11687 else
11688 {
11689 /* we have no peer (our parent is the newly created machine);
11690 * just commit changes to the network adapters */
11691 commitNetworkAdapters = true;
11692 }
11693 if (commitNetworkAdapters)
11694 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11695 mNetworkAdapters[slot]->i_commit();
11696
11697 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11698 mSerialPorts[slot]->i_commit();
11699 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11700 mParallelPorts[slot]->i_commit();
11701
11702 bool commitStorageControllers = false;
11703
11704 if (mStorageControllers.isBackedUp())
11705 {
11706 mStorageControllers.commit();
11707
11708 if (mPeer)
11709 {
11710 /* Commit all changes to new controllers (this will reshare data with
11711 * peers for those who have peers) */
11712 StorageControllerList *newList = new StorageControllerList();
11713 StorageControllerList::const_iterator it = mStorageControllers->begin();
11714 while (it != mStorageControllers->end())
11715 {
11716 (*it)->i_commit();
11717
11718 /* look if this controller has a peer device */
11719 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11720 if (!peer)
11721 {
11722 /* no peer means the device is a newly created one;
11723 * create a peer owning data this device share it with */
11724 peer.createObject();
11725 peer->init(mPeer, *it, true /* aReshare */);
11726 }
11727 else
11728 {
11729 /* remove peer from the old list */
11730 mPeer->mStorageControllers->remove(peer);
11731 }
11732 /* and add it to the new list */
11733 newList->push_back(peer);
11734
11735 ++it;
11736 }
11737
11738 /* uninit old peer's controllers that are left */
11739 it = mPeer->mStorageControllers->begin();
11740 while (it != mPeer->mStorageControllers->end())
11741 {
11742 (*it)->uninit();
11743 ++it;
11744 }
11745
11746 /* attach new list of controllers to our peer */
11747 mPeer->mStorageControllers.attach(newList);
11748 }
11749 else
11750 {
11751 /* we have no peer (our parent is the newly created machine);
11752 * just commit changes to devices */
11753 commitStorageControllers = true;
11754 }
11755 }
11756 else
11757 {
11758 /* the list of controllers itself is not changed,
11759 * just commit changes to controllers themselves */
11760 commitStorageControllers = true;
11761 }
11762
11763 if (commitStorageControllers)
11764 {
11765 StorageControllerList::const_iterator it = mStorageControllers->begin();
11766 while (it != mStorageControllers->end())
11767 {
11768 (*it)->i_commit();
11769 ++it;
11770 }
11771 }
11772
11773 bool commitUSBControllers = false;
11774
11775 if (mUSBControllers.isBackedUp())
11776 {
11777 mUSBControllers.commit();
11778
11779 if (mPeer)
11780 {
11781 /* Commit all changes to new controllers (this will reshare data with
11782 * peers for those who have peers) */
11783 USBControllerList *newList = new USBControllerList();
11784 USBControllerList::const_iterator it = mUSBControllers->begin();
11785 while (it != mUSBControllers->end())
11786 {
11787 (*it)->i_commit();
11788
11789 /* look if this controller has a peer device */
11790 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11791 if (!peer)
11792 {
11793 /* no peer means the device is a newly created one;
11794 * create a peer owning data this device share it with */
11795 peer.createObject();
11796 peer->init(mPeer, *it, true /* aReshare */);
11797 }
11798 else
11799 {
11800 /* remove peer from the old list */
11801 mPeer->mUSBControllers->remove(peer);
11802 }
11803 /* and add it to the new list */
11804 newList->push_back(peer);
11805
11806 ++it;
11807 }
11808
11809 /* uninit old peer's controllers that are left */
11810 it = mPeer->mUSBControllers->begin();
11811 while (it != mPeer->mUSBControllers->end())
11812 {
11813 (*it)->uninit();
11814 ++it;
11815 }
11816
11817 /* attach new list of controllers to our peer */
11818 mPeer->mUSBControllers.attach(newList);
11819 }
11820 else
11821 {
11822 /* we have no peer (our parent is the newly created machine);
11823 * just commit changes to devices */
11824 commitUSBControllers = true;
11825 }
11826 }
11827 else
11828 {
11829 /* the list of controllers itself is not changed,
11830 * just commit changes to controllers themselves */
11831 commitUSBControllers = true;
11832 }
11833
11834 if (commitUSBControllers)
11835 {
11836 USBControllerList::const_iterator it = mUSBControllers->begin();
11837 while (it != mUSBControllers->end())
11838 {
11839 (*it)->i_commit();
11840 ++it;
11841 }
11842 }
11843
11844 if (i_isSessionMachine())
11845 {
11846 /* attach new data to the primary machine and reshare it */
11847 mPeer->mUserData.attach(mUserData);
11848 mPeer->mHWData.attach(mHWData);
11849 /* mMediaData is reshared by fixupMedia */
11850 // mPeer->mMediaData.attach(mMediaData);
11851 Assert(mPeer->mMediaData.data() == mMediaData.data());
11852 }
11853}
11854
11855/**
11856 * Copies all the hardware data from the given machine.
11857 *
11858 * Currently, only called when the VM is being restored from a snapshot. In
11859 * particular, this implies that the VM is not running during this method's
11860 * call.
11861 *
11862 * @note This method must be called from under this object's lock.
11863 *
11864 * @note This method doesn't call #commit(), so all data remains backed up and
11865 * unsaved.
11866 */
11867void Machine::i_copyFrom(Machine *aThat)
11868{
11869 AssertReturnVoid(!i_isSnapshotMachine());
11870 AssertReturnVoid(aThat->i_isSnapshotMachine());
11871
11872 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11873
11874 mHWData.assignCopy(aThat->mHWData);
11875
11876 // create copies of all shared folders (mHWData after attaching a copy
11877 // contains just references to original objects)
11878 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11879 it != mHWData->mSharedFolders.end();
11880 ++it)
11881 {
11882 ComObjPtr<SharedFolder> folder;
11883 folder.createObject();
11884 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11885 AssertComRC(rc);
11886 *it = folder;
11887 }
11888
11889 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11890 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11891 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11892 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11893 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11894
11895 /* create private copies of all controllers */
11896 mStorageControllers.backup();
11897 mStorageControllers->clear();
11898 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11899 it != aThat->mStorageControllers->end();
11900 ++it)
11901 {
11902 ComObjPtr<StorageController> ctrl;
11903 ctrl.createObject();
11904 ctrl->initCopy(this, *it);
11905 mStorageControllers->push_back(ctrl);
11906 }
11907
11908 /* create private copies of all USB controllers */
11909 mUSBControllers.backup();
11910 mUSBControllers->clear();
11911 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11912 it != aThat->mUSBControllers->end();
11913 ++it)
11914 {
11915 ComObjPtr<USBController> ctrl;
11916 ctrl.createObject();
11917 ctrl->initCopy(this, *it);
11918 mUSBControllers->push_back(ctrl);
11919 }
11920
11921 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11922 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11923 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11924 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11925 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11926 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11927 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11928}
11929
11930/**
11931 * Returns whether the given storage controller is hotplug capable.
11932 *
11933 * @returns true if the controller supports hotplugging
11934 * false otherwise.
11935 * @param enmCtrlType The controller type to check for.
11936 */
11937bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11938{
11939 ComPtr<ISystemProperties> systemProperties;
11940 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11941 if (FAILED(rc))
11942 return false;
11943
11944 BOOL aHotplugCapable = FALSE;
11945 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11946
11947 return RT_BOOL(aHotplugCapable);
11948}
11949
11950#ifdef VBOX_WITH_RESOURCE_USAGE_API
11951
11952void Machine::i_getDiskList(MediaList &list)
11953{
11954 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11955 it != mMediaData->mAttachments.end();
11956 ++it)
11957 {
11958 MediumAttachment* pAttach = *it;
11959 /* just in case */
11960 AssertStmt(pAttach, continue);
11961
11962 AutoCaller localAutoCallerA(pAttach);
11963 if (FAILED(localAutoCallerA.rc())) continue;
11964
11965 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11966
11967 if (pAttach->i_getType() == DeviceType_HardDisk)
11968 list.push_back(pAttach->i_getMedium());
11969 }
11970}
11971
11972void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11973{
11974 AssertReturnVoid(isWriteLockOnCurrentThread());
11975 AssertPtrReturnVoid(aCollector);
11976
11977 pm::CollectorHAL *hal = aCollector->getHAL();
11978 /* Create sub metrics */
11979 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11980 "Percentage of processor time spent in user mode by the VM process.");
11981 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11982 "Percentage of processor time spent in kernel mode by the VM process.");
11983 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11984 "Size of resident portion of VM process in memory.");
11985 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11986 "Actual size of all VM disks combined.");
11987 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11988 "Network receive rate.");
11989 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11990 "Network transmit rate.");
11991 /* Create and register base metrics */
11992 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11993 cpuLoadUser, cpuLoadKernel);
11994 aCollector->registerBaseMetric(cpuLoad);
11995 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11996 ramUsageUsed);
11997 aCollector->registerBaseMetric(ramUsage);
11998 MediaList disks;
11999 i_getDiskList(disks);
12000 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12001 diskUsageUsed);
12002 aCollector->registerBaseMetric(diskUsage);
12003
12004 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12005 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12006 new pm::AggregateAvg()));
12007 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12008 new pm::AggregateMin()));
12009 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12010 new pm::AggregateMax()));
12011 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12012 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12013 new pm::AggregateAvg()));
12014 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12015 new pm::AggregateMin()));
12016 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12017 new pm::AggregateMax()));
12018
12019 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12020 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12021 new pm::AggregateAvg()));
12022 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12023 new pm::AggregateMin()));
12024 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12025 new pm::AggregateMax()));
12026
12027 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12028 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12029 new pm::AggregateAvg()));
12030 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12031 new pm::AggregateMin()));
12032 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12033 new pm::AggregateMax()));
12034
12035
12036 /* Guest metrics collector */
12037 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12038 aCollector->registerGuest(mCollectorGuest);
12039 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12040 this, __PRETTY_FUNCTION__, mCollectorGuest));
12041
12042 /* Create sub metrics */
12043 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12044 "Percentage of processor time spent in user mode as seen by the guest.");
12045 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12046 "Percentage of processor time spent in kernel mode as seen by the guest.");
12047 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12048 "Percentage of processor time spent idling as seen by the guest.");
12049
12050 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12051 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12052 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12053 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12054 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12055 pm::SubMetric *guestMemCache = new pm::SubMetric(
12056 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12057
12058 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12059 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12060
12061 /* Create and register base metrics */
12062 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12063 machineNetRx, machineNetTx);
12064 aCollector->registerBaseMetric(machineNetRate);
12065
12066 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12067 guestLoadUser, guestLoadKernel, guestLoadIdle);
12068 aCollector->registerBaseMetric(guestCpuLoad);
12069
12070 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12071 guestMemTotal, guestMemFree,
12072 guestMemBalloon, guestMemShared,
12073 guestMemCache, guestPagedTotal);
12074 aCollector->registerBaseMetric(guestCpuMem);
12075
12076 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12077 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12078 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12079 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12080
12081 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12082 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12083 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12084 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12085
12086 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12087 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12088 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12089 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12090
12091 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12092 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12093 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12094 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12095
12096 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12097 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12098 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12099 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12100
12101 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12102 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12103 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12104 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12105
12106 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12107 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12108 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12109 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12110
12111 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12112 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12113 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12114 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12115
12116 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12117 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12118 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12119 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12120
12121 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12122 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12123 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12124 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12125
12126 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12127 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12128 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12129 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12130}
12131
12132void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12133{
12134 AssertReturnVoid(isWriteLockOnCurrentThread());
12135
12136 if (aCollector)
12137 {
12138 aCollector->unregisterMetricsFor(aMachine);
12139 aCollector->unregisterBaseMetricsFor(aMachine);
12140 }
12141}
12142
12143#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12144
12145
12146////////////////////////////////////////////////////////////////////////////////
12147
12148DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12149
12150HRESULT SessionMachine::FinalConstruct()
12151{
12152 LogFlowThisFunc(("\n"));
12153
12154 mClientToken = NULL;
12155
12156 return BaseFinalConstruct();
12157}
12158
12159void SessionMachine::FinalRelease()
12160{
12161 LogFlowThisFunc(("\n"));
12162
12163 Assert(!mClientToken);
12164 /* paranoia, should not hang around any more */
12165 if (mClientToken)
12166 {
12167 delete mClientToken;
12168 mClientToken = NULL;
12169 }
12170
12171 uninit(Uninit::Unexpected);
12172
12173 BaseFinalRelease();
12174}
12175
12176/**
12177 * @note Must be called only by Machine::LockMachine() from its own write lock.
12178 */
12179HRESULT SessionMachine::init(Machine *aMachine)
12180{
12181 LogFlowThisFuncEnter();
12182 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12183
12184 AssertReturn(aMachine, E_INVALIDARG);
12185
12186 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12187
12188 /* Enclose the state transition NotReady->InInit->Ready */
12189 AutoInitSpan autoInitSpan(this);
12190 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12191
12192 HRESULT rc = S_OK;
12193
12194 /* create the machine client token */
12195 try
12196 {
12197 mClientToken = new ClientToken(aMachine, this);
12198 if (!mClientToken->isReady())
12199 {
12200 delete mClientToken;
12201 mClientToken = NULL;
12202 rc = E_FAIL;
12203 }
12204 }
12205 catch (std::bad_alloc &)
12206 {
12207 rc = E_OUTOFMEMORY;
12208 }
12209 if (FAILED(rc))
12210 return rc;
12211
12212 /* memorize the peer Machine */
12213 unconst(mPeer) = aMachine;
12214 /* share the parent pointer */
12215 unconst(mParent) = aMachine->mParent;
12216
12217 /* take the pointers to data to share */
12218 mData.share(aMachine->mData);
12219 mSSData.share(aMachine->mSSData);
12220
12221 mUserData.share(aMachine->mUserData);
12222 mHWData.share(aMachine->mHWData);
12223 mMediaData.share(aMachine->mMediaData);
12224
12225 mStorageControllers.allocate();
12226 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12227 it != aMachine->mStorageControllers->end();
12228 ++it)
12229 {
12230 ComObjPtr<StorageController> ctl;
12231 ctl.createObject();
12232 ctl->init(this, *it);
12233 mStorageControllers->push_back(ctl);
12234 }
12235
12236 mUSBControllers.allocate();
12237 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12238 it != aMachine->mUSBControllers->end();
12239 ++it)
12240 {
12241 ComObjPtr<USBController> ctl;
12242 ctl.createObject();
12243 ctl->init(this, *it);
12244 mUSBControllers->push_back(ctl);
12245 }
12246
12247 unconst(mBIOSSettings).createObject();
12248 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12249 /* create another VRDEServer object that will be mutable */
12250 unconst(mVRDEServer).createObject();
12251 mVRDEServer->init(this, aMachine->mVRDEServer);
12252 /* create another audio adapter object that will be mutable */
12253 unconst(mAudioAdapter).createObject();
12254 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12255 /* create a list of serial ports that will be mutable */
12256 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12257 {
12258 unconst(mSerialPorts[slot]).createObject();
12259 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12260 }
12261 /* create a list of parallel ports that will be mutable */
12262 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12263 {
12264 unconst(mParallelPorts[slot]).createObject();
12265 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12266 }
12267
12268 /* create another USB device filters object that will be mutable */
12269 unconst(mUSBDeviceFilters).createObject();
12270 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12271
12272 /* create a list of network adapters that will be mutable */
12273 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12274 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12275 {
12276 unconst(mNetworkAdapters[slot]).createObject();
12277 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12278 }
12279
12280 /* create another bandwidth control object that will be mutable */
12281 unconst(mBandwidthControl).createObject();
12282 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12283
12284 /* default is to delete saved state on Saved -> PoweredOff transition */
12285 mRemoveSavedState = true;
12286
12287 /* Confirm a successful initialization when it's the case */
12288 autoInitSpan.setSucceeded();
12289
12290 miNATNetworksStarted = 0;
12291
12292 LogFlowThisFuncLeave();
12293 return rc;
12294}
12295
12296/**
12297 * Uninitializes this session object. If the reason is other than
12298 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12299 * or the client watcher code.
12300 *
12301 * @param aReason uninitialization reason
12302 *
12303 * @note Locks mParent + this object for writing.
12304 */
12305void SessionMachine::uninit(Uninit::Reason aReason)
12306{
12307 LogFlowThisFuncEnter();
12308 LogFlowThisFunc(("reason=%d\n", aReason));
12309
12310 /*
12311 * Strongly reference ourselves to prevent this object deletion after
12312 * mData->mSession.mMachine.setNull() below (which can release the last
12313 * reference and call the destructor). Important: this must be done before
12314 * accessing any members (and before AutoUninitSpan that does it as well).
12315 * This self reference will be released as the very last step on return.
12316 */
12317 ComObjPtr<SessionMachine> selfRef = this;
12318
12319 /* Enclose the state transition Ready->InUninit->NotReady */
12320 AutoUninitSpan autoUninitSpan(this);
12321 if (autoUninitSpan.uninitDone())
12322 {
12323 LogFlowThisFunc(("Already uninitialized\n"));
12324 LogFlowThisFuncLeave();
12325 return;
12326 }
12327
12328 if (autoUninitSpan.initFailed())
12329 {
12330 /* We've been called by init() because it's failed. It's not really
12331 * necessary (nor it's safe) to perform the regular uninit sequence
12332 * below, the following is enough.
12333 */
12334 LogFlowThisFunc(("Initialization failed.\n"));
12335 /* destroy the machine client token */
12336 if (mClientToken)
12337 {
12338 delete mClientToken;
12339 mClientToken = NULL;
12340 }
12341 uninitDataAndChildObjects();
12342 mData.free();
12343 unconst(mParent) = NULL;
12344 unconst(mPeer) = NULL;
12345 LogFlowThisFuncLeave();
12346 return;
12347 }
12348
12349 MachineState_T lastState;
12350 {
12351 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12352 lastState = mData->mMachineState;
12353 }
12354 NOREF(lastState);
12355
12356#ifdef VBOX_WITH_USB
12357 // release all captured USB devices, but do this before requesting the locks below
12358 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12359 {
12360 /* Console::captureUSBDevices() is called in the VM process only after
12361 * setting the machine state to Starting or Restoring.
12362 * Console::detachAllUSBDevices() will be called upon successful
12363 * termination. So, we need to release USB devices only if there was
12364 * an abnormal termination of a running VM.
12365 *
12366 * This is identical to SessionMachine::DetachAllUSBDevices except
12367 * for the aAbnormal argument. */
12368 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12369 AssertComRC(rc);
12370 NOREF(rc);
12371
12372 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12373 if (service)
12374 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12375 }
12376#endif /* VBOX_WITH_USB */
12377
12378 // we need to lock this object in uninit() because the lock is shared
12379 // with mPeer (as well as data we modify below). mParent lock is needed
12380 // by several calls to it, and USB needs host lock.
12381 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12382
12383#ifdef VBOX_WITH_RESOURCE_USAGE_API
12384 /*
12385 * It is safe to call Machine::i_unregisterMetrics() here because
12386 * PerformanceCollector::samplerCallback no longer accesses guest methods
12387 * holding the lock.
12388 */
12389 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12390 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12391 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12392 this, __PRETTY_FUNCTION__, mCollectorGuest));
12393 if (mCollectorGuest)
12394 {
12395 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12396 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12397 mCollectorGuest = NULL;
12398 }
12399#endif
12400
12401 if (aReason == Uninit::Abnormal)
12402 {
12403 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12404 Global::IsOnlineOrTransient(lastState)));
12405
12406 /* reset the state to Aborted */
12407 if (mData->mMachineState != MachineState_Aborted)
12408 i_setMachineState(MachineState_Aborted);
12409 }
12410
12411 // any machine settings modified?
12412 if (mData->flModifications)
12413 {
12414 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12415 i_rollback(false /* aNotify */);
12416 }
12417
12418 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12419 || !mConsoleTaskData.mSnapshot);
12420 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12421 {
12422 LogWarningThisFunc(("canceling failed save state request!\n"));
12423 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12424 }
12425 else if (!mConsoleTaskData.mSnapshot.isNull())
12426 {
12427 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12428
12429 /* delete all differencing hard disks created (this will also attach
12430 * their parents back by rolling back mMediaData) */
12431 i_rollbackMedia();
12432
12433 // delete the saved state file (it might have been already created)
12434 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12435 // think it's still in use
12436 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12437 mConsoleTaskData.mSnapshot->uninit();
12438 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12439 }
12440
12441 mData->mSession.mPID = NIL_RTPROCESS;
12442
12443 if (aReason == Uninit::Unexpected)
12444 {
12445 /* Uninitialization didn't come from #checkForDeath(), so tell the
12446 * client watcher thread to update the set of machines that have open
12447 * sessions. */
12448 mParent->i_updateClientWatcher();
12449 }
12450
12451 /* uninitialize all remote controls */
12452 if (mData->mSession.mRemoteControls.size())
12453 {
12454 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12455 mData->mSession.mRemoteControls.size()));
12456
12457 Data::Session::RemoteControlList::iterator it =
12458 mData->mSession.mRemoteControls.begin();
12459 while (it != mData->mSession.mRemoteControls.end())
12460 {
12461 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12462 HRESULT rc = (*it)->Uninitialize();
12463 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12464 if (FAILED(rc))
12465 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12466 ++it;
12467 }
12468 mData->mSession.mRemoteControls.clear();
12469 }
12470
12471 /* Remove all references to the NAT network service. The service will stop
12472 * if all references (also from other VMs) are removed. */
12473 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12474 {
12475 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12476 {
12477 NetworkAttachmentType_T type;
12478 HRESULT hrc;
12479
12480 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12481 if ( SUCCEEDED(hrc)
12482 && type == NetworkAttachmentType_NATNetwork)
12483 {
12484 Bstr name;
12485 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12486 if (SUCCEEDED(hrc))
12487 {
12488 multilock.release();
12489 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12490 mUserData->s.strName.c_str(), name.raw()));
12491 mParent->i_natNetworkRefDec(name.raw());
12492 multilock.acquire();
12493 }
12494 }
12495 }
12496 }
12497
12498 /*
12499 * An expected uninitialization can come only from #checkForDeath().
12500 * Otherwise it means that something's gone really wrong (for example,
12501 * the Session implementation has released the VirtualBox reference
12502 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12503 * etc). However, it's also possible, that the client releases the IPC
12504 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12505 * but the VirtualBox release event comes first to the server process.
12506 * This case is practically possible, so we should not assert on an
12507 * unexpected uninit, just log a warning.
12508 */
12509
12510 if ((aReason == Uninit::Unexpected))
12511 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12512
12513 if (aReason != Uninit::Normal)
12514 {
12515 mData->mSession.mDirectControl.setNull();
12516 }
12517 else
12518 {
12519 /* this must be null here (see #OnSessionEnd()) */
12520 Assert(mData->mSession.mDirectControl.isNull());
12521 Assert(mData->mSession.mState == SessionState_Unlocking);
12522 Assert(!mData->mSession.mProgress.isNull());
12523 }
12524 if (mData->mSession.mProgress)
12525 {
12526 if (aReason == Uninit::Normal)
12527 mData->mSession.mProgress->i_notifyComplete(S_OK);
12528 else
12529 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12530 COM_IIDOF(ISession),
12531 getComponentName(),
12532 tr("The VM session was aborted"));
12533 mData->mSession.mProgress.setNull();
12534 }
12535
12536 /* remove the association between the peer machine and this session machine */
12537 Assert( (SessionMachine*)mData->mSession.mMachine == this
12538 || aReason == Uninit::Unexpected);
12539
12540 /* reset the rest of session data */
12541 mData->mSession.mMachine.setNull();
12542 mData->mSession.mState = SessionState_Unlocked;
12543 mData->mSession.mType.setNull();
12544
12545 /* destroy the machine client token before leaving the exclusive lock */
12546 if (mClientToken)
12547 {
12548 delete mClientToken;
12549 mClientToken = NULL;
12550 }
12551
12552 /* fire an event */
12553 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12554
12555 uninitDataAndChildObjects();
12556
12557 /* free the essential data structure last */
12558 mData.free();
12559
12560 /* release the exclusive lock before setting the below two to NULL */
12561 multilock.release();
12562
12563 unconst(mParent) = NULL;
12564 unconst(mPeer) = NULL;
12565
12566 LogFlowThisFuncLeave();
12567}
12568
12569// util::Lockable interface
12570////////////////////////////////////////////////////////////////////////////////
12571
12572/**
12573 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12574 * with the primary Machine instance (mPeer).
12575 */
12576RWLockHandle *SessionMachine::lockHandle() const
12577{
12578 AssertReturn(mPeer != NULL, NULL);
12579 return mPeer->lockHandle();
12580}
12581
12582// IInternalMachineControl methods
12583////////////////////////////////////////////////////////////////////////////////
12584
12585/**
12586 * Passes collected guest statistics to performance collector object
12587 */
12588HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12589 ULONG aCpuKernel, ULONG aCpuIdle,
12590 ULONG aMemTotal, ULONG aMemFree,
12591 ULONG aMemBalloon, ULONG aMemShared,
12592 ULONG aMemCache, ULONG aPageTotal,
12593 ULONG aAllocVMM, ULONG aFreeVMM,
12594 ULONG aBalloonedVMM, ULONG aSharedVMM,
12595 ULONG aVmNetRx, ULONG aVmNetTx)
12596{
12597#ifdef VBOX_WITH_RESOURCE_USAGE_API
12598 if (mCollectorGuest)
12599 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12600 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12601 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12602 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12603
12604 return S_OK;
12605#else
12606 NOREF(aValidStats);
12607 NOREF(aCpuUser);
12608 NOREF(aCpuKernel);
12609 NOREF(aCpuIdle);
12610 NOREF(aMemTotal);
12611 NOREF(aMemFree);
12612 NOREF(aMemBalloon);
12613 NOREF(aMemShared);
12614 NOREF(aMemCache);
12615 NOREF(aPageTotal);
12616 NOREF(aAllocVMM);
12617 NOREF(aFreeVMM);
12618 NOREF(aBalloonedVMM);
12619 NOREF(aSharedVMM);
12620 NOREF(aVmNetRx);
12621 NOREF(aVmNetTx);
12622 return E_NOTIMPL;
12623#endif
12624}
12625
12626/**
12627 * @note Locks this object for writing.
12628 */
12629HRESULT SessionMachine::setRemoveSavedStateFile(BOOL aRemove)
12630{
12631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12632
12633 mRemoveSavedState = RT_BOOL(aRemove);
12634
12635 return S_OK;
12636}
12637
12638/**
12639 * @note Locks the same as #i_setMachineState() does.
12640 */
12641HRESULT SessionMachine::updateState(MachineState_T aState)
12642{
12643 return i_setMachineState(aState);
12644}
12645
12646/**
12647 * @note Locks this object for writing.
12648 */
12649HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12650{
12651 IProgress* pProgress(aProgress);
12652
12653 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12654
12655 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12656
12657 if (mData->mSession.mState != SessionState_Locked)
12658 return VBOX_E_INVALID_OBJECT_STATE;
12659
12660 if (!mData->mSession.mProgress.isNull())
12661 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12662
12663 /* If we didn't reference the NAT network service yet, add a reference to
12664 * force a start */
12665 if (miNATNetworksStarted < 1)
12666 {
12667 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12668 {
12669 NetworkAttachmentType_T type;
12670 HRESULT hrc;
12671 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12672 if ( SUCCEEDED(hrc)
12673 && type == NetworkAttachmentType_NATNetwork)
12674 {
12675 Bstr name;
12676 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12677 if (SUCCEEDED(hrc))
12678 {
12679 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12680 mUserData->s.strName.c_str(), name.raw()));
12681 mPeer->lockHandle()->unlockWrite();
12682 mParent->i_natNetworkRefInc(name.raw());
12683#ifdef RT_LOCK_STRICT
12684 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12685#else
12686 mPeer->lockHandle()->lockWrite();
12687#endif
12688 }
12689 }
12690 }
12691 miNATNetworksStarted++;
12692 }
12693
12694 LogFlowThisFunc(("returns S_OK.\n"));
12695 return S_OK;
12696}
12697
12698/**
12699 * @note Locks this object for writing.
12700 */
12701HRESULT SessionMachine::endPowerUp(LONG aResult)
12702{
12703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12704
12705 if (mData->mSession.mState != SessionState_Locked)
12706 return VBOX_E_INVALID_OBJECT_STATE;
12707
12708 /* Finalize the LaunchVMProcess progress object. */
12709 if (mData->mSession.mProgress)
12710 {
12711 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
12712 mData->mSession.mProgress.setNull();
12713 }
12714
12715 if (SUCCEEDED((HRESULT)aResult))
12716 {
12717#ifdef VBOX_WITH_RESOURCE_USAGE_API
12718 /* The VM has been powered up successfully, so it makes sense
12719 * now to offer the performance metrics for a running machine
12720 * object. Doing it earlier wouldn't be safe. */
12721 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12722 mData->mSession.mPID);
12723#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12724 }
12725
12726 return S_OK;
12727}
12728
12729/**
12730 * @note Locks this object for writing.
12731 */
12732HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
12733{
12734 LogFlowThisFuncEnter();
12735
12736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12737
12738 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12739 E_FAIL);
12740
12741 /* create a progress object to track operation completion */
12742 ComObjPtr<Progress> pProgress;
12743 pProgress.createObject();
12744 pProgress->init(i_getVirtualBox(),
12745 static_cast<IMachine *>(this) /* aInitiator */,
12746 Bstr(tr("Stopping the virtual machine")).raw(),
12747 FALSE /* aCancelable */);
12748
12749 /* fill in the console task data */
12750 mConsoleTaskData.mLastState = mData->mMachineState;
12751 mConsoleTaskData.mProgress = pProgress;
12752
12753 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12754 i_setMachineState(MachineState_Stopping);
12755
12756 pProgress.queryInterfaceTo(aProgress.asOutParam());
12757
12758 return S_OK;
12759}
12760
12761/**
12762 * @note Locks this object for writing.
12763 */
12764HRESULT SessionMachine::endPoweringDown(LONG aResult,
12765 const com::Utf8Str &aErrMsg)
12766{
12767 LogFlowThisFuncEnter();
12768
12769 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12770
12771 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
12772 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
12773 && mConsoleTaskData.mLastState != MachineState_Null,
12774 E_FAIL);
12775
12776 /*
12777 * On failure, set the state to the state we had when BeginPoweringDown()
12778 * was called (this is expected by Console::PowerDown() and the associated
12779 * task). On success the VM process already changed the state to
12780 * MachineState_PoweredOff, so no need to do anything.
12781 */
12782 if (FAILED(aResult))
12783 i_setMachineState(mConsoleTaskData.mLastState);
12784
12785 /* notify the progress object about operation completion */
12786 Assert(mConsoleTaskData.mProgress);
12787 if (SUCCEEDED(aResult))
12788 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12789 else
12790 {
12791 if (aErrMsg.length())
12792 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
12793 COM_IIDOF(ISession),
12794 getComponentName(),
12795 aErrMsg.c_str());
12796 else
12797 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
12798 }
12799
12800 /* clear out the temporary saved state data */
12801 mConsoleTaskData.mLastState = MachineState_Null;
12802 mConsoleTaskData.mProgress.setNull();
12803
12804 LogFlowThisFuncLeave();
12805 return S_OK;
12806}
12807
12808
12809/**
12810 * Goes through the USB filters of the given machine to see if the given
12811 * device matches any filter or not.
12812 *
12813 * @note Locks the same as USBController::hasMatchingFilter() does.
12814 */
12815HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
12816 BOOL *aMatched,
12817 ULONG *aMaskedInterfaces)
12818{
12819 LogFlowThisFunc(("\n"));
12820
12821#ifdef VBOX_WITH_USB
12822 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
12823#else
12824 NOREF(aDevice);
12825 NOREF(aMaskedInterfaces);
12826 *aMatched = FALSE;
12827#endif
12828
12829 return S_OK;
12830}
12831
12832/**
12833 * @note Locks the same as Host::captureUSBDevice() does.
12834 */
12835HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
12836{
12837 LogFlowThisFunc(("\n"));
12838
12839#ifdef VBOX_WITH_USB
12840 /* if captureDeviceForVM() fails, it must have set extended error info */
12841 clearError();
12842 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12843 if (FAILED(rc)) return rc;
12844
12845 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12846 AssertReturn(service, E_FAIL);
12847 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
12848#else
12849 NOREF(aId);
12850 return E_NOTIMPL;
12851#endif
12852}
12853
12854/**
12855 * @note Locks the same as Host::detachUSBDevice() does.
12856 */
12857HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
12858 BOOL aDone)
12859{
12860 LogFlowThisFunc(("\n"));
12861
12862#ifdef VBOX_WITH_USB
12863 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12864 AssertReturn(service, E_FAIL);
12865 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
12866#else
12867 NOREF(aId);
12868 NOREF(aDone);
12869 return E_NOTIMPL;
12870#endif
12871}
12872
12873/**
12874 * Inserts all machine filters to the USB proxy service and then calls
12875 * Host::autoCaptureUSBDevices().
12876 *
12877 * Called by Console from the VM process upon VM startup.
12878 *
12879 * @note Locks what called methods lock.
12880 */
12881HRESULT SessionMachine::autoCaptureUSBDevices()
12882{
12883 LogFlowThisFunc(("\n"));
12884
12885#ifdef VBOX_WITH_USB
12886 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12887 AssertComRC(rc);
12888 NOREF(rc);
12889
12890 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12891 AssertReturn(service, E_FAIL);
12892 return service->autoCaptureDevicesForVM(this);
12893#else
12894 return S_OK;
12895#endif
12896}
12897
12898/**
12899 * Removes all machine filters from the USB proxy service and then calls
12900 * Host::detachAllUSBDevices().
12901 *
12902 * Called by Console from the VM process upon normal VM termination or by
12903 * SessionMachine::uninit() upon abnormal VM termination (from under the
12904 * Machine/SessionMachine lock).
12905 *
12906 * @note Locks what called methods lock.
12907 */
12908HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
12909{
12910 LogFlowThisFunc(("\n"));
12911
12912#ifdef VBOX_WITH_USB
12913 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12914 AssertComRC(rc);
12915 NOREF(rc);
12916
12917 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12918 AssertReturn(service, E_FAIL);
12919 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12920#else
12921 NOREF(aDone);
12922 return S_OK;
12923#endif
12924}
12925
12926/**
12927 * @note Locks this object for writing.
12928 */
12929HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
12930 ComPtr<IProgress> &aProgress)
12931{
12932 LogFlowThisFuncEnter();
12933
12934 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12935 /*
12936 * We don't assert below because it might happen that a non-direct session
12937 * informs us it is closed right after we've been uninitialized -- it's ok.
12938 */
12939
12940 /* get IInternalSessionControl interface */
12941 ComPtr<IInternalSessionControl> control(aSession);
12942
12943 ComAssertRet(!control.isNull(), E_INVALIDARG);
12944
12945 /* Creating a Progress object requires the VirtualBox lock, and
12946 * thus locking it here is required by the lock order rules. */
12947 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12948
12949 if (control == mData->mSession.mDirectControl)
12950 {
12951 /* The direct session is being normally closed by the client process
12952 * ----------------------------------------------------------------- */
12953
12954 /* go to the closing state (essential for all open*Session() calls and
12955 * for #checkForDeath()) */
12956 Assert(mData->mSession.mState == SessionState_Locked);
12957 mData->mSession.mState = SessionState_Unlocking;
12958
12959 /* set direct control to NULL to release the remote instance */
12960 mData->mSession.mDirectControl.setNull();
12961 LogFlowThisFunc(("Direct control is set to NULL\n"));
12962
12963 if (mData->mSession.mProgress)
12964 {
12965 /* finalize the progress, someone might wait if a frontend
12966 * closes the session before powering on the VM. */
12967 mData->mSession.mProgress->notifyComplete(E_FAIL,
12968 COM_IIDOF(ISession),
12969 getComponentName(),
12970 tr("The VM session was closed before any attempt to power it on"));
12971 mData->mSession.mProgress.setNull();
12972 }
12973
12974 /* Create the progress object the client will use to wait until
12975 * #checkForDeath() is called to uninitialize this session object after
12976 * it releases the IPC semaphore.
12977 * Note! Because we're "reusing" mProgress here, this must be a proxy
12978 * object just like for LaunchVMProcess. */
12979 Assert(mData->mSession.mProgress.isNull());
12980 ComObjPtr<ProgressProxy> progress;
12981 progress.createObject();
12982 ComPtr<IUnknown> pPeer(mPeer);
12983 progress->init(mParent, pPeer,
12984 Bstr(tr("Closing session")).raw(),
12985 FALSE /* aCancelable */);
12986 progress.queryInterfaceTo(aProgress.asOutParam());
12987 mData->mSession.mProgress = progress;
12988 }
12989 else
12990 {
12991 /* the remote session is being normally closed */
12992 Data::Session::RemoteControlList::iterator it =
12993 mData->mSession.mRemoteControls.begin();
12994 while (it != mData->mSession.mRemoteControls.end())
12995 {
12996 if (control == *it)
12997 break;
12998 ++it;
12999 }
13000 BOOL found = it != mData->mSession.mRemoteControls.end();
13001 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13002 E_INVALIDARG);
13003 // This MUST be erase(it), not remove(*it) as the latter triggers a
13004 // very nasty use after free due to the place where the value "lives".
13005 mData->mSession.mRemoteControls.erase(it);
13006 }
13007
13008 /* signal the client watcher thread, because the client is going away */
13009 mParent->i_updateClientWatcher();
13010
13011 LogFlowThisFuncLeave();
13012 return S_OK;
13013}
13014
13015/**
13016 * @note Locks this object for writing.
13017 */
13018HRESULT SessionMachine::beginSavingState(ComPtr<IProgress> &aProgress,
13019 com::Utf8Str &aStateFilePath)
13020{
13021 LogFlowThisFuncEnter();
13022
13023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13024
13025 AssertReturn( mData->mMachineState == MachineState_Paused
13026 && mConsoleTaskData.mLastState == MachineState_Null
13027 && mConsoleTaskData.strStateFilePath.isEmpty(),
13028 E_FAIL);
13029
13030 /* create a progress object to track operation completion */
13031 ComObjPtr<Progress> pProgress;
13032 pProgress.createObject();
13033 pProgress->init(i_getVirtualBox(),
13034 static_cast<IMachine *>(this) /* aInitiator */,
13035 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13036 FALSE /* aCancelable */);
13037
13038 /* stateFilePath is null when the machine is not running */
13039 if (mData->mMachineState == MachineState_Paused)
13040 i_composeSavedStateFilename(aStateFilePath);
13041
13042 /* fill in the console task data */
13043 mConsoleTaskData.mLastState = mData->mMachineState;
13044 mConsoleTaskData.strStateFilePath = aStateFilePath;
13045 mConsoleTaskData.mProgress = pProgress;
13046
13047 /* set the state to Saving (this is expected by Console::SaveState()) */
13048 i_setMachineState(MachineState_Saving);
13049
13050 pProgress.queryInterfaceTo(aProgress.asOutParam());
13051
13052 return S_OK;
13053}
13054
13055/**
13056 * @note Locks mParent + this object for writing.
13057 */
13058HRESULT SessionMachine::endSavingState(LONG aResult,
13059 const com::Utf8Str &aErrMsg)
13060{
13061 LogFlowThisFunc(("\n"));
13062
13063 /* endSavingState() need mParent lock */
13064 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13065
13066 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_Saved)
13067 || (FAILED(aResult) && mData->mMachineState == MachineState_Saving))
13068 && mConsoleTaskData.mLastState != MachineState_Null
13069 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13070 E_FAIL);
13071
13072 /*
13073 * On failure, set the state to the state we had when BeginSavingState()
13074 * was called (this is expected by Console::SaveState() and the associated
13075 * task). On success the VM process already changed the state to
13076 * MachineState_Saved, so no need to do anything.
13077 */
13078 if (FAILED(aResult))
13079 i_setMachineState(mConsoleTaskData.mLastState);
13080
13081 return i_endSavingState(aResult, aErrMsg);
13082}
13083
13084/**
13085 * @note Locks this object for writing.
13086 */
13087HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13088{
13089 LogFlowThisFunc(("\n"));
13090
13091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13092
13093 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13094 || mData->mMachineState == MachineState_Teleported
13095 || mData->mMachineState == MachineState_Aborted
13096 , E_FAIL); /** @todo setError. */
13097
13098 com::Utf8Str stateFilePathFull;
13099 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13100 if (RT_FAILURE(vrc))
13101 return setError(VBOX_E_FILE_ERROR,
13102 tr("Invalid saved state file path '%s' (%Rrc)"),
13103 aSavedStateFile.c_str(),
13104 vrc);
13105
13106 mSSData->strStateFilePath = stateFilePathFull;
13107
13108 /* The below i_setMachineState() will detect the state transition and will
13109 * update the settings file */
13110
13111 return i_setMachineState(MachineState_Saved);
13112}
13113
13114HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13115 std::vector<com::Utf8Str> &aValues,
13116 std::vector<LONG64> &aTimestamps,
13117 std::vector<com::Utf8Str> &aFlags)
13118{
13119 LogFlowThisFunc(("\n"));
13120
13121#ifdef VBOX_WITH_GUEST_PROPS
13122 using namespace guestProp;
13123
13124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13125
13126 size_t cEntries = mHWData->mGuestProperties.size();
13127 aNames.resize(cEntries);
13128 aValues.resize(cEntries);
13129 aTimestamps.resize(cEntries);
13130 aFlags.resize(cEntries);
13131
13132 size_t i = 0;
13133 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13134 it != mHWData->mGuestProperties.end();
13135 ++it, ++i)
13136 {
13137 char szFlags[MAX_FLAGS_LEN + 1];
13138 aNames[i] = it->first;
13139 aValues[i] = it->second.strValue;
13140 aTimestamps[i] = it->second.mTimestamp;
13141
13142 /* If it is NULL, keep it NULL. */
13143 if (it->second.mFlags)
13144 {
13145 writeFlags(it->second.mFlags, szFlags);
13146 aFlags[i] = szFlags;
13147 }
13148 else
13149 aFlags[i] = "";
13150 }
13151 return S_OK;
13152#else
13153 ReturnComNotImplemented();
13154#endif
13155}
13156
13157HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13158 const com::Utf8Str &aValue,
13159 LONG64 aTimestamp,
13160 const com::Utf8Str &aFlags)
13161{
13162 LogFlowThisFunc(("\n"));
13163
13164#ifdef VBOX_WITH_GUEST_PROPS
13165 using namespace guestProp;
13166
13167 try
13168 {
13169 /*
13170 * Convert input up front.
13171 */
13172 uint32_t fFlags = NILFLAG;
13173 if (aFlags.length())
13174 {
13175 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13176 AssertRCReturn(vrc, E_INVALIDARG);
13177 }
13178
13179 /*
13180 * Now grab the object lock, validate the state and do the update.
13181 */
13182
13183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13184
13185 switch (mData->mMachineState)
13186 {
13187 case MachineState_Paused:
13188 case MachineState_Running:
13189 case MachineState_Teleporting:
13190 case MachineState_TeleportingPausedVM:
13191 case MachineState_LiveSnapshotting:
13192 case MachineState_DeletingSnapshotOnline:
13193 case MachineState_DeletingSnapshotPaused:
13194 case MachineState_Saving:
13195 case MachineState_Stopping:
13196 break;
13197
13198 default:
13199 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13200 VBOX_E_INVALID_VM_STATE);
13201 }
13202
13203 i_setModified(IsModified_MachineData);
13204 mHWData.backup();
13205
13206 bool fDelete = !aValue.length();
13207 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13208 if (it != mHWData->mGuestProperties.end())
13209 {
13210 if (!fDelete)
13211 {
13212 it->second.strValue = aValue;
13213 it->second.mTimestamp = aTimestamp;
13214 it->second.mFlags = fFlags;
13215 }
13216 else
13217 mHWData->mGuestProperties.erase(it);
13218
13219 mData->mGuestPropertiesModified = TRUE;
13220 }
13221 else if (!fDelete)
13222 {
13223 HWData::GuestProperty prop;
13224 prop.strValue = aValue;
13225 prop.mTimestamp = aTimestamp;
13226 prop.mFlags = fFlags;
13227
13228 mHWData->mGuestProperties[aName] = prop;
13229 mData->mGuestPropertiesModified = TRUE;
13230 }
13231
13232 /*
13233 * Send a callback notification if appropriate
13234 */
13235 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13236 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13237 RTSTR_MAX,
13238 aName.c_str(),
13239 RTSTR_MAX, NULL)
13240 )
13241 {
13242 alock.release();
13243
13244 mParent->i_onGuestPropertyChange(mData->mUuid,
13245 Bstr(aName).raw(),
13246 Bstr(aValue).raw(),
13247 Bstr(aFlags).raw());
13248 }
13249 }
13250 catch (...)
13251 {
13252 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13253 }
13254 return S_OK;
13255#else
13256 ReturnComNotImplemented();
13257#endif
13258}
13259
13260
13261HRESULT SessionMachine::lockMedia()
13262{
13263 AutoMultiWriteLock2 alock(this->lockHandle(),
13264 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13265
13266 AssertReturn( mData->mMachineState == MachineState_Starting
13267 || mData->mMachineState == MachineState_Restoring
13268 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13269
13270 clearError();
13271 alock.release();
13272 return i_lockMedia();
13273}
13274
13275HRESULT SessionMachine::unlockMedia()
13276{
13277 HRESULT hrc = i_unlockMedia();
13278 return hrc;
13279}
13280
13281HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13282 ComPtr<IMediumAttachment> &aNewAttachment)
13283{
13284 // request the host lock first, since might be calling Host methods for getting host drives;
13285 // next, protect the media tree all the while we're in here, as well as our member variables
13286 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13287 this->lockHandle(),
13288 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13289
13290 IMediumAttachment *iAttach = aAttachment;
13291 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13292
13293 Bstr ctrlName;
13294 LONG lPort;
13295 LONG lDevice;
13296 bool fTempEject;
13297 {
13298 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13299
13300 /* Need to query the details first, as the IMediumAttachment reference
13301 * might be to the original settings, which we are going to change. */
13302 ctrlName = pAttach->i_getControllerName();
13303 lPort = pAttach->i_getPort();
13304 lDevice = pAttach->i_getDevice();
13305 fTempEject = pAttach->i_getTempEject();
13306 }
13307
13308 if (!fTempEject)
13309 {
13310 /* Remember previously mounted medium. The medium before taking the
13311 * backup is not necessarily the same thing. */
13312 ComObjPtr<Medium> oldmedium;
13313 oldmedium = pAttach->i_getMedium();
13314
13315 i_setModified(IsModified_Storage);
13316 mMediaData.backup();
13317
13318 // The backup operation makes the pAttach reference point to the
13319 // old settings. Re-get the correct reference.
13320 pAttach = i_findAttachment(mMediaData->mAttachments,
13321 ctrlName.raw(),
13322 lPort,
13323 lDevice);
13324
13325 {
13326 AutoCaller autoAttachCaller(this);
13327 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13328
13329 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13330 if (!oldmedium.isNull())
13331 oldmedium->i_removeBackReference(mData->mUuid);
13332
13333 pAttach->i_updateMedium(NULL);
13334 pAttach->i_updateEjected();
13335 }
13336
13337 i_setModified(IsModified_Storage);
13338 }
13339 else
13340 {
13341 {
13342 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13343 pAttach->i_updateEjected();
13344 }
13345 }
13346
13347 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13348
13349 return S_OK;
13350}
13351
13352// public methods only for internal purposes
13353/////////////////////////////////////////////////////////////////////////////
13354
13355#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13356/**
13357 * Called from the client watcher thread to check for expected or unexpected
13358 * death of the client process that has a direct session to this machine.
13359 *
13360 * On Win32 and on OS/2, this method is called only when we've got the
13361 * mutex (i.e. the client has either died or terminated normally) so it always
13362 * returns @c true (the client is terminated, the session machine is
13363 * uninitialized).
13364 *
13365 * On other platforms, the method returns @c true if the client process has
13366 * terminated normally or abnormally and the session machine was uninitialized,
13367 * and @c false if the client process is still alive.
13368 *
13369 * @note Locks this object for writing.
13370 */
13371bool SessionMachine::i_checkForDeath()
13372{
13373 Uninit::Reason reason;
13374 bool terminated = false;
13375
13376 /* Enclose autoCaller with a block because calling uninit() from under it
13377 * will deadlock. */
13378 {
13379 AutoCaller autoCaller(this);
13380 if (!autoCaller.isOk())
13381 {
13382 /* return true if not ready, to cause the client watcher to exclude
13383 * the corresponding session from watching */
13384 LogFlowThisFunc(("Already uninitialized!\n"));
13385 return true;
13386 }
13387
13388 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13389
13390 /* Determine the reason of death: if the session state is Closing here,
13391 * everything is fine. Otherwise it means that the client did not call
13392 * OnSessionEnd() before it released the IPC semaphore. This may happen
13393 * either because the client process has abnormally terminated, or
13394 * because it simply forgot to call ISession::Close() before exiting. We
13395 * threat the latter also as an abnormal termination (see
13396 * Session::uninit() for details). */
13397 reason = mData->mSession.mState == SessionState_Unlocking ?
13398 Uninit::Normal :
13399 Uninit::Abnormal;
13400
13401 if (mClientToken)
13402 terminated = mClientToken->release();
13403 } /* AutoCaller block */
13404
13405 if (terminated)
13406 uninit(reason);
13407
13408 return terminated;
13409}
13410
13411void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13412{
13413 LogFlowThisFunc(("\n"));
13414
13415 strTokenId.setNull();
13416
13417 AutoCaller autoCaller(this);
13418 AssertComRCReturnVoid(autoCaller.rc());
13419
13420 Assert(mClientToken);
13421 if (mClientToken)
13422 mClientToken->getId(strTokenId);
13423}
13424#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13425IToken *SessionMachine::i_getToken()
13426{
13427 LogFlowThisFunc(("\n"));
13428
13429 AutoCaller autoCaller(this);
13430 AssertComRCReturn(autoCaller.rc(), NULL);
13431
13432 Assert(mClientToken);
13433 if (mClientToken)
13434 return mClientToken->getToken();
13435 else
13436 return NULL;
13437}
13438#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13439
13440Machine::ClientToken *SessionMachine::i_getClientToken()
13441{
13442 LogFlowThisFunc(("\n"));
13443
13444 AutoCaller autoCaller(this);
13445 AssertComRCReturn(autoCaller.rc(), NULL);
13446
13447 return mClientToken;
13448}
13449
13450
13451/**
13452 * @note Locks this object for reading.
13453 */
13454HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13455{
13456 LogFlowThisFunc(("\n"));
13457
13458 AutoCaller autoCaller(this);
13459 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13460
13461 ComPtr<IInternalSessionControl> directControl;
13462 {
13463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13464 directControl = mData->mSession.mDirectControl;
13465 }
13466
13467 /* ignore notifications sent after #OnSessionEnd() is called */
13468 if (!directControl)
13469 return S_OK;
13470
13471 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13472}
13473
13474/**
13475 * @note Locks this object for reading.
13476 */
13477HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13478 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13479 IN_BSTR aGuestIp, LONG aGuestPort)
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 * instead acting like callback we ask IVirtualBox deliver corresponding event
13497 */
13498
13499 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13500 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13501 return S_OK;
13502}
13503
13504/**
13505 * @note Locks this object for reading.
13506 */
13507HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13508{
13509 LogFlowThisFunc(("\n"));
13510
13511 AutoCaller autoCaller(this);
13512 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13513
13514 ComPtr<IInternalSessionControl> directControl;
13515 {
13516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13517 directControl = mData->mSession.mDirectControl;
13518 }
13519
13520 /* ignore notifications sent after #OnSessionEnd() is called */
13521 if (!directControl)
13522 return S_OK;
13523
13524 return directControl->OnSerialPortChange(serialPort);
13525}
13526
13527/**
13528 * @note Locks this object for reading.
13529 */
13530HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13531{
13532 LogFlowThisFunc(("\n"));
13533
13534 AutoCaller autoCaller(this);
13535 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13536
13537 ComPtr<IInternalSessionControl> directControl;
13538 {
13539 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13540 directControl = mData->mSession.mDirectControl;
13541 }
13542
13543 /* ignore notifications sent after #OnSessionEnd() is called */
13544 if (!directControl)
13545 return S_OK;
13546
13547 return directControl->OnParallelPortChange(parallelPort);
13548}
13549
13550/**
13551 * @note Locks this object for reading.
13552 */
13553HRESULT SessionMachine::i_onStorageControllerChange()
13554{
13555 LogFlowThisFunc(("\n"));
13556
13557 AutoCaller autoCaller(this);
13558 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13559
13560 ComPtr<IInternalSessionControl> directControl;
13561 {
13562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13563 directControl = mData->mSession.mDirectControl;
13564 }
13565
13566 /* ignore notifications sent after #OnSessionEnd() is called */
13567 if (!directControl)
13568 return S_OK;
13569
13570 return directControl->OnStorageControllerChange();
13571}
13572
13573/**
13574 * @note Locks this object for reading.
13575 */
13576HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13577{
13578 LogFlowThisFunc(("\n"));
13579
13580 AutoCaller autoCaller(this);
13581 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13582
13583 ComPtr<IInternalSessionControl> directControl;
13584 {
13585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13586 directControl = mData->mSession.mDirectControl;
13587 }
13588
13589 /* ignore notifications sent after #OnSessionEnd() is called */
13590 if (!directControl)
13591 return S_OK;
13592
13593 return directControl->OnMediumChange(aAttachment, aForce);
13594}
13595
13596/**
13597 * @note Locks this object for reading.
13598 */
13599HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13600{
13601 LogFlowThisFunc(("\n"));
13602
13603 AutoCaller autoCaller(this);
13604 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13605
13606 ComPtr<IInternalSessionControl> directControl;
13607 {
13608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13609 directControl = mData->mSession.mDirectControl;
13610 }
13611
13612 /* ignore notifications sent after #OnSessionEnd() is called */
13613 if (!directControl)
13614 return S_OK;
13615
13616 return directControl->OnCPUChange(aCPU, aRemove);
13617}
13618
13619HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13620{
13621 LogFlowThisFunc(("\n"));
13622
13623 AutoCaller autoCaller(this);
13624 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13625
13626 ComPtr<IInternalSessionControl> directControl;
13627 {
13628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13629 directControl = mData->mSession.mDirectControl;
13630 }
13631
13632 /* ignore notifications sent after #OnSessionEnd() is called */
13633 if (!directControl)
13634 return S_OK;
13635
13636 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13637}
13638
13639/**
13640 * @note Locks this object for reading.
13641 */
13642HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13643{
13644 LogFlowThisFunc(("\n"));
13645
13646 AutoCaller autoCaller(this);
13647 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13648
13649 ComPtr<IInternalSessionControl> directControl;
13650 {
13651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13652 directControl = mData->mSession.mDirectControl;
13653 }
13654
13655 /* ignore notifications sent after #OnSessionEnd() is called */
13656 if (!directControl)
13657 return S_OK;
13658
13659 return directControl->OnVRDEServerChange(aRestart);
13660}
13661
13662/**
13663 * @note Locks this object for reading.
13664 */
13665HRESULT SessionMachine::i_onVideoCaptureChange()
13666{
13667 LogFlowThisFunc(("\n"));
13668
13669 AutoCaller autoCaller(this);
13670 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13671
13672 ComPtr<IInternalSessionControl> directControl;
13673 {
13674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13675 directControl = mData->mSession.mDirectControl;
13676 }
13677
13678 /* ignore notifications sent after #OnSessionEnd() is called */
13679 if (!directControl)
13680 return S_OK;
13681
13682 return directControl->OnVideoCaptureChange();
13683}
13684
13685/**
13686 * @note Locks this object for reading.
13687 */
13688HRESULT SessionMachine::i_onUSBControllerChange()
13689{
13690 LogFlowThisFunc(("\n"));
13691
13692 AutoCaller autoCaller(this);
13693 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13694
13695 ComPtr<IInternalSessionControl> directControl;
13696 {
13697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13698 directControl = mData->mSession.mDirectControl;
13699 }
13700
13701 /* ignore notifications sent after #OnSessionEnd() is called */
13702 if (!directControl)
13703 return S_OK;
13704
13705 return directControl->OnUSBControllerChange();
13706}
13707
13708/**
13709 * @note Locks this object for reading.
13710 */
13711HRESULT SessionMachine::i_onSharedFolderChange()
13712{
13713 LogFlowThisFunc(("\n"));
13714
13715 AutoCaller autoCaller(this);
13716 AssertComRCReturnRC(autoCaller.rc());
13717
13718 ComPtr<IInternalSessionControl> directControl;
13719 {
13720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13721 directControl = mData->mSession.mDirectControl;
13722 }
13723
13724 /* ignore notifications sent after #OnSessionEnd() is called */
13725 if (!directControl)
13726 return S_OK;
13727
13728 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13729}
13730
13731/**
13732 * @note Locks this object for reading.
13733 */
13734HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13735{
13736 LogFlowThisFunc(("\n"));
13737
13738 AutoCaller autoCaller(this);
13739 AssertComRCReturnRC(autoCaller.rc());
13740
13741 ComPtr<IInternalSessionControl> directControl;
13742 {
13743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13744 directControl = mData->mSession.mDirectControl;
13745 }
13746
13747 /* ignore notifications sent after #OnSessionEnd() is called */
13748 if (!directControl)
13749 return S_OK;
13750
13751 return directControl->OnClipboardModeChange(aClipboardMode);
13752}
13753
13754/**
13755 * @note Locks this object for reading.
13756 */
13757HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13758{
13759 LogFlowThisFunc(("\n"));
13760
13761 AutoCaller autoCaller(this);
13762 AssertComRCReturnRC(autoCaller.rc());
13763
13764 ComPtr<IInternalSessionControl> directControl;
13765 {
13766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13767 directControl = mData->mSession.mDirectControl;
13768 }
13769
13770 /* ignore notifications sent after #OnSessionEnd() is called */
13771 if (!directControl)
13772 return S_OK;
13773
13774 return directControl->OnDnDModeChange(aDnDMode);
13775}
13776
13777/**
13778 * @note Locks this object for reading.
13779 */
13780HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13781{
13782 LogFlowThisFunc(("\n"));
13783
13784 AutoCaller autoCaller(this);
13785 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13786
13787 ComPtr<IInternalSessionControl> directControl;
13788 {
13789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13790 directControl = mData->mSession.mDirectControl;
13791 }
13792
13793 /* ignore notifications sent after #OnSessionEnd() is called */
13794 if (!directControl)
13795 return S_OK;
13796
13797 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13798}
13799
13800/**
13801 * @note Locks this object for reading.
13802 */
13803HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13804{
13805 LogFlowThisFunc(("\n"));
13806
13807 AutoCaller autoCaller(this);
13808 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13809
13810 ComPtr<IInternalSessionControl> directControl;
13811 {
13812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13813 directControl = mData->mSession.mDirectControl;
13814 }
13815
13816 /* ignore notifications sent after #OnSessionEnd() is called */
13817 if (!directControl)
13818 return S_OK;
13819
13820 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13821}
13822
13823/**
13824 * Returns @c true if this machine's USB controller reports it has a matching
13825 * filter for the given USB device and @c false otherwise.
13826 *
13827 * @note locks this object for reading.
13828 */
13829bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13830{
13831 AutoCaller autoCaller(this);
13832 /* silently return if not ready -- this method may be called after the
13833 * direct machine session has been called */
13834 if (!autoCaller.isOk())
13835 return false;
13836
13837#ifdef VBOX_WITH_USB
13838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13839
13840 switch (mData->mMachineState)
13841 {
13842 case MachineState_Starting:
13843 case MachineState_Restoring:
13844 case MachineState_TeleportingIn:
13845 case MachineState_Paused:
13846 case MachineState_Running:
13847 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13848 * elsewhere... */
13849 alock.release();
13850 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13851 default: break;
13852 }
13853#else
13854 NOREF(aDevice);
13855 NOREF(aMaskedIfs);
13856#endif
13857 return false;
13858}
13859
13860/**
13861 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13862 */
13863HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13864 IVirtualBoxErrorInfo *aError,
13865 ULONG aMaskedIfs,
13866 const com::Utf8Str &aCaptureFilename)
13867{
13868 LogFlowThisFunc(("\n"));
13869
13870 AutoCaller autoCaller(this);
13871
13872 /* This notification may happen after the machine object has been
13873 * uninitialized (the session was closed), so don't assert. */
13874 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13875
13876 ComPtr<IInternalSessionControl> directControl;
13877 {
13878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13879 directControl = mData->mSession.mDirectControl;
13880 }
13881
13882 /* fail on notifications sent after #OnSessionEnd() is called, it is
13883 * expected by the caller */
13884 if (!directControl)
13885 return E_FAIL;
13886
13887 /* No locks should be held at this point. */
13888 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13889 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13890
13891 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
13892}
13893
13894/**
13895 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13896 */
13897HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13898 IVirtualBoxErrorInfo *aError)
13899{
13900 LogFlowThisFunc(("\n"));
13901
13902 AutoCaller autoCaller(this);
13903
13904 /* This notification may happen after the machine object has been
13905 * uninitialized (the session was closed), so don't assert. */
13906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13907
13908 ComPtr<IInternalSessionControl> directControl;
13909 {
13910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13911 directControl = mData->mSession.mDirectControl;
13912 }
13913
13914 /* fail on notifications sent after #OnSessionEnd() is called, it is
13915 * expected by the caller */
13916 if (!directControl)
13917 return E_FAIL;
13918
13919 /* No locks should be held at this point. */
13920 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13921 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13922
13923 return directControl->OnUSBDeviceDetach(aId, aError);
13924}
13925
13926// protected methods
13927/////////////////////////////////////////////////////////////////////////////
13928
13929/**
13930 * Helper method to finalize saving the state.
13931 *
13932 * @note Must be called from under this object's lock.
13933 *
13934 * @param aRc S_OK if the snapshot has been taken successfully
13935 * @param aErrMsg human readable error message for failure
13936 *
13937 * @note Locks mParent + this objects for writing.
13938 */
13939HRESULT SessionMachine::i_endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13940{
13941 LogFlowThisFuncEnter();
13942
13943 AutoCaller autoCaller(this);
13944 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13945
13946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13947
13948 HRESULT rc = S_OK;
13949
13950 if (SUCCEEDED(aRc))
13951 {
13952 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13953
13954 /* save all VM settings */
13955 rc = i_saveSettings(NULL);
13956 // no need to check whether VirtualBox.xml needs saving also since
13957 // we can't have a name change pending at this point
13958 }
13959 else
13960 {
13961 // delete the saved state file (it might have been already created);
13962 // we need not check whether this is shared with a snapshot here because
13963 // we certainly created this saved state file here anew
13964 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13965 }
13966
13967 /* notify the progress object about operation completion */
13968 Assert(mConsoleTaskData.mProgress);
13969 if (SUCCEEDED(aRc))
13970 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13971 else
13972 {
13973 if (aErrMsg.length())
13974 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13975 COM_IIDOF(ISession),
13976 getComponentName(),
13977 aErrMsg.c_str());
13978 else
13979 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
13980 }
13981
13982 /* clear out the temporary saved state data */
13983 mConsoleTaskData.mLastState = MachineState_Null;
13984 mConsoleTaskData.strStateFilePath.setNull();
13985 mConsoleTaskData.mProgress.setNull();
13986
13987 LogFlowThisFuncLeave();
13988 return rc;
13989}
13990
13991/**
13992 * Deletes the given file if it is no longer in use by either the current machine state
13993 * (if the machine is "saved") or any of the machine's snapshots.
13994 *
13995 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13996 * but is different for each SnapshotMachine. When calling this, the order of calling this
13997 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13998 * is therefore critical. I know, it's all rather messy.
13999 *
14000 * @param strStateFile
14001 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14002 * the test for whether the saved state file is in use.
14003 */
14004void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14005 Snapshot *pSnapshotToIgnore)
14006{
14007 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14008 if ( (strStateFile.isNotEmpty())
14009 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14010 )
14011 // ... and it must also not be shared with other snapshots
14012 if ( !mData->mFirstSnapshot
14013 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14014 // this checks the SnapshotMachine's state file paths
14015 )
14016 RTFileDelete(strStateFile.c_str());
14017}
14018
14019/**
14020 * Locks the attached media.
14021 *
14022 * All attached hard disks are locked for writing and DVD/floppy are locked for
14023 * reading. Parents of attached hard disks (if any) are locked for reading.
14024 *
14025 * This method also performs accessibility check of all media it locks: if some
14026 * media is inaccessible, the method will return a failure and a bunch of
14027 * extended error info objects per each inaccessible medium.
14028 *
14029 * Note that this method is atomic: if it returns a success, all media are
14030 * locked as described above; on failure no media is locked at all (all
14031 * succeeded individual locks will be undone).
14032 *
14033 * The caller is responsible for doing the necessary state sanity checks.
14034 *
14035 * The locks made by this method must be undone by calling #unlockMedia() when
14036 * no more needed.
14037 */
14038HRESULT SessionMachine::i_lockMedia()
14039{
14040 AutoCaller autoCaller(this);
14041 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14042
14043 AutoMultiWriteLock2 alock(this->lockHandle(),
14044 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14045
14046 /* bail out if trying to lock things with already set up locking */
14047 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14048
14049 MultiResult mrc(S_OK);
14050
14051 /* Collect locking information for all medium objects attached to the VM. */
14052 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14053 it != mMediaData->mAttachments.end();
14054 ++it)
14055 {
14056 MediumAttachment* pAtt = *it;
14057 DeviceType_T devType = pAtt->i_getType();
14058 Medium *pMedium = pAtt->i_getMedium();
14059
14060 MediumLockList *pMediumLockList(new MediumLockList());
14061 // There can be attachments without a medium (floppy/dvd), and thus
14062 // it's impossible to create a medium lock list. It still makes sense
14063 // to have the empty medium lock list in the map in case a medium is
14064 // attached later.
14065 if (pMedium != NULL)
14066 {
14067 MediumType_T mediumType = pMedium->i_getType();
14068 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14069 || mediumType == MediumType_Shareable;
14070 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14071
14072 alock.release();
14073 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14074 !fIsReadOnlyLock /* fMediumLockWrite */,
14075 false /* fMediumLockWriteAll */,
14076 NULL,
14077 *pMediumLockList);
14078 alock.acquire();
14079 if (FAILED(mrc))
14080 {
14081 delete pMediumLockList;
14082 mData->mSession.mLockedMedia.Clear();
14083 break;
14084 }
14085 }
14086
14087 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14088 if (FAILED(rc))
14089 {
14090 mData->mSession.mLockedMedia.Clear();
14091 mrc = setError(rc,
14092 tr("Collecting locking information for all attached media failed"));
14093 break;
14094 }
14095 }
14096
14097 if (SUCCEEDED(mrc))
14098 {
14099 /* Now lock all media. If this fails, nothing is locked. */
14100 alock.release();
14101 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14102 alock.acquire();
14103 if (FAILED(rc))
14104 {
14105 mrc = setError(rc,
14106 tr("Locking of attached media failed"));
14107 }
14108 }
14109
14110 return mrc;
14111}
14112
14113/**
14114 * Undoes the locks made by by #lockMedia().
14115 */
14116HRESULT SessionMachine::i_unlockMedia()
14117{
14118 AutoCaller autoCaller(this);
14119 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14120
14121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14122
14123 /* we may be holding important error info on the current thread;
14124 * preserve it */
14125 ErrorInfoKeeper eik;
14126
14127 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14128 AssertComRC(rc);
14129 return rc;
14130}
14131
14132/**
14133 * Helper to change the machine state (reimplementation).
14134 *
14135 * @note Locks this object for writing.
14136 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14137 * it can cause crashes in random places due to unexpectedly committing
14138 * the current settings. The caller is responsible for that. The call
14139 * to saveStateSettings is fine, because this method does not commit.
14140 */
14141HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14142{
14143 LogFlowThisFuncEnter();
14144 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14145
14146 AutoCaller autoCaller(this);
14147 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14148
14149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14150
14151 MachineState_T oldMachineState = mData->mMachineState;
14152
14153 AssertMsgReturn(oldMachineState != aMachineState,
14154 ("oldMachineState=%s, aMachineState=%s\n",
14155 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14156 E_FAIL);
14157
14158 HRESULT rc = S_OK;
14159
14160 int stsFlags = 0;
14161 bool deleteSavedState = false;
14162
14163 /* detect some state transitions */
14164
14165 if ( ( oldMachineState == MachineState_Saved
14166 && aMachineState == MachineState_Restoring)
14167 || ( ( oldMachineState == MachineState_PoweredOff
14168 || oldMachineState == MachineState_Teleported
14169 || oldMachineState == MachineState_Aborted
14170 )
14171 && ( aMachineState == MachineState_TeleportingIn
14172 || aMachineState == MachineState_Starting
14173 )
14174 )
14175 )
14176 {
14177 /* The EMT thread is about to start */
14178
14179 /* Nothing to do here for now... */
14180
14181 /// @todo NEWMEDIA don't let mDVDDrive and other children
14182 /// change anything when in the Starting/Restoring state
14183 }
14184 else if ( ( oldMachineState == MachineState_Running
14185 || oldMachineState == MachineState_Paused
14186 || oldMachineState == MachineState_Teleporting
14187 || oldMachineState == MachineState_LiveSnapshotting
14188 || oldMachineState == MachineState_Stuck
14189 || oldMachineState == MachineState_Starting
14190 || oldMachineState == MachineState_Stopping
14191 || oldMachineState == MachineState_Saving
14192 || oldMachineState == MachineState_Restoring
14193 || oldMachineState == MachineState_TeleportingPausedVM
14194 || oldMachineState == MachineState_TeleportingIn
14195 )
14196 && ( aMachineState == MachineState_PoweredOff
14197 || aMachineState == MachineState_Saved
14198 || aMachineState == MachineState_Teleported
14199 || aMachineState == MachineState_Aborted
14200 )
14201 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14202 * snapshot */
14203 && ( mConsoleTaskData.mSnapshot.isNull()
14204 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14205 )
14206 )
14207 {
14208 /* The EMT thread has just stopped, unlock attached media. Note that as
14209 * opposed to locking that is done from Console, we do unlocking here
14210 * because the VM process may have aborted before having a chance to
14211 * properly unlock all media it locked. */
14212
14213 unlockMedia();
14214 }
14215
14216 if (oldMachineState == MachineState_Restoring)
14217 {
14218 if (aMachineState != MachineState_Saved)
14219 {
14220 /*
14221 * delete the saved state file once the machine has finished
14222 * restoring from it (note that Console sets the state from
14223 * Restoring to Saved if the VM couldn't restore successfully,
14224 * to give the user an ability to fix an error and retry --
14225 * we keep the saved state file in this case)
14226 */
14227 deleteSavedState = true;
14228 }
14229 }
14230 else if ( oldMachineState == MachineState_Saved
14231 && ( aMachineState == MachineState_PoweredOff
14232 || aMachineState == MachineState_Aborted
14233 || aMachineState == MachineState_Teleported
14234 )
14235 )
14236 {
14237 /*
14238 * delete the saved state after Console::ForgetSavedState() is called
14239 * or if the VM process (owning a direct VM session) crashed while the
14240 * VM was Saved
14241 */
14242
14243 /// @todo (dmik)
14244 // Not sure that deleting the saved state file just because of the
14245 // client death before it attempted to restore the VM is a good
14246 // thing. But when it crashes we need to go to the Aborted state
14247 // which cannot have the saved state file associated... The only
14248 // way to fix this is to make the Aborted condition not a VM state
14249 // but a bool flag: i.e., when a crash occurs, set it to true and
14250 // change the state to PoweredOff or Saved depending on the
14251 // saved state presence.
14252
14253 deleteSavedState = true;
14254 mData->mCurrentStateModified = TRUE;
14255 stsFlags |= SaveSTS_CurStateModified;
14256 }
14257
14258 if ( aMachineState == MachineState_Starting
14259 || aMachineState == MachineState_Restoring
14260 || aMachineState == MachineState_TeleportingIn
14261 )
14262 {
14263 /* set the current state modified flag to indicate that the current
14264 * state is no more identical to the state in the
14265 * current snapshot */
14266 if (!mData->mCurrentSnapshot.isNull())
14267 {
14268 mData->mCurrentStateModified = TRUE;
14269 stsFlags |= SaveSTS_CurStateModified;
14270 }
14271 }
14272
14273 if (deleteSavedState)
14274 {
14275 if (mRemoveSavedState)
14276 {
14277 Assert(!mSSData->strStateFilePath.isEmpty());
14278
14279 // it is safe to delete the saved state file if ...
14280 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14281 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14282 // ... none of the snapshots share the saved state file
14283 )
14284 RTFileDelete(mSSData->strStateFilePath.c_str());
14285 }
14286
14287 mSSData->strStateFilePath.setNull();
14288 stsFlags |= SaveSTS_StateFilePath;
14289 }
14290
14291 /* redirect to the underlying peer machine */
14292 mPeer->i_setMachineState(aMachineState);
14293
14294 if ( aMachineState == MachineState_PoweredOff
14295 || aMachineState == MachineState_Teleported
14296 || aMachineState == MachineState_Aborted
14297 || aMachineState == MachineState_Saved)
14298 {
14299 /* the machine has stopped execution
14300 * (or the saved state file was adopted) */
14301 stsFlags |= SaveSTS_StateTimeStamp;
14302 }
14303
14304 if ( ( oldMachineState == MachineState_PoweredOff
14305 || oldMachineState == MachineState_Aborted
14306 || oldMachineState == MachineState_Teleported
14307 )
14308 && aMachineState == MachineState_Saved)
14309 {
14310 /* the saved state file was adopted */
14311 Assert(!mSSData->strStateFilePath.isEmpty());
14312 stsFlags |= SaveSTS_StateFilePath;
14313 }
14314
14315#ifdef VBOX_WITH_GUEST_PROPS
14316 if ( aMachineState == MachineState_PoweredOff
14317 || aMachineState == MachineState_Aborted
14318 || aMachineState == MachineState_Teleported)
14319 {
14320 /* Make sure any transient guest properties get removed from the
14321 * property store on shutdown. */
14322 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14323
14324 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14325 settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14326 while (it != llGuestProperties.end())
14327 {
14328 const settings::GuestProperty &prop = *it;
14329 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14330 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14331 {
14332 it = llGuestProperties.erase(it);
14333 fNeedsSaving = true;
14334 }
14335 else
14336 {
14337 ++it;
14338 }
14339 }
14340
14341 if (fNeedsSaving)
14342 {
14343 mData->mCurrentStateModified = TRUE;
14344 stsFlags |= SaveSTS_CurStateModified;
14345 }
14346 }
14347#endif /* VBOX_WITH_GUEST_PROPS */
14348
14349 rc = i_saveStateSettings(stsFlags);
14350
14351 if ( ( oldMachineState != MachineState_PoweredOff
14352 && oldMachineState != MachineState_Aborted
14353 && oldMachineState != MachineState_Teleported
14354 )
14355 && ( aMachineState == MachineState_PoweredOff
14356 || aMachineState == MachineState_Aborted
14357 || aMachineState == MachineState_Teleported
14358 )
14359 )
14360 {
14361 /* we've been shut down for any reason */
14362 /* no special action so far */
14363 }
14364
14365 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14366 LogFlowThisFuncLeave();
14367 return rc;
14368}
14369
14370/**
14371 * Sends the current machine state value to the VM process.
14372 *
14373 * @note Locks this object for reading, then calls a client process.
14374 */
14375HRESULT SessionMachine::i_updateMachineStateOnClient()
14376{
14377 AutoCaller autoCaller(this);
14378 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14379
14380 ComPtr<IInternalSessionControl> directControl;
14381 {
14382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14383 AssertReturn(!!mData, E_FAIL);
14384 directControl = mData->mSession.mDirectControl;
14385
14386 /* directControl may be already set to NULL here in #OnSessionEnd()
14387 * called too early by the direct session process while there is still
14388 * some operation (like deleting the snapshot) in progress. The client
14389 * process in this case is waiting inside Session::close() for the
14390 * "end session" process object to complete, while #uninit() called by
14391 * #checkForDeath() on the Watcher thread is waiting for the pending
14392 * operation to complete. For now, we accept this inconsistent behavior
14393 * and simply do nothing here. */
14394
14395 if (mData->mSession.mState == SessionState_Unlocking)
14396 return S_OK;
14397
14398 AssertReturn(!directControl.isNull(), E_FAIL);
14399 }
14400
14401 return directControl->UpdateMachineState(mData->mMachineState);
14402}
14403
14404HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14405{
14406 NOREF(aRemove);
14407 ReturnComNotImplemented();
14408}
14409
14410HRESULT Machine::updateState(MachineState_T aState)
14411{
14412 NOREF(aState);
14413 ReturnComNotImplemented();
14414}
14415
14416HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14417{
14418 NOREF(aProgress);
14419 ReturnComNotImplemented();
14420}
14421
14422HRESULT Machine::endPowerUp(LONG aResult)
14423{
14424 NOREF(aResult);
14425 ReturnComNotImplemented();
14426}
14427
14428HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14429{
14430 NOREF(aProgress);
14431 ReturnComNotImplemented();
14432}
14433
14434HRESULT Machine::endPoweringDown(LONG aResult,
14435 const com::Utf8Str &aErrMsg)
14436{
14437 NOREF(aResult);
14438 NOREF(aErrMsg);
14439 ReturnComNotImplemented();
14440}
14441
14442HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14443 BOOL *aMatched,
14444 ULONG *aMaskedInterfaces)
14445{
14446 NOREF(aDevice);
14447 NOREF(aMatched);
14448 NOREF(aMaskedInterfaces);
14449 ReturnComNotImplemented();
14450
14451}
14452
14453HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14454{
14455 NOREF(aId); NOREF(aCaptureFilename);
14456 ReturnComNotImplemented();
14457}
14458
14459HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14460 BOOL aDone)
14461{
14462 NOREF(aId);
14463 NOREF(aDone);
14464 ReturnComNotImplemented();
14465}
14466
14467HRESULT Machine::autoCaptureUSBDevices()
14468{
14469 ReturnComNotImplemented();
14470}
14471
14472HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14473{
14474 NOREF(aDone);
14475 ReturnComNotImplemented();
14476}
14477
14478HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14479 ComPtr<IProgress> &aProgress)
14480{
14481 NOREF(aSession);
14482 NOREF(aProgress);
14483 ReturnComNotImplemented();
14484}
14485
14486HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14487 com::Utf8Str &aStateFilePath)
14488{
14489 NOREF(aProgress);
14490 NOREF(aStateFilePath);
14491 ReturnComNotImplemented();
14492}
14493
14494HRESULT Machine::endSavingState(LONG aResult,
14495 const com::Utf8Str &aErrMsg)
14496{
14497 NOREF(aResult);
14498 NOREF(aErrMsg);
14499 ReturnComNotImplemented();
14500}
14501
14502HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14503{
14504 NOREF(aSavedStateFile);
14505 ReturnComNotImplemented();
14506}
14507
14508HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14509 const com::Utf8Str &aName,
14510 const com::Utf8Str &aDescription,
14511 const ComPtr<IProgress> &aConsoleProgress,
14512 BOOL aFTakingSnapshotOnline,
14513 com::Utf8Str &aStateFilePath)
14514{
14515 NOREF(aInitiator);
14516 NOREF(aName);
14517 NOREF(aDescription);
14518 NOREF(aConsoleProgress);
14519 NOREF(aFTakingSnapshotOnline);
14520 NOREF(aStateFilePath);
14521 ReturnComNotImplemented();
14522}
14523
14524HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14525{
14526 NOREF(aSuccess);
14527 ReturnComNotImplemented();
14528}
14529
14530HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14531 const com::Guid &aStartId,
14532 const com::Guid &aEndId,
14533 BOOL aDeleteAllChildren,
14534 MachineState_T *aMachineState,
14535 ComPtr<IProgress> &aProgress)
14536{
14537 NOREF(aInitiator);
14538 NOREF(aStartId);
14539 NOREF(aEndId);
14540 NOREF(aDeleteAllChildren);
14541 NOREF(aMachineState);
14542 NOREF(aProgress);
14543 ReturnComNotImplemented();
14544}
14545
14546HRESULT Machine::finishOnlineMergeMedium()
14547{
14548 ReturnComNotImplemented();
14549}
14550
14551HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14552 const ComPtr<ISnapshot> &aSnapshot,
14553 MachineState_T *aMachineState,
14554 ComPtr<IProgress> &aProgress)
14555{
14556 NOREF(aInitiator);
14557 NOREF(aSnapshot);
14558 NOREF(aMachineState);
14559 NOREF(aProgress);
14560 ReturnComNotImplemented();
14561}
14562
14563HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14564 std::vector<com::Utf8Str> &aValues,
14565 std::vector<LONG64> &aTimestamps,
14566 std::vector<com::Utf8Str> &aFlags)
14567{
14568 NOREF(aNames);
14569 NOREF(aValues);
14570 NOREF(aTimestamps);
14571 NOREF(aFlags);
14572 ReturnComNotImplemented();
14573}
14574
14575HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14576 const com::Utf8Str &aValue,
14577 LONG64 aTimestamp,
14578 const com::Utf8Str &aFlags)
14579{
14580 NOREF(aName);
14581 NOREF(aValue);
14582 NOREF(aTimestamp);
14583 NOREF(aFlags);
14584 ReturnComNotImplemented();
14585}
14586
14587HRESULT Machine::lockMedia()
14588{
14589 ReturnComNotImplemented();
14590}
14591
14592HRESULT Machine::unlockMedia()
14593{
14594 ReturnComNotImplemented();
14595}
14596
14597HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14598 ComPtr<IMediumAttachment> &aNewAttachment)
14599{
14600 NOREF(aAttachment);
14601 NOREF(aNewAttachment);
14602 ReturnComNotImplemented();
14603}
14604
14605HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14606 ULONG aCpuUser,
14607 ULONG aCpuKernel,
14608 ULONG aCpuIdle,
14609 ULONG aMemTotal,
14610 ULONG aMemFree,
14611 ULONG aMemBalloon,
14612 ULONG aMemShared,
14613 ULONG aMemCache,
14614 ULONG aPagedTotal,
14615 ULONG aMemAllocTotal,
14616 ULONG aMemFreeTotal,
14617 ULONG aMemBalloonTotal,
14618 ULONG aMemSharedTotal,
14619 ULONG aVmNetRx,
14620 ULONG aVmNetTx)
14621{
14622 NOREF(aValidStats);
14623 NOREF(aCpuUser);
14624 NOREF(aCpuKernel);
14625 NOREF(aCpuIdle);
14626 NOREF(aMemTotal);
14627 NOREF(aMemFree);
14628 NOREF(aMemBalloon);
14629 NOREF(aMemShared);
14630 NOREF(aMemCache);
14631 NOREF(aPagedTotal);
14632 NOREF(aMemAllocTotal);
14633 NOREF(aMemFreeTotal);
14634 NOREF(aMemBalloonTotal);
14635 NOREF(aMemSharedTotal);
14636 NOREF(aVmNetRx);
14637 NOREF(aVmNetTx);
14638 ReturnComNotImplemented();
14639}
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